Move character in facing direction

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By vonflyhighace2
:warning: Old Version Published before Godot 3 was released.

I’m trying to get my character to turn/rotate in the direction that the player is moving. I tried using the below code but that doesn’t work.

if (moveDirection != Vector3(0,0,0)): 			
var test = Quat().slerp( Quat(Vector3(0,1,0), deg2rad(moveDirection) ),get_process_delta_time() * rotationSmoothing)
var m = Matrix3(test)
set_rotation(m.get_euler().normalized())
:bust_in_silhouette: Reply From: Zylann

Note, if moveDirection != Vector3(0,0,0) looks weird because you use moveDirection like an angle, not a vector.

Maybe this should do the trick, where moveDirection is a single angle in radians:

var current_rot = Quat(get_transform().basis)
var target_rot = Quat(Vector3(0,1,0), moveDirection)

var smooth_rot = current_rot.slerp(target_rot, delta * rotationSmoothing)

set_rotation(Matrix3(smooth_rot).get_euler())

I removed normalizations because they caused trouble, and also changed the slerp because you were starting it from an identity rotation, not the current one.

Hey Zylann I’m trying to move my player similar to a mmo were the camera can pan around the player. the code I have below doesn’t really work and I was hoping you could take a look and see where I’m going wrong with it. Basically what I’m trying to do is rotate and move the character based on the directional buttons [AWSD]. I’m pretty sure that I need to take the camera’s current forward direction into account since the players sense of direction will be based off of it. Appreciate any help. I’m still a noob at the end of the day I suppose.

func PlayerControls():
	
	if(Input.is_action_pressed("move_forward")):# Input.is_key_pressed(KEY_W):
		
		#get the vector that points positive in Z
		var forward = -camera.get_transform().basis.z
		#print(forward.z)
		Motion(0, forward.z);
		Rotation(forward.z);
	
	elif(Input.is_action_pressed("move_backward")): #Input.is_key_pressed(KEY_S):
		
		#get the vector that points negetive in Z
		var backward = camera.get_transform().basis.z
		Motion(0, backward.z);
		Rotation(backward.z);
	
	elif(Input.is_action_pressed("move_left")):# Input.is_key_pressed(KEY_A):
		#get the vector that points positive in X
		var left = -camera.get_transform().basis.x
		#print(left.x)
		Motion(left.x, 0);
		Rotation(left.x);
	
	elif(Input.is_action_pressed("move_right")):# Input.is_key_pressed(KEY_D):
		
		#get the vector that points negetive in X
		var right = camera.get_transform().basis.x
		#print(right.x)
		Motion(right.x, 0);
		Rotation(right.x);  

func Motion(var x, var z):
	
	#calculate movement speed
	var delta = get_fixed_process_delta_time();
	
	if(Input.is_mouse_button_pressed(BUTTON_RIGHT)):
		move_speed = max(min(move_speed + (4 * delta), walk_speed * 2.0), walk_speed);
	else:
		move_speed = max(min(move_speed - ( 4 * delta), walk_speed * 2.0), walk_speed);
		#AnimationControl("walk", hspeed/max_speed);
	
	moveDirection = Vector3(x, 0, z) * move_speed; 
	self.global_translate(Vector3().linear_interpolate(moveDirection, delta));

func Rotation(var dir):
	
	var delta = get_fixed_process_delta_time();
	var current_rot = Quat(get_transform().basis)
	var target_rot = Quat(Vector3(0,1,0), dir)
	var smooth_rot = current_rot.slerp(target_rot, delta * rotation_smoothing)
	set_rotation(Matrix3(smooth_rot).get_euler())

vonflyhighace2 | 2016-11-30 03:11

I fiddled a little and came up with this, I hope this helps:

- Avatar (Node2D) <-- movement.gd
    - Camera <-- tps_camera.gd
    - Visual (TestCube)

movement.gd

extends Spatial

var speed = 5.0
# The visual needs to be separated from the root because we can control its rotation independently
onready var visual = get_node("Visual")
onready var camera = get_node("Camera")


func _ready():
	set_fixed_process(true)


func _fixed_process(delta):
	# Forward as seen by the camera (OpenGL convention)
	var view_forward = -camera.get_transform().basis.z
	var view_right = -camera.get_transform().basis.x
	# Forward as seen by the avatar
	var forward = Vector3(view_forward.x, 0.0, view_forward.z).normalized()
	var right = view_right
	
	# Some classic movement control
	var motion = Vector3()
	if Input.is_key_pressed(KEY_LEFT):
		motion += right
	if Input.is_key_pressed(KEY_RIGHT):
		motion -= right
	if Input.is_key_pressed(KEY_UP):
		motion += forward
	if Input.is_key_pressed(KEY_DOWN):
		motion -= forward
	
	# Calculate next position by adding the bit of distance walked during this frame
	var next_pos = get_translation() + motion.normalized() * (speed * delta)
	set_translation(next_pos)
	# Look forward
	visual.look_at(next_pos+forward, Vector3(0,1,0))

tps_camera.gd

extends Camera

# Distance from the target
var radius = 8.0
# Here we assume the target will be the parent, but could be any node
onready var target = get_parent()

# Horizontal angle
var _yaw = 0
# Vertical angle
var _pitch = 0

# The vertical angle must be limited otherwise we would be able to look upside down
var _pitch_min = -PI/2+0.001
var _pitch_max = PI/2-0.001

# How fast the camera moves when the mouse moves (in degrees per pixel)
var sensitivity = Vector2(1,1)


func _ready():
	set_process_input(true)
	#set_fixed_process(true)


# Technically not needed in the current scene,
# but would be needed if the camera was not a child of the avatar
#func _fixed_process(delta):
#	_update_transform()


func _update_transform():
	var target_pos = target.get_translation()
	# 3D trigonometry: calculate position from X and Y angles.
	# The 3rd one (Z, or roll) would not affect the position so we ignore it
	var pos = Vector3(cos(_yaw) * cos(_pitch), sin(_pitch), sin(_yaw) * cos(_pitch))
	# Adjust distance from target
	pos *= radius
	# Set the actual position and rotation.
	# (note: if a 3rd angle Z is needed, it would affect the up vector, last param)
	look_at_from_pos(target_pos + pos, target_pos, Vector3(0,1,0))


func _input(event):
	# Did the mouse move?
	if event.type == InputEvent.MOUSE_MOTION:
		# Get how many pixels it moved
		var rmotion = event.relative_pos
		# Apply this to rotations
		_yaw += rmotion.x * deg2rad(sensitivity.x)
		_pitch += rmotion.y * deg2rad(sensitivity.y)
		_pitch = clamp(_pitch, _pitch_min, _pitch_max)
		_update_transform()

Example project: http://zylannprods.fr/dl/godot/TpsCamera.zip

Zylann | 2016-11-30 23:35

I took a look at the project you sent me and played with it a bit. The problem is that the character rotates with the camera. For my needs, the character just uses the camera as a reference for sense of direction. I tweaked the project just a bit but now I can’t get rotation right. Can you take a look and see if I’m totally off. Tps Camera Edited

vonflyhighace2 | 2016-12-02 19:51

The character doesn’t rotate, only its visual does. I made it follow the Y rotation just as an example. You’ll notice that the root node only moves, it’s not meant to rotate already, so the camera and the character visual can have their own rotations.

So to fix this you have to modify the rotation of the visual, not the root node itself (which only needs to be moved).

I updated your code and it works now:

	if(motion != Vector3()):
		# Used atan2 but you can also use Vector2(motion.x, -motion.z).angle()
		var angle = atan2(motion.x, -motion.z)
		print("target angle is::", round(rad2deg(angle)), " (", str(motion), ")")
		
		Rotation(angle);
		
		# This cannot work because lerp on an angle will not choose the shortest arc
#		var body_rot = visual.get_rotation()
#		body_rot.y = lerp(body_rot.y, angle, 5 * delta)
#		visual.set_rotation(Vector3(body_rot.x, body_rot.y, body_rot.z))

func Rotation(var angle):
	var delta = get_fixed_process_delta_time()
	var current_rot = Quat(visual.get_transform().basis)
	var target_rot = Quat(Vector3(0,1,0), angle)
	var smooth_rot = current_rot.slerp(target_rot, delta * 5)
	visual.set_rotation(Matrix3(smooth_rot).get_euler())

Alternatively, if you want the camera to be completely separated from the character, you can move it outside of the character tree, which needs the commented section to be activated in tps_camera.gd (see previous post) and the target property to be set.

Zylann | 2016-12-03 00:13

Errata: I was wrong in the comment about angle_to, it will actually return a signed angle.
So the line with atan2 can be replaced by:

var angle = Vector2(motion.x, -motion.z).angle()

It does the same thing anyway :stuck_out_tongue:

Zylann | 2016-12-03 16:44