في الدرس السابق كتبنا اوامر الحركة الاساسية في الجهات الاربعة
وكان امر الحركة الى اليمين كهذا
var speed = 300
func _physics_process(delta):
# Move Right
if Input.is_action_pressed("ui_right"):
position.x += speed * delta
بالطبع يمكننا، لقد عرفنا ان Input.is_action_pressed("ui_right")
دالة ترجع لنا قيمة boolean
تكون true
اذا ضغط المستخدم على زر السهم الايمن في لوحة المفاتيح وfalse
ان لم يضغط، حسنا مادام انها قيمة boolean
فيمكننا تخزينها في متغير، شيء مثل هذا
var speed = 300
func _physics_process(delta):
var right := Input.is_action_pressed("ui_right")
# Move Right
if right:
position.x += speed * delta
خزنا القيمة في متغير يدعى right
ثم استخدمناهُ في الشرط، المتغير اصبح يصف وظيفته
قد تظن ان هذا الامر تافه في بادئ الأمر لكن دعنا نرى الاوامر مع باقي الاتجاهات
var speed = 300
func _physics_process(delta):
# Define the input
var right := Input.is_action_pressed("ui_right")
var left := Input.is_action_pressed("ui_left")
var up := Input.is_action_pressed("ui_up")
var down := Input.is_action_pressed("ui_down")
# Movement
if right:
position.x += speed * delta
if left:
position.x -= speed * delta
if up:
position.y -= speed * delta
if down:
position.y += speed * delta
أترى كيف اصبح الأمر منظم اكثر بتعريفنا للمتغيرات في البداية ثم استخدامها، وكل متغير يصف وظيفته
اصبحت اوامر الحركة يسهل قراءتها وتعديلها ومنظمة بشكل افضل، هذا سيحدث فارقا في المشاريع الكبيرة لذا اجعل عملك منظم من البداية بشكل دائم
يمكننا ان نجعل الحركة افضل من هذا، دعونا نرى المتغيرات التى تحدد الاتجاهات
var right := Input.is_action_pressed("ui_right")
var left := Input.is_action_pressed("ui_left")
var up := Input.is_action_pressed("ui_up")
var down := Input.is_action_pressed("ui_down")
حسنا نحن نعرف ان الاتجاه الى اليمين تكون قيمة ال x
موجبة واليسار تكون قيمة ال x
سالب
الان قيمة كل متغير ستكون true
اذا تم ضغط على الزر الذي يشير اليه و false
اذا لم يضغط
var right := Input.is_action_pressed("ui_right")
var left := Input.is_action_pressed("ui_left")
var directionX := int(right) - int(left)
حسنا هنا اذا تحرك اللاعب الى اليمين فقط، ستكون قيمة ال right
ب true
و قيمة ال left
ب false
فهكذا عملية الطرح ستكون هكذا
int(true) - int(false)
لكن هنا لا يمكننا عمل معادلات كالضرب والجمع والطرح على انواع boolean
فيجب تحويلها الى int
ثم نقوم بعملية الطرح
عند التحويل نضع القيمة داخل int(value)
هكذا قيمة ال value
ستتحول الى int
مهما كانت فستكون
int(true) = 1
و int(false) = 0
var directionX := int(right) - int(left)
# directionX = 1 - 0 is 1
بالتالي اذا تحرك اللاعب نحو اليمين ستكون قيمة directionX
تساوي 1
، واذا تحرك نحو اليسار ؟ ستكون 1-
var directionX := int(right) - int(left)
# directionX = 0 - 1 is -1
بالتالي اصبح لدينا متغير قيمتة تكون ب 1
حين يتحرك اللاعب لليمين و1-
حين يتحرك لليسار
وإذا لم يضغط على اي اتجاه ستكون قيمته ب 0
دعونا نرى كيف سيكون أمر الحركة الأن
قبل هذا، انوه اننا سنختصر الأمر الى هذا
var directionX := int(Input.is_action_pressed("ui_right")) - int(Input.is_action_pressed("ui_left"))
بدلًا من هذا
var right := Input.is_action_pressed("ui_right")
var left := Input.is_action_pressed("ui_left")
var directionX := int(right) - int(left)
حسنا الان سنكتب كيف سيكون أمر الحركة في اتجاه اليمين واليسار الان
var directionX := int(Input.is_action_pressed("ui_right")) - int(Input.is_action_pressed("ui_left"))
if directionX:
position.x += directionX * speed * delta
اصبح الان لدينا متغير واحد يحدد اتجاه الحركة في اليمين واليسار
بالطبع !
var directionY := int(Input.is_action_pressed("ui_down")) - int(Input.is_action_pressed("ui_up"))
if directionY:
position.y += directionY * speed * delta
اصبح لدينا متغير اخر وهو directionY
يحدد الاتجاه لأعلى ولأسفل
اذا كان قيمته 1
يكون الاتجاه لأسفل واذا كانت 1-
يكون الاتجاه لأعلى لان محور y
معكوس في محرك غودوت
سيكون أوامر الحركة كالأتي
var directionX := int(Input.is_action_pressed("ui_right")) - int(Input.is_action_pressed("ui_left"))
var directionY := int(Input.is_action_pressed("ui_down")) - int(Input.is_action_pressed("ui_up"))
if directionX:
position.x += directionX * speed * delta
if directionY:
position.y += directionY * speed * delta
حسنا انظر الأن لدينا متغيرين الاول يمثل اتجاه الحركة في محور ال x
والمتغير التاني يحدد اتجاه الحركة في محور y
ماذا لو دمجنا هذين رالمتغيرين في متغير واحدة يحدد اتجاه الحركة ؟
نستطيع فعل هذا باستخدام نوع البيانات Vector2
فنحن نعرف ان من خواصه انه يحتوي على متغيرين في داخله x, y
نستطيع الوصول لهما والتعديل عليهما وتخزين القيم فيهما
var speed = 500
var direction : Vector2
func _physics_process(delta):
direction.x = int(Input.is_action_pressed("ui_right")) - int(Input.is_action_pressed("ui_left"))
direction.y = int(Input.is_action_pressed("ui_down")) - int(Input.is_action_pressed("ui_up"))
if direction:
position += direction * speed * delta
لم يختلف الأمر كثيرة، حتى ان الشرط اصبح واحد
if direction
اي طالما ان هناك حركة اي طالما ان قيمتي ال x,y
في ال direction
كلايهما لا يساويان 0
نفذ الأمر التالي
position += direction * speed * delta
دعونا نلقى نظرة على تحركات اللاعب بشكل عملي
سنلاحظ مشكلة مزعجة وهي ان التحركات في الزواية تكون اسرع بكثير
اي اذا تحرك اللاعب في اليمين مع الاسفل في نفس الوقت فان سرعته في اتجاه الجنوب الشرقي سيكون اسرع من الطبيعي
ولكي نفسر الامر انظر الى الصور التالية
هنا عندما يتحرك اللاعب في اتجاه اليمين فقط يكون محور x
يساوي 1
ومحور y
يساوي 0
position += direction * speed * delta
الأمر هنا كانك تقول له (1, 0) * speed
حيث ان (1, 0)
هو متجه اتجاه ناحية اليمين طوله 1
وان حسبنا طوله عن طريق ايجاد الحد المطلق للمتجه
سيعطينا 1
وهو ما سينضرب في ال سرعة speed
بالتالي السرعة لن تتغير اي ستظل كما هي، فقط الاتجاه من له عامل التاثر هنا وهذا ما نريده
وعندما يتحرك نحو الاسفل يصبح y
يساوي 1
و x
يساوي 0
سيكون نفس الأمر كانك تقول له (0, 1) * speed
حيث ان (0, 1)
هو متجه اتجاه ناحية الأسفل طوله 1
والحسبة ستعطينا نفس النتيجة
اذا تحرك ناحية اليمين والاسفل في ان واحد يصبح x
وy
كلايهما يساويان 1
وهنا تظهر المشكلة
بحيث ان المتجه (1, 1)
ان حسبت قيمته او طوله
سيعطيك
اي 1.4142
وهكذا سيزيد سرعة اللاعب عن المتوقع وهكذا اصبح المتجه يؤثر على السرعة وهذا ما لا نريده
نحن نريده ان يحدد لنا الاتجاه فقط لذا يجب على طوله ان يكون مساويًا ل 1 لكي لا يؤثر على السرعة
position += direction * speed * delta
هنا سيتجه اللاعب ناحية الجنوب الشرقي لكن السرعة speed
ستزيد على الطبيعي لانها سيتم ضربها في 1.4142
وليس 1
حل هذه المشكلة سيكون بعمل بعض المعادلات الخاصة على المتجه ال direction
نرجع لمثالنا الاول وهو اذا تحرك اللاعب ناحية اليمين والاسفل في ان واحد يصبح direction
يساوي (1, 1)
وهذا ما يجعل طوله اي الوتر يكون 1.4142
كما وضحنا
اذا نحن نريد ان نغير قيم ال x
و y
الخاصة بال direction
ليكون طوله 1
اي اننا سنحول متجه ال direction
الى متجه الوحدة
وهذا هو مفهوم ال Normalization
ال normalization
يحول اي متجه الى متجه وحدة، لكن كيف ؟
عن طريق بعض المعادلات البسيطة، وهي كالأتي ان كان لديك متجه قيمه (3, 4)
وان اردت ايجاد طوله سيكون 5
لنحوله الى متجه الوحدة كل ما علينا فعله هو ان نقسم قيم المتجه على طوله اي الوتر والوتر يقسم ايضا في نفسه
فسوف نقسم قيم x
و y
والوتر
على 5
، كما هو موضح في الصورة التالية
هكذا سيصبح طول المتجه اي الوتر يساوي 1
وقيم x
و y
سيتغيران ليتناسبا مع ناتج الوتر
في غودوت لدينا دالة متواجدة داخل خواص Vector2
تدعى normalized
تحول المتجه الى متجه الوحدة
نحن نعرف ان المتغير direction
من نوع Vector2
فنستطيع استدعاء دالة ال normalized
direction.normalized()
لكن دالة normalized
لا تعدل في قيم المتجه direction
ذاتها
لذا ان كتبته كهذا لن يحدث اي تغير في قيم direction
لانها ترجع قيمة ال direction
بعد ان تحول الى متجه الوحدة لذا يجب ان نستقبلها في نفسها بهذا الشكل
direction = direction.normalized()
لذا فإن حل مشكلة الحركة في الزوايا يكون باستعمالها
وهذا توضيع عملي للأمر
ستلاحظ سرعة اللاعب في الزوايا تقل عند استخدامنا لدالى ال normalized
ونحن نطبع طول المتجه قبل وبعد استخدام الدالة لتلاحظ الفرق
هذا كان كل شيء في درس اليوم
سيكون هناك درس اخر لتحسين الحركة افضل من هذا وجعلها اكثر سلاسة ومرونة
لكن سنغطي هذا في درس قريب لان درس اليوم كان طول الى حد ما
أوامر الحركة بشكل كامل
extends KinematicBody2D
var speed = 500
var direction : Vector2
func _physics_process(delta):
# define direction of movement
direction.x = int(Input.is_action_pressed("ui_right")) - int(Input.is_action_pressed("ui_left"))
direction.y = int(Input.is_action_pressed("ui_down")) - int(Input.is_action_pressed("ui_up"))
# make a vector transform into the unit vector
# i.e. its length equals 1
direction = direction.normalized()
# Movement
if direction:
position += direction * speed * delta