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