How to optimize jump like in Flappy Bird in Unity? - unity3d

I can write somehow this code for optimization?
If not use coroutines, when I click on space the next jump has more force and so on.
If use rb.MovePosition, the character will move as if 15 fps. I know, change Time in settings. But I want to know if exist another method...
private void Update() {
if(Input.GetKeyDown(KeyCode.Space)) {
StopAllCoroutines();
StartCoroutine(Jump());
}
}
private IEnumerator Jump() {
if(rb.bodyType != RigidbodyType2D.Dynamic) {
rb.bodyType = RigidbodyType2D.Dynamic;
}
rb.constraints = RigidbodyConstraints2D.FreezePositionY;
_pos = transform.position;
for (float t = 0; t < 1; t += Time.deltaTime * 4f)
{
transform.position = Vector3.Lerp(transform.position, new Vector3(transform.position.x, _pos.y + .35f, transform.position.z), t);
yield return null;
}
rb.constraints = RigidbodyConstraints2D.None;
}

Rigidbodies exist so you don't need to directly adjust an object's transform. Since you have a Rigidbody2d you can just set the velocity directly:
public float jumpSpeed = 5f; // Whatever feels right
private void FixedUpdate() {
if(Input.GetKeyDown(KeyCode.Space)) {
rb.velocity = Vector2.up * jumpSpeed;
}
}
(Edited to use velocity instead of AddForce)

Related

Unity - Vector3.Lerp / MoveTowards doesn't work

The problem I can't solve is: cloned object, slow progression of parent object to X axis. But no matter what I did I could do it based on time, this is the latest version of the code.
public GameObject Ball;
private void OnTriggerExit(Collider other)
{
if (other.CompareTag("Gate"))
{
GameObject clone2 = Instantiate(Ball, transform.position, transform.rotation);
transform.position = Vector3.Lerp(transform.position, new Vector3(transform.position.x * 2,
transform.position.y,
transform.position.z), 25);
clone2.transform.parent = gameObject.transform;
}
if (other.CompareTag("Push"))
{
Destroy(gameObject);
}
}
#BugFinder already mentioned it in the comments. But the likely issue is that OnTriggerExit only gets called once and moves only one frame. Not only that but you are moving it over 25 seconds. Which is a long time for a Lerp function.
Below is a solution you can try:
float speed = 5;
private void OnTriggerExit(Collider other)
{
if (other.CompareTag("Gate"))
{
GameObject clone2 = Instantiate(Ball, transform.position, transform.rotation);
Vector3 targetLocation = new Vector3(transform.position.x * 2, transform.position.y, transform.position.z);
StartCoroutine(MoveTowardsLocation(clone2.transform, targetLocation, speed));
}
else if (other.CompareTag("Push"))
{
Destroy(gameObject);
}
}
private IEnumerator MoveTowardsLocation(Transform movedObj, Vector3 targetLocation, float speed)
{
Vector3 direction = (movedObj - movedObj.position).normalized;
while(Vector3.Distance(movedObj.position, targetLocation) > 0.01f)
{
movedObj.position = Vector3.Lerp(movedObj.position, movedObj.position + direction * speed * Time.deltatime, 1f);
yield return null;
}
movedObj.position = targetLocation;
}
In your code Lerp is called only once on trigger exit and position is updated only in one frame which is not resulting to visible change. To fix it you need start a coroutine on trigger exit and change transform.position in coroutine.

The best navigation code to using rigidbody in unity3d?

I want to press the key once
My cube moves to the right and rotates 90 degrees
The rotation is well done
But it does not move well
using UnityEngine;
using System.Collections;
public class Player : MonoBehaviour
{
public float speed;
public float time;
public GameObject contactPoint;
private Rigidbody rig;
private void Start()
{
rig = GetComponent<Rigidbody>();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.D))
{
StartCoroutine(RotatePlayer(Vector3.forward * 90, Vector3.right, time));
}
if (Input.GetKeyDown(KeyCode.A))
{
StartCoroutine(RotatePlayer(Vector3.back * 90, Vector3.left, time));
}
}
private IEnumerator RotatePlayer(Vector3 byAngle, Vector3 dir, float inTime)
{
Quaternion fromAngle = contactPoint.transform.rotation;
Quaternion toAngle = Quaternion.Euler(transform.eulerAngles - byAngle);
for (float t = 0; t < 1; t += Time.deltaTime / inTime)
{
rig.MovePosition(transform.position + (dir * speed * Time.deltaTime));
rig.MoveRotation(Quaternion.Slerp(fromAngle, toAngle, t));
yield return null;
}
}
}
Your main issue is: You should do Physics related things like movement of a Rigidbody only in FixedUpdate.
For Coroutines Unity provides WaitForFixedUpdate which does exactly what the name says, making sure the code after it is executed in a FixedUpdate physics call.
private IEnumerator RotatePlayer(Vector3 byAngle, Vector3 dir, float inTime)
{
yield return new WaitForFixedUpdate();
// here I would also use "rig" instead of "transform"
Quaternion fromAngle = contactPoint.transform.rotation;
Quaternion toAngle = Quaternion.Euler(rig.eulerAngles - byAngle);
for (float t = 0; t < 1; t += Time.deltaTime / inTime)
{
yield return new WaitForFixedUpdate();
// here I would also use "rig" instead of "transform"
rig.MovePosition(rig.position + (dir * speed * Time.deltaTime));
rig.MoveRotation(Quaternion.Slerp(fromAngle, toAngle, t));
}
}
beyond that it is a bit unclear what exactly you define as not move well. You also should somehow make sure that only one routine is running at a time. Either by terminating already running routines like
private void Update()
{
if (Input.GetKeyDown(KeyCode.D))
{
StopAllCoroutines();
StartCoroutine(RotatePlayer(Vector3.forward * 90, Vector3.right, time));
}
if (Input.GetKeyDown(KeyCode.A))
{
StopAllCoroutines();
StartCoroutine(RotatePlayer(Vector3.back * 90, Vector3.left, time));
}
}
or preventing new routines from starting until the current one is finished using a flag like
private bool alreadyRotating;
private IEnumerator RotatePlayer(Vector3 byAngle, Vector3 dir, float inTime)
{
if(alreadyRotating) yield break;
alreadyRotating = true;
......
alreadyRotating = false;
}

Physics2D.OverlapBox shows inconsistent behaviour

Recently I started working with Unity, this is the first time I'm trying to build a 2d platformer.
For some reason, when I press the jump button, there is a random chance that it will actually make the player jump. Probably around 1 in 50 that it actually jumps.
I just can't figure out why its doing that. Do you know what I'm doing wrong here?
using UnityEngine;
public class Player : MonoBehaviour
{
public float movespeed = 5f;
public float jumpforce = 5f;
public Rigidbody2D player;
public LayerMask layerMaskPlatforms;
private float movementHorizontalInput;
private bool jumpInput;
private float lastTimeOnGroundInSeconds = 0f;
private float lastTimePressedJump = 0f;
void OnBecameInvisible()
{
// todo: restart game
}
void Update()
{
movementHorizontalInput = Input.GetAxisRaw("Horizontal");
jumpInput = Input.GetButtonDown("Jump");
}
void FixedUpdate()
{
if (jumpInput)
{
AttemptJump();
lastTimePressedJump = 0.2f;
}
else if (lastTimePressedJump > 0)
{
AttemptJump();
lastTimePressedJump -= Time.deltaTime;
}
if (IsOnGround())
{
lastTimeOnGroundInSeconds = 0.2f;
}
else if (lastTimeOnGroundInSeconds > 0)
{
lastTimeOnGroundInSeconds -= Time.deltaTime;
}
player.velocity = new Vector2(movementHorizontalInput * movespeed * Time.deltaTime * 50f, player.velocity.y);
}
private void AttemptJump()
{
if (lastTimeOnGroundInSeconds > 0)
{
player.AddForce(new Vector2(0, jumpforce), ForceMode2D.Impulse);
lastTimeOnGroundInSeconds = 0;
}
}
private bool IsOnGround()
{
Vector2 groundedCheckPosition = (Vector2)transform.position + new Vector2(0, -0.01f);
var overlapBox = Physics2D.OverlapBox(groundedCheckPosition, transform.localScale, 0, layerMaskPlatforms);
return overlapBox;
}
}
One major issue is that the jump input is being polled every frame (Update), but the jump code is done every several frames (FixedUpdate), so if you press jump, it is most likely the FixedUpdate method will never see that jump, explaining why it happens so rarely.
You'd need to save the jump state (maybe in a boolean), so by the time Fixed Update happens, it knows that that a jump occurred. Then set that jump to false, and do the jump logic.

Smooth Player Ball Rolling - Unity 3D

I was trying to achieve this kind of player ball movement:
Catch Up (Ketchapp)
From my side I have tried and record a video of my current implementation:
CatchUpBallMovementDemo
Two kinds of problem, I was facing:
ball making so much jerk while moving on the plain track that I hope you have clearly noticed in my recorded video
when ball reach left or right edge and you try to swipe its making jerk again rather than remain restricted because clamping related code already added
I have just created a demo project so here I am providing the link for it so personally you can check and provide me a suggestion for making ball movement perfect.
Demo Project Source Link: CatchUpBallDemo
Demo Project SIZE 20MB
What at present making jerk in ball movement that I can't able to decide, whether its following camera jerk, whether ball not moving properly though I have created a plain track for checking purpose.
Ball Inspector Detail:
Complete code added within the working demo project. Share your suggestions with me to solve this.
Code Scripts:
BallController
[RequireComponent (typeof(Rigidbody))]
public class BallController : MonoBehaviour
{
//
private Rigidbody myRigidBody;
private bool isJumper;
private bool allowSpeedIncrease;
private BallInputHandler ballInputHandler;
private float speed;
private float speedMilestone;
private float jumpCounter;
private float scoreElapsedTime;
[SerializeField]
private bool isGrounded;
//
public float ballHorzRange;
public float ballStartSpeed;
public float ballTopSpeed;
public float smoothnessValue;
public float smoothnessX;
private void Awake ()
{
DoOnAwake ();
}
private void DoOnAwake ()
{
ballInputHandler = GetComponent<BallInputHandler> ();
myRigidBody = GetComponent<Rigidbody> ();
speed = ballStartSpeed;
speedMilestone = ballStartSpeed;
}
public void Start ()
{
DoOnStart ();
}
private void DoOnStart ()
{
// assinging player transform to camera to follow
Camera.main.GetComponent<CameraFollow> ().FollowPlayer (transform);
}
void Update ()
{
// slowly increase ball moving speed
if (allowSpeedIncrease) {
speed += Time.deltaTime;
if (speed >= speedMilestone) {
allowSpeedIncrease = false;
speed = speedMilestone;
}
}
}
void FixedUpdate ()
{
// do jumping
if (isJumper) {
jumpCounter++;
if (jumpCounter >= 3) {
isJumper = false;
jumpCounter = 0;
}
myRigidBody.AddForce (Vector3.up * 700f);
}
// applying continuous forward velocity
Vector3 nextVelocity = myRigidBody.velocity;
nextVelocity.x = ballInputHandler.horizontalInput * smoothnessX;
nextVelocity.z = speed;
if (isGrounded) {
nextVelocity.y = 0;
} else if (!isJumper) {
nextVelocity.y -= speed * 0.1f;
}
myRigidBody.velocity = nextVelocity.normalized * speed;
ClampingBallMovement ();
}
// ball horizontal movement limitation
private void ClampingBallMovement ()
{
Vector3 currRigidbodyPos = myRigidBody.position;
if (currRigidbodyPos.x <= -ballHorzRange || currRigidbodyPos.x >= ballHorzRange) {
currRigidbodyPos.x = Mathf.Clamp (currRigidbodyPos.x, -ballHorzRange, ballHorzRange);
myRigidBody.position = currRigidbodyPos;
}
}
void OnTriggerEnter (Collider other)
{
if (other.CompareTag (GameConstants.TAG_TRACK_SPAWNER)) {
GameController.Instance.SpawnPlateform ();
} else if (other.CompareTag (GameConstants.TAG_TRACK_DESTROYER)) {
Destroy (other.transform.parent.gameObject);
}
}
}
BallMeshRolling
public class BallMeshRolling : MonoBehaviour
{
private Vector3 ballLastPosition;
void Start ()
{
ballLastPosition = transform.parent.position;
}
void Update ()
{
// implementation-1
float speed = Vector3.Distance (transform.parent.position, ballLastPosition) * 30f;
transform.RotateAround (transform.position, Vector3.right, speed);
// float dragDifference = (transform.position.x - ballLastPosition.x) * 30f;
// transform.RotateAround (transform.position, Vector3.forward, dragDifference);
ballLastPosition = transform.parent.position;
}
}
CameraFollow
public class CameraFollow : MonoBehaviour
{
//
private Vector3 newPos;
private Vector3 initialPosition;
//
public Transform player;
public Vector3 offSet;
void Awake ()
{
initialPosition = transform.position;
}
void LateUpdate ()
{
if (!player)
return;
newPos = player.position + offSet;
newPos.x = ReMap (newPos.x);
newPos.y = Mathf.Clamp (newPos.y, initialPosition.y, initialPosition.y + 1f);
// transform.position = newPos;
transform.position = Vector3.Lerp (transform.position, newPos, 10f * Time.deltaTime);
}
public void FollowPlayer (Transform target)
{
player = target;
ResetCamera ();
}
public float ReMap (float value, float from1 = -4f, float to1 = 4f, float from2 = -2.5f, float to2 = 2.5f)
{
return (value - from1) / (to1 - from1) * (to2 - from2) + from2;
}
public void ResetCamera ()
{
transform.position = initialPosition;
}
}
I could solve the second problem by simply adding this to BallController.ClampingBallMovement():
private void ClampingBallMovement ()
{
Vector3 currRigidbodyPos = myRigidBody.position;
if (currRigidbodyPos.x <= -ballHorzRange || currRigidbodyPos.x >= ballHorzRange) {
currRigidbodyPos.x = Mathf.Clamp (currRigidbodyPos.x, -ballHorzRange, ballHorzRange);
myRigidBody.position = currRigidbodyPos;
}
// I ADDED THIS
// Clamp the velocity as well
if (currRigidbodyPos.x <= -ballHorzRange && myRigidBody.velocity.x < 0 || currRigidbodyPos.x >= ballHorzRange && myRigidBody.velocity.x > 0)
{
myRigidBody.velocity = new Vector3(0, myRigidBody.velocity.y, myRigidBody.velocity.z);
}
}
you clamped the position but did not clamp the velocity as well.
I could not reproduce the first jerking arround on my PC.

I want to create the ball movement like the one in 'chilly snow' game. I want to curve the ball, but it revolves

I am new to unity and don't know a lot of stuff. I've been watching tutorials and I saw one in which the guy created a replica of famous 'Chilly Snow'. The game is complete but the movement of ball isn't like the one in chilly snow. The ball starts orbiting continuously when I press mouse button. I wanted to know how to create that kind of movement, so that the ball turns left and right in a curve but doesn't go in to an orbit. I googled a lot but wasn't able to find my required result. I would really appreciate if anyone could point me in the right direction. Images are attached.Chilly Snow | Movement of my ball
public class movement : MonoBehaviour {
private float points;
public float playerSpeed;
private float rotationSpeed;
public Text score;
private bool isMovingLeft;
public GameObject player;
public bool isDead;
void Start () {
Time.timeScale = 0;
isDead = false;
isMovingLeft = true;
points = 0;
}
void Update ()
{
if (isDead == false)
{
points += Time.deltaTime;
}
transform.Translate (Vector3.down * playerSpeed * Time.deltaTime);
if (Input.GetMouseButtonDown (0))
{
Time.timeScale = 1;
isMovingLeft = !isMovingLeft;
rotationSpeed += 0.5f * Time.deltaTime;
}
if (Input.GetMouseButton (0))
{
rotationSpeed = 1f;
}
if (isMovingLeft) {
rotationSpeed += 1.5f * Time.deltaTime;
transform.Rotate(0,0,rotationSpeed);
} else
transform.Rotate(0,0, - rotationSpeed);
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Obstacle") {
Die ();
}
}
public void Die()
{
playerSpeed = 0f;
isDead = true;
Invoke ("Restart", 2f);
}
void Restart(){
SceneManager.LoadScene ("Ski_scene_1");
}
void FixedUpdate()
{
score.GetComponent<Text>().text = points.ToString("0");
}
}
Here is how I would approach it without doing a rotation... using your code.
public class movement : MonoBehaviour {
private float points;
public Text score;
public GameObject player;
public bool isDead;
private float currentXSpeed;
private float targetSpeed;
public float maxXSpeed;
public float speedChange;
void Start () {
Time.timeScale = 0;
isDead = false;
isMovingLeft = true;
points = 0;
targetSpeed = maxXSpeed;
}
void Update ()
{
if (isDead == false)
{
points += Time.deltaTime;
}
if(Input.GetMouseButtonDown(0))
{
Time.timeScale = 1;
targetSpeed = -targetSpeed;
}
currentSpeed = mathf.MoveTowards(currentSpeed, targetSpeed, speedChange * Time.deltaTime);
Vector3 movementDirection = new Vector3(currentSpeed, Vector3.down.y * playerSpeed, 0.0f);
transform.Translate (movementDirection * Time.deltaTime);
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Obstacle") {
Die ();
}
}
public void Die()
{
playerSpeed = 0f;
isDead = true;
Invoke ("Restart", 2f);
}
void Restart(){
SceneManager.LoadScene ("Ski_scene_1");
}
void FixedUpdate()
{
score.GetComponent<Text>().text = points.ToString("0");
}
}
You need something like sinusoidal movement or any other graph you fancy.
An example would be for this is like;
gameObject.transform.Translate(Vector3.right * Time.deltaTime*cubeSpeed);
gameObject.transform.position += transform.up * Mathf.Sin (Time.fixedTime * 3.0f ) * 0.1f;
Above pseudo is for 2D graph simulation, can be adapted to your situation.
The object is always moving to right and going up and down while making a sinusoidal movement. Because the up and down speed is not fixed hence you get the sinusoidal or like sinusoidal movement.
In your case, while the object is always going down it will make the sinusoidal movement to left and right.
Your movement is based on the rotation so, if you give this sinusoidal speed as your rotation speed, you can achieve this.
Another aproach can be lerp or slerp
Lerp allows you to make kinda smooth transactions between 2 vectors.
Like moving from pointA to pointB in X seconds.
For rotation you will need Quaternion.Lerp There is a great answer on Unity Answers you can check that if you haven't before.
Hope this helps! Cheers!