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

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);

Related

Why is my player movement inverted when I turn around?

I used rigidbodies a lot and recently started using character controllers. I have basic movement code for my character controller here:
public void Update()
{
isGrounded = controller.isGrounded;
if (isGrounded)
{
x = Input.GetAxis("Horizontal");
z = Input.GetAxis("Vertical");
}
Vector3 move = Vector3.forward * z + Vector3.right * x;
controller.Move(move * speed * Time.deltaTime);
if (Input.GetButtonDown("Jump") && isGrounded)
{
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
if (!isGrounded)
{
}
velocity.y += gravity * Time.deltaTime; // Gravity for jumps
controller.Move(velocity * Time.deltaTime);
}
The reason I am using this code is because I'm trying to make sure that when my player jumps, the jump does not follow my camera around. This code does that, however if I rotate 180 degrees on the y axis, my WASD keys all become inverted. I've tried adding * (1) but that just makes it worse. If I replace Vector3.forward & Vector3.right to transform.forward & transform.right, the keys are no longer inverted but then my jump is controlled by my mouse movement, so I'm really trying to get the first line of code to work.
Vector3.forward is always (1, 0, 0). If you want the movement to be based on the camera's rotation you need to use Transform.forward and Transform.right(Assuming your character only has rotation around the y axis)
If you don't want the jump to be controllable mid-air, you have to logic to create momentum and prevent the player from changing their momentum if they are in the air. The first line is never going to work.

Player movement is frame dependant even with Time.deltaTime

I'm trying to port my friend's Scratch game to Unity for fun, but the code for deceleration is causing the player to speed up and slow down depending on the framerate. The maximum X velocity should be 6.75, which it is at 30 fps, (scratch's usual framerate), but when the FPS changes his speed varies up to ~7.5.
Code (Unfinished):
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public Vector2 Velocity, WallJumpForce;
public float Acceleration, Decceleration;
public float Gravity, JumpForce;
public int TargetFPS;
void Update()
{
Velocity.y -= Gravity;
if(Input.GetKey("right") || Input.GetKey("d"))
Velocity.x += Acceleration * SDT();
if(Input.GetKey("left") || Input.GetKey("a"))
Velocity.x -= Acceleration * SDT();
Velocity.x -= Velocity.x*(1-Decceleration) * SDT();
Move(Velocity.x, 0);
Debug.Log(1 / Time.deltaTime + ", " + SDT());
Application.targetFrameRate = TargetFPS;
}
void Move(float x, float y) //Converts Scratch Units to Unity Units
{
transform.position += new Vector3(x, y, 0) * 1.2f * Time.deltaTime;
}
float SDT() //Basically allows Scratch variables designed for 30fps work in unity
{
return 30 * Time.deltaTime;
}
}
I've discovered that changing Velocity.x *= Decceleration * SDT();
to
Velocity.x -= Velocity.x*(1-Decceleration) * SDT(); helps, but does not fix the problem.
For reference, this is the game I'm porting.
https://scratch.mit.edu/projects/590825095
Do yourself a favor and verify your units every single time you write a physics equation. In this case verifying your units will solve the problem.
Velocity has units of distance/time and gravity has units of distance/(time^2).
So lets examine the units on this line:
Velocity.y -= Gravity;
You are saying distance/time -= distance/(time^2). Those units don't work!
Clearly you will have to multiply Gravity by some amount of time to get a change in velocity. For that we have Δt, the amount of time that has passed since the last frame:
Velocity.y -= Gravity * Time.deltaTime;
Now the units are distance/time -= distance/(time^2)*time. That makes a whole lot more sense. Do that for all your physics equations and your problem will be solved.

Smooth Turning C# unity

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);
}*/
}

Unity 3D Jumping issue

I've been following along with a 3D shooter tutorial (pretty good so far) but I've hit a snag when it comes to my framerate and jumping. The framerate on my PC is just not consistent and consequently the jump height varies constantly and sometimes the character doesn't jump at all. I know handling jumping in Update (rather than FixedUpdate) can cause issues regarding framerates but the tutorial insists that using Time.deltaTime should resolve that. Any ideas on what I should do to try and keep my jumps consistent?
//Jumping
public float jumpHeight = 10f;
public Transform ground;
private bool readyToJump;
public LayerMask groundLayer;
public float groundDistance = 0.5f;
// Update is called once per frame
private void Update()
{
Jump();
PlayerMovement();
CameraMovement();
Shoot();
}
void PlayerMovement()
{
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 movement = x * transform.right + z * transform.forward;
myController.Move(movement * speed * Time.deltaTime);
//9.8 meters/second^2
velocity.y += Physics.gravity.y * Mathf.Pow(Time.deltaTime, 2) * gravityModifier;
if (myController.isGrounded)
{
velocity.y = Physics.gravity.y * Time.deltaTime;
}
myController.Move(velocity);
}
private void CameraMovement()
{
float mouseX = Input.GetAxisRaw("Mouse X") * mouseSensitivity * Time.deltaTime;
float mouseY = Input.GetAxisRaw("Mouse Y") * mouseSensitivity * Time.deltaTime;
cameraVerticalRotation -= mouseY;
cameraVerticalRotation = Mathf.Clamp(cameraVerticalRotation, minVertCameraAngle, maxVertCameraAngle);
transform.Rotate(Vector3.up * mouseX);
myHead.localRotation = Quaternion.Euler(cameraVerticalRotation, 0f, 0f);
}
void Jump()
{
readyToJump = Physics.OverlapSphere(ground.position, groundDistance, groundLayer).Length > 0;
if (Input.GetButtonDown("Jump") && readyToJump)
{
velocity.y = Mathf.Sqrt(jumpHeight * -2f * Physics.gravity.y) * Time.deltaTime;
}
myController.Move(velocity);
}
Handling the acceleration yourself is a bad idea, especially in Update rather than FixedUpdate. You should know that, in real life, the velocity changes smoothly with the acceleration against time. If you draw a curve, it is a straight slope. However, in computer, if you just calculate the velocity frame by frame, the curve will be looked like stairs.
You may use the physics engine in Unity and add an instant velocity to the character when it jumps. Just let Unity handle the acceleration.
First, you need to add a Rigidbody component to it. Then, you may modify your code to:
private Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void Jump()
{
readyToJump = Physics.OverlapSphere(ground.position, groundDistance, groundLayer).Length > 0;
if (Input.GetButtonDown("Jump") && readyToJump)
{
rb.velocity = Vector3.up * jumpHeight * 2f / -Physics.gravity.y * gravityModifier;
}
}
Don't forget to remove the code in PlayerMovement on handling falling after you have Use Gravity in the Rigidbody component.
Edit
If you are using CharacterController, you can only handle the acceleration yourself. You may just move the logic to FixedUpdate to solve the various height problem, or make a more accurate simulation. For example, use this to handle jumping:
velocity.y = jumpHeight * 2f / -Physics.gravity.y * gravityModifier;
and use this to handle falling:
myController.Move(velocity * Time.deltaTime + Physics.gravity * Mathf.Pow(Time.deltaTime, 2) / 2f * gravityModifier);
// Here you should check whether it will fall through the ground.
// If no, use this line to update the velocity.
velocity.y += Physics.gravity.y * gravityModifier * Time.deltaTime;
// Otherwise, update the position to make it on ground and set velocity.y to 0.

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