ايجاد المسار في محرك Godot 4 (نظام الملاحة)

Godot

المِلاحة هو علم وتقنية توجيه السفن أو الطائرات من مكان لآخر وتحديد موقعها. في محركات الألعاب يوجد نفس ذلك النظام المسمى بالملاحة، والذي يهدف الى إيجاد مسار بين نقطتين دون المرور بالعوائق.

في محرك الألعاب godot يوجد ذلك النظام أيضا في عقدة باسم Navigation -اي ملاحة- منها النسخة ثلاثية الأبعاد والنسخة ثنائية الأبعاد. ولكن اركز في مقالي هذا الاولى منهم.

يمكن استخدام نظام المِلاحة في تحديد حركة الكائنات، مثل لحاق الأعداء باللاعب وإيجاده، أو حتى تحريك اللاعب نفسه إلى نقطة ما تلقائيا بالضغط عليها، 

كالعادة يجب شرح هذا النظام اولا في بُعد ثنائي اولا (2D) حتى يتضح فكرته وكيفية التعامل معه، بعد ذلك يمكن الانتقال الى البُعد الثلاثي فيما بعد. كما اني اشرح الاصدار الرابع من المحرك لانه افضل في هذا الشأن

تجهيز المشروع ونظام الملاحة

أبدء بإنشاء مشروع جديد في محرك غودو، فيه عقدة Node اساسية، بالاضافة الى كائن الشخصية (Character body) بدون أي شيفرة (script) حاليا.

وبعيدا عن العقد الاساسية مثل الصورة (sprite) والتصادم (collection) أيضا اضف عقدة عميل المِلاحة (NavigationAgent2D) ويأتي شرحها فيما بعد في فقرة أخرى من المقال.

بالنسبة لي اضفت عقدة الخط (Line2D)، والتي استعملها فقط لتوضيح المسار أثناء الشرح، ولكن لا حاجة لك بها أن لم ترد ذلك.

اخيرا اضف عقدة المِلاحة (Navigation Region 2D)، وتقنيتها تعتمد على إنشاء مخطط محدد، والتي منها يمكن إيجاد مسار بين نقطتين فيها، أي أن حددت نقطتين بداخل تلك المساحة، فان العقدة لها القدرة على إيجاد أقصر مسار بينهما.

لذا يمكنك الآن الضغط على عقدة الملاحة، وفي خصائصها اضغط على مضلعات المِلاحة (Navigation polygon)، ثم أنشأ واحدة جديدة، الان يمكنك رسم مضلعات في عالم اللعبة وهي المساحة المطلوب التعامل معها.

أن اردت التعديل عليها، فيوجد في شريط الأدوات ما تحتاجه من مسح وانشاء وتعديل لرؤوس المضلعات، الان تاكد من وضع كائن الشخصية داخل المخطط، فحاليا يجب كتابة تعليمات اللاعب لإيجاد المسار.

إيجاد مسار للشخصية

قم بإنشاء شيفرة (script) للاعب، حيث التعليمات (code) ستكون مبنية على 3 أشياء، وهي كالتالي:

  1. النقطة الهدف، وهي ما يجب على اللاعب الذهاب إليها، وذلك باستخدام الفأرة 
  2. إيجاد المسار بين مكان اللاعب والنقطة الهدف، وهذا دور عُقد الملاحة
  3. حركة اللاعب من مكانه الى النقطة المطلوبة على المسار الموضوع.

هكذا يحدد المستخدم نقطة ما، فيبدء نظام المِلاحة في ايجاد اقرب مسار من اللاعب وحتى تلك النقطة، واخيرا يتحرك اللاعب على ذلك المسار للوصول إلى النقطة المطلوبة.

ولا تقلق، في نهاية المقال تجد التعليمات مكتوبة كاملة وظاهرة بتعليقات توضح فائدة كل سطر 🙂

النقطة الهدف

أضف رمزا جديدا في خريطة المدخلات Input map في اعدادات المحرك، وهو رمز خاص بالفأرة، أي عندما يضغط المستخدم بزر الفأرة فإن ذلك الرمز يُفعل، وهكذا تستطيع معرفة إذا ما ضغط المستخدم بالفأرة ام لا.

في تعليمات اللاعب، وفي دالة المعالجة _process، اكتب تعليميتين، الأولى هو تعريف متغير باسم هدف Target وله قيمة متجه ثنائي Vector2، والتعليمة الثانية هو شرط للتأكد اذا ما ضغط المستخدم على الفأرة.

واخيرا بداخل الشرط نفسه، يمكنك كتابة التعليمة التي منها يُعرف مكان الفارة، ثم تعينها لمتغير الهدف، اي عندما يضغط المستخدم بزرة الفأرة في مكان ما، ففي تلك اللحظة يتم تسجيل مكانها في متغير الهدف.

func _process(delta): 
  var target = Vector2() # متغير نقطة الهدف 
  if Input.is_action_just_pressed("left_click"): # شرط التعرف على ضغطة المستخدم 
    target = get_global_mouse_position() 
    
    pass
  pass

إيجاد المسار

فائدة عقدة عميل المِلاحة (NavigationAgent2D) تكمن في تحديد النقطة الابتدائية ثم إيجاد المسار الى النقطة الهدف، أي بدلا من تحديد نقطتين، الان فقط يكفى تحديد النقطة النهائية، وهو ما قمت به بالفعل.

لذا بعد تعليمة إيجاد مكان الفأرة، يمكنك استعمال الأمر target_position الخاص بعقدة العميل وهكذا يتعرف العميل على النقطة النهائية ويبني المسار إليها.

ولكن لتحسين شكل التعليمات (code)، ففي بداية الشفرة (script)، قم بعمل متغير جديد باسم nav، وبه استعدي عقدة العميل، وذلك لحاجتها فيما بعد مجددا في مكان آخر.

حيث استعملها مع عقدة الخط (Line2D) لرؤية المسار من موقع العميل وحتى الهدف، وذلك باستدعاء امر get_current_navigation_path() والذي منه نحصل على المسار، واخيرا يُعين ذلك المسار لعقدة الخط حتى يظهر.

@onready var nav = $NavigationAgent2D # استدعاء عقدة الملاحة
func _process(delta):
	var target = Vector2()
	if Input.is_action_just_pressed("left_click"):
		target = get_global_mouse_position()
		nav.target_position = target # تعين النقطة الهدف للعميل حتى يبني المسار
		pass
	# استدعاء عقدة الخط وتعين المسار لها حتى يظهر للعين
	owner.get_node("Line2D").points = nav.get_current_navigation_path()

لقد غيرت بالفعل لون الخط إلى الأحمر، وان اردت رؤية مُخطط المِلاحة تلك في اللعبة، فيمكنك اظهارها عن طريق الضغط على زر الفحص (Debug)، الموجود في أعلى المحرك، ومنه حدد خيار إظهار المِلاحة (Visible Navigation).

اما الان فقد حان وقت تحريك اللاعب على ذلك المسار.

حركة اللاعب

قلت بالفعل أن عميل المِلاحة يكمن سره في إنشاء مسار من النقطة الاولى الى النقطة الهدف، وذلك المسار ما هو إلا مجموعة من النقاط.

فسواء قررت تحريك اللاعب أو تحريك عدو او اي كائن على ذلك المسار، فمهمتك هو إعطاء تلك النقطة في شكل متجه للكائن، ثم جعله يتحرك بسرعة محددة، لذا أول ما تكتبه هو متغير السرعة speed، وعين قيمته لـ 300.

هكذا عندما يصل اللاعب الى نقطة ما، نتخذ المتجه الذي بعده واعطيه اياه، لذا وجب الآن صناعة متغير باسم “اتجاه” direction وهو متغير من نوع متجه ثنائي Vector2، وهو ما سيحمل المتجه القادم للاعب.

extends CharacterBody2D

var direction = Vector2() # متغير الاتجاه القادم للاعب الذي يجب أن يتحرك له
var speed = 300 # متغير السرعة
@onready var nav = $NavigationAgent2D

قيمة متغير الاتجاه عبارة عن المعادلة (النقطة القادمة مطروحة من مكان اللاعب)، وللحصول على النقطة القادمة، يمكنك استعمال الأمر get_next_path_position() من عميل الملاحة، ثم مكان اللاعب العام global_position.

يأتي حاليا دور المتغير المدمج في عقدة الشخصية (CharacterBody2D) وهو متجه السرعة المتجهة velocity، وقيمته متغير “الاتجاه” ولكن بعد توحيده (أي جعل طوله واحد)، ثم ضرب المتجه في السرعة.

واخيرا استعمال الامر move_and_slide() حتى تتحرك الشخصية بالفعل.

func _process(delta):
	var target = Vector2()
	if Input.is_action_just_pressed("left_click"):
		target = get_global_mouse_position()
		nav.target_position = target
		pass
	owner.get_node("Line2D").points = nav.get_current_navigation_path()
	# الحصول على الاتجاه القادم للاعب (من نقطته وحتى القادمة)
	direction = nav.get_next_path_position() - global_position
	# تعيين السرعة المتجهة للاعب
	velocity = direction .normalized() * speed
	move_and_slide() # تحريك اللاعب باستخدام السرعة المتجهة

تحسين الحركة

تلاحظ الآن أن اللاعب يتحرك تماما على حافة المخطط، وتعديل ذلك سهل، فقط اضغط على عقدة العميل، وفي لوحة الخصائص، غير خاصية معالجة المسار (path postprocessing) إلى خيار “منتصف الحدود”.

مشكلة اخرى موجودة أيضا، وذلك أن قررت الضغط على مكان خارج مساحة المِلاحة تلك، فعندما يصل اللاعب الى النقطة الاخيرة، فقد يبدأ بالتحرك في عدة اتجاهات صغيرة، فيبدو كأنه يتراقص في مكانه !

تعديل ذلك بوضع شرط جديد يتاكد اذا ما وصل اللاعب إلى آخر نقطة في المسار، وإن وصل فلا يتحرك أكثر من ذلك، أي يتوقف تماما، ويوجد شرط بداخل العميل باسم is_navigation_finished()، يمكنك استعماله كشرط للتأكد أن اللاعب وصل للنقطة النهائية، وهكذا توقف حركته.

ليكون شكل الشفرة النهائية هكذا:

extends CharacterBody2D

var direction = Vector2()
var speed = 300
@onready var nav = $NavigationAgent2D

func _process(delta):
	var target = Vector2()
	if Input.is_action_just_pressed("left_click"):
		target = get_global_mouse_position()
		nav.target_position = target
		pass
	owner.get_node("Line2D").points = nav.get_current_navigation_path()
	
	direction = nav.get_next_path_position() - global_position
	
	velocity = direction .normalized() * speed
	if nav.is_navigation_finished():
		velocity = Vector2()

	move_and_slide()
	pass

استعمال TileMap

استعمال عقدة المِلاحة Navigation 2D به مصيبة كبيرة، وهو حاجة المخطط لأن يكون متصلاً، فلا يمكنك وضع فراغات في منتصف تلك المساحة بسهولة، بل يجب عليك رسمها بطريقة معقدة.

غير أن الرسم والتحديد في حد ذاته يأخذ وقتاً، فإن أفضل طريقة تكمن في استخدام عقدة خريطة القِطع (TileMap)، وقد وضحت من قبل كيفية تجهيزها واستخدامها.

مقدمة لكيفية استخدام الـtilemap في godot 4

ويمكن أيضا استخدام هذه العقدة كعقدة ملاحة، فقط اضفها الى المشهد وجهزها بصورة من القَطع المختلفة (او حتى قطعة واحدة فقط)، وفي خصائصها، اضغط على TileSet، وهناك تجد خيار طبقة المِلاحة (Navigation layer)، واضغط على الزر الوحيد الموجود الا وهو “إضافة عنصر” (Add Element).

في درس خريطة القِطع وضحت كيفية اضافة فيزياء/تصادم للقطع، هذه المرة يمكنك فعل نفس الأمر مع الملاحة، فقط حدد تلك القطع التي تريد من اللاعب أن يمشي عليها، ثم اختر خيار الملاحة، وأبدأ برسم حدودها.

الان يمكنك بناء المرحلة التي تريد، حيث القِطع التي وضعت لها المِلاحة هي فقط ما يمكن للاعب المشي عليها، واي قطعة اخرى لن يستطيع، ففي هذا المثال لم اضع اي ملاحة على الأشجار.

ملفات المشروع للتنزيل

ختاما اعرض لك ملفات المشروع لتنزيلها والتعلم منها، حتى يساعدك ذلك أن تعثرت في اثناء المقال

وفي النهاية أخبرني برايك، ما الذي يمكن أن يُستخدم فيه هذا النظام غير إيجاد المسار؟

0 0 votes
Article Rating
Subscribe
نبّهني عن
guest

0 تعليقات
Inline Feedbacks
View all comments