0 votes

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())
in Engine by (271 points)

1 Answer

+1 vote

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.

by (28,833 points)
edited by

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

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

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

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.

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 :p

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.