目标

本节实现一个经典的平台左右移动和跳跃。

image.png 我们新建一个场景,创建Node2D类型的根节点,分别添加一个KinematicBody2D和StaticBody2D子节点,并修改名称为“Player”和“Floor”。
KinematicBody2D是Godot专门用于处理移动的物理体之一,常用于处理有碰撞体积且可以移动的物体,比如玩家、敌人、单位等。
StaticBody2D则经常用于模拟墙体或地面等具有碰撞体积但无法移动的物体。当然,静止体也是有办法移动的,只是不会提供相应类型的方法去主动检测碰撞和移动。

2D物理体(PhysicsBody2D)

image.png 物理体是个统称,而且2D部分的物理体被叫做“2D物理体”,因为还有3D版本的物理体。
可以看到,PhysicsBody2D下有三个子类型,其中就有我们本节涉及的KinematicBody2D和StaticBody2D。
image.png 此时我们在“Player”和“Floor”节点上依次,右键然后选择“将分支保存为场景”。然后将两者都存储为单独的场景文件。
image.png 此时,“Player”和“Floor”节点就被替换为了“Player”和“Floor”场景的实例。点击后面的image.png图标,可以快速打开场景,进行编辑。
这样做的好处是将Player与Floor做成了可以复用的元件。并且有一定的隔离作用。同时保持了单个场景结构的清晰。
image.png
image.png
我们打开Player场景,为其添加一个Sprite节点,并拖入Icon.png为纹理,添加一个CollisionShape2D节点,并为该节点的Shape属性添加一个RectangleShape2D类型的碰撞形状。
此处Sprite为Player提供“长相”,因为KinematicBody2D自身是没有长相的。
CollisionShape2D是专门为节点添加碰撞形状的,碰撞形状提供了一种类似体积的东西,用于检测碰撞。
长相和体积可以完全一致,也可以不完全一致,所以灵活性就在于它们是分别由两个不同的节点提供的。
image.png 此处,我们需要长相和体积完全一致,所以我们的碰撞形状完全覆盖了Sprite的矩形区域。
image.png image.png 对于Floor场景,我们可以如法炮制,因为它也需要长相和体积。
这里有一个小技巧,你可以跨场景复制粘贴多个节点和节点结构,这里我们将Player场景的Sprite和CollisionShape2D复制粘贴给Floor场景的根节点。
image.png 为了区分地板和玩家,所以我们将地板做一定的拉伸,并且将体积形状重新设置为与地板的形状完全重合。(注意这里不要直接缩放碰撞形状,而是应该用角点控制)
image.png 然后我们调整主场景中地板的位置,设置为大概如下图所示:

代码

然后我们就要为我们的Player节点添加脚本了。脚本如下:

  1. extends KinematicBody2D
  2. var speed = 60.0 # 速度 像素/秒
  3. var velocity = Vector2.ZERO # 速度向量
  4. var gravity = 5000 # 重力加速度
  5. var jump_force = 200.0 # 单次跳跃高度
  6. func _process(delta):
  7. velocity = Vector2.ZERO
  8. # 左右移动
  9. velocity.x = Input.get_axis("ui_left","ui_right") * speed
  10. # 实现下落
  11. velocity.y += gravity * delta
  12. # 跳跃
  13. if Input.is_action_pressed("ui_accept"): # 按空格键
  14. velocity.y = -jump_force
  15. velocity = move_and_slide(velocity,Vector2.UP)

代码分析

我们在第一节左右移动的基础上,引入了一些新的变量,其中用新的Vector2类型的变量velocity来表示速度向量,使用 Input.get_axis()来获取左右方向键按下时获得的方向。
在 _process中,我们用velocity.y 每帧加上重力加速度来实现重力的模拟,它可以同时实现初始高空中下落,以及跳跃后的下落,遇到没有地板的地方,还会不停的下落。
因为我们每帧还会将velocity重置为(0,0),所以玩家下落的垂直速度并不会随着时间增加,而是始终等于gravity。如果我们要模拟真实的重力加速度,则可以修改代码如下:此时只有当我们接触到地板时才会将垂直方向的速度设为0,否则就会一直加速。

改进的代码

  1. extends KinematicBody2D
  2. var speed = 60.0 # 速度 像素/秒
  3. var velocity = Vector2.ZERO # 速度向量
  4. var gravity = 5000 # 重力加速度
  5. var jump_force = 200.0 # 单次跳跃高度
  6. func _process(delta):
  7. if is_on_floor():
  8. velocity .y = 0
  9. # 左右移动
  10. velocity.x = Input.get_axis("ui_left","ui_right") * speed
  11. # 实现下落
  12. velocity.y += gravity * delta
  13. # 跳跃
  14. if Input.is_action_pressed("ui_accept"): # 按空格键
  15. velocity.y = -jump_force
  16. velocity = move_and_slide(velocity,Vector2.UP)

限制空中持续跳跃

基础动画示例2.gif 如果我们按照上面的代码运行主场景,就会发现我们的跳跃未被限制,只要我们持续按下空格键,玩家就会一直在垂直方向上一直上升。
为此我们需要添加条件判断is_on_floor(),也就是玩家在地面时,按空格键才让其进行跳跃。

改进代码

  1. extends KinematicBody2D
  2. var speed = 60.0 # 速度 像素/秒
  3. var velocity = Vector2.ZERO # 速度向量
  4. var gravity = 3000 # 重力加速度
  5. var jump_force = 3000.0 # 单次跳跃高度
  6. func _process(delta):
  7. velocity = Vector2.ZERO
  8. # 左右移动
  9. velocity.x = Input.get_axis("ui_left","ui_right") * speed
  10. # 实现下落
  11. velocity.y += gravity * delta
  12. # 跳跃
  13. if is_on_floor() and Input.is_action_pressed("ui_accept"):
  14. velocity.y = -jump_force
  15. velocity = move_and_slide(velocity,Vector2.UP)

实现二级跳

  1. extends KinematicBody2D
  2. var speed = 60.0 # 速度 像素/秒
  3. var velocity = Vector2.ZERO # 速度向量
  4. var gravity = 3000 # 重力加速度
  5. var jump_force = 3000.0 # 单次跳跃高度
  6. var is_jumping = false
  7. func _process(delta):
  8. velocity = Vector2.ZERO
  9. # 左右移动
  10. velocity.x = Input.get_axis("ui_left","ui_right") * speed
  11. # 实现下落
  12. velocity.y += gravity * delta
  13. # 落地
  14. if is_on_floor() and is_jumping == true:
  15. is_jumping = false
  16. return
  17. # 跳跃
  18. if Input.is_action_just_pressed("ui_accept"):
  19. if is_on_floor(): # 在地面
  20. velocity.y = -jump_force
  21. is_jumping = true
  22. else: # 在空中
  23. if is_jumping: # 正在跳跃
  24. # 二级跳
  25. velocity.y = -jump_force
  26. is_jumping = false
  27. velocity = move_and_slide(velocity,Vector2.UP)

代码分析

如果我们想要实现二级跳的效果,则需要引入一个新的变量is_jumping,来判断玩家是否处于正在跳跃且没有下落到地面的状态。如果处于这个状态则我们就可以继续接受一次空格键,来实现空中二级跳。
注意这里我们将Input.is_action_pressed改为了Input.is_action_just_pressed,这样,只会判断空格按下的瞬间,而不是持续判断按下的状态。

运行效果

基础动画示例2.gif

推荐教程

image.png 如果你看完这个实例,对制作Godot平台跳跃游戏感兴趣,推荐关注铁木球大佬的平台跳跃游戏。
可以访问这里贴出的地址,同时地址我也会放到评论区。