运行此场景,您将看到它move_and_collide()按预期运行,沿速度矢量移动了身体。现在,让我们看看添加一些障碍时会发生什么。添加具有矩形碰撞形状的StaticBody2D。为了获得可见性,可以使用sprite,Polygon2D或从“调试”菜单中打开“可见碰撞形状”。
再次运行场景,然后尝试移入障碍物。您会看到KinematicBody2D 无法穿透障碍物。但是,尝试以一定角度移入障碍物,您会发现障碍物就像胶水一样-感觉身体被卡住了。
发生这种情况是因为没有碰撞响应。move_and_collide()发生碰撞时停止身体的运动。我们需要对碰撞产生的任何响应进行编码。
尝试将功能更改为move_and_slide(velocity)并再次运行。注意我们delta从速度计算中删除了。
move_and_slide()提供沿碰撞对象滑动主体的默认碰撞响应。这对于许多游戏类型都非常有用,并且可能只是获得所需行为的全部。
弹跳/反射
如果不想滑动碰撞响应怎么办?对于此示例(示例项目中的“ BounceandCollide.tscn”),我们有一个射击子弹的角色,我们希望这些子弹从墙上弹起。
本示例使用三个场景。主要场景包含播放器和墙壁。子弹头和墙是分开的场景,因此可以被实例化。
播放器由w和s键控制前进和后退。瞄准使用鼠标指针。这是Player的代码,使用move_and_slide():
using Godot; using System; public class KBExample : KinematicBody2D { private PackedScene _bullet = (PackedScene)GD.Load("res://Bullet.tscn"); public int Speed = 200; private Vector2 _velocity = new Vector2(); public void GetInput() { // add these actions in Project Settings -> Input Map _velocity = new Vector2(); if (Input.IsActionPressed("backward")) { _velocity = new Vector2(-Speed/3, 0).Rotated(Rotation); } if (Input.IsActionPressed("forward")) { _velocity = new Vector2(Speed, 0).Rotated(Rotation); } if (Input.IsActionPressed("mouse_click")) { Shoot(); } } public void Shoot() { // "Muzzle" is a Position2D placed at the barrel of the gun var b = (Bullet)_bullet.Instance(); b.Start(GetNode<Node2D>("Muzzle").GlobalPosition, Rotation); GetParent().AddChild(b); } public override void _PhysicsProcess(float delta) { GetInput(); var dir = GetGlobalMousePosition() - GlobalPosition; // Don't move if too close to the mouse pointer if (dir.Length() > 5) { Rotation = dir.Angle(); _velocity = MoveAndSlide(_velocity); } } }
和子弹的代码:
using Godot; using System; public class Bullet : KinematicBody2D { public int Speed = 750; private Vector2 _velocity = new Vector2(); public void Start(Vector2 pos, float dir) { Rotation = dir; Position = pos; _velocity = new Vector2(speed, 0).Rotated(Rotation); } public override void _PhysicsProcess(float delta) { var collision = MoveAndCollide(_velocity * delta); if (collision != null) { _velocity = _velocity.Bounce(collision.Normal); if (collision.Collider.HasMethod("Hit")) { collision.Collider.Call("Hit"); } } } public void OnVisibilityNotifier2DScreenExited() { QueueFree(); } }
该动作发生在中_physics_process()。使用后move_and_collide(),如果发生冲突,KinematicCollision2D则返回一个对象(否则返回Nil)。
如果有返回的碰撞,我们使用的normal来velocity通过Vector2.bounce()方法反映子弹的碰撞。
如果碰撞对象(collider)具有hit方法,我们也将其称为。在示例项目中,我们向“墙”添加了闪烁的色彩效果以演示这一点。
平台运动
让我们尝试一个更流行的示例:2D平台程序。move_and_slide() 是快速启动和运行功能字符控制器的理想选择。如果您已经下载了示例项目,则可以在“ Platformer.tscn”中找到它。
对于此示例,我们假设您有一个由StaticBody2D对象组成的关卡。它们可以是任何形状和大小。在示例项目中,我们使用 Polygon2D创建平台形状。
这是播放器主体的代码:
using Godot; using System; public class KBExample : KinematicBody2D { [Export] public int RunSpeed = 100; [Export] public int JumpSpeed = -400; [Export] public int Gravity = 1200; Vector2 velocity = new Vector2(); bool jumping = false; public void GetInput() { velocity.x = 0; bool right = Input.IsActionPressed("ui_right"); bool left = Input.IsActionPressed("ui_left"); bool jump = Input.IsActionPressed("ui_select"); if (jump && IsOnFloor()) { jumping = true; velocity.y = JumpSpeed; } if (right) velocity.x += RunSpeed; if (left) velocity.x -= RunSpeed; } public override void _PhysicsProcess(float delta) { GetInput(); velocity.y += Gravity * delta; if (jumping && IsOnFloor()) jumping = false; velocity = MoveAndSlide(velocity, new Vector2(0, -1)); } }
当使用时move_and_slide(),该函数返回一个向量,该向量表示发生滑动碰撞后剩余的运动。将该值重新设置为角色的值,velocity可以使我们平稳地上下倾斜。尝试删除并查看如果不这样做会发生什么。velocity =
另请注意,我们已将其添加为下限法线。该向量指向正上方。结果,如果角色与具有该法线的对象碰撞,则将其视为地板。Vector2(0, -1)
使用地面法线可以使用进行跳跃工作is_on_floor()。此功能仅会返回true一个后move_and_slide()碰撞,其中碰撞体的法线是在45度定地板载体。您可以通过设置来控制最大角度floor_max_angle。
例如,该角度还允许您使用来实现其他功能,例如墙跳 is_on_wall()。