用手指旋转字符

rotation of character by finger

任务是围绕垂直轴旋转角色。作为输入,我们想在屏幕的任何位置使用手指的圆周运动。我们首先尝试通过使用射线来使用两种方法,但随后您需要在角色周围拖动手指(有效但不像我们需要的那样)

func _process(_delta):
    look_at_cursor()

func look_at_cursor():
    var player_position = global_transform.origin
    var drop_plane = Plane(Vector3(0, 1, 0), player_position.y)
    var mouse_position = get_viewport().get_mouse_position()
    var ray_length  = 1000
    var ray_start = $Position3D/Camera.project_ray_origin(mouse_position)
    var end_ray = ray_start + $Position3D/Camera.project_ray_normal(mouse_position) * ray_length
    var cursor_pos = drop_plane.intersects_ray(ray_start, end_ray)
    look_at(cursor_pos, Vector3.UP)
    shoot_point = end_ray

第二个是与 InputEventScreenDrag 一起使用的,它可以工作,但只能在一个方向上工作。请在这里帮忙。

func _input(event):
    if event is InputEventScreenDrag:
            var rotx = event.relative.x * rot_speed 
            var roty = event.relative.y * rot_speed
            #if rotx < 0:
            #    rotx = - rotx
            #if roty < 0:
            #    roty = - roty
            #rotate_y(rotx+roty)

我花了一段时间才弄清楚测试和解决一些抖动问题……我会解释的。

首先,我决定从我在 中描述的技术开始。所以我的 _input 的开头是这样的:

func _input(event:InputEvent):
    if not event is InputEventMouse\
      and not event is InputEventScreenDrag\
      and not event is InputEventScreenTouch:
        return

    var is_left_click:bool = event is InputEventMouse\
      and ((event as InputEventMouse).button_mask & BUTTON_LEFT) != 0
    var is_drag := event is InputEventScreenDrag\
      or (event is InputEventMouseMotion and is_left_click)

对于旋转,我决定使用 curl 的概念。我的意思是,我将使用叉积来确定旋转的方向。这样我们就不需要定义旋转中心(所以你可以在屏幕的任何部分做圆圈,它应该仍然有效)。

让我们声明curl:

var curl:float = 0.0

因为我们要做叉积,所以我们需要保留最后一个亲戚,这样我们就可以和当前的亲戚做叉积。所以声明 last_relative:

var last_relative:Vector3 = Vector3.ZERO

让我们做叉积:

    var relative := Vector3(event.relative.x, event.relative.y, 0).normalized()
    curl = relative.cross(last_relative).z
    last_relative = relative

但是,我们需要确保为下一次拖动重置它。我们将在 is_drag 为 false 时设置它(即新闻发布时):

    if is_drag:
        var relative := Vector3(event.relative.x, event.relative.y, 0).normalized()
        curl = relative.cross(last_relative).z
        last_relative = relative
    else:
        last_relative = Vector3.ZERO
        curl = 0.0

table 的下一期是旋转速度。我最终使用了 _physics_process。这也允许我们使用delta,所以旋转速度是stable。

说到旋转速度,声明rot_speed:

var rot_speed:float = TAU

TAU表示每秒转一圈

现在我们可以写 _physics_process:

func _physics_process(delta: float) -> void:
    rotate_y(sign(curl) * rot_speed * delta)

接下来是抖动。其中一部分是输入仿真。所以我禁用了它。但这还不够。为了彻底解决抖动,_input不会写curl,而是写target_curl

首先声明target_curl:

var target_curl:float = 0.0

然后在_input中设置:

    if is_drag:
        var relative := Vector3(event.relative.x, event.relative.y, 0).normalized()
        target_curl = relative.cross(last_relative).z
        last_relative = relative
    else:
        last_relative = Vector3.ZERO
        target_curl = 0.0

现在,我们将在physics_process中将curl简化为target_curl

func _physics_process(delta: float) -> void:
    if target_curl > curl:
        curl += delta
        if curl > target_curl:
            curl = target_curl
    elif target_curl < curl:
        curl -= delta
        if curl < target_curl:
            curl = target_curl

    rotate_y(sign(curl) * rot_speed * delta)

最后,当用户保持位置(而不是拖动)时,我们没有得到任何 _input。这意味着用户可以做一点弧线,并保持位置,物理体将继续旋转。我们通过擦除 target_curl 每个物理框架来解决这个问题。

完整代码清单:

extends KinematicBody

var rot_speed:float = TAU
var last_relative:Vector3 = Vector3.ZERO
var curl:float = 0.0
var target_curl:float = 0.0

func _input(event:InputEvent):
    if not event is InputEventMouse\
      and not event is InputEventScreenDrag\
      and not event is InputEventScreenTouch:
        return

    var is_left_click:bool = event is InputEventMouse\
      and ((event as InputEventMouse).button_mask & BUTTON_LEFT) != 0
    var is_drag := event is InputEventScreenDrag\
      or (event is InputEventMouseMotion and is_left_click)

    if is_drag:
        var relative := Vector3(event.relative.x, event.relative.y, 0).normalized()
        target_curl = relative.cross(last_relative).z
        last_relative = relative
    else:
        last_relative = Vector3.ZERO
        target_curl = 0.0

func _physics_process(delta: float) -> void:
    if target_curl > curl:
        curl += delta
        if curl > target_curl:
            curl = target_curl
    elif target_curl < curl:
        curl -= delta
        if curl < target_curl:
            curl = target_curl

    rotate_y(sign(curl) * rot_speed * delta)
    target_curl = 0.0

在 Windows 上用鼠标测试,在 Android 上用触摸测试。