Weird rb.velocity.y - c#

When I'm moving left or right rb.velocity.y changes to weird numbers. How can I get rid of this?
I want to change the gravity when player is jumping and because of this bug it changes even if player is moving left and right.
void FixedUpdate(){
isGrounded = Physics2D.BoxCast(coll.bounds.center,coll.bounds.size, 0, Vector2.down, .1f,WhatIsGround);
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
if(jumped){
rb.AddForce(Vector2.up * jump, ForceMode2D.Impulse);
jumped = false;
}
if(rb.velocity.y < 0){
rb.gravityScale = fallMultiplier;
}
else if(rb.velocity.y > 0 && !(Input.GetKeyDown(KeyCode.Space) || Input.GetKeyDown(KeyCode.UpArrow))){
rb.gravityScale = lowJumpMultiplier;
}
else{
rb.gravityScale = 1f;
}
}

Both floating point calculations and Unity physics aren't precise. When your Rigidbody is moving, the gravity is trying to push it into the ground collider, while collision detection is pushing it upwards. This results in tiny oscillations in velocity.
You should never use direct comparison when working with float values. In this case, you need a small threshold to reliably detect the Y velocity direction:
if(rb.velocity.y < -.1f)
{
rb.gravityScale = fallMultiplier;
}
else if (rb.velocity.y > .1f && !(Input.GetKeyDown(KeyCode.Space) || Input.GetKeyDown(KeyCode.UpArrow)))
{
rb.gravityScale = lowJumpMultiplier;
}
else
{
rb.gravityScale = 1f;
}

Related

How can I refactor my 2d unity movement script to resolve the behavioral issues I have?

Below is the code I've written for my 2D movement:
void HandleInput()
{
float verticalMoveAmount = Input.GetAxis("Vertical") * playerSpeed * Time.deltaTime;
float horizontalMoveAmount = Input.GetAxis("Horizontal") * playerSpeed * Time.deltaTime;
if (facingRight)
{
transform.Translate(horizontalMoveAmount, verticalMoveAmount, 0);
}
else
{
transform.Translate(-horizontalMoveAmount, verticalMoveAmount, 0);
}
if (horizontalMoveAmount > 0 && !facingRight)
{
Flip();
}
else if (horizontalMoveAmount < 0 && facingRight)
{
Flip();
}
if (Input.GetKeyDown(KeyCode.Space) && hasBall)
{
Shoot();
}
}
void Flip()
{
facingRight = !facingRight;
transform.Rotate(0f, 180f, 0f);
}
I have the below problems though:
I have a Cinemachine virtual camera following the player around. However, whenever the player changes direction they vanish from the camera view. I think this is something to do with the Flip(), but I don't know why or how to fix it.
The if statement for if facingRight where I have to use negative movement on the horizontal axis, this feels like a really poor way to handle it, but if I don't do that I can't move left when the player changes directions to the left. Is there a better way I can do this?
Thank you in advance.
To 1) my guess is that since you rotate the object by 180° you are looking at the backface of it -> it is not being rendered due to backface culling.
To 2) since you rotate the object by 180° it's local coordinate system is now pointing in the other direction. Note that by default Transform.Translate works in the local coordinate system. If you want to rather use global axes then simply pass in
transform.Translate(horizontalMoveAmount, verticalMoveAmount, 0, Space.World);
In general simply assuming you are using a SpriteRenderer which is the most common thing for 2D games you should rather use the property SpriteRenderer.flipX and not rotate your object at all. This would solve both issues at the same time
So together your code could be simply
public SpriteRenderer spriteRenderer;
void HandleInput()
{
var input = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
// ensure that diagonal movement isn't faster
input = Vector2.ClampMagnitude(input, 1f);
var movement = input * playerSpeed * Time.deltaTime;
transform.Translate(movement, Space.World);
// basically the same now as
//transform.position += movement;
if (input.x != 0)
{
spriteRenderer.flipX = input.x < 0;
}
if (hasBall && Input.GetKeyDown(KeyCode.Space))
{
Shoot();
}
}

Why does mashing jump let the player jump twice?

I'm having an issue where if the player mashes the jump button fast enough, they get an extra jump.
I'm pretty sure it has to do with my implementation of coyote time since it's letting the player jump even when slightly off the ground, but I'm not sure how I could change it to fix this issue without removing the mechanic entirely.
Here's the code I'm using for jumping.
//Check if player is grounded
IsGrounded = Physics2D.OverlapCircle(GroundCheck.position, 0.1f, GroundLayer);
//Coyote Jump
if (IsGrounded == true)
{
HangCount = HangTime;
}
else if(IsGrounded == false)
{
HangCount -= Time.deltaTime;
}
//Jump Buffer
if (Input.GetButtonDown("Jump"))
{
JumpBufferCount = JumpBufferLength;
}
else
{
JumpBufferCount -= Time.deltaTime;
}
//Jump
if (JumpBufferCount >= 0 && HangCount > 0)
{
RB.velocity = new Vector2(RB.velocity.x, JumpHeight);
FindObjectOfType<AudioManager>().Play("Jump");
HangCount = 0;
JumpBufferCount = 0;
}
}
}
Found a solution. I just added a check to see if my Y velocity was equal to or less than 0 so the player couldn't jump while already ascending.

Player keeps rotating when going left in Unity 2d

I'm using Unity to move my player sprite in a 2D platformer where it'll go right if right key is pressed & left if left key is pressed.
The right movement is working fine, but whenever the player moves left, I'm changing the Y rotation to 180 and the player is supposed to show left animation only but it keeps rotating back and forth when going left.
See the video sample.
This is my code:
private Rigidbody2D rb;
private void FixedUpdate() {
InputManager();
}
private void InputManager()
{
float hDir = Input.GetAxis("Horizontal");
if (state != State.hurt) {
if(!anm.GetCurrentAnimatorStateInfo(0).IsTag("attack"))
{
if (hDir < 0) // Go left
{
rb.velocity = new Vector2(-speedX, rb.velocity.y);
transform.Rotate(0f, 180f, 0f);
}
else if (hDir > 0) // Go right
{
rb.velocity = new Vector2(speedX, rb.velocity.y);
transform.Rotate(0f, 0f, 0f);
}
}
}
}
How can I make my player stick to left animation when going on left? Please do not suggest changing localScale as I know it works but for shooting purpose it's best that my player rotates.
Here is a thing transform.Rotate(0,180,0); is used to rotate 180 degree in y axis each time when this line of code execute that's why you player does not stop rotating
Here is the code you can use it will stop you player rotate according to the directions
private void InputManager()
{
float hDir = Input.GetAxis("Horizontal");
if (state != State.hurt)
{
if (!anm.GetCurrentAnimatorStateInfo(0).IsTag("attack"))
{
if (hDir < 0) // Go left
{
rb.velocity = new Vector2(-speedX, rb.velocity.y);
this.transform.rotation = Quaternion.AngleAxis(180, Vector3.up);
}
else if (hDir > 0) // Go right
{
rb.velocity = new Vector2(speedX, rb.velocity.y);
this.transform.rotation = Quaternion.AngleAxis(0, Vector3.up);
}
}
}
}
You need to add a bool Variable that is checking if the player is facing right, as well as a Flip Function that flips him:
bool bFacingRight = true;
private Rigidbody2D rb;
int _switch;
private void FixedUpdate() {
InputManager();
}
private void InputManager()
{
if (state == State.hurt || anm.GetCurrentAnimatorStateInfo(0).IsTag("attack"))
return;
float hDir = Input.GetAxis("Horizontal");
// Move the character by finding the target velocity
rb.velocity = new Vector2(hDir * 10f, rb.velocity.y);
if (hDir < 0 && bFacingRight) { // Flip Left
_switch = 0;
Flip(_switch);
}
else if (hDir > 0 && !bFacingRight) { // Flip Right
_switch= 1;
Flip(_switch);
}
}
private void Flip(int swth)
{
// Switch the way the player is labelled as facing.
bFacingRight = !bFacingRight;
// Multiply the player's x local scale by -1.
switch(swth)
{
case 0:
this.transform.rotation = Quaternion.AngleAxis(180, Vector3.up);
break;
case 1:
this.transform.rotation = Quaternion.AngleAxis(0, Vector3.up);
break;
}
}

Why Coroutine Method Work Only Once Unity3D

I have an object that I want to move by swipe, for example when the swipe is up the object should move forward smoothly from point A to point B, swipe right the object move smoothly to the right etc...
To do that, I've tried Lerp, MoveTowards and SmoothDamp but every time the object just disappear from point A and appear on point B instantly.
So I used coroutine to give a time to the movement, and as you can see in the code bellow, there's 4 coroutine methods, each one is for a direction. the problem I have is that when playing, the first movement work properly, but in the second swipe the object didn't reach the destination point, and the third one also and the object have some weird movements.
Can you tell me what's wrong in my code?
Here's the Coroutine methods for movements:
public IEnumerator MoveForward()
{
Vector3 DestinationF = new Vector3(transform.position.x, transform.position.y, transform.position.z + DistanceF);
while (Vector3.Distance(transform.localPosition, DestinationF) > 0)
{
float totalMovementTimeF = 0.3f;
float currentMovementTimeF = 0f;
currentMovementTimeF += Time.deltaTime;
transform.localPosition = Vector3.Lerp(transform.position, DestinationF, currentMovementTimeF / totalMovementTimeF);
yield return null;
}
}
public IEnumerator MoveBackward()
{
Vector3 DestinationB = new Vector3(transform.position.x, transform.position.y, transform.position.z - DistanceB);
while (Vector3.Distance(transform.localPosition, DestinationB) > 0)
{
float totalMovementTimeB = 0.3f;
float currentMovementTimeB = 0f;
currentMovementTimeB += Time.deltaTime;
transform.localPosition = Vector3.Lerp(transform.position, DestinationB, currentMovementTimeB / totalMovementTimeB);
yield return null;
}
}
and there is still 2 coroutine methods MoveRight() and MoveLeft().
And here's the code for the swipe directions:
if (Input.GetMouseButtonDown(0))
{
//save began touch 2d point
firstPressPos = new Vector3(Input.mousePosition.x, Input.mousePosition.y);
}
if (Input.GetMouseButtonUp(0))
{
//save ended touch 2d point
secondPressPos = new Vector3(Input.mousePosition.x, Input.mousePosition.y);
//create vector from the two points
currentSwipe = new Vector3(secondPressPos.x - firstPressPos.x, secondPressPos.y - firstPressPos.y);
//normalize the 2d vector
currentSwipe.Normalize();
// swipe up
if (currentSwipe.y > 0 && currentSwipe.x > -0.5f && currentSwipe.x < 0.5f)
{
StartCoroutine(MoveForward());
}
// swipe down
if (currentSwipe.y < 0 && currentSwipe.x > -0.5f && currentSwipe.x < 0.5f)
{
StartCoroutine(MoveBackward());
}
//swipe left
if (currentSwipe.x < 0 && currentSwipe.y > -0.5f && currentSwipe.y < 0.5f)
{
StartCoroutine(MoveLeft());
}
//swipe right
if (currentSwipe.x > 0 && currentSwipe.y > -0.5f && currentSwipe.y < 0.5f)
{
StartCoroutine(MoveRight());
}
}
Your first Coroutine works because:
Vector3 DestinationF = new Vector3(transform.position.x, transform.position.y, transform.position.z + DistanceF);
will result in a positive position, so, the distance will be greater than 0:
while (Vector3.Distance(transform.localPosition, DestinationF) > 0)
On the other hand, while subtracting the distanceB from the z value:
Vector3 DestinationB = new Vector3(transform.position.x, transform.position.y, transform.position.z - DistanceB);
may result in a negative value, therefore:
while (Vector3.Distance(transform.localPosition, DestinationB) > 0)
will start as < 0, so the condition is never met. Check your condition. Do you want absolute values, or not equal to 0?
The issue is that you never reach the target.
Your lerping with factor
currentMovementTimeF / totalMovementTimeF
makes not much sense since you reset it every frame to
var currentMovementTimeF = Time.deltaTime;
which in the most cases will be < 0.3f (this would mean you have only about 3 frames per second) so it will always be
currentMovementTimeF < totalMovementTimeF
and therefore
currentMovementTimeF / totalMovementTimeF < 1
So you always start a new interpolation between the current position and the target. So the distance gets smaller and smaller but literally never reaches the final position actually (though it seems so).
Additionally you mixed position and localPosition there so if the GameObject is not on root level it gets even worse!
What you would want instead is probebly either using MoveTowards with a certain speed. (position based)
// adjust these in the Inspector
public float speed;
public float MoveDistance;
public IEnumerator Move(Vector3 direction)
{
var destinaton = transform.position + direction * MoveDistance;
while (Vector3.Distance(transform.position, destinaton) > 0)
{
transform.position = Vector3.MoveTowards(transform.position, MoveDistance, Time.deltaTime* speed);
yield return null;
}
}
MoveTowards makes sure there is no overshooting.
or using Lerp (time based) like
// adjust these in the Inspector
public float totalMovementTime = 0.3f;
public float MoveDistance;
public IEnumerator Move(Vector3 direction)
{
var originalPosition = transform.position;
var destination = transform.position + direction * MoveDistance;
// here you could make it even more accurate
// by moving always with the same speed
// regardless how far the object is from the target
//var moveDuration = totalMovementTime * Vector3.Distance(transform.position, destinaton);
// and than replacing totalMovementTime with moveDuration
var currentDuration = 0.0f;
while (currentDuration < totalMovementTime)
{
transform.position = Vector3.Lerp(originalPosition, destination, currentDuration / totalMovementTime);
currentDuration += Time.deltaTime;
yield return null;
}
// to be really sure set a fixed position in the end
transform.position = destinaton;
}
Another issue is that you currently still could start two concurrent coroutines which leeds to strange behaviours. You rather should either interrupt the coroutines everytime you start a new one like
if (currentSwipe.y > 0 && currentSwipe.x > -0.5f && currentSwipe.x < 0.5f)
{
// stop all current routines
stopAllCoroutines();
StartCoroutine(MoveForward());
}
or add a flag to have only one routine running and ignore input in the meantime:
private bool isSwiping;
public IEnumerator MoveForward()
{
if(isSwiping)
{
Debug.LogWarning("Already swiping -> ignored", this);
yield break;
}
isSwiping = true;
//...
isSwiping = false;
}

Object not orienting correctly

I have a small script that causes an object to bounce back and forth. It is a bird for an upwards scrolling endless runner. So it represents its flight path. This script moves it from one end to the other and when it reaches the end it flips the 2D sprite and travels in the opposite direction. It works most of the time. But the problem is that sometimes the image flips twice so now it looks like its flying backwards until it does it again. Each time it does it it appears to be random.
public class Fly : MonoBehaviour {
private bool dirRight = false;
public float speed;
public bool facingRight = false;
void Start (){
speed = Random.Range (15.0f, 22.0f);
}
void Update () {
if(transform.position.x >= 25.0f) {
dirRight = false;
Flip();
}
if(transform.position.x <= -25.0f) {
dirRight = true;
Flip();
}
if (dirRight)
transform.Translate (Vector2.right * speed * Time.deltaTime);
else
transform.Translate (-Vector2.right * speed * Time.deltaTime);
}
void Flip()
{
// Switch the way the player is labelled as facing
facingRight = !facingRight;
// Multiply the player's x local scale by -1
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
I modified the if statements and use my bools as well as the position as such:
if(transform.position.x >= 25.0f && dirRight == true) {
dirRight = false;
Flip();
}
if(transform.position.x <= -25.0f && dirRight == false) {
dirRight = true;
Flip();
}
I am running it now and waiting to see if it works.
you are calling Flip() method based on position. and position is updated per frame. So it takes time to Lerp from >=25 to <25 so in every frame when position is >= 25 or <= -25 it calls to Flip(). So you need to add another check for calling Flip(). May be facingright == true will work.

Categories