Unity Rigidbody is sliding after jumping - c#

I would like my character to jump diagonally but when it hits the ground it slides.
private void Update()
{
if (Input.GetMouseButtonUp(0) && isGrounded())
{
//jump
timeHeld = 0;
Debug.Log("MouseButtonUp = true");
rb.AddForce(jumpDirection, ForceMode2D.Impulse);
}
else if (isGrounded())
{
rb.velocity = Vector2.zero;
}
}
this code somehow works but it glitches when the player hits the ground. I need to put some amount of force to get him out of isGrounded() or he will just get some pixels up and be instantly pushed down again.
This is my isGrounded() function
private bool isGrounded()
{
RaycastHit2D raycastHit = Physics2D.BoxCast(boxCollider2d.bounds.center, boxCollider2d.bounds.size, 0f, Vector2.down, 0.1f, platformLayerMask);
return raycastHit.collider != null;
}
the problem is obviously my distance which is 0.1f in Boxcast(). Any ideas how to fix that?

No need to check the ground by yourself.
Add a unity event called OnCollisionEnter
Inside, check if the collider is the ground
If true, then velocity = zero

Related

Checking if RigidBody is grounded?

I have a GameObject with a Rigidbody, Mesh Collider, Skinned Mesh Renderer, and the below script.
I'm trying to check if it is Grounded, but the Console continuously spits out "Not Grounded!" when it is, so obviously something is wrong here. Can anyone please help?
public class GroundCheck : MonoBehaviour
{
public float Height;
bool IsGrounded;
Ray ray;
MeshRenderer renda;
private void Start()
{
Height = renda.bounds.size.y;
}
void Update()
{
if (Physics.Raycast(transform.position, Vector3.down, Height))
{
IsGrounded = true;
Debug.Log("Grounded");
}
else
{
IsGrounded = false;
Debug.Log("Not Grounded!");
}
}
}
Another option for checking if the rigidBody is grounded is using the OnTriggerStay function.
void OnTriggerStay(Collider other)
{
if (other.Transform.Tag == "Ground")
{
IsGrounded = true;
Debug.Log("Grounded");
}
else
{
IsGrounded = false;
Debug.Log("Not Grounded!");
}
}
I've checked your code with a simple scene with a plane and a cube, and it works.
It only spawns NotGrounded when it's clearly "floating" arround or the object has the half of it's body outside the plane.
Check those things drawing the Ray, this should give your more information about what is going wrong with your mesh.
Also if the problem is how the game is perceiving the Height of your Skinned Mesh you can also use SkinnedMeshRenderer.localBounds who returns the AABB of the object.
There is a better way to check if your rigidbody is grounded than collision checking and rays. But first, why is collision checking not a good idea: If your level is a single model the walls will too be tagged with the "Ground" tag and hitting a wall will return true, which you don't want. Rays can be used, but that's too much math you don't need to waste time with.
Instead of using new objects and methods, just check if the position is changing:
private float lastYposition;
private bool grounded;
void Start() {
lastYposition = transform.position.y;
}
void Update() {
grounded = (lastYposition == transform.position.y); // Checks if Y has changed since last frame
lastYposition = transform.position.y;
}
From here it's up to you what logic you are going to add.
Get the closest contact point to the bottom. Check if the contact normal is within range.
void OnCollisionStay(Collision collision)
{
var bottom = renderer.bounds.center;
bottom.y -= renderer.bounds.extents.y;
float minDist = float.PositiveInfinity;
float angle = 180f;
// Find closest point to bottom.
for (int i = 0; i < collision.contactCount; i++)
{
var contact = collision.GetContact(i);
var tempDist = Vector3.Distance(contact.point, bottom);
if(tempDist < minDist)
{
minDist = tempDist;
// Check how close the contact normal is to our up vector.
angle = Vector3.Angle(transform.up, contact.normal);
}
}
// Check if the angle is too steep.
if (angle <= 45f) IsGrounded = true;
else IsGrounded = false;
}
void OnCollisionExit(Collision collision)
{
IsGrounded = false;
}
I came up with this answer after reading amitklein's answer.
private void Jump()
{
if (Input.GetKeyDown(KeyCode.Space))
{
if (transform.position.y == 0f)
{
rb.AddRelativeForce(Vector3.up * jumpForce);
}
}
}
I used this approach to jump using addrelativeforce of rigid body only when the position of y in 0.

How unity raycast prevent double jump problem

I am trying to prevent the player to double jump, for that I am using Raycast to check if the player is in the layer Floor. My solution works sometimes, but sometimes it allows the player to double jump.
This is my code:
void Movement()
{
float movement = Input.GetAxis("Horizontal");
Vector3 playerVelocity = new Vector3(movement*speed, myRigidbody.velocity.y, speed);
myRigidbody.velocity = playerVelocity;
if (Input.GetKeyDown("space") && isGrounded)
{
Vector3 jumpVelocity = new Vector3(0f, jumpSpeed, 0f);
myRigidbody.velocity += jumpVelocity;
isGrounded = false;
Debug.Log("floor:"+isGrounded);
anim.SetInteger("AnimationPar", 3);
myAudioSource.Stop();
AudioSource.PlayClipAtPoint(jumpSound,transform.position, volumeSoundEffects);
}
else if (isGrounded && !myAudioSource.isPlaying && !levelFinished)
{
myAudioSource.Play();
}
if (!isGrounded)
{
IsGrounded();
}
}
void IsGrounded()
{
RaycastHit hit;
int mask = 1 << LayerMask.NameToLayer("Floor");
if (Physics.Raycast(transform.position, Vector3.down, out hit, 0.01f, mask))
{
//Debug.Log("Is touching floor");
isGrounded = true;
Debug.Log("floot:" + isGrounded);
anim.SetInteger("AnimationPar", 1);
if (!myAudioSource.isPlaying)
{
myAudioSource.Play();
}
}
}
The Movement method is called in Update. I think that maybe the IsGrounded method is called to fast and the player is still to close to the floor.
An easy way to prevent double jump is using a counter instead of raycast, it really makes everything easy. put a counter in this portion of the code:
if (Input.GetKeyDown("space") && isGrounded && counter <2)
{
Vector3 jumpVelocity = new Vector3(0f, jumpSpeed, 0f);
counter++;
Im unable to debug your code at this time, but another way you could do it is using OnCollisionEnter and OnCollisionExit
You can detect all things your players collides with, if one of them is the Floor then its likely hes grounded. But it would be possible to touch the floor on the side of the player while falling, to avoid this use the grounded method agian. You said it does work but sometimes fails. So try something like:
void OnCollisionEnter (Collision col)
{
if(col.gameobject.layer == LayerMask.NameToLayer("Floor") && IsGrounded())
{
isGrounded = true;
}
}
void OnCollisionExit (Collision col)
{
if(col.gameobject.layer == LayerMask.NameToLayer("Floor") && !IsGrounded())
{
isGrounded = false;
}
}
Thats untested code so im not 100% sure if it will work. The Exit may not work since IsGrounded may still be able to raycast to the ground. But if you handle your layers/collision in your levels right then you can remove the IsGrounded calls entirely.
This isnt the perfect solution by far but it might work.

Why isn't my 2D bullet's collider colliding with anything?

I'm trying to make a platformer game that uses 2D sprites in 3D space. For some reason, though the bullet the player shoots fires correctly from the GO that's a child of the player, it won't collide with anything (enemies, other objects, etc.) in either 2D or 3D. I have a 3D character controller on the parent player GO, but that doesn't affect the bullet object I'm firing, does it?
Colliders on everything appropriately.
Tried with IsTrigger both on and off in various combinations.
Objects are on the same layer and the same Z axis position.
//BULLET FIRING SCRIPT
void Update()
{
if (Input.GetButtonDown("Fire2") && !Input.GetButton("Fire3") &&
Time.time > nextFireTime)
{
Rigidbody2D cloneRb = Instantiate(bullet,
bulletSpawn.position, Quaternion.identity) as Rigidbody2D;
cloneRb.AddForce(bulletPrefab.transform.right *
projectileForce);
nextFireTime = Time.time + fireRate;
}
}
//____________________________________________________________
//BULLET OBJECT SCRIPT
private void Start()
{
direction = Input.GetAxisRaw("Horizontal");
if (direction == -1)
{
facingLeft = true;
}
else if (direction == 1)
{
facingLeft = false;
}
else if (direction == 0)
{
facingLeft = false;
}
if (facingLeft == false)
{
rb.velocity = transform.right * speed;
Debug.Log("Fired Bullet");
}
else
{
bulletPrefab.transform.Rotate(0, 180, 0);
firePoint.transform.Rotate(0, 180, 0);
Debug.Log("Rotated Bullet");
Debug.Log("Fired Bullet Left");
rb.velocity = transform.right * speed;
}
}
// Update is called once per frame
void Update()
{
}
public void OnTriggerEnter2D(Collider2D collider)
{
Debug.Log("Bullet Hit:");
Debug.Log(collider.name);
Enemy enemy = collider.GetComponent<Enemy>();
if (enemy != null)
{
enemy.TakeDamage(damage);
}
Destroy(gameObject);
}
Expected Result: Bullet object collides with other objects, prints Debug.Log output and gets destroyed.
Actual Result: Bullet object fires through, in front of, or behind other objects that also have colliders and there is no Debug.Log output. Bullet does not get destroyed and so instantiates clones infinitely when assigned input is entered.
Fixed issue by checking Default/Default in the Layer Collision Matrix under Edit-->Project Settings-->Physics. Not sure if it's supposed to be checked by default and somehow got unchecked but checking it solved the problem.

Prevent moving in raycast direction when raycast hits something

I'm working on a third person controller and I'm trying to prevent movement when a raycast in front of the player hits something. Currently when the player collides with a block the blend tree is set to 0 so that it shows the Idle animation. In this case the player appears to be not moving, which it obviously isn't because it's colliding with something, but the input of the user is still there.
This is visible when the player presses the jump button, because the player moves in the direction the user was initially trying to move. In the player controller there is a part that prevents the user from moving after the jump button has been pressed during the length of the jump (Until the player is grounded again). What I'm trying to achieve is that when the user is moving against something and presses the jump button the player will just jump up and won't go in the direction the user was initially trying to go, even if there is no more collider or obstruction.
Something like if the raycast hits something the user won't be able to add movement in that direction, but is still able to turn the player away from the object and move in that direction as long as the raycast doesn't hit a collider.
I'm no c# wizard, so most of the code below comes from a tutorial. I can write a raycast myself, but have no clue how to write the rest of what I'm trying to achieve.
Edit: Current script
public RaycastHit _Hit;
public LayerMask _RaycastCollidableLayers; //Set this in inspector, makes you able to say which layers should be collided with and which not.
public float _CheckDistance = 5f;
void Update()
{
PushStates();
// Input
Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
Vector2 inputDir = input.normalized;
bool running = Input.GetButton("Run");
if (IsNotBlocked())
{
Move(inputDir, running);
}
float animationSpeedPercent = ((running) ? currentSpeed / runSpeed : currentSpeed / walkSpeed * .5f);
anim.SetFloat("speedPercent", animationSpeedPercent, speedSmoothTime, Time.deltaTime);
anim.SetFloat("speedPercentPush", animationSpeedPercent, speedSmoothTime, Time.deltaTime);
// Check if walking or running
if (animationSpeedPercent < 0.51f)
{
if (ShortJumpRaycast())
{
if (Input.GetButtonDown("Jump") && Time.time > canJump)
{
ShortJump();
}
}
else
{
if (Input.GetButtonDown("Jump") && Time.time > canJump)
{
JumpWalk();
}
}
}
else
{
//Debug.Log("You are Running");
if (Input.GetButtonDown("Jump") && Time.time > canJump)
{
JumpRun();
}
}
JumpCheck();
}
void Move(Vector2 inputDir, bool running)
{
if (inputDir != Vector2.zero)
{
float targetRotation = Mathf.Atan2(inputDir.x, inputDir.y) * Mathf.Rad2Deg + cameraT.eulerAngles.y;
transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref turnSmoothVelocity, GetModifiedSmoothTime(turnSmoothTime));
}
float targetSpeed = ((running) ? runSpeed : walkSpeed) * inputDir.magnitude;
currentSpeed = Mathf.SmoothDamp(currentSpeed, targetSpeed, ref speedSmoothVelocity, GetModifiedSmoothTime(speedSmoothTime));
velocityY += Time.deltaTime * gravity;
Vector3 velocity = transform.forward * currentSpeed + Vector3.up * velocityY;
controller.Move(velocity * Time.deltaTime);
currentSpeed = new Vector2(controller.velocity.x, controller.velocity.z).magnitude;
// Checks if player is not grounded and is falling
if (GroundCheck())
{
velocityY = 0;
anim.SetBool("onAir", false);
}
else
{
anim.SetBool("onAir", true);
}
}
bool IsNotBlocked()
{
Vector3 forward = transform.TransformDirection(Vector3.forward);
if (Physics.Raycast(transform.position, forward, out _Hit, _CheckDistance + 0.1f, _RaycastCollidableLayers))
if (_Hit.collider == null)
{
Debug.Log("Raycast hit nothing");
return true;
}
GameObject go = _Hit.collider.gameObject;
if (go == null) //If no object hit, nothing is blocked.
return true;
else //An object was hit.
return false;
}
Directly before move you can raycast in the direction of the movement and if it hits something you can cancel movement.
Something like:
Vector3 velocity = transform.forward * currentSpeed + Vector3.up * velocityY;
if(!Physics.Raycast(transform.position, transform.forward, distance)
{
controller.Move(velocity * Time.deltaTime);
}
else
{
controller.Move(Vector3.up * velocityY * Time.deltaTime);
}
Something like if the raycast hits something the user won't be able to add movement in that direction
Here is an example of how you can perform a raycast to check whether something was hit or not.
//Example raycast code
//Variables
public RayCastHit _Hit;
public LayerMask _RaycastCollidableLayers; //Set this in inspector, makes you able to say which layers should be collided with and which not.
public float _CheckDistance = 5f;
//Method
bool IsNotBlocked(){
Vector3 forward = transform.TransformDirection(Vector3.forward);
if (Physics.Raycast(transform.position, forward, out _Hit, _CheckDistance + 0.1f, _RaycastCollidableLayers))
if (_Hit.collider == null)
{
Debug.Log("Raycast hit nothing");
return true;
}
GameObject go = _Hit.collider.gameObject;
if (go == null) //If no object hit, nothing is blocked.
return true;
else //An object was hit.
return false;
}
Basically,
The length of the ray is CheckDistance.
RayCastCollidableLayers determines which layers an object can be in for it to be collidable with the ray we create.
The code sets a direction stored in "forward", a raycast is then performed from the transform.position (position of object this script is attached to) in direction Vector3.Forward.
_Hit saves whatever the raycast hits. An object can then be accessed through it and stored as a GameObject. I call this object 'go'.
Feel free to ask questions.

Character won't jump in Unity2D but entered the jump statement

I have a little problem with my player control script (C#) in the unity enigne. I worked out the following script with the basic movement of the player. The problem is that the player can enter the jump statement (the debug log printed it out)
Debug Log
but it will not work. The character is still on the ground.
The jump function will be enabled when the player is on the ground (grounded) and did not a double jump.
So my question is are there any "code mistakes" or maybe some configuration problems which I do not see?
Thank you for your help in advance!
using UnityEngine;
using System.Collections;
public class PlayerControl : MonoBehaviour
{
// public variables
public float speed = 3f;
public float jumpHeight = 5f;
// private variables
Vector3 movement;
Animator anim;
Rigidbody2D playerRigidbody;
// variables for the ground check
public Transform groundCheck;
public float groundCheckRadius;
public LayerMask whatIsGround;
private bool grounded;
private bool doubleJump;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
// Proves if the player is on the ground and activate the double jump function
if (grounded)
{
doubleJump = false;
}
// First line of proving the jump
if (Input.GetMouseButtonDown(0) && grounded)
{
Debug.Log("Jump if entered");
Jump();
}
if (Input.GetMouseButtonDown(0) && !doubleJump && !grounded)
{
Debug.Log("double Jump");
Jump();
doubleJump = true;
}
// Flipping the Player when he runs back
if (Input.GetAxis("Horizontal") < 0)
{
playerRigidbody.transform.localScale = new Vector2(-1.7f, 1.7f);
}
else
{
playerRigidbody.transform.localScale = new Vector2(1.7f, 1.7f);
}
}
void Awake()
{
// References setting up
playerRigidbody = this.GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
}
void FixedUpdate()
{
float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");
// simple Movement without a speed control
Move(horizontal, vertical);
Animating(horizontal, vertical);
// Section for ground detection
grounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
// Set the parameter for the jump animation false or true
anim.SetBool("Grounded", grounded);
}
void Move(float horizontal, float vertical)
{
movement.Set(horizontal, 0f, vertical);
movement = movement.normalized * speed * Time.deltaTime;
playerRigidbody.MovePosition(transform.position + movement);
}
void Jump()
{
playerRigidbody.AddForce(Vector3.up * jumpHeight);
// playerRigidbody.AddForce(new Vector2(0f, jumpHeight), ForceMode2D.Impulse);
Debug.Log("Jump function");
}
void Animating(float h, float v)
{
bool walking = h != 0f || v != 0f;
anim.SetBool("IsWalking", walking);
}
}
Just guessing here, but maybe Vector3.up does not work for 2D physics? I'm not really into 2D, but you could try
playerRigidbody.AddForce(transform.up * jumpHeight);
instead.
Also, have you tried different values for jumpHeight? 5 might be way to small depending on the mass you set for your rigidbody.
And make sure you haven't restricted any axes in the inspector.
// Note: If you want the object to move in a reliable predictable way but still allow physics interactions, use MovePosition (set the object to kinematic if you want it to be unaffected by physics but still be able to affect other things, and uncheck kinematic if you want both objects to be able to be acted on by physics.
If you want to move your object but let physics handle the finer details, add a force.
playerRigidbody.rigidbody2D.AddForce(Vector3.up * 10 * Time.deltaTime);
use Vector.up, Vector.down, vector.right, Vectore.left along with time.deltaTime to have smooth movement along the frame.

Categories