Godot 引擎中的转向运动
Steering motion in Godot Engine
我想了解 Godot Engine 中的游戏开发。我正在尝试制作一款类似于导弹游戏的手机游戏:
现在我有一个可以正常工作的操纵杆。我得到的值是标准化的 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)
也许您最好使用 speed
和 direction
而不是 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)
如果我们想要一个 angle
而不是 direction
向量怎么办?当然:
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
。
当然,我们可以从 velocity
:
得到旋转 angle
extends KinematicBody2D
var velocity:Vector2 = Vector2.ZERO # pixels/second
func _physics_process(_delta:float) -> void:
rotation = velocity.angle()
move_and_slide(velocity)
与 direction
向量类似,或者如果你有 angle
你可以直接使用它。
转向
对于转向,我们将保持速度并改变角度。所以我们想要上面显示的 speed
和 angle
版本。当然有轮换:
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
,因为它比 10
多了 1
,而 1 + 0
是 1
。 wrapf(4, 5, 10)
是 9
因为它低于 5
1
而 10 - 1
是 9
。希望这是有道理的。
我们在 -PI
到 PI
的范围内环绕,因此它给出了较短方向的角度差以进行旋转。
我们还需要 angular_speed
。即单位时间内角度变化了多少(单位在angle/time)。请注意,这与角度变化的幅度不同(单位为角度)。要转换,我们乘以自上次以来经过的时间:
var delta_angle = angular_speed * delta
啊,其实我们需要angle_difference
的方向。因此,它的 sign
:
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)
这里是一个angular加速的版本:
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)
以及带有 angular 缓动的闪亮版本:
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()
有效。
我想了解 Godot Engine 中的游戏开发。我正在尝试制作一款类似于导弹游戏的手机游戏:
现在我有一个可以正常工作的操纵杆。我得到的值是标准化的 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)
也许您最好使用 speed
和 direction
而不是 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)
如果我们想要一个 angle
而不是 direction
向量怎么办?当然:
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
。
当然,我们可以从 velocity
:
angle
extends KinematicBody2D
var velocity:Vector2 = Vector2.ZERO # pixels/second
func _physics_process(_delta:float) -> void:
rotation = velocity.angle()
move_and_slide(velocity)
与 direction
向量类似,或者如果你有 angle
你可以直接使用它。
转向
对于转向,我们将保持速度并改变角度。所以我们想要上面显示的 speed
和 angle
版本。当然有轮换:
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
,因为它比 10
多了 1
,而 1 + 0
是 1
。 wrapf(4, 5, 10)
是 9
因为它低于 5
1
而 10 - 1
是 9
。希望这是有道理的。
我们在 -PI
到 PI
的范围内环绕,因此它给出了较短方向的角度差以进行旋转。
我们还需要 angular_speed
。即单位时间内角度变化了多少(单位在angle/time)。请注意,这与角度变化的幅度不同(单位为角度)。要转换,我们乘以自上次以来经过的时间:
var delta_angle = angular_speed * delta
啊,其实我们需要angle_difference
的方向。因此,它的 sign
:
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)
这里是一个angular加速的版本:
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)
以及带有 angular 缓动的闪亮版本:
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()
有效。