game-physics - Godot Engine 中的转向运动

标签 game-physics godot

我想了解Godot Engine中的游戏开发。我正在尝试制作一款类似于导弹游戏的手机游戏: Missiles

现在我有一个可以正常工作的操纵杆。我得到的值是标准化的 Vector2:

var joystick_value = joystick.get_value()

但我不知道如何根据操纵杆值改变飞机的速度。另外还可以对飞机可以转动的程度(或最大角度)设置一些限制。

(平面是 KinematicBody2D)

有什么想法吗?

最佳答案

速度

如果我们谈论 KinematicBody2D 和 velocity,我们谈论的是这样的脚本,给出或采取:

extends KinematicBody2D

var velocity:Vector2 = Vector2.ZERO # pixels/second

func _physics_process(_delta:float) -> void:
    move_and_slide(velocity)

也许您更适合使用速度方向而不是速度。我们也可以这样做:

extends KinematicBody2D

var speed:float = 0                # pixels/second
var direction:Vector2 = Vector2.UP # pixels

func _physics_process(_delta:float) -> void:
    var velocity = direction.normalized() * speed
    move_and_slide(velocity)

如果我们想要一个角度而不是方向向量怎么办?当然:

extends KinematicBody2D

var speed:float = 0 # pixels/second
var angle:float = 0 # radians

func _physics_process(_delta:float) -> void:
    var velocity = Vector2.RIGHT.rotated(angle) * speed
    move_and_slide(velocity)

旋转

由于我们将进行转向,因此我们希望根据其速度来旋转 KinematicBody2D

当然,我们可以从速度获得旋转角度:

extends KinematicBody2D

var velocity:Vector2 = Vector2.ZERO # pixels/second

func _physics_process(_delta:float) -> void:
    rotation = velocity.angle()
    move_and_slide(velocity)

方向向量类似,或者如果您有角度,则可以直接使用它。


转向

对于转向,我们将保持速度并改变角度。所以我们需要上面展示的速度角度版本。当然,通过旋转:

extends KinematicBody2D

var speed:float = 0 # pixels/second
var angle:float = 0 # radians

func _physics_process(_delta:float) -> void:
    rotation = angle
    var velocity = Vector2.RIGHT.rotated(angle) * speed
    move_and_slide(velocity)

现在我们将有一个来自用户输入的target_angle。在你的情况下,这意味着:

var target_angle = joystick.get_value().angle()

现在,请注意我们不知道旋转的方向。执行 target_angle - angle 不起作用,因为相反旋转可能会更短。因此,我们将这样做:

var angle_difference = wrapf(target_angle - angle, -PI, PI)

wrapf 是什么意思?做?它将值“包装”到一个范围内。例如,wrapf(11, 0, 10)1,因为它超过了 1011 + 01wrapf(4, 5, 10)9,因为它低于 51 10 - 19。希望这是有道理的。

我们在从 -PIPI 的范围内进行换行,因此它给出了较短方向上的角度差以进行旋转。


我们还需要angular_speed。即单位时间内角度变化多少(单位为角度/时间)。请注意,这与角度变化量不同(单位为角度)。为了进行转换,我们乘以自上次以来耗时:

var delta_angle = angular_speed * delta

啊,实际上,我们需要在 angle_difference 方向上。因此,它的符号:

var delta_angle = angular_speed * delta * sign(angle_difference)

我们不想过度。因此,如果delta_angle的绝对值大于angle_difference,我们需要将delta_angle设置为angle_difference:

var angle_difference = wrapf(target_angle - angle, -PI, PI)
var delta_angle= angular_speed * delta * sign(angle_difference)
if abs(delta_angle) > abs(angle_difference):
    delta_angle = angle_difference

我们可以在那里保存对 abs 的一次调用:

var angle_difference = wrapf(target_angle - angle, -PI, PI)
var delta_angle_abs = angular_speed * delta
var delta_angle = delta_angle_abs * sign(angle_difference)
if delta_angle_abs > abs(angle_difference):
    delta_angle = angle_difference

把它们放在一起:

extends KinematicBody2D

var speed:float = 0         # pixels/second
var angle:float = 0         # radians
var angular_speed:float = 0 # radians/second

func _physics_process(delta:float) -> void:
    var target_angle = joystick.get_value().angle()
    var angle_difference = wrapf(target_angle - angle, -PI, PI)
    var delta_angle_abs = angular_speed * delta
    var delta_angle = delta_angle_abs * sign(angle_difference)
    if delta_angle_abs > abs(angle_difference):
        delta_angle = angle_difference

    angle += delta_angle
    rotation = angle
    var velocity = Vector2.RIGHT.rotated(angle) * speed
    move_and_slide(velocity)

最后,进行一些重构,包括但不限于将该代码块提取到另一个函数:

extends KinematicBody2D

var speed:float = 0         # pixels/second
var angle:float = 0         # radians
var angular_speed:float = 0 # radians/second

func _physics_process(delta:float) -> void:
    var target_angle = joystick.get_value().angle()
    angle = apply_rotation_speed(angle, target_angle, angular_speed, delta)
    rotation = angle
    var velocity = Vector2.RIGHT.rotated(angle) * speed
    move_and_slide(velocity)

static func apply_rotation_speed(from:float, to:float, angle_speed:float, delta:float) -> float:
    var diff = wrapf(to - from, -PI, PI)
    var angle_delta = angle_speed * delta
    if angle_delta > abs(diff):
        return to

    return from + angle_delta * sign(diff)

这是带有角加速度的版本:

extends KinematicBody2D

var speed:float = 0                # pixels/second
var angle:float = 0                # radians
var angular_speed:float = 0        # radians/second
var angular_acceleration:float = 0 # radians/second^2

func _physics_process(delta:float) -> void:
    var target_angle = joystick.get_value().angle()
    if angle == target_angle:
        angular_speed = 0
    else:
        angular_speed += angular_acceleration * delta
        angle = apply_rotation_speed(angle, target_angle, angular_speed, delta)

    rotation = angle
    var velocity = Vector2.RIGHT.rotated(angle) * speed
    move_and_slide(velocity)

static func apply_rotation_speed(from:float, to:float, angle_speed:float, delta:float) -> float:
    var diff = wrapf(to - from, -PI, PI)
    var angle_delta = angle_speed * delta
    if angle_delta > abs(diff):
        return to

    return from + angle_delta * sign(diff)

还有带有角度缓动的 Shiny 版本:

extends KinematicBody2D

var speed = 10
var angle:float = 0
var angular_speed:float = 0
export(float, EASE) var angular_easing:float = 1

func _physics_process(delta:float) -> void:
    var target_angle = (get_viewport().get_mouse_position() - position).angle()
    angle = apply_rotation_easing(angle, target_angle, angular_easing, delta)

    rotation = angle
    var velocity = Vector2.RIGHT.rotated(angle) * speed
    move_and_slide(velocity)

static func apply_rotation_easing(from:float, to:float, easing:float, delta:float) -> float:
    var diff = wrapf(to - from, -PI, PI)
    var diff_norm = abs(diff)
    var angle_speed = ease(diff_norm / PI, easing)
    var angle_delta = angle_speed * delta
    if angle_delta > diff_norm:
        return to

    return from + angle_delta * sign(diff)

angular_easing 设置为 0 到 1 之间的某个值,使其在开始旋转时加速,并在接近目标角度时减速。 值为 0 时不旋转。值为 1 时,它以恒定速度旋转。请参阅ease .


我测试了这个答案中的代码(使用一些非零值),这用于鼠标控制:

var target_angle = (get_viewport().get_mouse_position() - position).angle()

它有效。

关于game-physics - Godot Engine 中的转向运动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67620139/

相关文章:

c# - 使用正弦创建跳跃算法

algorithm - 简单的 parking 场算法

python - 非常快地增量增加 Sprite 位置,没有滞后 - Python

godot - 不知道如何将其他场景引用到脚本中

godot - 防止在 godot 中调节特定子 Sprite 的颜色

scons - 使用 SCons 编译 Godot

animation - 如何以编程方式在 Godot 中加载动画,将动画与骨架/骨架分开?

swift - SpriteBuilder | Cocos2d - swift |代码 | ccPhysicsCollisionBegin

keyboard-events - Godot 键盘事件

python - 使用 pyglet 和 pymunk 使 Sprite 角色自然跳跃