I have a third-person code, but the camera stutters when I walk backwards! Does anyone have a solution? The camera is separate from the player, the player rotates according to the view of the camera.
PlayerController.cs
if (Input.GetKey (KeyCode.LeftShift)) {
if(Input.GetKey(KeyCode.W) || Input.GetAxis ("Vertical") > 0) moveDirection += camera.forward;
if(Input.GetKey(KeyCode.S) || Input.GetAxis ("Vertical") < 0) moveDirection += -camera.forward;
if(Input.GetKey(KeyCode.A) || Input.GetAxis ("Horizontal") < 0) moveDirection += -camera.right;
if(Input.GetKey(KeyCode.D) || Input.GetAxis ("Horizontal") > 0) moveDirection += camera.right;
if (Input.GetAxis ("Horizontal") != 0 || Input.GetAxis ("Vertical") != 0) {
//Multiply it by speed.
moveDirection.Normalize ();
moveDirection *= run;
//Applying gravity to the controller
moveDirection.y -= gravity * Time.deltaTime;
//Making the character move
controller.Move (moveDirection * Time.deltaTime);
}
moveDirection.y = 0f;
if (moveDirection != Vector3.zero) {
transform.rotation = Quaternion.RotateTowards (transform.rotation, Quaternion.LookRotation (moveDirection), rotationSpeed * Time.deltaTime);
}
}
Camera.cs
// Update is called once per frame
void Update () {
// We setup the rotation of the sticks here
float inputX = Input.GetAxis ("RightStickHorizontal");
float inputZ = Input.GetAxis ("RightStickVertical");
mouseX = Input.GetAxis ("Mouse X");
mouseY = Input.GetAxis ("Mouse Y");
finalInputX = inputX + mouseX;
finalInputZ = inputZ - mouseY;
rotY += finalInputX * inputSensitivity * Time.deltaTime;
rotX += finalInputZ * inputSensitivity * Time.deltaTime;
rotX = Mathf.Clamp (rotX, -clampAngle, clampAngle);
Quaternion localRotation = Quaternion.Euler (rotX, rotY, 0.0f);
transform.rotation = localRotation;
void LateUpdate () {
CameraUpdater ();
}
void CameraUpdater() {
Transform target = CameraFollowObj.transform;
// set the target object to follow
//move towards the game object that is the target
float step = CameraMoveSpeed * Time.fixedDeltaTime;
transform.position = Vector3.MoveTowards (transform.position, CameraFollowObj.transform.position, step);
}
In my experience when you have a smooth camera that stutters, it's because the camera - once it has gotten up to speed - moves faster than the object it is following:
Problem
Object moves
Camera starts to slowly move towards object
It catches up and stops
Camera to slowly start move towards object
Repeat 2-4; stuttery behaviour
Solution
Simple; tweak the camera speed variables to make sure it never completely catches up when the object is moving, but rather stays just behind so it doesn't have to stop every few frames.
Bonus reading material
Other sources will often explain their solution is to incorrectly put the code in FixedUpdate to miraculously get rid off the stuttery behaviour. Sometimes getting the correct behaviour from doing this is due to a series of things:
Update runs every frame update, so the time step (Time.deltaTime) here depends on machine but on my machine it will be, on average, every 0.007 seconds.
FixedUpdate runs on a fixed timestep, standard is 0.002 but can be changed in settings.
The code: float step = CameraMoveSpeed * Time.deltaTime;
Let's assume that CameraMoveSpeed is 1, for the sake of simple maths.
So with these numbers we can understand that putting the code in Update gives the following result
Update
Every 0.007 seconds, we update the camera lerp tick with CameraMoveSpeed * 0.007
FixedUpdate
Every 0.02 seconds, we update the camera lerp tick with CameraMoveSpeed * 0.007
Result
They move the same length every tick, but the ticks are less frequent in the FixedUpdate, which results in a slower camera smoothing and thus, "solves" the issue described as answer to this post of, in a way, tweaking camera speed variables to get a slower camera follow but instead of tweaking variables you havev moved your code to the physics update instead of the frame update and you only solved the issue by accident, without understanding the cause.
Related
I was trying to make a test game in 3D Unity. In the game enemy throw rock and you try to escape. But every time it instantiates a sphere while i am moving my camera(it is tps) it suddenly moves like five times faster in one frame. When i am rotating camera from downwards to upwards at normal speed it suddenly start looking to the sky. I dont get it. Here is the instantiate code:
IEnumerator throwRock()
{
if (IsRockThrowable)
{
IsRockThrowable = false;
GameObject rockk = Instantiate(rock, transform.position, transform.rotation) as GameObject;
rockk.GetComponent<Rigidbody>().AddForce(transform.forward * 100, ForceMode.Impulse);
yield return new WaitForSeconds(2.5f);
IsRockThrowable = true;
}
}
And here is the camera movement code:
void Update()
{
float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity * Time.deltaTime;
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime;
xRotation -= mouseY;
xRotation = Mathf.Clamp(xRotation, -90f, 90f);
transform.localRotation = Quaternion.Euler(xRotation,0f,0f);
playerBody.Rotate(Vector3.up * mouseX);
}
Can you explain why it happens? Thanks for your attention
GOAL
I'm relatively new to Unity and I want my character to be able to run and jump at the same time causing the character to go up diagonally.
PROBLEM
However after making some adjustments to give the character some acceleration, the jump seems to clear all existing velocity, meaning the character goes up and then to the side instead of both at the same time:
(I apologise if its a bit hard to see)
CODE
This is my character movement script:
Rigidbody2D rb;
BoxCollider2D bc;
[Header("Run")]
float xInput = 0f;
public float maxRunSpeed;
public float acceleration;
[Space]
[Header("Jump")]
public float jumpHeight;
public float lowJumpHeight;
public float fallSpeed;
public float airControl;
[Space]
public LayerMask groundLayer;
public bool onGround;
[Space]
public Vector2 bottomOffset;
public Vector2 boxSize;
public float coyoteTime;
void Start() {
// Gets a reference to the components attatched to the player
rb = GetComponent<Rigidbody2D>();
bc = GetComponent<BoxCollider2D>();
}
void Update() {
Jump();
// Takes input for running and returns a value from 1 (right) to -1 (left)
xInput = Math.Sign(Input.GetAxisRaw("Horizontal"));
}
// Applies a velocity scaled by runSpeed to the player depending on the direction of the input
// Increaces the velocity by accerleration until the max velocity is reached
void FixedUpdate() {
rb.velocity = Math.Abs(rb.velocity.x) < Math.Abs(xInput) * maxRunSpeed ? rb.velocity + new Vector2(acceleration * xInput, rb.velocity.y) * Time.deltaTime : new Vector2(xInput * maxRunSpeed, rb.velocity.y);
}
void Jump() {
// Checks whether the player is on the ground and if it is, replenishes coyote time, but if not, it starts to tick it down
coyoteTime = onGround ? 0.1f : coyoteTime - Time.deltaTime;
// Draws a box to check whether the player is touching objects on the ground layer
onGround = Physics2D.OverlapBox((Vector2)transform.position + bottomOffset, boxSize, 0f, groundLayer);
// Adds an upwards velocity to player when there is still valid coyote time and the jump button is pressed
if (Input.GetButtonDown("Jump") && coyoteTime > 0) {
rb.velocity = Vector2.up * jumpHeight;
}
// Increases gravity of player when falling down or when the jump button is let go mid-jump
if (rb.velocity.y < 0 ) {
rb.velocity += Vector2.up * Physics2D.gravity.y * (fallSpeed - 1) * Time.deltaTime;
} else if (rb.velocity.y > 0 && !Input.GetButton("Jump")) {
rb.velocity += Vector2.up * Physics2D.gravity.y * (lowJumpHeight - 1) * Time.deltaTime;
}
}
Sorry for there being a lot of unecessary code, it's just im not sure what's causing the issue so i don't want to remove anything. Hopefully my comments make some sense?
This is happening because you're setting the velocity of your rigidbody directly with rb.velocity = Vector2.up * jumpHeight. So that will wipe all existing velocity.
If you want to just add a force to the velocity rather than replacing it entirely, you can do that with methods like Rigidbody2D.AddForce.
While in all other cases you keep your velocity values and only slightly modify them you are hard overwriting the absolute velocity in
rb.velocity = Vector2.up * jumpHeight;
erasing any velocity on X.
You could simply keep whatever you have on the other axis and only overwrite Y like
var velocity = rb.velocity;
velocity.y = jumpHeight;
rb.velocity = velocity;
or
rb.velocity = new Vector2(rb.velocity.x, jumpHeight);
basically the same way you do it also in FixedUpdate for the horizontal direction.
Whenever I'm running against a wall, like if there is a wall to my left and I hold 'a' against the wall it kinda spazes out. It looks like the character is going in and out of the wall. Hopefully that made sense and you know what I'm talking about. So my question is how could I fix this so that when I am actively running into a wall it doesnt do that and instead the character is just there against the wall and appearing to move at all.
code for the movement:
void Update()
{
var movement = Input.GetAxis("Horizontal");
transform.position += new Vector3(movement, 0, 0) * Time.deltaTime * MovementSpeed;
if (Input.GetButtonDown("Jump") && Mathf.Abs(_rigidbody.velocity.y) < .001f)
{
_rigidbody.AddForce(new Vector2(0, JumpForce), ForceMode2D.Impulse);
}
}
Setting the transform.position literally teleports the player, so it sometimes teleports them into the wall then they get pushed back.
To prevent that i suggest using the rigidbody's movePosition function. This takes into account physics while moving, so it interacts with the other objects that are there.
To change your current code to that it would be something like this:
void Update()
{
var movement = Input.GetAxis("Horizontal");
//This moves the GameObject to the currentPosition + The move direction. Which means to move it in the direction that you intended to move in.
_rigidbody.MovePosition(transform.position + new Vector3(movement, 0, 0) * Time.deltaTime * movementSpeed);
if (Input.GetButtonDown("Jump") && Mathf.Abs(_rigidbody.velocity.y) < .001f)
{
_rigidbody.AddForce(new Vector2(0, JumpForce), ForceMode2D.Impulse);
}
}
However i suggest splitting it into this:
float movement = 0f; //Setting a default value.
void Update()
{
movement = Input.GetAxis("Horizontal"); //Getting movement input from player
if (Input.GetButtonDown("Jump") && Mathf.Abs(_rigidbody.velocity.y) < .001f)
{
_rigidbody.AddForce(new Vector2(0, JumpForce), ForceMode2D.Impulse);
}
}
private void FixedUpdate()
{ //Moving in Physics update
_rigidbody.MovePosition(transform.position + new Vector3(movement, 0, 0) * Time.deltaTime * movementSpeed);
}
Because Update checks every frame, so its good for checking input, however you should apply physics in FixedUpdate(), because that is applied whenever there is a physics update (Or physics frame). (For even more smoothness!)
After rotation, whenever I move left or right now, it goes in the opposite direction.
Hi I'm trying to rotate my object by 90 degrees every time I press a button and it works but now when I press the right arrow, it moves left and vice versa. Can anyone help me with this? Thank you
public class PlayerMovement : MonoBehaviour
{
[SerializeField] private float _playerSpeed=1.5f;
[SerializeField] private float _playerRotation = 90f;
// Start is called before the first frame update
void Start()
{
transform.position = new Vector2(0,8f);
}
// Update is called once per frame
void Update()
{
float horizontalInput = Input.GetAxis("Horizontal");
float VerticalInput = Input.GetAxis("Vertical");
float smooth = 100f;
if (Input.GetKeyDown(KeyCode.UpArrow))
{
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(0, 0, _playerRotation), Time.time * smooth);
_playerRotation += 90f;
}
else if(horizontalInput !=0 || VerticalInput<=0)
{
Vector2 direction = new Vector2(horizontalInput, VerticalInput);
transform.Translate(direction * _playerSpeed * Time.deltaTime);
}
}
}
There are a number of issues with your code.
You had:
// Update is called once per frame
void Update()
{
float horizontalInput = Input.GetAxis("Horizontal");
float VerticalInput = Input.GetAxis("Vertical");
float smooth = 100f;
if (Input.GetKeyDown(KeyCode.UpArrow))
{
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(0, 0, _playerRotation), Time.time * smooth);
_playerRotation += 90f;
}
The first issue is that you only modify the transform.rotation ONCE per keypress, and that modification is only a fraction of the full intention because of the lerp. You need to pull the lerp outside of the if KeyDown check.
Second issue is a little moot as resolving the first issue will also resolve the second: You are setting _playerRotation after applying it to the transform. There are edge cases where this is sometimes correct, but I suspect this is not one of them.
Third, the lerp factor (Time.time * smooth). This is a BLEND factor and should only scale from zero to one (again, some edge cases as exceptions but this isn't one of them). Time.time on its own is going to be >1 after the first second of runtime. I expect your intention here was to animate the rotation over time, but this is done by accumulating Time.deltaTime.
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.