Need helping getting a character controller to jump - c#

So far I have a character controller that enables me to move around, sprint and crouch (no animation) , but I am unable to get the controller to jump. I know 100% the character is getting the input to jump, and the movement vector is around ~40 on the Y axis, so the player should be moving. The problem is, nothing happens. The player can still move around, and fall of ledges, but nothing happens when i press space
This is my code:
using UnityEngine;
public class KeyboardMovement : MonoBehaviour
{
private CharacterController controller;
public float walkSpeed;
public float sprintSpeed;
public float crouchSpeed;
public float jumpHeight;
Vector3 up = Vector3.zero;
Vector3 movement = Vector3.zero;
Vector3 forward = Vector3.zero;
Vector3 sideways = Vector3.zero;
void Start()
{
controller = GetComponent<CharacterController>();
}
void Update()
{
float speed = walkSpeed;
//If crouching, set speed to crouch speed. This ovverides sprinting
if (SingletonSettings.GetKey(SingletonSettings.Keybindings.crouch))
speed = crouchSpeed;
//Else if sprinting, set speed to sprint speed
else if (SingletonSettings.GetKey(SingletonSettings.Keybindings.sprint))
speed = sprintSpeed;
//Create vectors for movement
forward = transform.TransformDirection(Vector3.forward) * Input.GetAxis("Vertical");
sideways = transform.TransformDirection(Vector3.right) * Input.GetAxis("Horizontal");
//up = SingletonSettings.GetKey(SingletonSettings.Keybindings.jump) && controller.isGrounded ? Vector3.up * jumpHeight : Vector3.zero;
movement = (up * 100) + ((forward + sideways) * 10 * Time.deltaTime * speed);
//Combine vectors and Multiply by DeltaTime to ensure smooth movement at different framerates.
//Also multiply by 10*speed to ensure correct speed in different states
if (controller.isGrounded && Input.GetKey(KeyCode.Space))
{
movement.y = jumpHeight*50 * Time.deltaTime;
}
controller.SimpleMove(movement);
}
void OnGUI()
{
GUILayout.Label('\n' + Newtonsoft.Json.JsonConvert.SerializeObject(movement.y, Newtonsoft.Json.Formatting.Indented, new Newtonsoft.Json.JsonSerializerSettings
{
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
}));
}
}

CharacterController.SimpleMove from the docs
Moves the character with speed.
Velocity along the y-axis is ignored.
Gravity is automatically applied.
https://docs.unity3d.com/ScriptReference/CharacterController.SimpleMove.html
You should use CharacterController.Move
A more complex move function taking absolute movement deltas.
Attempts to move the controller by motion, the motion will only be constrained by collisions. It will slide along colliders
...
This function does not apply any gravity.
https://docs.unity3d.com/ScriptReference/CharacterController.Move.html
With this method, you'll need to apply gravity yourself
movement.y -= gravity * Time.deltaTime;
controller.Move(movement * Time.deltaTime);

Related

Why is my character's horizontal velocity lost when jumping?

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.

Unity mid air movement with velocitychange

I have made a script for player movement in a 3D game in Unity.
I like how the movement works, but when I jump while moving, the player keeps moving in that direction at the same speed till it falls to the ground, which is the expected behavior for this script.
But I want to be able to move the player in the air slightly (not fully controllable.)
I'm imagining kinda like a minecraft like movement to be exact.
Any advice to improve this code would be greatly apprecciated.
This is my code.
using System.IO;
using UnityEngine;
public class VelocityMovement : MonoBehaviour
{
#region Variables
public float speed;
public float gravity;
public float maxVelocityChange;
public float jumpHeight;
public float raycastDistance;
public Rigidbody rb;
#endregion
private void Awake()
{
//gets rigidbody, freezes rotation of the rigidbody, disables unity's built in gravity system on rigidbody.
rb = GetComponent<Rigidbody>();
rb.freezeRotation = true;
rb.useGravity = false;
}
private void Update()
{
//Jump system
if(Grounded())
{
if (Input.GetKeyDown(KeyCode.Space))
{
rb.velocity = new Vector3(rb.velocity.x, CalculateJumpSpeed(), rb.velocity.z);
}
}
}
private void FixedUpdate()
{
//Moves the player when on the ground.
if (Grounded())
{
//Calculate how fast the player should be moving.
Vector3 targetVelocity = new Vector3(playerInputs.hAxis, 0, playerInputs.vAxis);
targetVelocity = transform.TransformDirection(targetVelocity);
targetVelocity *= speed ;
//Calculate what the velocity change should be
Vector3 velocity = rb.velocity;
Vector3 velocityChange = (targetVelocity - velocity);
velocityChange.x = Mathf.Clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange);
velocityChange.z = Mathf.Clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange);
velocityChange.y = 0f;
//applies a force equal to the needed velocity change
rb.AddForce(velocityChange, ForceMode.VelocityChange);
}
//Adds gravity to the rigidbody
rb.AddForce(new Vector3(0, -gravity * rb.mass, 0));
}
//Checks if player is on the ground
private bool Grounded()
{
return Physics.Raycast(transform.position, Vector3.down, raycastDistance);
}
//calculates how high the player should be jumping
private float CalculateJumpSpeed()
{
return Mathf.Sqrt(2 * jumpHeight * gravity);
}
}
private void FixedUpdate()
{
//check how big the movement is. Swap SomeSmallValue with a float like 0.1f
float actualSpeed;
Vector3 velocity;
if(Grounded())
{
actualSpeed = speed;
//HERE IT IS NOT, THIS DOESN'T MATTER
velocity = rb.velocity
}
else{
actualSpeed = speed * SomeSmallValue;
//WITH THIS rb KEEPS THE SPEED IF ANY BUTTON IS PRESSED
velocity = 0;
}
//Moves the player ALWAYS.
//Calculate how fast the player should be moving.
Vector3 targetVelocity = new Vector3(playerInputs.hAxis, 0, playerInputs.vAxis);
targetVelocity = transform.TransformDirection(targetVelocity);
//if Grounded == true the movement is exactly the same than before, because actualSpeed = speed.
//If Grounded == false, the movement is so light
targetVelocity *= actualSpeed;
//Calculate what the velocity change should be
//I'VE PLACED THIS UP IN ORDER TO CALL Grounded() ONLY ONE TIME
//Vector3 velocity = rb.velocity;
Vector3 velocityChange = (targetVelocity - velocity);
velocityChange.x = Mathf.Clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange);
velocityChange.z = Mathf.Clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange);
velocityChange.y = 0f;
//applies a force equal to the needed velocity change
rb.AddForce(velocityChange, ForceMode.VelocityChange);
//Adds gravity to the rigidbody
rb.AddForce(new Vector3(0, -gravity * rb.mass, 0));
}
If your code is working fine, try something like this. Just moved the grounded condition. Now the movement works also at air time. We check with that grounded bool how big the movement is.
If I understood you correctly you want to slow down what your movements do while mid air.
at least that is the case for left and right.
If I did undersand you correctly, you would make an else{} after the if(Grounded())
and go from there, may make some float variable that acts as a multiplier for you left/right control while mid air, aswell as another float variable for forward and backwards.
Inside that else you could just multiply you speed with the variables (depending on the axis) and you should get some form of slower movement there.
I hope that helped you out at least a bit.

trouble understanding movement along local and global space in unity

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 have followed a tutorial on making controls for a fps game on unity. the controls work but if I leave the controls then I keep moving to the left

enter image description hereI have made controls for my character to move along the X axis. They seem to be working fine but my character keeps moving left and I'm not sure why or what I have missed.I have posted code from the two separate scripts
I have rechecked his code several times.I have checked to see if my arrow keys, numpad keys and WASD are sticking or any other.
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class PlayerMotor : MonoBehaviour
{
private Vector3 velocity = Vector3.zero;
private Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
}
//gets a movement vector
public void Move (Vector3 _velocity)
{
velocity = _velocity;
}
//run every physics iteration
void FixedUpdate()
{
PerformMovement();
}
//perform movement based on velocity variable
void PerformMovement ()
{
if (velocity != Vector3.zero)
{
rb.MovePosition(rb.position + velocity * Time.fixedDeltaTime);
}
}
}
And the controller:
using UnityEngine;
[RequireComponent(typeof(PlayerMotor))]
public class PlayerController : MonoBehaviour
{
[SerializeField] //makes speed show up in inspector even if set to private
private float speed = 5f;
private PlayerMotor motor;
void Start ()
{
motor = GetComponent<PlayerMotor>();
}
void Update()
{
//Calculate movement velocity as a 3D vector
float _xMov = Input.GetAxisRaw("Horizontal");
float _zMov = Input.GetAxisRaw("Vertical");
Vector3 _movHorizontal = transform.right * _xMov;
Vector3 _movVertical = transform.forward * _zMov;
//final movement vector
Vector3 _velocity = (_movHorizontal + _movVertical).normalized * speed;
//apply movement
motor.Move(_velocity);
}
}
I expect 0 output when not pressing buttons but it seems to make me move to the left at speed of 5
You have a controller connected and reporting a slight movement in one direction. Use Input.GetAxis instead of Input.GetAxisRaw to let Unity handle deadzone detection. This way, nearly neutral inputs will be treated as neutral inputs.
void Update()
{
//Calculate movement velocity as a 3D vector
float _xMov = Input.GetAxis("Horizontal");
float _zMov = Input.GetAxis("Vertical");
Vector3 _movHorizontal = transform.right * _xMov;
Vector3 _movVertical = transform.forward * _zMov;
//final movement vector
Vector3 _velocity = (_movHorizontal + _movVertical).normalized * speed;
//apply movement
motor.Move(_velocity);
}

Jumping without rigidbody in unity

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.

Categories

Resources