I am making a game with Unity. For some reason the character controller gets stuck.
Here is a video of that
This is the player controller code:
private CharacterController CharacterController;
public float Speed = 12;
private Vector3 Velocity;
private GameObject GroundCheck;
private bool IsGrounded;
public float CheckDistance = 0.4f;
public LayerMask GroundMask;
public float JumpHeight = 3;
public float Gravity = -9.81f;
private void Start()
{
GroundCheck = GameObject.Find("GroundCheck");
CharacterController = GetComponent<CharacterController>();
}
private void Update()
{
IsGrounded = Physics.CheckSphere(GroundCheck.transform.position, CheckDistance, GroundMask);
float X = Input.GetAxis("Horizontal");
float Y = Input.GetAxis("Jump");
float Z = Input.GetAxis("Vertical");
Vector3 Move = transform.right * X + transform.forward * Z;
CharacterController.Move(Move * Speed * Time.deltaTime);
if (Y == 1 && IsGrounded)
{
Velocity.y = Mathf.Sqrt(JumpHeight * -2 * Gravity);
}
if (IsGrounded && Velocity.y < 0)
{
Velocity.y = -2;
}
Velocity.y += Gravity * Time.deltaTime;
CharacterController.Move(Velocity * Time.deltaTime);
}
Happened to me too, judging by the code we watched the same youtube tutorial. The problem here is that you (and that tutorial guy) are making two calls to the Move function inside the Update function, this can cause the jitter you show on the video among other unexpected behaviors. You should only call the Move function once in the Update function and everything will be fine. So you need to combine X, Y, and Z movement in a single Vector3:
private void Update()
{
IsGrounded = Physics.CheckSphere(GroundCheck.transform.position, CheckDistance, GroundMask);
float X = Input.GetAxis("Horizontal");
float Y = Input.GetAxis("Jump");
float Z = Input.GetAxis("Vertical");
if (Y == 1 && IsGrounded)
{
Velocity.y = Mathf.Sqrt(JumpHeight * -2 * Gravity);
}
if (IsGrounded && Velocity.y < 0)
{
Velocity.y = -2;
}
Vector3 Move = transform.right * X * speed
+ transform.forward * Z * speed
+ transform.up * Velocity.y;
CharacterController.Move(Move * Time.deltaTime);
}
Note that now we are multiplying by the speed before, just the X and Z components, because we don't want the vertical velocity affected by the movement speed.
Although it should work now, there are a few details worth noting:
· You should name your variables in lower camel case: "move", not "Move".
· You don't really need a Vector3 for the vertical velocity, it could be just a float, since you are just using it for one axis. Note that you are reading Velocity.y, but never Velocity.x or Velocity.z. AFAIK this is because you copied the script from the mentioned Youtube tutorial, happened to me too until I realized.
· I don't really know why you are using "float Y = Input.GetAxis("Jump");" since the Jump key should not be an axis, if you run into problems try this instead:
private void Update()
{
IsGrounded = Physics.CheckSphere(GroundCheck.transform.position, CheckDistance, GroundMask);
if (IsGrounded && Velocity.y < 0)
{
Velocity.y = -2f;
}
float X = Input.GetAxis("Horizontal");
float Z = Input.GetAxis("Vertical");
if (Input.GetButtonDown("Jump") && IsGrounded)
{
Velocity.y = Mathf.Sqrt(JumpHeight * -2f * Gravity);
}
Vector3 Move = transform.right * X * speed
+ transform.forward * Z * speed
+ transform.up * Velocity.y;
CharacterController.Move(Move * Time.deltaTime);
}
Does it get stuck as in "It vibrates near the cube trying to climb up" or in "I can't move the character as soon as it touches the cube"?
My guess would be that your character tries to climb up the cube. The cube is approximately 2 meters high (at least if I can correctly get that from the video), your step offset is only 1, though.
Another thing to consider would be your character's skin width. From the Unity Manual:
It’s good practice to keep your Skin Width at least greater than 0.01 and more than 10% of the Radius.
But yours seems to have a good value.
If nothing helps, I would recommend actually going back to a CharacterController with a size of 2 meters. Citing the Unity Manual again:
You can modify the Height and Radius to fit your Character’s mesh
. It is recommended to always use around 2 meters for a human-like character.
One more thing: Start naming your variables after the C#-conventions, which would be camelCase. For example, your Velocity would become velocity, your GroundMask would become groundMask etc.
Related
Hey fellow programmers,
I have a small problem with Quaternion (who doesn't). I make an airship that is flying using rigidbody. Making it fly forward and turning was not a problem, but now I try to make it lean again the direction of the turn (like a person driving a motor leaning into when doing a turn). The
problem is that the ship is making rotations around the x-axis, while i didn't program anything in that would influence it. I checked rotationSpeed, but that only influence y and z-axis. It goes wrong around deltaRotation, where the x-axis is influenced. I tried forcing it to be 0 with deltaRotation.x = 0.0f;, but the ship still goes down without me changing the x-axis. Does anyone know what is happenning, and how to fix it?
if (Input.GetKey(KeyCode.W))
{
if (speed < maxSpeed)
{
speed += maxSpeed / incrementsSpeed;
}
}
if (Input.GetKey(KeyCode.S))
{
if (speed > 0)
{
speed -= maxSpeed / incrementsSpeed;
}
else
{
speed = 0;
}
}
if (Input.GetKey(KeyCode.D))
{
if (rotationSpeed.y < maxRotationSpeed)
{
rotationSpeed.y += maxRotationSpeed / incrementsRotationSpeed;
}
}
if (Input.GetKey(KeyCode.A))
{
if (rotationSpeed.y > -maxRotationSpeed)
{
rotationSpeed.y -= maxRotationSpeed / incrementsRotationSpeed;
}
}
forwardPower = transform.forward * speed;
//Calculate lean value of airship, depending on the speed and turn angle of the ship
float currentLeaningValue = rotationSpeed.y * speed;
float maxValue = maxRotationSpeed * maxSpeed;
float currentValue = ExtensionMethod.Mapz(currentLeaningValue, -maxValue, maxValue, -maxLeaningAngle, maxLeaningAngle);
//map the value of rotation to inspector rotation
float realRot = ExtensionMethod.Mapz(transform.eulerAngles.z, 0f, 360f, -180f, 180f);
realRot = 180 - Mathf.Abs(realRot);
if (currentValue < 0.0f && realRot > 0)
{
realRot *= -1f;
}
//Calculate force of leaning
float leanRotationSpeed = (currentValue - realRot) / incrementsRotationSpeed;
//Add lean force to turn rotation speed
rotationSpeed = new Vector3(0, rotationSpeed.y, -leanRotationSpeed);
//Convert to Quaternion, here something goes wrong
Quaternion deltaRotation = Quaternion.Euler(rotationSpeed);
//Did this to see if I manually put the x rotation to 0, it would work. Sadly it didn't
deltaRotation.x = 0.0f;
//Add forces to actual movement of airship
rb.MoveRotation(rb.rotation * deltaRotation);
rb.MovePosition(transform.position + forwardPower);
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.
I have been searching for an answer for hours, and I cant find the solution. I have a script that rotates a steering wheel when the car is turning. It has maximum rotation of 120 and a minimum rotation of -120. I have been trying to rotate with RotateTowards and other Quaternion methods but I can't figure them out. How can I make it when the turning angle is not zero and turning keys (a and d) are not pressed, it goes to its original rotation of 0, 0, 0, at a certain speed?
Here's my script for the steering wheel:
if (horizontalInput > 0)
{
wheel.transform.Rotate(Vector3.forward*Time.deltaTime*wheelspeed);
rotations -= wheelspeed;
}
else if (horizontalInput < 0)
{
wheel.transform.Rotate(-Vector3.forward * Time.deltaTime * wheelspeed);
rotations += wheelspeed;
}
And here is my (very bad) script for the minimum and maximum rotation of the steering wheel:
angle += Input.GetAxis("Horizontal") * Time.deltaTime*10;
angle = Mathf.Clamp(-120, angle, 120);
wheel.transform.localRotation = Quaternion.AngleAxis(angle, Vector3.forward);
angle += Input.GetAxis("Horizontal") * Time.deltaTime * 400;
angle = Mathf.Clamp(120, 0, angle);
wheel.transform.localRotation = Quaternion.AngleAxis(angle, Vector3.forward);
You should do something like this for the steering wheel script:
float rotateBack = 0f;
float angle = 0f;
void Update(){
angle += Input.GetAxis(“Horizontal”) * Time.deltaTime * 10;
angle = Mathf.Clamp(angle, -120, 120);
if (Input.GetAxis(“Horizontal”) == 0 && angle != 0f){
angle += rotateBack * Time.deltaTime;
if (angle > 0){
rotateBack = -10f;
}
else{
rotateBack = 10f;
}
}
transform.rotation = Quaternion.Euler(0f, 0f, angle);
}
What this does is setting a variable of angle to the input times a value to increase turning speed. Then it clamps if so it doesn’t go over 120, or under -120. Then, if there is no input and the angle isn’t zero: it will change the variable by how much to rotate back. The rotate back variable is set to either positive or negative based on which direction the steering wheel needs to be turned. The only problem with this script that I could see is the angle not being exactly 0, continuing the if statement. If this error affects your game, use the Mathf.Round(); function.
(If there are any errors, comment on this post.)
I would do what #ken is doing, but refactor the speed factors and other constants into fields so they are more easily changed and also use Mathf.MoveTowardsAngle to move the angle back to its neutral position.
[SerializeField] float rotateBackSpeed = 3f; // degrees per second
[SerializeField] float rotateSpeed = 10f; // degrees per second
[SerializeField] float angle = 0f; // degrees
[SerializeField] float minAngle = -120f; // degrees
[SerializeField] float maxAngle = 120f; // degrees
[SerializeField] float neutralAngle = 0f; // degrees
void Update()
{
angle = Mathf.Clamp(angle + Input.GetAxis(“Horizontal”) * rotateSpeed
* Time.deltaTime, minAngle, maxAngle);
if (Mathf.Approximately(0f, Input.GetAxis(“Horizontal”)))
{
angle = Mathf.MoveTowardsAngle(angle, neutralAngle,
rotateBackSpeed * Time.deltaTime);
}
transform.eulerAngles = angle * Vector3.forward;
}
For a few weeks, I've been trying to add strafing to my Player. I now have a working Script but when I try using the Mouse to Rotate the Player and the Camera the script is reacting wierd. When the Player is facing towards the Z-direction the Player is walking and running normaly, but as soon as I turn him around he slows down and moves super slow. Here's the code:
void Update () {
// input
Vector2 input = new Vector2 (Input.GetAxisRaw ("Horizontal"), Input.GetAxisRaw ("Vertical"));
Vector2 inputDir = input.normalized;
bool running = Input.GetKey (KeyCode.LeftShift);
if (!Input.GetMouseButtonDown(0) || Input.GetKey(KeyCode.JoystickButton2)) {
Move (inputDir, running);
Time.timeScale = 1;
}
if (Input.GetKeyDown (KeyCode.Space) || Input.GetKeyDown (KeyCode.JoystickButton0)) {
Jump ();
}
// animator
float animationSpeedPercent = ((running) ? currentSpeed.magnitude / runSpeed : currentSpeed.magnitude / walkSpeed * .5f);
void Move(Vector2 inputDir, bool running) {
float targetRotation = cameraT.eulerAngles.y;
transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref turnSmoothVelocity, GetModifiedSmoothTime(turnSmoothTime));
Vector2 targetSpeed = new Vector2(
((running) ? runSpeed : walkSpeed) * inputDir.normalized.x,
((running) ? runSpeed : walkSpeed) * inputDir.normalized.y);
currentSpeed = Vector2.SmoothDamp(currentSpeed, targetSpeed,
ref speedSmoothVelocity,
GetModifiedSmoothTime(speedSmoothTime));
velocityY += Time.deltaTime * gravity;
Vector3 velocity = (transform.forward * currentSpeed.y) +
(transform.right * currentSpeed.x) +
Vector3.up * velocityY;
controller.Move(velocity * Time.deltaTime);
currentSpeed = new Vector2(controller.velocity.x, controller.velocity.z);
if (controller.isGrounded)
{
velocityY = 0;
}
}
In order to allow a player to strafe in the default PlayerController, a couple edits must be made. I won't write the code for you, but here are the steps you need to take, they should be relatively easy to implement:
First, you need to comment out the code related to rotation
Then rather than obtaining the target speed as a scalar float, you should either calculate it as a Vector2 or as 2 separate floats (preferably the former)
Vector2 targetSpeed = new Vector2(
((running) ? runSpeed : walkSpeed) * inputDir.normalized.x,
((running) ? runSpeed : walkSpeed) * inputDir.normalized.y);
currentSpeed is a float, that relies on targetSpeed, but rather than~~ adjusting currentSpeed to also be a Vector2, you can use targetSpeed.magnitude when calculating the SmoothDamp
Since you need speed in both the Z context and the X context, you should make currentSpeed a Vector2 Thankfully there already exists a Vector2.SmoothDamp so that should be easy to refactor.
currentSpeed = Vector2.SmoothDamp(currentSpeed, targetSpeed,
ref speedSmoothVelocity,
GetModifiedSmoothTime(speedSmoothTime));
You need to include the X component in the velocity calculation. (remember that a Vector2's X and Y correspond to X and Z respectively on a Vector3)
Vector3 velocity = (transform.forward * currentSpeed.y) +
(transform.right * currentSpeed.x) +
Vector3.up * velocityY;
Finally, you want to adjust the currentSpeed to include the appropriate X, and Z velocities. This can be done simply by calculating the magnitude based on a Vector3 instead of a Vector2
currentSpeed = new Vector2(controller.velocity.x, controller.velocity.z);
By the way, welcome to StackOverflow - In the future you want to be sure to include a minimal, verifiable, and complete example, and not just the default unmodified code.
People are more willing to help if you've shown that you've made an effort yourself.
So i am coding in Unity3D and i need to make so that i loose half of my maximum health which is 100 if my jump is over 1.533287.
My problem is that my player can double jump and is supposed to land on platforms higher than the start place of the jump.
}
void Start()
{
}
public int doubleJump = 0;
public float speed = 6.0F;
public float jumpSpeed = 8.0F;
public float gravity = 20.0F;
public int health = 100;
public float maxHeight;
public float height;
public int maxHealth = 100;
private Vector3 moveDirection = Vector3.zero;
// Update is called once per frame
void Update()
{
CharacterController controller = GetComponent<CharacterController>();
if (controller.isGrounded)
{
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0,
Input.GetAxis("Vertical"));
moveDirection = transform.TransformDirection(moveDirection);
moveDirection *= speed;
if (Input.GetButtonDown("Jump"))
{
moveDirection.y = jumpSpeed;
}
doubleJump = 0;
}
if (!controller.isGrounded)
{
if (doubleJump == 0)
{
if (Input.GetButtonDown("Jump"))
{
moveDirection.y = jumpSpeed;
doubleJump = 1;
}
}
}
if (maxHeight < height) //This sets the maximum height
{
maxHeight = height;
}
height = transform.position.y;
moveDirection.y -= gravity * Time.deltaTime;
controller.Move(moveDirection * Time.deltaTime);
}
I would modify your code to use your player's rigidbody.
For example... if the player's velocity.y is in the positives, then he's moving upwards.
If the player's velocity.y is in the negatives, then it means they are falling.
Make it so that if the velocity.y is below a certain value, then they take fall damage.
That way if you player doubles jumps towards a platform, when they land the velocity.y might be a small negative number. However if they jumped off of that high platform, they'd have a longer fall time which means a larger negative velocity.
Here's an example, although I don't know what scale you're working in so the numbers may be inaccurate for your scene.
Your player double jumps towards a platform, and when they reach their maximum height their velocity.y is now equal to 0.
As they start falling, velocity.y dips into the negatives. This means that when your player lands, they'll have a negative velocity. Let's say -5.
You can make your code say something similar to
if(playerRigidBody.velocity.y <= -10)
{
FallDamage();
}
So if your player has been falling for a little while, they'd take fall damage. However since when they landed on your platform at a velocity of -5 they wouldn't take fall damage... and if they jump off the platform and land on the ground below their velocity will more than likely be below -10 in the example.
Hopefully this helps you figure this out!