روابط لمقالات سابقة
بادئ ذي بدء ، قررت جمع كل الكود السابق حتى يفهم أولئك الذين استخدموا المعلومات من المقالة السابقة كيف تخيلت البرنامج بالكامل:
extends KinematicBody2D
#
const GRAVITY: int = 40
const MOVE_SPEED: int = 120 #
const JUMP_POWER: int = 80 #
#
var velocity: Vector2 = Vector2.ZERO
func _physics_process(_delta: float) -> void:
#
move_character() #
jump()
#
self.velocity.y += GRAVITY
self.velocity = self.move_and_slide(self.velocity, Vector2(0, -1))
func move_character() -> void:
var direction: float = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
self.velocity.x = direction * MOVE_SPEED
func jump() -> void:
if self.is_on_floor():
if Input.is_action_pressed("ui_accept"): # ui_accept
#
self.velocity.y -= JUMP_POWER
آمل أن يكون أولئك الذين قرأوا المقال السابق قد فهموا تقريبًا كيف يعمل كل شيء. الآن دعنا نعود إلى التنمية.
آلة الدولة
آلة الحالة (حسب فهمي) هي جزء من البرنامج الذي يحدد حالة الشيء: في الهواء ، على الأرض ، في السقف ، أو على الحائط ، كما أنه يحدد ما يجب أن يحدث للشخصية في مكان أو آخر. يحتوي GodotEngine على شيء مثل التعداد ، والذي ينشئ تعدادًا ، حيث يكون كل عنصر ثابتًا محددًا في الكود. أعتقد أنه من الأفضل أن أظهر هذا بمثال:
enum States { # States, States.IN_AIR, States.ON_FLOOR...
IN_AIR, #
ON_FLOOR, #
ON_WALL #
}
يمكن وضع هذا الرمز بأمان في بداية نص شخصية اللعبة وتذكر أنه موجود. بعد ذلك ، نقوم بتهيئة المتغير في المكان المناسب var current_state: int = States.IN_AIR ، والذي يساوي صفرًا إذا استخدمنا print. بعد ذلك ، تحتاج إلى تحديد ما سيفعله اللاعب بطريقة ما في الحالة الحالية. أعتقد أن العديد من المطورين ذوي الخبرة الذين أتوا من C ++ على دراية ببناء التبديل () {case:}. يحتوي GDScript على بنية ملائمة مماثلة ، على الرغم من أن التبديل موجود أيضًا على جدول الأعمال. يسمى البناء المباراة.
أعتقد أنه سيكون من الأصح إظهار هذا البناء في الممارسة العملية ، حيث سيكون من الصعب تحديده بدلاً من إظهاره:
func _physics_process(_delta: float) -> void:
#
match (self.current_state):
States.IN_AIR:
# .
self.move_character()
States.ON_FLOOR:
# , .
self.move_character()
self.jump()
States.ON_WALL:
# , , . .
self.move_character()
#
لكننا ما زلنا لا نغير الدولة. نحتاج إلى إنشاء وظيفة منفصلة ، والتي سنسميها قبل المطابقة ، من أجل تغيير المتغير current_state ، الذي يجب إضافته إلى الكود إلى باقي المتغيرات. وسنسمي الدالة update_state ().
func update_state() -> void:
# .
if self.is_on_floor():
self.current_state = self.States.ON_FLOOR
elif self.is_on_wall() and !self.is_on_floor():
# .
self.current_state = self.States.ON_WALL
elif self.is_on_wall() and self.is_on_floor():
# . .
self.current_state = self.States.ON_WALL
else: #
self.current_state = self.states.IN_AIR
الآن بعد أن أصبحت آلة الحالة جاهزة ، يمكننا إضافة الكثير من الوظائف. بما في ذلك إضافة الرسوم المتحركة إلى الشخصية ... ولا حتى ذلك ... يمكننا إضافة الكثير من الرسوم المتحركة إلى الشخصية. أصبح النظام معياريًا. لكننا لم ننتهي من الكود هنا. قلت في البداية إنني سأوضح لك كيفية القيام بقفزة إضافية في الهواء ، والصعود ، والقفز من على الحائط. لنبدأ بالترتيب.
قفزة إضافية في الهواء
أولاً ، أضف استدعاء القفز في الحالة States.IN_AIR إلى مباراتنا ، والتي سنقوم بتعديلها قليلاً.
هذا هو رمز القفزة التي أصلحتها:
func jump() -> void:
# . .
if Input.is_action_pressed("ui_accept"): #
if self.current_state == self.States.ON_FLOOR:
# , _
self.velocity.y -= JUMP_POWER
elif (self.current_state == self.States.IN_AIR or self.current_state == self.States.ON_WALL)
and self.second_jump == true:
#
self.velocity.y = -JUMP_POWER
#
self.second_jump = false
# var second_jump: bool = true . update_state()
# if self.is_on_floor(): self.second_jump = true # .
التعليقات على الكود توضح بشكل أساسي كيف قمت بتغيير البرنامج ، وآمل أن تفهم كلامي هناك. لكن في الواقع ، هذه الإصلاحات كافية لتغيير ميكانيكا القفز وتحسينها لمضاعفة. استغرق الأمر مني شهرين لابتكار الطرق التالية. لقد كتبتها بالفعل أول من أمس ، 1 أكتوبر 2020.
تسلق الجدران
لسوء الحظ بالنسبة لنا ، لا يسمح لنا نظام GodotEngine Wall Normal بالمعرفة ، مما يعني أنه يتعين علينا إنشاء عكاز صغير. بادئ ذي بدء ، سأضع حاشية سفلية للمتغيرات المتاحة حاليًا بحيث يمكنك بسهولة معرفة ما تغير.
extends KinematicBody2D
#
signal timer_ended # yield wall_jump, .
#
const GRAVITY: int = 40
const MOVE_SPEED: int = 120 #
const JUMP_POWER: int = 80 #
const WALL_JUMP_POWER: int = 60 # .
const CLIMB_SPEED: int = 30 #
#
var velocity: Vector2 = Vector2.ZERO
var second_jump: bool = true
var climbing: bool = false # , , .
var timer_working: bool = false
var is_wall_jump: bool = false # , ,
var left_pressed: bool = false #
var right_pressed: bool = false #
var current_state: int = States.IN_AIR
var timer: float = 0 # , _process(delta: float)
var walls = [false, false, false] # . - . - .
#
#
enum States {
IN_AIR, #
ON_FLOOR, #
ON_WALL #
}
# , _process()
func _process(delta: float):
if timer_working:
timer -= delta
if timer <= 0:
emit_signal("timer_ended")
timer = 0
أنت الآن بحاجة إلى تحديد الجدار الذي يتسلقه اللاعب.
هنا شجرة المشهد التي يجب أن تعدها لتنفيذ محدد جانب
الجدار.ضع 2 Area2Ds على جانبي الشخصية ويجب ألا يتداخل كل من CollisionShape2D مع الحرف. قم بتوقيع كائنات WallLeft / WallRight بشكل مناسب وقم بإرفاق الإشارات _on_body_endered و _on_body_exited بنص حرف واحد. إليك الكود المطلوب لتعريف الجدران (أضف إلى نهاية البرنامج النصي):
#
# ,
func _on_WallRight_body_entered(_body):
if (_body.name != self.name):
self.walls[0] = true # , -
func _on_WallRight_body_exited(_body):
self.walls[0] = false # -
func _on_WallLeft_body_entered(_body):
if (_body.name != self.name):
self.walls[2] = true # , -
func _on_WallLeft_body_exited(_body):
self.walls[2] = false # -
لنبدأ بطريقة التسلق. سيقول الرمز كل شيء بالنسبة لي
func climbing() -> void:
if (self.walls[0] or self.walls[2]): #
# action ui_climb. .
self.climbing = Input.is_action_pressed("ui_climb")
else:
self.climbing = false
ونحتاج إلى إعادة كتابة عنصر تحكم move_character () حتى لا نكتفي بالتمسك بل الصعود والنزول ، لأن لدينا الاتجاه:
func move_character() -> void:
var direction: float = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
if !self.climbing:
self.velocity.x = direction * MOVE_SPEED
else:
self.velocity.y = direction * CLIMB_SPEED
ونصلح _physics_process ():
func _physics_process(_delta: float) -> void:
#
match (self.current_state):
States.IN_AIR:
self.move_character()
States.ON_FLOOR:
self.move_character()
self.jump()
States.ON_WALL:
self.move_character()
#
if !self.climbing:
self.velocity.y += GRAVITY
self.velocity = self.move_and_slide(self.velocity, Vector2(0, -1))
يجب أن تكون الشخصية الآن قادرة على تسلق الجدران.
القفز من الحائط
الآن دعونا ننفذ القفز من الحائط.
func wall_jump() -> void:
if Input.is_action_just_pressed("ui_accept") and Input.is_action_pressed("ui_climb"):
# 1
self.is_wall_jump = true # =
self.velocity.y = -JUMP_POWER # -JUMP_POWER
if walls[0]: #
self.timer = 0.5 # self.timer 0.5
self.timer_enabled = true #
self.left_pressed = true # left_pressed
yield(self, "timer_ended") # timer_ended
self.left_pressed = false # left_pressed
if walls[2]: #
self.timer = 0.5 # self.timer 0.5
self.timer_enabled = true #
self.right_pressed = true # right_pressed
yield(self, "timer_ended") # timer_ended
self.right_pressed = false # right_pressed
self.is_wall_jump = false # .
أضفنا استدعاءًا إلى هذه الطريقة لمطابقتنا -> States.ON_WALL وأرفقنا طريقتنا ببقية _physics_process ().
خاتمة
في هذه المقالة ، لقد أوضحت تطبيق ميكانيكا معقدة نسبيًا (للمبتدئين) في GodotEngine. لكن هذا ليس الجزء الأخير من سلسلة المقالات ، لذلك أسأل من يعرف كيفية تنفيذ الأساليب التي بينتها في هذه المقالة من الأفضل أن يكتب عنها في التعليقات. أنا والعديد من القراء سنكون ممتنين لحلول سريعة وعالية الجودة.