I use rigidbody.MovePosition to move around my character , and have a camera following it. The problem is when i switch directions suddenly the player will teleport a bit instead of smoothly moving in the opposite direction of motion. The scripts for the player and Camera are set to FixedUpdate , if I try moving camera to a LateUpdate then the whole thing jitters a lot.
Player Script :
private void Start()
{
m_Rb = GetComponent<Rigidbody>();
m_InputAxisName = "Vertical" + m_playerNumber;
m_StrafeAxisName = "Horizontal" + m_playerNumber;
}
private void FixedUpdate()
{
m_InputAxisValue = Input.GetAxis(m_InputAxisName);
m_StrafeAxisValue = Input.GetAxis(m_StrafeAxisName);
//Movement
Vector3 movement = (transform.forward * m_InputAxisValue * m_Speed * Time.deltaTime) + (transform.right * m_StrafeAxisValue * m_Speed * Time.deltaTime);
m_Rb.MovePosition(m_Rb.position + movement);
}
Camera Script
void FixedUpdate () {
m_NewPos = m_player.transform.position + m_offset;
if (m_Rotate)
{
Quaternion newRot = Quaternion.AngleAxis(Input.GetAxis("Mouse X") * m_rotSpeed, Vector3.up);
m_offset = newRot * m_offset;
}
transform.position = Vector3.SmoothDamp(transform.position, m_NewPos, ref m_MoveSpeed, m_DampTime); ;
if (m_Rotate)
transform.LookAt(m_player);
}
the problem is that your motion logic is implemented within FixedUpdate but you take the time.deltaTime. You have to use the fixedDeltaTime version.
Also do not try to get the Input within the FixedUpdate that can also cause jitter and stutter/jumping of objects.
Instead get the Input from Update and use variables to pass it inside the FixedUpdate.
A last one, look at your RigidBody for the Interpolation/Extrapolation depending on your Scene Setup and Camera this could also help.
I finally figured out what was wrong with it, something about the rotation of the camera in the fixed update. I moved it to update and it works fine now
Related
I am working on a 3D Camera controller, where player is able to rotate the camera around an object using mouse. transform.RotateAround() is perfect for this, except for one thing - I can't figure out a way to smoothly lerp it.
For reference - this is old code, which made camera rotate smoothly, but without a specific Target, just around an arbitrary point:
private void Rotate(){
if(!EnableRotation || RotationTarget == null) return;
if(Input.GetKeyDown(KeyCode.R)) _newRotation = Quaternion.identity;
if(Input.GetKey(KeyCode.Mouse1)){
float xInput = Input.GetAxis("Mouse X");
_newRotation *= Quaternion.Euler(Vector3.up * RotationSpeed * xInput * RotationSensitivity);
}
}
private void UpdateRotation(){
transform.rotation = Quaternion.Lerp(transform.rotation,_newRotation,Time.deltaTime * RotationDampening);
}
And this, is what I have currently:
private void Rotate(){
if(!EnableRotation || RotationTarget == null) return;
if(Input.GetKeyDown(KeyCode.R)) _newRotation = Quaternion.identity;
if(Input.GetKey(KeyCode.Mouse1)){
float xInput = Input.GetAxis("Mouse X");
transform.RotateAround(RotationTarget.position,Vector3.up,RotationSpeed * xInput);
}
}
So my big question is - is there a way to lerp the rotation of transform.RotateAround()? Make it smooth. Or are there any other ways of doing this?
Thanks in advance.
I feel like I've tried everything, but no success so far.
Where is Rotate() called? Is the rotationSpeed * xInput meant to represent degrees as required?
I figure first the UpdateRotation() method was called frequently, while the Rotate() method was called to set the new rotation, in the new version you do the rotation on Rotate() are you calling it as often as you did with the UpdateRotation()?.
You could Lerp 360 degrees such as Vector3.Lerp(0, 360, t) where t should range from 0 to 1. Or simply adjust the rotation speed accordingly, making sure you call the method enough times to have a smooth rotation.
I'm trying to make a simple hyper casual. I couldn't understand what's wrong with my codes.
My code:
public class CameraFollow : MonoBehaviour
{
public Transform Target;
public Vector3 offset;
void LateUptade()
{
transform.position = Vector3.Lerp(transform.position, Target.position + offset, Time.deltaTime * 2);
}
}
Two Things to note in your code.
Its LateUpdate and not LateUptade.
In lerp If the third input is 1 the camera will instantly jump to target+offset and if you want the camera to move slowly from its position to target+offse then you need to increase the value of the third input from 0 to 1 every frame.
You can also use Cinemachine to make camera follow player. Check out this Camera follow player tutorial for different ways to make the camera follow player.
Do not put Time.deltaTime in Lerp. The lazy solution is to simply write a constant that is between 0 and 1.
Additionally there is a typo in the function name. It should be LateUpdate and not LateUptade.
transform.position = Vector3.Lerp(transform.position, Target.position + offset, 0.3f);
i'm not really sure how to phrase the question sorry about that. I have encountered this hurdle while setting up animations on a character for learning a top down character controller (No rigid body) I've seen this question asked on Reddit and unity forums like a decade ago, but I don't really understand their answers.
My problem right now is that I can't find out a way to tell mecanim if the player is moving towards the direction they're facing. For example, if the player is moving left and aiming to the left, the moveForward animation should be played. If the player is moving left but aiming to the right, the moveBackwards animation should be played.
This is the first time i'm posting a question. I am sorry if my formatting is wrong.
Here is the code I have, I would greatly appreciate any help.
public class CharacterMovement : MonoBehaviour
{
private Vector3 velocity;
private Vector3 PlayerMoveInput;
Animator animationS;
[SerializeField] CharacterController characterrController;
[SerializeField] private float MoveSpeed;
[SerializeField] private float JumpHeight;
[SerializeField] private float Gravity = -9.81f;
// Start is called before the first frame update
void Start()
{
animationS = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
PlayerMoveInput = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")); //collects the input for the player
MovePlayer();
PlayerRotation();
}
private void MovePlayer()
{
if (characterrController.isGrounded)
{
velocity.y = -1f;
}
if (Input.GetKeyDown(KeyCode.Space) && characterrController.isGrounded)
{
velocity.y = JumpHeight;
}
///
else
{
velocity.y -= Gravity * -2f * Time.deltaTime;
}
Vector3 MoveVector = transform.TransformDirection(PlayerMoveInput);
characterrController.Move(MoveSpeed * Time.deltaTime * MoveVector);
characterrController.Move(velocity * Time.deltaTime);
float velocityX = Vector3.Dot(PlayerMoveInput, transform.forward);
float velocityZ = Vector3.Dot(PlayerMoveInput, transform.right);
animationS.SetFloat("velocityX", velocityZ, 0.1f, Time.deltaTime);
animationS.SetFloat("velocityZ", velocityX, 0.1f, Time.deltaTime);
}
void PlayerRotation()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
Debug.DrawRay(ray.origin, ray.direction, Color.yellow);
if (Physics.Raycast(ray, out hit))
{
Vector3 targetPosition = new Vector3(hit.point.x, transform.position.y, hit.point.z);
Quaternion rotation = Quaternion.LookRotation(targetPosition - transform.position);
transform.rotation = Quaternion.Lerp(transform.rotation, rotation, Time.deltaTime * 10.0f);
}
}
}
This isn't the exact implementation that would be used with your code but should give you a good start.
// get the angle between where the player is pointing, and where he is moving
float angle = Vector3.Angle(MoveVector, targetPosition - transform.position);
if (angle < 90f){
//move forward animation
} else {
//move back animation
}
I haven't tested this code but the idea is that if the angle between the direction the player is facing and the direction the player is moving is less than 90 degrees, then he is moving forward. Also the variables used here: MoveVector and targetPosition are private so you will need to fix that issue before this method can be implemented.
With mechanim, its very common to have 4 separate float value params in your animator. Two are used for movement info, two are for look direction info. This lets you use blend trees based on those functions that handle alot of the pain, all you need to do is update the values in the animator with the correct ones on each update/lateupdate depending on what you are doing.
This is how my animator params are layed out as well as the locomotion blend tree, I have another on a separate layer just for the head that uses Head left right, and head updown params to, you know, control the head.
And here is the code (or at least some of it) that sets the values on the animator component
EDIT:
Sorry, forgot to mention that the function i call "LerpFloat" on the anim variable is an extention method i defined myself for the animator component, all it does is gets the float value, lerps it, then sets the float value back. It just uses Mathf.Lerp.
Thank you both for your answers, the problem was the settings of the animation clips.....
Where you tick
Root transform Rotation
Root transform position (Y)
Root transform position (XZ)
I had ticked all of them, where just the root transform rotation was needed.
I will need to be more careful with the animator and animation clips in the future to avoid headaches.
Im trying to code so that my Character dashes to the right when pressing the Left mouse button, but instead of dashing it just starts slowly glieding or lets say floating.
This is the code i´ve used;
if (Input.GetMouseButton(0))
{
rb.velocity = Vector2.right * DashSpeed;
}
Im not sure but a other part of my code might be the reason for this problem but if so i would like to know how i could solve it. Thats the part im talking about
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
thats the code im using for movement.
void Start()
{
cam = Camera.main;
rb = GetComponent<Rigidbody2D>();
}
private void Update()
{
horizontal = Input.GetAxisRaw("Horizontal");
animator.SetFloat("Horizontal", Input.GetAxis("Horizontal"));
if (Input.GetKeyDown(KeyCode.Space) && isGrounded == true)
{
float jumpVelocity = 7f;
rb.velocity = Vector2.up * jumpVelocity;
jumpsound.Play();
}
Vector3 worldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
if (Input.GetKey(KeyCode.RightAlt))
{
Dashing();
}
}
void FixedUpdate()
{
isGrounded = Physics2D.OverlapCircle(groundCheck.position, CheckRadius, whatisGround);
moveInput = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
if (Input.GetKeyDown(KeyCode.Escape))
{
SceneManager.LoadScene("Main menu");
}
}
void Dashing()
{
rb.AddForce(Vector2.right * DashSpeed, ForceMode2D.Impulse);
}
The issue with your current code is you are directly changing velocity in a few places. Some basic physics, the integral of position vs. time graph is velocity, and the integral of velocity is acceleration vs. time. To get a more realistic movement, it is better to apply a Force to objects. When doing this, the physics engine Unity uses can add a new force at a given time, then using acceleration can accelerate the object in that direction over time, then can change the velocity over time which will result in the position changing over time.
The example code you posted, you are directly setting velocity in a few places.
rb.velocity = Vector2.up * jumpVelocity; (Jump)
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y); (Movement)
rb.velocity = Vector2.right * DashSpeed; (Dash)
When directly setting these values, the new values overwrite the old ones. As your movement code is not in any sort of if conditional it will continually write to the velocity causing the dash to never change anything regardless if you use add-force or change velocity directly.
I would consider making both your jump and dash use AddForce, and if you like the feel of your movement by applying velocity directly, then add the velocity do not set it.
Your previous line rb.velocity = new Vector2(moveInput * speed, rb.velocity.y); would then become rb.AddForce(new Vector2(moveInput * speed, 0), ForceMode2D.Impulse);. Similarly you can update your jump and dash to match this. Let me know if you get this working or have more questions.
It could be a problem with your animation. Link to a thread on unity answers:
https://answers.unity.com/questions/674516/rigidbody-character-problems-constant-floating-jum.html
You should go over to the animation place and hit bake into pose.
You should use the Rigidbody2D.AddForce(Vector2, ForceMode2D). What this does is moving the GameObject in the direction of the Vector2, with the force mode of ForceMode2D. What is different about this from just translating it, is that it interacts with physics and improves the quality of your game. Here is a link, and the script:
https://docs.unity3d.com/ScriptReference/Rigidbody2D.AddForce.html
Rigidbody2D rb;
float dashSpeed;
void Update()
{
if (Input.GetMouseButton(0))
{
rb.AddForce(Vector2.right * dashSpeed);
}
}
And if the other part of the code you were talking about, if that is glitching, then do the same trick.
I am trying to create a game in Unity where the player can only move in the direction it is facing, but the following code allows the player to move in all 4 directions.
(This is for a 3D project)
Any help would be appreciated! Thanks!
public class PlayerController : MonoBehaviour {
public float speed;
private Rigidbody rb;
void Start() {
rb = GetComponent<Rigidbody>();
}
void FixedUpdate() {
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
rb.AddForce(movement * speed);
}
}
So I didn't get what you wanted: first you said you wanted it to move only forward, then the only thing you have to do is get when he pressed a key and move forward while is not unpressed.
If you wanted to say that it can move all directions BUT only one at a time, the you will have to put the same code but with some changes:
First of all to make it move forward you have to get the forward of the transform, otherwise it will move in the same direction if you rotate it (you don't want that, no?).
Vector3 moveDirection = (transform.forward * Input.GetAxis("Vertical") + transform.right * Input.GetAxis("Horizontal")).normalized;
moveDirection.y = 0;
rb.velocity = moveDirection;
Then, to make that it ONLY moves to one direction at a time, you have to put the priority of the greatest axis number and if it's equal then you should think if you want to move forward or right (with it's axis value).
From the code you posted, I'm not sure where you are storing the player's facing-direction. However, I presume that it is stored as a Quaternion. If you have a player rotation quaternion called playerRotation, then you could do this (warning - untested):
Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
Vector3 normal = playerRotation * Vector3.forward;
Vector3 movement = Vector3.Dot(normal, input) * input;
If the game is first-person, then you can take a shortcut and just use Camera.current.transform.forward instead of the normal vector.
This will project the input direction onto the normal with the player's facing direction so that your movement force can only be in that direction.