- Home /
How do I face my character in the direction the player is moving?
This seems like it should be something simple, but how do I simply set the character the character is using in the direction the player is moving in? I've been playing with this topic all day and haven't found anything that even remotely works. If anyone has played any Diablo style action adventure games, then you can imagine the way this functions. The rotation should happen on the y axis as the character on the screen adjusts to whatever direction the player is moving in.
All of the options I've looked at does some crazy rotation stuff with the transform, and that just seems overly complicated for what should be:
Moving left? Character faces left.
Instantly looking right? Character faces right.
Not sure what's going on with this, but my theory says take whatever direction the movement vector is going, then look in that direction.
Anyone have any ideas?
Answer by janzdott · Apr 07, 2013 at 04:59 AM
You can set the rotation of the character using Quaternion.LookRotation. For example..
GameObject character;
character.transform.rotation = Quaternion.LookRotation(Vector3.forward, Vector3.up);
That will make the character face forward. You can change the first Vector3 to any direction you want, and the character will face that direction
Jarz, beginners should really just use "LookAt" rather than touching quaternions for any reason
Thanks for the assist. I tried both ways and neither worked. Whebert's suggestion caused the graphic to turn strange directions and then it stopped moving in the direction the controller was pointing in.
I tried janzdott's idea by changing the forward Vector3, but the movement got all strange and I started getting errors that the rotation viewing vector is 0. The rotation does seem fine, though. However, now the movement is all messed up.
I'm pretty new to Unity, but this seems like such a simple thing. Something that should have been done many many times. Why is this so hard? Ugh.
Sorry guys, just a little frustrated. Been messing around with this for days now.
Anyway I have... (all in Update())
CharacterController Character$$anonymous$$ove = GetComponent<CharacterController>();
float cordX = Input.GetAxis("Horizontal");
float cordZ = Input.GetAxis("Vertical");
private Vector3 direction = Vector3.zero;
public float moveSpeed = 10.0f;
float cordX = Input.GetAxis("Horizontal");
float cordZ = Input.GetAxis("Vertical");
if (Character$$anonymous$$ove.isGrounded) {
direction = new Vector3(cordX, 0, cordZ);
direction = transform.TransformDirection(direction);
direction *= moveSpeed;
}
direction.y -= gravity * Time.deltaTime;
rotationDirection = new Vector3(cordX, 0, cordZ);
transform.rotation = Quaternion.LookRotation(rotationDirection, Vector3.up);
Character$$anonymous$$ove.$$anonymous$$ove(direction * Time.deltaTime);
I figured that something in there is causing the movement to be all messed up once the rotation is in. Perhaps you guys can see something I'm missing.
That's great if you want the character to look in one direction :P
Thank You So $$anonymous$$uch You Saved $$anonymous$$y Life :')
Answer by whebert · Apr 07, 2013 at 05:03 AM
Apply this to your game object to look in the direction of your movementVector:
transform.LookAt(transform.position + movementVector);
EDIT: (I first responded in a comment then realized I should've edited my answer)
Your sample code helps us understand what you want to accomplish a bit better. I think there are a couple of reasons you're not seeing what you want to see with your current code.
One issue I see is that you're moving your CharacterController in a relative direction (using the direction = transform.TransformDirection(direction)
), but rotating with the world absolute of Vector3(cordX, 0, cordZ)
, so the two won't match.
And also, since your relative direction changes every single frame based on your new forward direction, your movement direction will probably just be spinning around your current location since, say, moving to the right relative to where you are will make you go in a tiny circle.
The error you see about the look rotation viewing vector being 0 is that you're not checking for your direction vector to be (0,0,0), which will cause that error.
If you want the relative movement, you might want to use a damping so that you won't spin in place every frame. I took your code and made a few modifications. Not sure if this is what you want, and I don't know what else you have attached to this game object besides your CharacterController, but the following might do something like you want?
using UnityEngine;
using System.Collections;
public class CCTest : MonoBehaviour {
public float rotateDegreesPerSecond = 180.0f;
private Vector3 direction = Vector3.zero;
public float moveSpeed = 10.0f;
public float gravity = 10.0f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
CharacterController CharacterMove = GetComponent<CharacterController>();
float cordX = Input.GetAxis("Horizontal");
float cordZ = Input.GetAxis("Vertical");
if (CharacterMove.isGrounded) {
direction = new Vector3(cordX, 0, cordZ);
direction = transform.TransformDirection(direction);
}
// If direction is not zero, rotate towards direction (via rotateDegreesPerSecond) and move CharacterController in current forward vector
if(direction != Vector3.zero)
{
// Take care of direction being directly behind our current forward vector since the
// Quaternion.RotateTowards will continuously alternate between right and left rotations to get there,
// resulting in never getting there, just wiggling around the current transform.rotation.
if(Vector3.Angle(transform.forward, direction) > 179)
{
// This will cause us to always turn to the right to go the opposite direction
direction = transform.TransformDirection(new Vector3(.01f, 0, -1));
}
transform.rotation = Quaternion.RotateTowards( transform.rotation, Quaternion.LookRotation(direction), rotateDegreesPerSecond * Time.deltaTime);
Vector3 moveDirection = transform.forward;
moveDirection.y -= gravity * Time.deltaTime;
CharacterMove.Move(moveDirection * moveSpeed * Time.deltaTime);
}
}
}
Be careful with this one - movementVector.y must be zero or your dude will tilt. Hilarious, but probably not the intended effect. :)
Actually I meant dot-zee. $$anonymous$$oving between Blender and Unity sucks. edit- wait, no, that's blender dangit
yeah, you have to look at a point on your movement plane. If your dude moves on the X/Z plane like most flat games, the Y of movement has to be zero so the transform looks along its current X/Z plane ins$$anonymous$$d of looking up or down
I mean I guess to be technically most correct, if you want to avoid tilt, you should be sure that the movement vector lies on the plane of expected facing directions of the character. And if tilt is a thing you want - like with a spaceship - then never$$anonymous$$d, tilt away.
Yeah, I didn't know what type of game OP needed this for, but sounded like it was using fairly simple movementVectors, like (1,0,0) or (-1,0,0), at least in his example. But like you said, if the game is only moving in XY plane, just make sure Y component is 0, and in any case, make sure your movement vector is not Vector.zero.
Thanks for the information, Whebert. Unfortunately, now the character won't move at all. I have to get some work done around the house, but once I finish I'll look more closely at what was done.
Answer by Dracorat · Apr 08, 2013 at 05:26 PM
I'm writing a game with Diablo-style movement.
I have placed the "ground plane" at height 0 and turned off its material renderer (so it's invisible) There is no 3rd dimension (hills) in my game, but you could easily account for height in yours by adding a fixed value along the Y axis with your ground-based collider. (I have 0 hard coded at the appropriate spot.)
At any rate, here is the actual code that makes the character look at the mouse (and even better, I don't do it right away, I do it over time so there's a rotation speed.)
using UnityEngine;
using System.Collections;
public class MainMovement : MonoBehaviour {
private float lookSpeed = 200;
void Start () {
}
void FaceInput() {
GameObject shipModel = GameObject.Find("ShipModel"); // Necessary because I have a few objects all packaged together in to one prefab and not all of it needs to rotate.
Ray mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hitLocation;
if(Physics.Raycast(mouseRay, out hitLocation)){
Vector3 deltaLook = new Vector3(hitLocation.point.X, 0, hitLocation.Z) - this.transform.position; // Here, 0 is a hard-locked Y value. This is where you'd account for terrain height if you wanted. Most likely by using the model's Y value - not the raycast Y value so that the model doesn't "tilt" up or down.
Quaternion targetRotation = Quaternion.LookRotation(deltaLook);
shipModel.transform.rotation = Quaternion.RotateTowards(shipModel.transform.rotation, targetRotation, Time.deltaTime * lookSpeed); // LookAt is immediate, RotateTowards will go over time
}
}
void Update () {
FaceInput();
}
}