Task is to make a rotation of character around its vertical axis. as input we want to use a round motion by finger at any place of screen. we tried to use two methods firts by using ray but then you need to drag finger around character ( working but not like we need )
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
and second one is with InputEventScreenDrag, its working but only in one direction. help here plz.
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)
This took me a while to figure out between testing and solving some jitter problem… I'll explain.
First of all, I decided to start with the technique I describe in Holding screen touch in godot. So the start of my _input
looks like this:
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)
And for the rotation, I decided to use the concept of curl. And by that I mean, I'll be using the cross product to figure out in what direction to rotate. This way we don't need to define a center of rotation (so you can do circles in any part of the screen and it should still work).
Let us declare curl
:
var curl:float = 0.0
And since we will be doing cross product, we need to hold the last relative, so we can do a cross product with the current one. So declare last_relative
:
var last_relative:Vector3 = Vector3.ZERO
And let us do the cross product:
var relative := Vector3(event.relative.x, event.relative.y, 0).normalized()
curl = relative.cross(last_relative).z
last_relative = relative
However, we need to make sure to reset that for the next drag. We will set it when is_drag
is false (that is on press and on release):
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
Next issue on the table is rotation speed. I ended up using _physics_process
. Which also allows us to use delta
, so that rotation speed is stable.
Speaking of rotation speed, declare rot_speed
:
var rot_speed:float = TAU
TAU
means one rotation per second.
And now we can write _physics_process
:
func _physics_process(delta: float) -> void:
rotate_y(sign(curl) * rot_speed * delta)
Next up is jitter. One part of it was input emulation. So I disabled it. But that was not enough. To fully solve jitter, _input
will not write curl
, but target_curl
instead.
First declare target_curl
:
var target_curl:float = 0.0
And then set it in _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
Now, we will ease curl
into target_curl
in physics_process
:
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)
Finally, when the user is holding position (not dragging), we don't get any _input
. Which means the user can do a little arc, and hold position, and the physics body would continue to rotate. We solve that by erasing target_curl
every physics frame.
Complete code listing:
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
Tested on Windows with mouse, and on Android with touch.