【问题标题】:Steering motion in Godot EngineGodot引擎中的转向运动
【发布时间】:2021-05-20 22:53:09
【问题描述】:

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

现在我有一个正常工作的操纵杆。我将值作为标准化的 Vector2

var joystick_value = joystick.get_value()

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

(平面是 KinematicBody2D)

有什么想法吗?

【问题讨论】:

    标签: game-physics godot


    【解决方案1】:

    速度

    如果我们谈论的是 KinematicBody2D 和 velocity,我们谈论的是类似这样的脚本,给予或接受:

    extends KinematicBody2D
    
    var velocity:Vector2 = Vector2.ZERO # pixels/second
    
    func _physics_process(_delta:float) -> void:
        move_and_slide(velocity)
    

    也许您最好使用speeddirection 而不是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,您可以直接使用它。


    转向

    对于转向,我们将保持速度并改变角度。所以我们想要上面显示的speedangle 版本。当然是旋转:

    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 + 01。而wrapf(4, 5, 10)9,因为它比5 低了110 - 19。希望这是有道理的。

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


    我们还需要angular_speed。即每单位时间角度变化多少(单位为角度/时间)。请注意,这与角度变化的程度不同(单位为角度)。要转换,我们乘以自上次以来经过的时间:

    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)
    

    这是一个带角加速度的版本:

    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)
    

    还有带角度缓动的闪亮版本:

    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()
    

    有效。

    【讨论】:

    • 我知道我应该避免像“谢谢”这样的 cmets 但是哇,我真的没想到有人会给我这样的深入解释 - 所以,真的谢谢你!
    猜你喜欢
    • 2018-12-26
    • 1970-01-01
    • 2021-08-12
    • 1970-01-01
    • 2021-01-26
    • 2020-09-27
    • 1970-01-01
    • 2021-01-18
    • 2017-05-12
    相关资源
    最近更新 更多