Move an object to another object with Lerp if the distance between them becomes too far in Unity - unity3d

I want to move a cube A to another cube B using Lerp, but only if the distance between them is greater than 2.0f. But since Lerp is applied in multiple Update() frames, in the next Update() the distance between the two cubes is 1.9 for example, and the code doesn´t enter in the if statment and the cube A just stops there.
How could I move the cube A to cube B?
void Start()
{
startPos = transform.position;
}
void Update()
{
float distance = Vector3.Distance(transform.position, nextStepPos.position);
if (distance > 2.0f)
{
transform.position = Vector3.Lerp(transform.position, nextStepPos.position, speed * Time.deltaTime);
}
else
{
transform.position = startPos;
}
}

I would use Vector3.MoveTowards instead of Lerp and use a Coroutine for this:
[SerializeField] Transform nextStepPos;
Coroutine moveToOtherCoroutine;
float moveSpeed = 1f; // world units per second
void Start()
{
moveToOtherCoroutine = null;
}
void Update()
{
if (distance > 2.0f)
{
if (moveToOtherCoroutine == null)
moveToOtherCoroutine = StartCoroutine(DoMoveToOther());
}
}
IEnumerator DoMovetoOther()
{
while (true)
{
Vector3 newPos = Vector3.MoveTowards(transform.position, nextStepPos.position,
Time.deltaTime * moveSpeed);
transform.position = newPos;
if (newPos == nextStepPos.position)
{
break;
}
yield return null;
}
// If you want to allow moving to the cube on distance > 2.0f again
// moveToOtherCoroutine = null;
// Stuff that should happen when reach next step should happen here
}

Related

Is there a way to have an object with rigidbody tilt but not rotate (cube)?

I am working on a project where a player can control a cube and move it around. I love having physics on the cube, but when manuevering, it is very difficult to time a jump when the cube is spinning all over the place. I would like the player to be able to move the cube along the ground, and the cube would gradually tilt towards the direction they are moving it, but not flip entirely. I don't know if it would even work conceptually, but if someone could help me figure out how to alter my code to keep it grounded but still tilt. Thank you.
{
public GameObject Camera;
Collider coll;
private bool isGrounded;
Rigidbody rb;
Vector3 velocity;
public float speed = 12f;
public float gravity = -9.8f;
public float jumpStrength = 1000f;
// Start is called before the first frame update
void Start()
{
Camera = GameObject.Find("Main Camera");
rb = GetComponent<Rigidbody>();
}
void OnCollisionEnter(UnityEngine.Collision collision)
{
if (collision.gameObject.tag == "Floor")
{
isGrounded = true;
Debug.Log("Is Grounded");
}
}
void OnCollisionExit(UnityEngine.Collision collision)
{
if (collision.gameObject.tag == "Floor")
{
isGrounded = false;
Debug.Log("Is Not Grounded");
}
}
// Update is called once per frame
void Update()
{
Camera.transform.position = new Vector3(transform.position.x, transform.position.y + 2.38f, transform.position.z - 3.45f);
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
float mH = Input.GetAxis("Horizontal");
float mV = Input.GetAxis("Vertical");
rb.velocity = new Vector3(mH * speed, rb.velocity.y, mV * speed);
if(isGrounded && Input.GetButtonDown("Jump"))
{
velocity.y = Mathf.Sqrt(3 * -2 * gravity);
}
velocity.y += gravity * Time.deltaTime;
if(Input.GetButtonDown("Jump") && isGrounded)
{
rb.AddForce(0f, jumpStrength, 0f);
Debug.Log("Jump");
}
}
}

How to keep player momentum after jumping (2D)?

I'm creating a Mario clone and I need to keep the player's forward momentum into the jump and then onward into the landing. I can't figure out how to achieve this. Every time I land, the player has to build up momentum again. Any Idea how to fix this? I've tried several solutions to no avail. I'm thinking it has something to do with how I'm adding force and acceleration to the player when holding left or right. Not sure though any help would be much appreciated. thanks in advance.
Here's my code:
Animator animator;
Rigidbody2D rb;
bool isGrounded;
public float moveSpeed;
public Vector2 acceleration;
public float jumpHeight;
public float lowjumpMultiplier;
public Transform groundCheckM;
public Transform groundCheckL;
public Transform groundCheckR;
public float storedValue;
void Start()
{
animator = GetComponent<Animator>();
rb = GetComponent<Rigidbody2D>();
transform.eulerAngles = new Vector3(0, 0, 0);
}
private void Update()
{
if
((Physics2D.Linecast(transform.position,groundCheckM.position, 1 << LayerMask.NameToLayer("Floor/Platforms"))) || //Check if grounded
(Physics2D.Linecast(transform.position, groundCheckL.position, 1 << LayerMask.NameToLayer("Floor/Platforms"))) ||
(Physics2D.Linecast(transform.position, groundCheckR.position, 1 << LayerMask.NameToLayer("Floor/Platforms"))))
{
isGrounded = true;
animator.SetBool("Jump", false);
}
else
{
isGrounded = false;
}
animator.SetFloat("Walk", rb.velocity.x); //Set animation float to x velocity
if (rb.velocity.x <= 0.03f && rb.velocity.x >= -0.03f && isGrounded) //Play "Idle" animation
{
animator.Play("Mario_Idle");
}
if (rb.velocity.x >=4 || rb.velocity.x <=-4)
{
animator.speed = Mathf.Abs(rb.velocity.x / 5.5f); //Increase speed of walking animation with player's walking speed
}
}
void FixedUpdate()
{
if (Input.GetKey("d") || Input.GetKey("right")) //Move player to the right
{
rb.AddForce(acceleration * rb.mass);
transform.rotation = Quaternion.Euler (0, 0, 0);
}
else if (Input.GetKey("a") || Input.GetKey("left")) //Move player to the left
{
rb.AddForce(-acceleration * rb.mass);
transform.rotation = Quaternion.Euler(0, 180, 0);
}
if (rb.velocity.x >= 10)
{
rb.velocity = new Vector2(10, rb.velocity.y); //Cap player speed at 10 when moving right
}
else if (rb.velocity.x <= -10)
{
rb.velocity = new Vector2(-10, rb.velocity.y); //Cap player speed at 10 when moving left
}
if (Input.GetKey("space") && isGrounded) //Player jump
{
rb.velocity += new Vector2(rb.velocity.x, jumpHeight);
animator.SetBool("Jump", true);
}
}
}
I feel dumb but the answer was in the physics material. Once I lowered the friction, it allowed momentum from the jump to carry into the player's run speed. I guess it's a good reminder to tinker directly inside Unity and its built in physics system.
If you want to keep your momentum when jumping, you could store it in a variable that will increase until it reaches the max, or you let go of the key.
float acceleration;
float accelFactor = 0.6f;
float deAccelFactor = 1f;
bool jumping; //you should set it to true when jumping and false, when not.
Rigidbody2D rb;
void Start(){
rb = GetComponent<Rigidbody2D>();
}
void Update(){
if (Input.GetKeyDown(KeyCode.D))
{
Accelerate();
rb.AddForce(acceleration * rb.Mass);
}
else if (jumping)
{
rb.AddForce(acceleration * rb.Mass);
}
else
{
DeAccel();
}
}
void Accelerate(){
acceleration += accelFactor * Time.deltaTime;
acceleration = Mathf.Clamp(acceleration, 0, maxAccel);
}
void DeAccel(){
acceleration -= deAccelFactor * Time.deltaTime;
acceleration = Mathf.Clamp(acceleration, 0, maxAccel);
}
This is what I would recommend using, that is, if I understand what you mean.

Unity jump only once per trigger

I need help with my code where I can only jump once but then it's locked for me I think I'm onto something.
Here is my code:
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Vector3 v = transform.position;
v.x += speed * Input.GetAxis("Horizontal") * Time.deltaTime;
v.z += speed * Input.GetAxis("Vertical") * Time.deltaTime;
transform.position = v;
if (Input.GetKeyDown(KeyCode.Space) && isGrounded==true)
{
GetComponent<Rigidbody2D>().velocity = Vector2.up * jumpVelocity;
isGrounded = false;
}
void OnCollisionEnter(Collision col)
{
if (col.gameObject.tag == ("Ground") && isGrounded == false)
{
isGrounded = true;
}
}
}
}
You nested the OnCollisionEnter method inside the Update.
This way it is a local function and not found by the Unity Physics / Message System -> it will never be called.
Another issue in your code: Whenever there is a Rigibody/Rigibody2D involved you do not want to set or access stuff via the Transform component. This will break the physics and collision detection!
And finally note that Physics and Physics2D are two completely separate engines! If you want to be moving and detect collisions in 3D space then you have to use a Rigidbody, not a Rigibody2D!
It should rather be
[Header("References")]
[SerializeField] private Rigidbody _rigidbody;
[Header("Debugging")]
[SerializeField] private bool isGrounded;
private void Awake()
{
if(!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
}
// Update is called once per frame
private void Update()
{
var x = Input.GetAxis("Horizontal");
var y = Input.GetAxis("Vertical");
// Clamp the vector so it has a maximum magnitude of 1
// this prevents that diagonal movement is overall faster
var moveInput = Vector2.ClampMagnitude(new Vector2(x,y), 1) * speed;
// instead of using the Transform simply change the rigidbody velocity accordingly
var velocity = _rigidbody.velocity;
velocity.x = moveInput.x;
velocity.z = moveInput.y;
_rigidbody.velocity = velocity;
// it is cheaper to first check a simple bool flag
// if this already fails we can skip the more expensive "GetKeyDown"
if (isGrounded && Input.GetKeyDown(KeyCode.Space))
{
_rigidbody.velocity = Vector2.up * jumpVelocity;
isGrounded = false;
}
}
// This method should be on class level and not nested under Update
private void OnCollisionEnter(Collision col)
{
// if already "isGrounded" we can skip the more expensive "CompareTag"
if (!isGrounded && col.gameObject.CompareTag("Ground"))
{
isGrounded = true;
}
}

Why is there a constant stutter when i move in Unity 2d?

So i have a simple scene in unity, a player with a parallax background and a Tilemap as a ground, as well as some very simple post processing. I know this isn't a The minute i move, there is a consistent stutter just under ever second. I'm not sure whether it's to do with my player movement code, camera movement or anything else. I'm also using a Cinemachine virtual camera. My rigidbody interpolation is set to interpolate and collision detection set to continuous. Here's my player movement if this helps. Here is a sample of what it looks like, if you look at the background or the tilemap it's quite noticeable. https://youtu.be/h2rSheZWtKs
[SerializeField] private LayerMask groundLayerMask;
public float speed;
public float Jump;
public sword swordScript;
public GameObject swordSprite;
private float move;
private Rigidbody2D rb;
private BoxCollider2D boxCollider2d;
private bool facingRight;
public SpriteRenderer spr;
public Animator PlayerAnims;
public bool movementAllowed;
void Awake()
{
Application.targetFrameRate = 60;
Application.targetFrameRate = Screen.currentResolution.refreshRate;
boxCollider2d = GetComponent<BoxCollider2D>();
rb = GetComponent<Rigidbody2D>();
facingRight = true;
spr = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
boxCollider2d = GetComponent<BoxCollider2D>();
rb = GetComponent<Rigidbody2D>();
facingRight = true;
spr = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void FixedUpdate()
{
if(movementAllowed == true)
{
rb.velocity = new Vector2(move * speed, rb.velocity.y);
if (isGrounded() && Input.GetButtonDown("Jump"))
{
rb.AddForce(new Vector2(rb.velocity.x, Jump));
}
}
}
void Update()
{
move = Input.GetAxis("Horizontal");
rb.velocity = new Vector2(move * speed, rb.velocity.y);
if (movementAllowed == true)
{
Flip(move);
if (move == 0)
{
PlayerAnims.SetBool("isRunning", false);
}
else
{
PlayerAnims.SetBool("isRunning", true);
}
}
}
private bool isGrounded()
{
float extraHeightText = .1f;
RaycastHit2D raycasthit2d = Physics2D.BoxCast(boxCollider2d.bounds.center, boxCollider2d.bounds.size, 0f, Vector2.down, extraHeightText, groundLayerMask);
Color rayColour;
if (raycasthit2d.collider != null)
{
rayColour = Color.green;
PlayerAnims.SetBool("isJumping", false);
}
else
{
rayColour = Color.red;
PlayerAnims.SetBool("isJumping", true);
}
Debug.DrawRay(boxCollider2d.bounds.center + new Vector3(boxCollider2d.bounds.extents.x, 0), Vector2.down * (boxCollider2d.bounds.extents.y + extraHeightText), rayColour);
Debug.DrawRay(boxCollider2d.bounds.center - new Vector3(boxCollider2d.bounds.extents.x, 0), Vector2.down * (boxCollider2d.bounds.extents.y + extraHeightText), rayColour);
Debug.DrawRay(boxCollider2d.bounds.center - new Vector3(boxCollider2d.bounds.extents.x, boxCollider2d.bounds.extents.y + extraHeightText), Vector2.right * (boxCollider2d.bounds.extents.x), rayColour);
return raycasthit2d.collider != null;
}
private void Flip(float move)
{
if (move > 0 && !facingRight || move < 0 && facingRight)
{
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
if (swordScript.isFollowing == true)
{
Vector3 swordScale = swordSprite.transform.localScale;
swordScale.x *= -1;
swordSprite.transform.localScale = swordScale;
}
}
}
You are setting rb.velocity in both the Update() and FixedUpdate() methods. I would try only using one of those.
On top of that, your jump check also re-applies the X velocity along with the upward force. So if you're jumping the player will be pushed forward at double speed.
You also have an error being outputted in the console about an Index being out of range... I would look into that.
Can you also post your code for the parallax background?
FixedUpdate is a method where you want to do everything physics, player-controller and simulation related.
Update is just for rendering-related fluff i.e. code of no real consequence other than making things look correctly.
Hence:
Move all your player/physics controller code to FixedUpdate.
Move bg parallax code to Update.
Use Time.deltaTime (in Update) and Time.fixedDeltaTime (in FixedUpdate) time steps when animating or interpolating between values.
Ad #3.: Although - as #Menyus noted (below) - Time.deltaTime is all you really need.
Time.fixedDeltaTime is for that extra intent explicitness (but was necessary in earlier unity versions).

Cameras rotation is too slow

I have a game object that moves and rotates. I want the camera to stay behind the object all the time, so when the user presses w, it will look like the gameobject moves forward.
This is my skript for the camera movement.
public Transform target;
public Vector3 offset;
public void FixedUpdate()
{
transform.position = target.TransformPoint(offset);
transform.LookAt(target);
}
But I camera is not rotating around the player fast enough, so it looks like he is moving sidewards.
This is my player movement script, but I don't see any mistake in there.
public float smoothSpeed = 0.125f;
public float forwardSpeed;
public float sideSpeed;
// Start is called before the first frame update
void Start()
{
}
void FixedUpdate()
{
if (Input.GetKey("w"))
{
Vector3 movement = transform.rotation * Vector3.forward / (100 / forwardSpeed);
transform.Translate(movement);
}
else if (Input.GetKey("s"))
{
Vector3 movement = transform.rotation * Vector3.back / (100 / sideSpeed);
transform.Translate(movement);
}
else if (Input.GetKey("a"))
{
Vector3 movement = transform.rotation * Vector3.left / (100 / sideSpeed);
transform.Translate(movement);
}
else if (Input.GetKey("d"))
{
Vector3 movement = transform.rotation * Vector3.right / (100 / sideSpeed);
transform.Translate(movement);
}
else if (Input.GetKey("e"))
{
transform.Rotate(0, 1, 0);
}
else if (Input.GetKey("q"))
{
transform.Rotate(0, -1, 0);
}
}
Thanks for your help]1
When the object moves sidewards it should move forwards.
Here are my settings in Unity: