I'm using a controller script from the official Unity website to move my character around. I'm also using a script to turn the camera using the mouse. Everything works fine until the character looks around and faces a different direction. Then the WASD controls move them according to the original orientation. For example, if I turn 180 degrees W moves me backward and S moves me forward.
I've tried to figure this out using transform.forward but I don't really know what I'm doing.
The movement script:
CharacterController characterController;
public float speed = 6.0f;
public float jumpSpeed = 8.0f;
public float gravity = 20.0f;
private Vector3 moveDirection = Vector3.zero;
void Start()
{
characterController = GetComponent<CharacterController>();
}
void Update()
{
if (characterController.isGrounded)
{
// We are grounded, so recalculate
// move direction directly from axes
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0.0f, Input.GetAxis("Vertical"));
moveDirection *= speed;
if (Input.GetButton("Jump"))
{
moveDirection.y = jumpSpeed;
}
}
// Apply gravity. Gravity is multiplied by deltaTime twice (once here, and once below
// when the moveDirection is multiplied by deltaTime). This is because gravity should be applied
// as an acceleration (ms^-2)
moveDirection.y -= gravity * Time.deltaTime;
// Move the controller
characterController.Move(moveDirection * Time.deltaTime);
}
Thank you for any help :)
You can use Transform.TransformDirection to convert from local direction to world direction. Call that with the local direction you want to move and it will return the corresponding world direction which you can give to CharacterController.Move:
void Update()
{
if (characterController.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. Gravity is multiplied by deltaTime twice (once here, and once below
// when the moveDirection is multiplied by deltaTime). This is because gravity should be applied
// as an acceleration (ms^-2)
moveDirection.y -= gravity * Time.deltaTime;
// Move the controller
characterController.Move(worldMoveDirection * Time.deltaTime);
}
In fact, the old documentation for CharacterController.Move used this very method!
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's because Move uses world space coordinates but you generating local.
Try change
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0.0f, Input.GetAxis("Vertical"));
to
moveDirection = transform.right * Input.GetAxis("Horizontal") + transform.forward * Input.GetAxis("Vertical");
Related
I'm trying to make a game using planet gravity physics. I'm stuck in rotating player.
private void FixedUpdate()
{
ProcessInput();
Vector3 gravityUp = (transform.position - gravityTarget.position).normalized;
Vector3 bodyUp = transform.up;
rb.AddForce(gravityUp * gravity);
Quaternion targetRotation = Quaternion.FromToRotation(bodyUp , gravityUp) * transform.rotation;
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation , 50 * Time.fixedDeltaTime);
transform.rotation =targetRotation;
Quaternion desRot = Quaternion.LookRotation(moveDir);
desRot = Quaternion.RotateTowards(transform.rotation, desRot , 360 * Time.deltaTime);
transform.rotation = desRot;
}
private void Update()
{
rb.MovePosition(rb.position + transform.TransformDirection(moveDir) * 15f * Time.deltaTime);
}
private void ProcessInput()
{
moveDir = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized;
}
Lets say I pressed Left Arrow and wanted to player to go left and turn left at the same time. The problem is transform.TransformDirection(moveDir). When I make the player turn to the left, player starts the move its left side instead of the world position. If I only use moveDir for positioning, it goes out of the world. How can I make this work?
You can see the video here!
Thanks for helping!
I am trying to connect up to the new input system specifically with my Nintendo Switch Pro controller (wireless). At runtime the character only moves in a large circle. You can only slightly modify the movement with the controller. What am I doing wrong? I attached photos. Action Map
Action Map 2
public class PlayerController : MonoBehaviour {
public float moveSpeed = 5.0f;
public float rotationSpeed = 280.0f;
float horizontal;
float vertical;
// Update is called once per frame
private void Update()
{
Vector3 moveDirection = Vector3.forward * vertical + Vector3.right * horizontal;
Vector3 projectedCameraForward = Vector3.ProjectOnPlane(Camera.main.transform.forward, Vector3.up);
Quaternion rotationToCamera = Quaternion.LookRotation(projectedCameraForward, Vector3.up);
moveDirection = rotationToCamera * moveDirection;
Quaternion rotationToMoveDirection = Quaternion.LookRotation(moveDirection, Vector3.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, rotationToCamera, rotationSpeed * Time.deltaTime);
transform.rotation = Quaternion.RotateTowards(transform.rotation, rotationToMoveDirection, rotationSpeed * Time.deltaTime);
transform.position += moveDirection * moveSpeed * Time.deltaTime;
}
public void onMoveInput(float horizontal, float vertical)
{
this.horizontal = horizontal;
this.vertical = vertical;
Debug.Log($"Player Input: {vertical}, {horizontal}");
}
}
Hi there it seems that you have created the Action Maps, but you have not used them in your code. I believe to fix this you would need something more like this found on Unity documentation regarding Input Action: "Gamepad.current.leftStick.x.ReadValue();" wherever you have moveDirection on the right-hand side.
CharacterController _charController;
// ...
moveInputX = Input.GetAxis("Horizontal") * speed;
moveInputZ = Input.GetAxis("Vertical") * speed;
Vector3 movement = new Vector3(moveInputX, 0, moveInputZ);
movement = Vector3.ClampMagnitude(movement, speed);
movement.y = -9.81f;
movement *= Time.deltaTime;
movement = transform.TransformDirection(movement);
_charController.Move(movement);
I have a player that rotates and I want to make him move to his local direction but i don't understand when I remove movement = transform.TransformDirection(movement); from the code it moves to global direction which doesn't make any sense because this line of code converts the direction from local to global space.
It's because CharacterController.Move expects a vector in world space. Sadly, the documentation on that has never been clear about that.
When you calculate movement here:
Vector3 movement = new Vector3(moveInputX, 0, moveInputZ);
You can see that it doesn't take the rotation of the character's transform into account. In order for it to do that, you want to find what this vector looks like in world space when interpreted as being in the character's local space. That's exactly what this line does:
movement = transform.TransformDirection(movement);
It converts movement from local space to world space.
The 2018.1 CharacterController.Move documentation actually uses transform.TransformDirection in its example:
using UnityEngine;
using System.Collections;
// The GameObject is made to bounce using the space key.
// Also the GameOject can be moved forward/backward and left/right.
// Add a Quad to the scene so this GameObject can collider with a floor.
public class ExampleScript : MonoBehaviour
{
public float speed = 6.0f;
public float jumpSpeed = 8.0f;
public float gravity = 20.0f;
private Vector3 moveDirection = Vector3.zero;
private CharacterController controller;
void Start()
{
controller = GetComponent<CharacterController>();
// let the gameObject fall down
gameObject.transform.position = new Vector3(0, 5, 0);
}
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);
}
}
Your movement vector is in local space
You want the player to move forward locally and move horizontally locally and so you construct a forward/rightward movement vector based on the player input. This vector is in local coordinates and you TransformDirection back to world space before applying the result to world position with _charController.Move (presumably, you didn't include that method's code).
I'm trying to add jumping to the controls of our control script but it just doesn't work yet and I'm getting kind of frustrated because I tried a lot to make it work. I don't use rigidbodies and rather want to use script based physics and - but later - raycasts (to detect ground). It's 3D but with a fixed perspective because the characters are sprites (we are thinking about trying a style similar to Don't Starve, Paper Mario etc.). And the thing I want to put in for now is to jump upwards when the character stands still and later on jumps that also let the character travel a bit of distance when the character moves. Well, you get the picture. For that I first need to get jumping working at all - and also gravity to put the character back down while also considering that the character could land on a ground that has a different height than the starting ground.
What happens now is that the character jumps just a tiny little bit, like not even a hop, and then falls down endlessly - or goes up endlessly when jump is pressed again. So, how do I fix that?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Controls3D : MonoBehaviour
{
public float player3DSpeed = 5f;
private bool IsGrounded = true;
private const float groundY = (float) 0.4;
private Vector3 Velocity = Vector3.zero;
public Vector3 gravity = new Vector3(0, -10, 0);
void Start()
{
}
void Update()
{
if (IsGrounded)
{
if (Input.GetButtonDown("Jump"))
{
IsGrounded = false;
Velocity = new Vector3(Input.GetAxis("Horizontal"), 20, Input.GetAxis("Vertical"));
}
Velocity.x = Input.GetAxis("Horizontal");
Velocity.z = Input.GetAxis("Vertical");
if (Velocity.sqrMagnitude > 1)
Velocity.Normalize();
transform.position += Velocity * player3DSpeed * Time.deltaTime;
}
else
{
Velocity += gravity * Time.deltaTime;
Vector3 position = transform.position + Velocity * Time.deltaTime;
if (position.y < groundY)
{
position.y = groundY;
IsGrounded = true;
}
transform.position = position;
}
}
}
If i were you, i think i wouldve changed the whole thing into a character controller, as this simplifies the process, a ####ton :P
If you figure out you do want to use the CC. This is the solution i usually use:
CharacterController controller = GetComponent<CharacterController>();
// is the controller on the ground?
if (controller.isGrounded)
{
//Feed moveDirection with input.
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
moveDirection = transform.TransformDirection(moveDirection);
//Multiply it by speed.
moveDirection *= speed;
//Jumping
if (Input.GetButton("Jump"))
moveDirection.y = jumpSpeed;
}
//Applying gravity to the controller
moveDirection.y -= gravity * Time.deltaTime;
//Making the character move
controller.Move(moveDirection * Time.deltaTime);
moveDirection is a Vector3 Type variable, and speed and jumpSpeed are public float values used to modify the speed.
Please NOTE: Character Controllers, let you program your own Physics.
I have a script which allows you to control a player, and jump.
However, I'm trying to make it so the player is constantly moving, and not controllable via WASD keys on the keyboard.
Whenever I try to only use controller.Move() my Gravity function goes away.
Right now, with this code Gravity works, but WASD is enabled as-well.
My question is: How can I make this code make my player constantly move, and still use gravity?
using UnityEngine;
using System.Collections;
public class PlayerMotor : MonoBehaviour {
public float speed = 6.0F;
public float jumpSpeed = 8.0F;
public float gravity = 20.0F;
private Vector3 moveDirection = Vector3.back;
void Update() {
CharacterController controller = GetComponent<CharacterController>();
if (controller.isGrounded)
{
controller.Move (Vector3.back * Time.deltaTime);
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
moveDirection = transform.TransformDirection(moveDirection);
moveDirection *= speed;
if (Input.GetButton("Jump"))
moveDirection.y = jumpSpeed;
}
moveDirection.y -= gravity * Time.deltaTime;
controller.Move(moveDirection * Time.deltaTime);
}
}
Whenever I try to only use controller.Move() my Gravity function goes away
It is an expected behavior as stated in documentation: https://docs.unity3d.com/ScriptReference/CharacterController.Move.html
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
Instead of getting input from player, specify your own moveDirection. For example: moveDirection = new Vector3(1, 0, 1);
Take a look at the docs for possible values: https://docs.unity3d.com/ScriptReference/Input.GetAxis.html
A side note: CharacterController controller = GetComponent<CharacterController>();
I know you copy from the docs but GetComponent every Update is not performance wise. Cache it instead!