2D 运动概述

前言

每个初学者都问过“我该如何移动我的游戏角色呢?”根据你正在制作的游戏的风格,可能有特殊的需求,但一般来说,大多数 2D 游戏的运动都基于一组不太多的操作之上。

在这些示例中,我们将使用 CharacterBody2D,但这些原则也适用于其他节点类型(如 Area2D、RigidBody2D)。

场景布置

以下每个示例都使用相同的场景布置。从带有 Sprite2DCollisionShape2D 这两个子节点的 CharacterBody2D 开始。你可以将 Godot 图标(“icon.png”)用于 Sprite2D 的纹理,也可以使用你拥有的任何其他 2D 图像。

打开项目 -> 项目设置并选择“输入映射”选项卡。添加以下输入操作(相关详细信息请参阅 InputEvent):

../../_images/movement_inputs.webp

八向移动

在这种情况下,你希望用户按下四个方向键(上/左/下/右或W / A / S / D)并沿所选方向移动。“8向移动” 意味着游戏角色可以通过同时按下两个键实现斜向移动。

../../_images/movement_8way.gif

为角色体添加脚本,并添加以下代码:

GDScriptC#

  1. extends CharacterBody2D
  2. @export var speed = 400
  3. func get_input():
  4. var input_direction = Input.get_vector("left", "right", "up", "down")
  5. velocity = input_direction * speed
  6. func _physics_process(delta):
  7. get_input()
  8. move_and_slide()
  1. using Godot;
  2. public partial class Movement : CharacterBody2D
  3. {
  4. [Export]
  5. public int Speed { get; set; } = 400;
  6. public void GetInput()
  7. {
  8. Vector2 inputDirection = Input.GetVector("left", "right", "up", "down");
  9. Velocity = inputDirection * Speed;
  10. }
  11. public override void _PhysicsProcess(double delta)
  12. {
  13. GetInput();
  14. MoveAndSlide();
  15. }
  16. }

在函数 get_input() 中,我们使用 Inputget_vector() 来检查四个按键事件,并返回一个方向向量的累加值。

然后,我们可以将长度为 1 的方向矢量乘以所需的速度来设定速度。

小技巧

如果你之前从未接触过向量数学, 或者需要复习, 你可以在 向量数学 看到Godot中向量用法的解释.

备注

如果在你按下键时上面的代码不起任何作用, 请仔细检查你是否按照本教程的 场景布置 部分所描述的正确设置了输入操作.

旋转+移动

这种类型的运动有时被称为 “Asteroids式运动”, 因为它类似于经典街机游戏Asteroids的工作方式. 按左/右旋转角色, 而按上/下使得角色在面向的方向上向前或向后.

../../_images/movement_rotate1.gif

GDScriptC#

  1. extends CharacterBody2D
  2. @export var speed = 400
  3. @export var rotation_speed = 1.5
  4. var rotation_direction = 0
  5. func get_input():
  6. rotation_direction = Input.get_axis("left", "right")
  7. velocity = transform.x * Input.get_axis("down", "up") * speed
  8. func _physics_process(delta):
  9. get_input()
  10. rotation += rotation_direction * rotation_speed * delta
  11. move_and_slide()
  1. using Godot;
  2. public partial class Movement : CharacterBody2D
  3. {
  4. [Export]
  5. public int Speed { get; set; } = 400;
  6. [Export]
  7. public float RotationSpeed { get; set; } = 1.5f;
  8. private float _rotationDirection;
  9. public void GetInput()
  10. {
  11. _rotationDirection = Input.GetAxis("left", "right");
  12. Velocity = Transform.X * Input.GetAxis("down", "up") * Speed;
  13. }
  14. public override void _PhysicsProcess(double delta)
  15. {
  16. GetInput();
  17. Rotation += _rotationDirection * RotationSpeed * (float)delta;
  18. MoveAndSlide();
  19. }
  20. }

这里我们添加了两个变量来跟踪我们的旋转方向和速度。旋转直接应用于主体的 rotation 属性。

要设置速度,我们使用物体的 transform.x ,这是一个指向物体 “前进” 方向的矢量,然后乘以速度。

旋转+移动(鼠标)

这种运动方式是前一种运动方式的变体。这次,方向由鼠标位置而不是键盘设置。角色将始终“看向”鼠标指针。前进/后退输入保持不变。

../../_images/movement_rotate2.gif

GDScriptC#

  1. extends CharacterBody2D
  2. @export var speed = 400
  3. func get_input():
  4. look_at(get_global_mouse_position())
  5. velocity = transform.x * Input.get_axis("down", "up") * speed
  6. func _physics_process(delta):
  7. get_input()
  8. move_and_slide()
  1. using Godot;
  2. public partial class Movement : CharacterBody2D
  3. {
  4. [Export]
  5. public int Speed { get; set; } = 400;
  6. public void GetInput()
  7. {
  8. LookAt(GetGlobalMousePosition());
  9. Velocity = Transform.X * Input.GetAxis("down", "up") * Speed;
  10. }
  11. public override void _PhysicsProcess(double delta)
  12. {
  13. GetInput();
  14. MoveAndSlide();
  15. }
  16. }

这里我们用到 Node2D 中的 look_at() 方法,使玩家朝向鼠标的位置。如果没有此功能,可以通过如下设置角度以获得相同的效果:

GDScriptC#

  1. rotation = get_global_mouse_position().angle_to_point(position)
  1. var rotation = GetGlobalMousePosition().AngleToPoint(Position);

点击并移动

最后一个示例仅使用鼠标来控制角色. 单击屏幕将使游戏角色移动到目标位置.

../../_images/movement_click.gif

GDScriptC#

  1. extends CharacterBody2D
  2. @export var speed = 400
  3. var target = position
  4. func _input(event):
  5. if event is InputEventMouseButton:
  6. if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
  7. target = get_global_mouse_position()
  8. func _physics_process(delta):
  9. velocity = position.direction_to(target) * speed
  10. # look_at(target)
  11. if position.distance_to(target) > 10:
  12. move_and_slide()
  1. using Godot;
  2. public partial class Movement : CharacterBody2D
  3. {
  4. [Export]
  5. public int Speed { get; set; } = 400;
  6. private Vector2 _target;
  7. public override void _Input(InputEvent @event)
  8. {
  9. if (@event is InputEventMouseButton eventMouseButton)
  10. {
  11. if (eventMouseButton.ButtonIndex == MouseButton.Left && eventMouseButton.Pressed)
  12. {
  13. _target = GetGlobalMousePosition();
  14. }
  15. }
  16. }
  17. public override void _PhysicsProcess(double delta)
  18. {
  19. Velocity = Position.DirectionTo(_target) * Speed;
  20. // LookAt(_target);
  21. if (Position.DistanceTo(_target) > 10)
  22. {
  23. MoveAndSlide();
  24. }
  25. }
  26. }

注意我们在移动之前做的 distance_to() 检查. 如果没有这个检查, 物体在到达目标位置时会 “抖动”, 因为它稍微移过该位置时就会试图向后移动, 只是每次移动步长都会有点远从而导致来回重复移动.

如果你喜欢, 取消注释的 rotation 代码可以使物体转向其运动方向.

小技巧

该技术也可以用到“跟随”的游戏角色中。target 目标位置可以是任何你想移动到的对象的位置。

总结

你可能觉得这些代码示例可以作为你自己的项目的一个有用的出发点. 请随意使用它们并试验它们, 看看你能做些什么.

你可以在此处下载这个示例项目:2d_movement_starter.zip