I am using a Cinemachine state driver to transition between my 8 directional cameras orbiting my player. Right now my player script is set to a basic isometric character controller:
Player.cs
public float speed = 5f;
Vector3 forward;
Vector3 right;
// Start is called before the first frame update
void Start()
{
forward = Camera.main.transform.forward;
forward.y = 0;
forward = Vector3.Normalize(forward);
right = Quaternion.Euler(new Vector3(0, 90, 0)) * forward;
}
// Update is called once per frame
void Update()
{
if (Input.anyKey)
{
Move();
}
}
void Move ()
{
Vector3 direction = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
Vector3 rightMovement = right * speed * Time.deltaTime * Input.GetAxis("Horizontal");
Vector3 upMovement = forward * speed * Time.deltaTime * Input.GetAxis("Vertical");
Vector3 heading = Vector3.Normalize(rightMovement + upMovement);
transform.forward += heading;
transform.position += rightMovement;
transform.position += upMovement;
}
I want the players move direction to reflect the direction of the camera. For example, using W (from WASD) will always move the player up. Could I edit the script to pick up the direction of each of my virtual cameras and add that to my player controller or is there a better way?
To solve this problem you have to change the movement of the keys according to the angle of the camera. This is done as follows with transform.TransformDirection. When the movement is synchronized with the direction of the camera, it causes the W key to press the character towards the ground, because the angle in front of the camera is inside the ground. To solve the problem, we set y to zero and then normalize the axis.
public float speed = 10f;
void Update()
{
var moveInput = new Vector3(Input.GetAxis("Horizontal"), 0f , Input.GetAxis("Vertical"));
moveInput = Camera.main.transform.TransformDirection(moveInput);
moveInput.y = 0;
moveInput = moveInput.normalized;
transform.position += moveInput * Time.deltaTime * speed;
}
Related
I'm working on developing a game, and my current roadblock is Camera Rotation. I want the mouse to control the camera, and when the camera turns, I want to rotate the player as well. However, the code I am using rotates the player object COMPLETELY with the player, causing the player to turn like this:
The player rotating forward not only looks strange, but causes clipping problems with the terrain. How do I make my code only rotate the player along one axis, while allowing the camera to rotate along any axis (to allow the audience to look around, without the player object being turned upwards or down).
this is the code that is rotating the player:
Quaternion QT = Quaternion.Euler(_LocalRotation.y, _LocalRotation.x, 0);
this._XForm_Parent.rotation = Quaternion.Lerp(this._XForm_Parent.rotation, QT, Time.deltaTime * OrbitDampening);
if (this._XForm_Camera.localPosition.z != this._CameraDistance * -1f)
{
this._XForm_Camera.localPosition = new Vector3(0f, 0f, Mathf.Lerp(this._XForm_Camera.localPosition.z, this._CameraDistance * -1f, Time.deltaTime * ScrollDampening));
}
and this is the complete script I am using:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraOrbit : MonoBehaviour
{
protected Transform _XForm_Camera;
protected Transform _XForm_Parent;
protected Vector3 _LocalRotation;
protected float _CameraDistance = 10f;
public float MouseSensitivity = 4f;
public float ScrollSensitvity = 2f;
public float OrbitDampening = 10f;
public float ScrollDampening = 6f;
public bool CameraDisabled = false;
// Use this for initialization
void Start()
{
this._XForm_Camera = this.transform;
this._XForm_Parent = this.transform.parent;
}
void LateUpdate()
{
if (Input.GetKeyDown(KeyCode.LeftShift))
CameraDisabled = !CameraDisabled;
if (!CameraDisabled)
{
//Rotation of the Camera based on Mouse Coordinates
if (Input.GetAxis("Mouse X") != 0 || Input.GetAxis("Mouse Y") != 0)
{
_LocalRotation.x += Input.GetAxis("Mouse X") * MouseSensitivity;
_LocalRotation.y += Input.GetAxis("Mouse Y") * MouseSensitivity;
//Clamp the y Rotation to horizon and not flipping over at the top
if (_LocalRotation.y < 0f)
_LocalRotation.y = 0f;
else if (_LocalRotation.y > 90f)
_LocalRotation.y = 90f;
}
//Zooming Input from our Mouse Scroll Wheel
if (Input.GetAxis("Mouse ScrollWheel") != 0f)
{
float ScrollAmount = Input.GetAxis("Mouse ScrollWheel") * ScrollSensitvity;
ScrollAmount *= (this._CameraDistance * 0.3f);
this._CameraDistance += ScrollAmount * -1f;
this._CameraDistance = Mathf.Clamp(this._CameraDistance, 1.5f, 100f);
}
}
//Actual Camera Rig Transformations
Quaternion QT = Quaternion.Euler(_LocalRotation.y, _LocalRotation.x, 0);
this._XForm_Parent.rotation = Quaternion.Lerp(this._XForm_Parent.rotation, QT, Time.deltaTime * OrbitDampening);
if (this._XForm_Camera.localPosition.z != this._CameraDistance * -1f)
{
this._XForm_Camera.localPosition = new Vector3(0f, 0f, Mathf.Lerp(this._XForm_Camera.localPosition.z, this._CameraDistance * -1f, Time.deltaTime * ScrollDampening));
}
}
}
A picture of my heirarchy:
(Please note that Player is an empty object containing all of my scripts and physics components, whereas the GFXs component is simply the model, with an animator component clean of any physics components.)
It sounds like what you want is basically to rotate the player around its local Y axis until it is facing the same direction as the camera. You can find out if things are facing the same direction using Vector3.SignedAngle and passing in the axis that you care about. Here is some sample code that should get you going.
float turnThresholdDegrees = 15;
float playerTurnSpeed = 1;
// Get delta angle in degrees on the Y axis between the direction player is facing and the direction camera is facing
float angleDelta = Vector3.SignedAngle(player.transform.forward, camera.transform.forward, Vector3.up);
// Calculate a rotation speed, clamping it to 0 when the angle delta is < some threshold
float rotateSpeed = Mathf.Abs(angleDelta) > turnThresholdDegrees ? Mathf.Sign(angleDelta) * playerTurnSpeed : 0;
// Rotate the player around its local Y axis by the rotation speed
player.transform.Rotate(0, rotateSpeed * Time.deltaTime, 0, Space.Self);
Guys I am making an endless runner game which will be controlled with body position.
I'm trying to move character left or right (x-axis) with my body position by using Kinect sensor. The character is free to move forward (z-axis) with Time.deltaTime. The character has CharacterController and script attached. The code is below for movement:
CharacterController controller;
KinectManager kinectManager;
float speed = 5.0f
Vector3 moveDir;
void Update()
{
moveDir = Vector3.zero;
moveDir.z = speed;
moveDir.x = kinectManager.instance.BodyPosition * speed;
//controller.Move(moveDir * Time.deltaTime);
controller.Move(new Vector3 (moveDir.x, 0, moveDir.z * Time.deltaTime));
}
This statement controller.Move(moveDir * Time.deltaTime); keeps moving character to the left or right because x position is being incremented with Time.deltaTime so I wanted to restrict that and I changed that to controller.Move(new Vector3 (moveDir.x, 0, moveDir.z * Time.deltaTime));.
Now whats happening is the character is stuck at the same position. I can move left or right with body position but cannot move forward. What am I missing here?
Please help.
Indentifying issues
First try to watch you axis carefully that where is your gameobject y axis because you are assigning 0 value to it . The below code will help you find the issue and resolve it .
Solution
void Update()
{
if (controller.isGrounded)
{
// We are grounded, so recalculate
// move direction directly from axes
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0.0f, Input.GetAxis("Vertical"));
moveDirection = transform.TransformDirection(moveDirection);
moveDirection = moveDirection * speed;
if (Input.GetButton("Jump"))
{
moveDirection.y = jumpSpeed;
}
}
// Apply gravity
moveDirection.y = moveDirection.y - (gravity * Time.deltaTime);
// Move the controller
controller.Move(moveDirection * Time.deltaTime);
}
I have made a script with movement, that finally works as i want it to, except one thing... I want it to be a first person game, but the way the movement works now, is on the global axis, which means that W is always torwards one specific direction, no matter what direction my camera is turning... How do i fix this? i want the movement to stay how it is, but with the W key to always be forward depending on the way the camera or player is looking.
Please let me know how my script would look edited, or atleast what part i have to change.
I would also like to add that i would love to be able to do a wall jump, but i am not sure how to add that behavior.
Here is my movement script:
public class MovementScript : MonoBehaviour {
public float speed;
public float jumpforce;
public float gravity = 25;
private Vector3 moveVector;
private Vector3 lastMove;
private float verticalVelocity;
private CharacterController controller;
// Use this for initialization
void Start () {
controller = GetComponent<CharacterController> ();
//Låser og gemmer musen
Cursor.lockState = CursorLockMode.Locked;
}
// Update is called once per frame
void Update () {
//Låser musen op
if (Input.GetKeyDown ("escape"))
Cursor.lockState = CursorLockMode.None;
moveVector = Vector3.zero;
moveVector.x = Input.GetAxis("Horizontal");
moveVector.z = Input.GetAxis("Vertical");
if (controller.isGrounded) {
verticalVelocity = -1;
if (Input.GetButton("Jump")) {
verticalVelocity = jumpforce;
}
} else {
verticalVelocity -= gravity * Time.deltaTime;
moveVector = lastMove;
}
moveVector.y = 0;
moveVector.Normalize ();
moveVector *= speed;
moveVector.y = verticalVelocity;
controller.Move (moveVector * Time.deltaTime);
lastMove = moveVector;
}
}
You need to go from local to world space:
for example when you want to move on the x-axis regarding the player's orientation, the local vector is (1, 0, 0), which you get from your input axis, but the CharacterController need a world based direction (see the doc of the Move function)
A more complex move function taking absolute movement deltas.
To get this, use Transform.TransformDirection like this
worldMove = transform.TransformDirection(moveVector);
controller.Move (worldMove * Time.deltaTime);
EDIT regarding your issue with the controller moving a bit after releasing the input:
That's because GetAxis gives you a smoothed value. Replace GetAxis by GetAxisRaw and it should work
Modify the final moving code to
controller.Move(moveVector.z * transform.forward * Time.deltaTime);
controller.Move(moveVector.x * transform.right * Time.deltaTime);
controller.Move(moveVector.y * transform.up * Time.deltaTime);
Alternatively, suppose your character is only rotated about the Y axis, you can look into rotating your moveVector by your character's Y rotation so moveVector's forward points parallel to your chracter's forward.
I found a good explaination of rotating a vector here: https://answers.unity.com/questions/46770/rotate-a-vector3-direction.html
Rotating a vector:
moveVector = Quaternion.Euler(0, transform.eulerAngles.y, 0) * moveVector;
I have colliders where i can turn to the sides and i want camera to rotate as a player turn to the side. I'm using Mathf.LerpAngle for that, but when I press the key for turn to side, camera is rotating in loop. How can I make rotation stop?
The problem is that everytime I turn player should go -90 degrees to the left +90 to the right and there will be more turns so i can't use functions for setting rotation.
I was already trying to make it stop by that if statement with (lAngle > 90f)
float lAngle = Mathf.LerpAngle(minAngle, lMaxAngle, Time.deltaTime);
float rAngle = Mathf.LerpAngle(minAngle, rMaxAngle, Time.deltaTime);
Quaternion leftRotation = Quaternion.Euler(new Vector3 (0, lAngle, 0));
Quaternion rightRotation = Quaternion.Euler(new Vector3 (0, rAngle, 0));
transform.position = player.transform.position + offSet;
transform.LookAt (player.transform);
if (Input.GetKeyDown (KeyCode.LeftArrow) && GameObject.Find("Player").GetComponent<PlayerMovement>().turn) {
turnLeft = true;
} else if (Input.GetKeyDown (KeyCode.RightArrow) && GameObject.Find("Player").GetComponent<PlayerMovement>().turn) {
turnRight = true;
}
if(turnLeft) {
offSet = leftRotation * offSet;
transform.position = player.transform.position + offSet;
transform.LookAt (player.transform);
if (lAngle > 90f)
turnLeft = false;
}
if(turnRight) {
offSet = rightRotation * offSet;
transform.position = player.transform.position + offSet;
transform.LookAt (player.transform);
if (rAngle < -90f)
turnRight = false;
}
An alternative would be to use Quaternion.RotateTowards, as this allows rotations to negative values without the GameObject infinitely rotating (as it can't approach a negative value). Below you can see I'm storing the initial value of the GameObject's rotation, as a Vector3, before modifying this value whenever A or D keys are pressed. The rotation of the transform is then set to the result of Quaternion.RotateTowards.
public class FixedRotate : MonoBehaviour
{
[SerializeField]
private float m_rotationAngle;
[SerializeField]
private float m_rotationSpeed;
private Vector3 m_targetRotation;
public void Start()
{
m_targetRotation = transform.eulerAngles;
}
public void Update()
{
//Left
if(Input.GetKeyDown(KeyCode.A))
m_targetRotation.y -= m_rotationAngle;
//Right
if (Input.GetKeyDown(KeyCode.D))
m_targetRotation.y += m_rotationAngle;
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.Euler(m_targetRotation), m_rotationSpeed);
}
}
The unfortunate thing about this approach is that you can't specify the amount of time a rotation takes, only the incremental value of which the object will rotate by. An alternative approach would be to use Mathf.Clamp to add Time.deltaTime / rotationTime to the existing y rotation of the GameObject and stop once it's reached +/- 90. Hope this helps.
i have a character controller which jumps but while jumping i want to change the x position of the character so basically he can turn while jumping, this is my attempt so far
//start of character controller
CharacterController controller = GetComponent<CharacterController>();
if (controller.isGrounded) {
//get the player vector movement vector
movePlayer = new Vector3(Input.acceleration.x,0,1);
//float h = Input.acceleration.x;
//translate the players movement
transform.Translate(movePlayer * moveSpeed * Time.deltaTime);
//the run animation
animation.CrossFade("run");
//restrict the movement of x-axis for player
Vector3 pos = transform.position;
pos.x = Mathf.Clamp(pos.x, -3.0f, 3.0f);
transform.position = pos;
if (Input.GetButton("Jump")){
//my ptoblem is here, the x axis on the vector3 is not happening
movePlayer =transform.TransformDirection(new Vector3(Input.acceleration.x,jumpSpeed,forwardJumpSpeed));
}
}
// attach the gravity and move controller
movePlayer.y -= gravity * Time.deltaTime;
controller.Move(movePlayer * Time.deltaTime);
current code:
void Update() {
CharacterController controller = GetComponent<CharacterController>();
//get the player vector movement vector
movePlayer = new Vector3(Input.GetAxis("Horizontal"),0,1);
//float h = Input.acceleration.x;
//translate the players movement
transform.Translate(movePlayer * moveSpeed * Time.deltaTime);
//the run animation
animation.CrossFade("run");
//restrict the movement of x-axis for player
Vector3 pos = transform.position;
pos.x = Mathf.Clamp(pos.x, -3.0f, 3.0f);
transform.position = pos;
if (controller.isGrounded) {
if (Input.GetButton("Jump")){
movePlayer =transform.TransformDirection(new Vector3(Input.acceleration.x,jumpSpeed,forwardJumpSpeed));
}
}
movePlayer.y -= gravity * Time.deltaTime;
controller.Move(movePlayer * Time.deltaTime);
this is my current code, the jump if statement is inside the if(controller.isGrounded) the character still moves but when i press spacebar it deosnt jump anymore.
When the player jumps its no longer grounded so any code under the if(controller.isGrounded) is not called. Put your movement code outside it but keep the jumping code inside it and it'll work fine.
// movement code goes here
// Keep the jumping code inside this if-statement
if (controller.isGrounded) {
if (Input.GetButton("Jump")){
movePlayer =transform.TransformDirection(new Vector3(Input.acceleration.x,jumpSpeed,forwardJumpSpeed));
}