Smooth Turning C# unity - c#

I have managed to go backwards and forwards with my kart in Unity. But when it comes to turning, it becomes a real challenge and I am unable to turn my vehicle smoothly with my a & d keys.
Please help!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class KartController : MonoBehaviour
{
private Rigidbody rb;
// adding a speed variable
public float speed = 3.0f;
// adding a brake variable
public float BrakeSpeed = 1.0f;
// adding a rotation position variable
// Start is called before
void Start()
{
// refering to the game object component/class
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
// adding movement for forward
if (Input.GetKey(KeyCode.W))
{
rb.velocity = new Vector3(speed * -2, 0, 0);
}
// adding movement for backward
if (Input.GetKeyDown(KeyCode.S))
{
rb.velocity = new Vector3(speed * 2, 0, 0);
}
// adding movement for rotation
float rotation_speed = 10f;
if (Input.GetButtonDown("Horizontal"))
{
transform.Rotate(0.0f, -Input.GetAxis("Horizontal") * rotation_speed, 0.0f);
}
Debug.Log(rotation_speed);
}
}

You've got a few different options and you are mixing a few things up.
Your Kart is a Rigidbody with a physics simulation. To move it forward or backwards you are setting the Karts velocity. If an object has a velocity in the physics simulation it will try to move because in the simulation the standard physics formula are solved. I.e. distance moved = velocity * time
For rotation the same concepts apply. There is the angular velocity of a rigidbody. If an object has angular velocity it tries to rotate accordingly.
With transform Rotate you are setting the rotation of the object directly. You are basically skipping the physics simulation. This is good if you want to for example reset your kart orientation and position at the start of the race but is less suited to control your Kart in the simulation.
You could directly set rb.angularVelocity similar how you set the Karts directional velocity.
Depending on the type of game and the realism you might want to look into controlling the acceleration instead of the velocity. Or even wheelcollider... (https://docs.unity3d.com/Manual/WheelColliderTutorial.html)

You can try the following code to select the car, I hope it can help you, thank you
private float y;
//vehicle control speed parameters
private float speedOne = 0f; //Vehicle real-time speed
private float speedMax = 120f; //The maximum speed of the vehicle
private float speedMin = -20f; //The minimum speed of the vehicle (the maximum speed of reversing)
private float speedUpA = 2f; //Vehicle acceleration acceleration (A key control)
private float speedDownS = 4f; //Vehicle deceleration acceleration (S key control)
private float speedTend = 0.5f; //Acceleration when no operation real-time speed tends to 0
private float speedBack = 1f; //Vehicle reversing acceleration
// Update is called once per frame
void Update()
{
//mouse hide
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
//Press the W key and the speed does not reach the maximum, then the speed increases
if (Input.GetKey(KeyCode.W) && speedOne < speedMax)
{
speedOne = speedOne + Time.deltaTime * speedUpA;
}
//Press the S key and the speed does not reach zero, then the speed is reduced
if (Input.GetKey(KeyCode.S) && speedOne > 0f)
{
speedOne = speedOne - Time.deltaTime * speedDownS;
}
// No speed operation is performed and the speed is greater than the minimum speed, then the slow operation
if (!Input.GetKey(KeyCode.W) && !Input.GetKey(KeyCode.S) && speedOne > 0f)
{
speedOne = speedOne - Time.deltaTime * speedTend;
}
if (!Input.GetKey(KeyCode.W) && !Input.GetKey(KeyCode.S) && speedOne < 0f)
{
speedOne = speedOne + Time.deltaTime * speedTend;
}
//When the S key is pressed and the speed does not reach the maximum reversing speed, and the vehicle is in a state where the vehicle can be reversed, the vehicle reverses
if (Input.GetKey(KeyCode.S) && speedOne > speedMin && speedOne<=0)
{
speedOne = speedOne - Time.deltaTime * speedBack;
}
/// press space, the car stops
if (Input.GetKey(KeyCode.Space) && speedOne != 0)
{
speedOne = Mathf.Lerp(speedOne, 0, 0.4f);
if (speedOne < 5) speedOne = 0;
}
transform.Translate(Vector3.forward * speedOne * Time.deltaTime);
//Use A and D to control the left and right rotation of the object
if(speedOne>1f||speedOne<-1f)
{
y = Input.GetAxis("Horizontal") * 60f * Time.deltaTime;
transform.Rotate(0, y, 0);
}
/* if (transform.eulerAngles.z != 0)
{
transform.eulerAngles = new Vector3(transform.eulerAngles.x, t
ransform.eulerAngles.y, 0);
}*/
}

Related

Swerve Control Unity C#

Hi I am creating a hyper casual game with unity, but I have encountered a problem with the swerve control (I have also seen many git hubs but even these do not work perfectly)
I've put this in my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
private float lastframeposx;
private float movefactorx;
public float MoveFactorX => movefactorx;
public Camera m_MainCam;
private float speed = 2.0f;
[SerializeField]
GameObject character;
[SerializeField] private float swerveSpeed = 0.5f;
[SerializeField] private float maxSwerveAmount = 1f;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.position += Vector3.forward * speed * Time.deltaTime;
Cammina();
/*Vector3 destra = Camera.main.ScreenToWorldPointt(Input.touches[i].position);
transform.position += Vector3.zero destra;*/
}
void Cammina()
{
if(Input.GetMouseButtonDown(0))
{
lastframeposx = Input.mousePosition.x;
float swerveAmount = Time.deltaTime * swerveSpeed * MoveFactorX;
swerveAmount = Mathf.Clamp(swerveAmount, -maxSwerveAmount, maxSwerveAmount);
transform.Translate(swerveAmount, 0, 0);
}
else if (Input.GetMouseButton(0))
{
movefactorx = Input.mousePosition.x - lastframeposx;
lastframeposx = Input.mousePosition.x;
float swerveAmount = Time.deltaTime * swerveSpeed * MoveFactorX;
swerveAmount = Mathf.Clamp(swerveAmount, -maxSwerveAmount, maxSwerveAmount);
transform.Translate(swerveAmount, 0, 0);
}
else if(Input.GetMouseButtonUp(0))
{
movefactorx = 0f;
float swerveAmount = Time.deltaTime * swerveSpeed * MoveFactorX;
swerveAmount = Mathf.Clamp(swerveAmount, -maxSwerveAmount, maxSwerveAmount);
transform.Translate(swerveAmount, 0, 0);
}
}
/*void vaidoveschiacciato()
{
if (Input.touchCount > 0 && Input.touches[0].phase == TouchPhase.Began)
{
Ray ray = Camera.main.ScreenPointToRay(Input.touches[0].position);
RaycastHit hit;
if(Physics.Raycast(ray, out hit))
{
if(hit.collider != null)
{
transform.position += hit.collider.GetComponent<Transform.position> * speed * Time.deltaTime;
}
}
}
}*/
}
1 Problem: he don't go when the finger is
2 Problem: How do I eliminate the movement from right to left (Without making it go out of the path)
(Langauge: C#)
The problem: When you swerve, it swerves just in the direction, there is no limits on how far it goes.
How I would fix this: I would put the movement to change it through a function. This could clamp it, so the higher the distance to the center of the track, the less it swerves. Or, you can altogether check if the distance is a maximum and then stop swerving.
Note: you can use other functions to do this (they just have to flatten out the larger the input).
Smooth, good looking bell curve way
For example you could use a bell curve. Look one up if you've never seen one before. It is at it's highest possible output of one, at a zero input. When it gets hiher or lower, it outputs lower to zero.
the simplest equation is y = i-(x2). The lower i is (above 1), the wider the curve, or the larger the output is for a larger input. I made a graph here.
x can be the distance to the center of the track, so you should adjust i, so the maximum distance from the track is flat.
This output is what you should change swerveAmount to.
The flatter parts of the graoh is how much you will swerve when you are that distance from the center (x axis)
Alternatively
You could just check the distance, and if it passes a certain distance don't translate it.
Let me know in the omments if there are any problems! :)

Slowly decrease movement speed after releasing left shift

So, I am still developing a game in Unity, where my only problem now is that I can't figure out how to slowly decrease the speed of my character after releasing left shift. Slowly increasing speed while holding shift is the only one I had worked out because I used the incrementing technique, but the speed slowly decreasing after releasing left shift is not. Can someone help me? Here's the code, I apologize, I am literally a beginner at this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController controller;
//Codes for setting walk and sprint speed (You should put speed in "Default Speed". "Walk Speed" is public so I can see if my speed is increasing)
public float defaultSpeed = 8f;
public float walkSpeed = 8f;
public float sprintIncrease = 15f;
public float sprintLimit = 16f;
//Gravity force
public float gravity = -60f;
//Probably jump height?
public float jumpHeight = 2f;
//For checking if you're on the ground
public Transform groundCheck;
public float groundDistance = 0.5f;
public LayerMask groundMask;
//Code to check if you are on the ground
Vector3 velocity;
bool isGrounded;
// Update is called once per frame
void Update()
{
//For checking again if you're on the ground
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
//Code to slowly increase sprint FORWARD while holding left shift
if (Input.GetKey(KeyCode.LeftShift) && Input.GetAxis("Vertical") > 0)
{
walkSpeed += sprintIncrease * Time.deltaTime;
}
//Code to prevent sprinting while holding S and when combined with A or D
else if (Input.GetKey(KeyCode.LeftShift) && Input.GetAxis("Vertical") < 0)
{
walkSpeed = defaultSpeed;
}
//Code for allowing to slowly increase sprint LEFT sideways
else if (Input.GetKey(KeyCode.LeftShift) && Input.GetAxis("Horizontal") < 0)
{
walkSpeed += sprintIncrease * Time.deltaTime;
}
//Code for allowing to slowly increase sprint RIGHT sideways
else if (Input.GetKey(KeyCode.LeftShift) && Input.GetAxis("Horizontal") > 0)
{
walkSpeed += sprintIncrease * Time.deltaTime;
}
//To stop Sprint Speed while holding shift from exceeding Sprint Limit
if (walkSpeed > sprintLimit)
{
walkSpeed = sprintLimit;
}
//Executes when shift is released, to slowly go back to default speed(This is my problem)
if (Input.GetKeyUp(KeyCode.LeftShift))
{
walkSpeed = defaultSpeed;
}
//To check if you're grounded so velocity doesn't increase
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
//The basic horizontal and vertical axises
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
//The basic code for movement
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * walkSpeed * Time.deltaTime);
//The code for jumping
if (Input.GetButtonDown("Jump") && isGrounded)
{
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
}
I thank everyone in advance.
if (!Input.GetKey(KeyCode.LeftShift))
{
walkSpeed -= sprintIncrease * Time.deltaTime;
}
the above code will do what you asked for. Just a heads up, the way you're implementing stuff is beginner-friendly but not performance friendly.
Ok, I have figured this out myself. I rearrange my ifs and else statement to look more organized. I added the else statement where if you don't hold shift while walking, it continuously subtracts the walk speed with the sprint increase, but to stop it from being lower than the default speed, I added an if statement where if the walk speed becomes lower than the default speed, it becomes equal to the default speed. Note that I rearranged my statements to trigger an else statement.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController controller;
//Codes for setting walk and sprint speed (You should put speed in "Default Speed". "Walk Speed" is public so I can see if my speed is changing or not)
public float defaultSpeed = 8f;
public float walkSpeed = 8f;
public float sprintIncrease = 15f;
public float sprintLimit = 16f;
//Gravity force
public float gravity = -60f;
//Probably jump height?
public float jumpHeight = 2f;
//For checking if you're on the ground
public Transform groundCheck;
public float groundDistance = 0.5f;
public LayerMask groundMask;
//Code to check if you are on the ground
Vector3 velocity;
bool isGrounded;
// Update is called once per frame
void Update()
{
//For checking again if you're on the ground
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
//Code to prevent sprinting while holding S and when combined with A or D(New Code)
if (Input.GetKey(KeyCode.LeftShift) && Input.GetAxis("Vertical") < 0)
{
walkSpeed -= sprintIncrease * Time.deltaTime;
}
//Code to slowly increase sprint FORWARD while holding left shift
else if (Input.GetKey(KeyCode.LeftShift) && Input.GetAxis("Vertical") > 0)
{
walkSpeed += sprintIncrease * Time.deltaTime;
}
//Code for allowing to slowly increase sprint LEFT sideways
else if (Input.GetKey(KeyCode.LeftShift) && Input.GetAxis("Horizontal") < 0)
{
walkSpeed += sprintIncrease * Time.deltaTime;
}
//Code for allowing to slowly increase sprint RIGHT sideways
else if (Input.GetKey(KeyCode.LeftShift) && Input.GetAxis("Horizontal") > 0)
{
walkSpeed += sprintIncrease * Time.deltaTime;
}
//To make sure that your speed decrease smoothly if not holding left shift(New code)
else
{
walkSpeed -= sprintIncrease * Time.deltaTime;
}
//To stop Sprint Speed while holding shift from exceeding Sprint Limit
if (walkSpeed > sprintLimit)
{
walkSpeed = sprintLimit;
}
//To stop Walk Speed from getting lower than Default Speed(New Code)
if (walkSpeed < defaultSpeed)
{
walkSpeed = defaultSpeed;
}
//To check if you're grounded so velocity doesn't increase
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
//The basic horizontal and vertical axises
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
//The basic code for movement
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * walkSpeed * Time.deltaTime);
//The code for jumping
if (Input.GetButtonDown("Jump") && isGrounded)
{
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
}

Unity Can't Do Ground Check with Objected Generated at Start Time

I have a game that generates a maze at the start of the game. The player is dropped into the maze from a height of about 10 feet or so. The problem is that if I click the player object and change the inspector from normal to debug, I see that the character is still continuously generating falling velocity. I have a sphere at the bottom of the character that is supposed to do a ground check but clearly that isn't working. This is the relevant code for one of the ground tiles and the ground check features of the character.
Ground Tile Code in GameManager.cs in Scripts folder.
var tile = Instantiate(_tilePrefab);
tile.transform.Rotate(90, 0, 0);
tile.transform.localPosition = new Vector3((x * tileWidth) + offsetTile, 0, (y * tileWidth) + offsetTile);
tile.gameObject.layer = LayerMask.NameToLayer("Ground");
tile.transform.parent = _mazePrefab;
PlayerMovement.cs in Scripts folder
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController controller;
public float speed = 15f;
public float gravity = -9.8f;
public Transform groundCheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
Vector3 velocity;
bool isGrounded;
// Update is called once per frame
void Update()
{
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
if (isGrounded && velocity.y > 0)
{
velocity.y = 0f;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
}
I have a ground layer created and everything and I have an empty that projects a sphere at the base of my character.
What am I doing wrong?
The problem must be that your gravity resetting isn’t working. I suppose you are using the tutorial from Brackeys — I have used his tutorial before, so I know it works. The problem must be that your gravity resetting. So, I go through your code. I notice there is an if statement meant to reset the velocity when the player is grounded. So, I check the isGrounded variable. The only thing that could set that incorrectly is that the maze doesn’t have the right layer. Your code does set the layer right, so I rule that one out. I then look back at the if statement. The only thing that could set it off is the second term in it:
if (... && velocity.y > 0)
Notice how later in your code the velocity rapidly decreases.
velocity.y += gravity * Time.deltaTime;
Notice how gravity is a negative variable.
float gravity = -9.8f;
And Time.deltaTime is positive. When multiplying positive and negative values, if the number of negative values in the equation is odd the result is negative, and if it is even the result is positive. Therefore, velocity decreases instead of increases.
In the if statement, you check if velocity.y > 0, in other words: if the velocity is greater than 0, do something. But according to the math, velocity is always decreasing, not increasing. But you are detecting if the velocity is above 0, not under it.
Change the if statement to:
if (isGrounded && velocity.y < 0)
Notice how I change the > to a < to detect if it is less than zero.
That would solve your problem and you could go off here, but there is something else that is less important. Notice how after your if statement, you decrease the velocity.
...
if (isGrounded && velocity.y > 0)// «— if statement.
{
velocity.y = 0f;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
velocity.y += gravity * Time.deltaTime;// «—- change velocity.
...
Since after you call the if statement you change it, you won’t need to detect if the velocity is less than 0. This is because the velocity will always be slightly less than 0 at the end of each frame.
There are two things you could do here.
You could remove the second part of the if statement.
You could add the velocity change to the else of the if statement.
For 1:
if (isGrounded)
{
velocity.y = 0f;
}
For 2:
Add:
if (isGrounded)
{
velocity.y = 0f;
}
else
{
velocity.y += gravity * Time.deltaTime;
}
And remove:
controller.Move(move * speed * Time.deltaTime);
//removed this line «—-
controller.Move(velocity * Time.deltaTime);

Dash against slopes using physics in Unity 2D

I'm working on a 2D project in Unity.
The character controller is physics based, so I use rigidbody to move the player. Everything is working fine except when I try to apply a high speed movement to the character, like a dash.
This is how the code looks like.
I just check if the player is dashing, so I increase the Vector2 movement in a certain amount.
private void DashMovement() {
if (isDashing) {
movement.x *= dashFactor;
}
}
I'm also calculating the ground angle, so I set the movement vector to follow the ground inclination.
private void OnSlopeMovement() {
if (isGrounded && !isJumping) {
float moveDistance = Mathf.Abs(movement.x);
float horizontalOnSlope = Mathf.Cos(groundAngle * Mathf.Deg2Rad) * moveDistance * Mathf.Sign(movement.x);
float verticalOnSlope = Mathf.Sin(groundAngle * Mathf.Deg2Rad) * moveDistance;
if (horizontalOnSlope != 0)
movement.x = horizontalOnSlope;
if (isGrounded && verticalOnSlope != 0)
movement.y = verticalOnSlope;
}
SetMaxFallVelocity();
}
So I set the rigidbody velocity for making it move.
private void Move() {
movement.x *= Time.fixedDeltaTime;
if(isGrounded && !isJumping) movement.y *= Time.fixedDeltaTime;
Vector3 targetVelocity = new Vector2(movement.x, movement.y);
PlayerController.rb2d.velocity = Vector3.SmoothDamp(PlayerController.rb2d.velocity, targetVelocity, ref velocity, movementSmoothing);
}
The problem appears when I apply a speed high enough. I understand this issue is because of physics.
I think the ray that checks the ground and is used to calculate the groundAngle doesn't work fast enough to keep track of that movement, so I can not keep the player fixed on the ground.
I would like to find a solution without making the player kinematic, or stopping the dash on slopes.
This is how it looks ingame.
And this is how the rigidbody movement remain right over the ground, following the slopes angle.
EDIT:
This is how I get the ground angle:
private void GroundAngle() {
Vector2 rayOrigin = feetCollider.bounds.center;
rayOrigin.y += 0.1f;
Vector2 rayDirection = (Input.GetAxisRaw("Horizontal") == 0) ? Vector2.right : new Vector2(Input.GetAxisRaw("Horizontal"), 0);
int groundCollisions = Physics2D.RaycastNonAlloc(rayOrigin, Vector2.down, groundResults, Mathf.Infinity, groundMask);
if (groundCollisions > 0) {
groundAngle = Vector2.Angle(groundResults[0].normal, rayDirection) - 90f;
//Debug.DrawRay(rayOrigin, Vector2.down, Color.green);
if (groundAngle > 0 && !isDashing) {
rayOrigin.x += Input.GetAxisRaw("Horizontal") * .125f;
Physics2D.RaycastNonAlloc(rayOrigin, Vector2.down, groundResults, Mathf.Infinity, groundMask);
groundAngle = Vector2.Angle(groundResults[0].normal, rayDirection) - 90f;
//Debug.DrawRay(rayOrigin, Vector2.down, Color.blue);
}
}
}
Thanks to #Ruzhim for the help. I just post a first "solution" for the problem.
According to Ruzhim advises, I've used him code this way.
private void SetPositionAfterTick() {
if (isDashMovement) {
Vector2 currentPosition = new Vector2(transform.position.x, transform.position.y);
currentPosition.y = feetCollider.bounds.min.y;
Vector2 feetPosAfterTick = currentPosition + PlayerController.rb2d.velocity * Time.deltaTime;
float maxFloorCheckDist = .1f;
RaycastHit2D groundCheckAfterTick = Physics2D.Raycast(feetPosAfterTick + Vector2.up * maxFloorCheckDist, Vector2.down, maxFloorCheckDist * 5f);
if (groundCheckAfterTick) {
Vector2 wantedFeetPosAfterTick = groundCheckAfterTick.point;
if (wantedFeetPosAfterTick != feetPosAfterTick) {
//PlayerController.rb2d.transform.position = (wantedFeetPosAfterTick + new Vector2(0f, feetCollider.bounds.min.y - PlayerController.rb2d.position.y));
PlayerController.rb2d.velocity = Vector2.zero;
}
}
}
}
This is how it looks like.
This is good enough to continue polishing that mechanic. I still need to set the position in some way. The rigidbody's position calculation is not working as it
is raised right now, as the condition (wantedFeetPosAfterTick != feetPosAfterTick) is always true, so the character goes throw the floor and fall.
As you can see, I also need to control the down slopes movement, as it uses the slopes movement sometimes, and dash straight forward others.
This is how asker Rubzero implemented the below code to work for them:
private void SetPositionAfterTick() {
if (isDashMovement) {
Vector2 currentPosition = new Vector2(transform.position.x, transform.position.y);
currentPosition.y = feetCollider.bounds.min.y;
Vector2 feetPosAfterTick = currentPosition + PlayerController.rb2d.velocity * Time.deltaTime;
float maxFloorCheckDist = .1f;
RaycastHit2D groundCheckAfterTick = Physics2D.Raycast(feetPosAfterTick + Vector2.up * maxFloorCheckDist,
Vector2.down, maxFloorCheckDist * 5f);
if (groundCheckAfterTick) {
Vector2 wantedFeetPosAfterTick = groundCheckAfterTick.point;
if (wantedFeetPosAfterTick != feetPosAfterTick) {
//PlayerController.rb2d.transform.position = (wantedFeetPosAfterTick + new Vector2(0f, feetCollider.bounds.min.y -
PlayerController.rb2d.position.y));
PlayerController.rb2d.velocity = Vector2.zero;
}
}
}
}
This is how it looks like.
This is good enough to continue polishing that mechanic. I still need
to set the position in some way. The rigidbody's position calculation
is not working as it is raised right now, as the condition
(wantedFeetPosAfterTick != feetPosAfterTick) is always true, so the
character goes throw the floor and fall.
As you can see, I need to control the down slopes movement, as it uses
the slopes movement sometimes, and dash straight forward others.
I agree with AresCaelum; using physics to do slope movement is pretty much the opposite of what you want to be doing if you don't want to preserve momentum when you're done going up/down the slope. Specifically, your problem is here:
float moveDistance = Mathf.Abs(movement.x);
float horizontalOnSlope = Mathf.Cos(groundAngle * Mathf.Deg2Rad) * moveDistance * Mathf.Sign(movement.x);
float verticalOnSlope = Mathf.Sin(groundAngle * Mathf.Deg2Rad) * moveDistance;
This is a problem because the more the player moves horizontally in a frame, the more they will move vertically based on the slope of the ramp they are on. However, this assumption doesn't hold if they should only be traveling up the ramp during only part of the movement during the frame. So, you need a way to handle that situation.
One solution is to use a raycast from where the player would be then if it's above the floor, alter the vertical velocity so that it would place them at that floor's position instead.
First, determine if slope movement has occurred in a physics frame...
private bool slopeMovementOccurred = false;
void FixedUpdate() {
slopeMovementOccurred = false;
// ...
}
private void OnSlopeMovement() {
if (isGrounded && !isJumping) {
slopeMovementOccurred = true;
// ...
}
SetMaxFallVelocity();
}
... and if it has, determine where the player is going to be after the physics update. Then do a physics2d raycast from above that position (by some amount) downward (double the previous amount) to find where the player's position should be, and then change the rb2d.velocity such that it will place the player exactly at the height they should be at.
Assuming you can calculate some kind of Vector2 feetOffset that has the local position of the player's feet:
void FixedUpdate() {
// ...
StickToSlopeLanding();
}
void StickToSlopeLanding() {
if (slopeMovementOccurred) {
Vector2 curVelocity = PlayerController.rb2d.velocity;
Vector2 feetPosAfterTick = PlayerController.transform.position
+ PlayerController.feetOffset
+ curVelocity * Time.deltaTime;
float maxFloorCheckDist = 1.0f;
// determine where the player should "land" after this frame
RaycastHit2D groundCheckAfterTick = Physics2D.Raycast(
feetPosAfterTick + Vector2.up * maxFloorCheckDist,
-Vector2.up, maxFloorCheckDist * 2f);
if (groundCheckAfterTick.collider != null) {
Vector2 wantedFeetPosAfterTick = groundCheckAfterTick.point;
// if basic physics won't take them to landing position
if (wantedFeetPosAfterTick != feetPosAfterTick) {
Vector2 wantedVelocity = curVelocity
+ Vector2.up
* ((wantedFeetPosAfterTick.y - feetPosAfterTick.y)
/ Time.deltaTime);
// adjust velocity so that physics will take them to landing position
PlayerController.rb2d.velocity = wantedVelocity;
// optionally, set a flag so that next frame
// it knows the player should be grounded
}
}
}
}
Hopefully this gets you towards a solution that will work.
Note: you may need to also move the rigidbody so that it doesn't try to clip through the corner at the top of the ramp, and you can determine where to put the rigidbody using another raycast, setting the velocity from that point to be horizontal:
void StickToSlopeLanding() {
if (slopeMovementOccurred) {
Vector2 curVelocity = PlayerController.rb2d.velocity;
Vector2 feetPosAfterTick = PlayerController.transform.position
+ PlayerController.feetOffset
+ curVelocity * Time.deltaTime;
float maxFloorCheckDist = 1.0f;
// determine where the player should "land" after this frame
RaycastHit2D groundCheckAfterTick = Physics2D.Raycast(
feetPosAfterTick + Vector2.up * maxFloorCheckDist,
-Vector2.up, maxFloorCheckDist * 2f);
if (groundCheckAfterTick.collider != null) {
Vector2 wantedFeetPosAfterTick = groundCheckAfterTick.point;
// if basic physics won't take them to landing position
if (wantedFeetPosAfterTick != feetPosAfterTick) {
// look for corner of ramp+landing.
// Offsets ensure we don't raycast from inside/above it
float floorCheckOffsetHeight = 0.01f;
float floorCheckOffsetWidth = 0.5f;
RaycastHit2D rampCornerCheck = Physics2D.Raycast(
wantedFeetPosAfterTick
- floorCheckOffsetHeight * Vector2.up
- floorCheckOffsetWidth * Mathf.Sign(movement.x) * Vector2.right,
Mathf.Sign(movement.x) * Vector2.right);
if (rampCornerCheck.collider != null) {
// put feet at x=corner position
Vector2 cornerPos = Vector2(rampCornerCheck.point.x,
wantedFeetPosAfterTick.y);
PlayerController.rb2d.position = cornerPos
- PlayerController.feetOffset;
// adjust velocity so that physics will take them from corner
// to landing position
Vector2 wantedVelocity = (wantedFeetPosAfterTick - cornerPos)
/ Time.deltaTime;
PlayerController.rb2d.velocity = wantedVelocity;
// optionally, set a flag so that next frame
// it knows the player should be grounded
}
}
}
}
}

Unity Player falling very slowly

I created controls for a 3D Platformer game. Somehow the player is falling down very very slowly.
My player object got 2 components, the default capsule collider and the default Rigidbody. I didnt change anything there.
So my code is this one here:
float movementSpeed = 8;
float currentMovementSpeed;
float speedSmoothTime = 0.1f;
float turnSmoothTime = 0.2f;
float jumpPower = 5;
float airControlPercentage = 0.2f;
float turnSmoothVelocity;
float speedSmoothVelocity;
bool isGrounded;
private void FixedUpdate()
{
isGrounded = GroundCheck(); // Is player grounded?
Vector2 inputDirection = (new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"))).normalized;
if (Input.GetButtonDown("Jump") && isGrounded) // Jump handling
Debug.Log("Player Jump");
if (inputDirection != Vector2.zero)
transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, Mathf.Atan2(inputDirection.x, inputDirection.y) * Mathf.Rad2Deg + cameraTransform.eulerAngles.y, ref turnSmoothVelocity, GetModifiedSmoothTime(turnSmoothTime)); // Rotate
currentMovementSpeed = Mathf.SmoothDamp(currentMovementSpeed, movementSpeed * inputDirection.magnitude, ref speedSmoothVelocity, GetModifiedSmoothTime(speedSmoothTime));
playerRigid.velocity = transform.forward * currentMovementSpeed + Vector3.up * playerRigid.velocity.y * Time.deltaTime; // Move
currentMovementSpeed = (new Vector2(playerRigid.velocity.x, playerRigid.velocity.z)).magnitude;
}
private float GetModifiedSmoothTime(float smoothTime) // Limit the control while in air
{
if (isGrounded)
return smoothTime;
if (airControlPercentage == 0)
return float.MaxValue;
return smoothTime / airControlPercentage;
}
private bool GroundCheck() // Player is grounded?
{
if (true)
return true;
return false;
}
Does someone knows what to do here?
It probably has something to do with your current gravity. Check in edit -> project settings -> physics the value of your gravity. In my case is -9,81. Change it to a higher value and see what happens.
I finally got it. How to fix it:
In this line of code
playerRigid.velocity = transform.forward * currentMovementSpeed + Vector3.up * playerRigid.velocity.y * Time.deltaTime;
Take out
* Time.deltaTime
Now the player is falling correctly.
Actually , transform.forward effect the y component of the body thus effects the gravity acting on the body.
Use
rb.velocity = new Vector3(horizontal , -1 , vertical) * speed ; .
It will work, or simply use AddForce to drive the player.
when you want to use gravity calculations, changing the rigidbody in your code can do this.
//rb.velocity = moveInput * moveSpeed;
for example will screw with your movement, even when a button isn't being pressed.
using something like:
if(Input.GetButtonDown("Jump") && Mathf.Abs(rb.velocity.y) < 0.001f)
{
rb.AddForce(new Vector2(0, jumpForce), ForceMode2D.Impulse);
}
will simply add forces on top of the calculations, instead of changing them prior to
In my case, the slow descending of my game character was solved by eliminating unnecessary calls to the physics system in the FixedUpdate() function. For example, when a joystick's X-axis and/or Y-axis are in the zero position.
I call addForce, velocity, rb transforms, etc. only if the absolute joystick value (- and +) exceeds a minimum value, in my case 0.04. Without these tests, these physics calls are done every time FixedUpdate() is called. This seems to overload the physics system, because at the same time, physics is also processing gravity etc.
Note that merely setting the joystick's dead zones in the Unity Input System doesn't solve this problem.
void FixedUpdate()
{
if (Mathf.Abs(stick.x) > 0.04) // prevent unnecessary physics call.
{
rb.transform.eulerAngles = rb.transform.eulerAngles - new Vector3(0, stick.x * Time.deltaTime * RotationSpeed * -1, 0);
}
if (Mathf.Abs(stick.y) > 0.04) // prevent unnecessary physics call.
{
rb.velocity = transform.forward * stick.y * Speed * Time.deltaTime;
}
}

Categories