How to smothly move the character controller between x positions like in Subway Surfer in Unity? - unity3d

I'm trying to replicate the movement from Subway Surfers in Unity but I can't achieve that.
The character is located in the position 0 in the x-axis and when the left button is pressed the player will subtract 3 from the current position, and if the right button is pressed the player will add 3 to the current position making him move between -3,0,3 in the x-axis and all of this keeping the y and z axes unchanged.
I've tried using both Lerp and Slerp but I just couldn't achieve what I wanted. I've come down to a broken code that also changes the y, z-axis what I don't want to do:
var currPos = transform.position;
var currPosX = currPos.x;
if (Input.GetKeyUp(KeyCode.A))
{
if (currPosX > -horizontalSteps)
{
var newPos = new Vector3(currPosX - horizontalSteps, currPos.y, currPos.z);
newPos = Vector3.Slerp(currPos, newPos, 3);
Controller.Move(newPos);
}
}
if (Input.GetKeyUp(KeyCode.D))
{
if (currPosX < horizontalSteps)
{
var newPos = new Vector3(currPosX + horizontalSteps, currPos.y, currPos.z);
newPos = Vector3.Slerp(currPos, newPos, 3);
Controller.Move(newPos);
}
}

there is a very simple way
the character have 3 states so let's say we have a state variable (-1 , 0 , 1) for left, mid, and right
, and init with 0
if the player press right state++, if press left state--
completed if statement
if(button.right && state < 1) state++; "move right script";
else if(button.left && state > -1) state-- "move left script";
well,
about moving
I prefer Vector3.MoveTowards()for smoothly moving and avoid issues
try this example
using UnityEngine;
using UnityEngine.UI;
public class movv : MonoBehaviour
{
public Button right;
public Button left;
public float speed;
private int state = 0;
void Start()
{
Button btn = right.GetComponent<Button>();
btn.onClick.AddListener(delegate { moveright(); });
Button btn2 = left.GetComponent<Button>();
btn2.onClick.AddListener(delegate { moveleft(); });
void moveright()
{
if (state < 1) state++;
}
void moveleft()
{
if(state > -1) state--;
}
}
void Update()
{
Vector3 x = new Vector3 (state * 3, transform.position.y, transform.position.z);
transform.position = Vector3.MoveTowards(transform.position, x, speed * Time.deltaTime);
}
}
that's a quick one...
Hint:
this code works when the character position on the x-axis equal to 0
if not,
this is a general example,
using UnityEngine;
using UnityEngine.UI;
public class movv : MonoBehaviour
{
public Button right;
public Button left;
public float speed;
private Vector3 x;
private int state = 0;
private int target = 0;
void Start()
{
x = transform.position;
Button btn = right.GetComponent<Button>();
btn.onClick.AddListener(delegate { moveright(); });
Button btn2 = left.GetComponent<Button>();
btn2.onClick.AddListener(delegate { moveleft(); });
void moveright()
{
if (state < 1)
{
target = 1;
x = new Vector3(transform.position.x + target * 3, transform.position.y, transform.position.z);
state++;
}
}
void moveleft()
{
if (state > -1)
{
target = -1;
x = new Vector3(transform.position.x + target * 3, transform.position.y, transform.position.z);
state--;
}
}
}
void Update()
{
transform.position = Vector3.MoveTowards(transform.position, x, speed * Time.deltaTime);
}
}

Related

Unity Question: How to make all positions (with array) initialized?

I'm the beginner of game coding, and meet some problem...
I would like to make coin stack and then click the coin all the coin would become small and fly to dedicate position, however, the coin position wasn't initialized which causes the Vector3 of coin move up 0.1f every time...
Please help me to solve it!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class createcoin : MonoBehaviour
{ public Transform RightCoinArea;
public Transform LeftCoinArea;
public Sprite sprite1;
List<Vector3> rightcoinpos = new List<Vector3>();
List<Vector3> leftcoinpos = new List<Vector3>();
public Vector3 [] rightpos= new Vector3 [6] ;
public Vector3[] leftpos = new Vector3[6];
// Start is called before the first frame update
void Start()
{
rightcoinpos.Add(rightpos);
leftcoinpos.Add(leftpos);
}
//create coin
public async void coincreate()
{
//coin stack random
for (float i = 0; i <= 0.1f; i += 0.1f)
{
var coin8 = await PoolSystem.Instance.GetGameObject(eBundle.Game, "Flycoin",
rightcoinpos[Random.Range(0, rightcoinpos.Count)] += Vector3.up * i, new Vector3(0.4f, 0.4f, 04f), sFileExtension.Prefab, RightCoinArea);
//to initialize sprite
coin8.GetComponent<SpriteRenderer>().sprite = sprite1;
}
//coin stack random
for (float i = 0; i < 0.2f; i += 0.1f)
{
var coin9 = await PoolSystem.Instance.GetGameObject(eBundle.Game, "Flycoin", leftcoinpos[Random.Range(0, leftcoinpos.Count)] += Vector3.up * i, new Vector3(0.4f, 0.4f, 04f), sFileExtension.Prefab, LeftCoinArea);
//to initialize sprite
coin9.GetComponent<SpriteRenderer>().sprite = sprite1;
}
}
public void Moveout()
{
//gameobject back to the objectpool
if (RightCoinArea.gameObject.transform.localScale == new Vector3(0, 0, 0))
{
PoolSystem.Instance.Recover(RightCoinArea.gameObject);
}
if (LeftCoinArea.gameObject.transform.localScale == new Vector3(0, 0, 0))
{
PoolSystem.Instance.Recover(LeftCoinArea.gameObject);
}
//delete all the position in List
rightcoinpos.Clear();
leftcoinpos.Clear();
//to initialize the position
Vector3 tempos_R = rightpos;
Vector3 tempos_L = leftpos;
rightcoinpos.Add(rightpos);
leftcoinpos.Add(leftpos);
}
public void click()
{
coin_test.GetComponent<SpriteRenderer>().sprite = sprite2_test;
//coin fly to end dedicate pos
DOTween.To(setter: value =>
{
coin_test.position = Parabola(start_test.position, new Vector3(0, 4.5f, 0), 1.5f, value);
}, startValue: 0, endValue: 1, duration: 1.5f)
.SetEase(Ease.Linear);
//change coin scale
transform.DOScale(0.0f, 2.0f).OnComplete(Moveout);
}
//Parabola
public static Vector3 Parabola(Vector3 start, Vector3 end, float height, float t)
{
float Func(float x) => 0.5f * (-height * x * x + height * x);
var mid = Vector3.Lerp(start, end, t);
return new Vector3(mid.x, Func(t) + Mathf.Lerp(start.y, end.y, t), mid.z);
}
}

How can I prevent a certain piece of code from running when my player reaches a specific score for a certain time?

I'm currently developing a game in Unity and I have a problem. When my player reaches a certain score, in this case, the score of 5 I change the way the obstacles encounter behave. However, for some reason on certain specific occasions when the player reaches the score of 5, I'm assuming when the score of 5 is reached at the exact same time as when the obstacles are being spawned the Obstacles spawn according to the the behaviour required before the score reached 5 but move according to the behaviour required after the score reached 5 instantly as the score of 5 is reached. This shouldn't happen. As the score of 5 is reached the game should keep the behaviour of the obstacles the same as if the score was still smaller than 5 for a second and then switch.
I've already tried to using a coroutine to solve this issue as shown in the code below, however this obviously doesn't work unless I find a way to put the entire class into the coroutine, which will be very messy. I've also tried to suspend the spawning of the obstacles for a second while the score increments from 4 to 5 using a coroutine in the class responsible for spawning the obstacles. However this didn't work
This is the class responsible for the movement of the obstacles:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InteractControl : MonoBehaviour, IPooledObject
{
private Rigidbody2D rb;
GameObject target;
Vector3 directionToTarget;
public static int LevelStart = 0;
//public GameObject[] Balls;
Renderer m_Renderer;
public static float moveSpeed = 5f;
public static bool isSlowMotion = true;
public void OnObjectSpawn()
{
if (ScoreScript.scoreValue > 4 && isSlowMotion)
{
moveSpeed = 10f;
}
target = GameObject.FindWithTag("White Ball");
rb = GetComponent<Rigidbody2D>();
MoveInteract(moveSpeed);
}
void MoveInteract(float moveSpeed)
{
if (target != null)
{
if (ScoreScript.scoreValue > 4 && ScoreScript.scoreValue < 11 && BallSpawnerControl.LevelTrans > 1)
{
directionToTarget = (target.transform.position - transform.position).normalized;
rb.velocity = new Vector2(directionToTarget.x * moveSpeed,
directionToTarget.y * moveSpeed);
}
else if (ScoreScript.scoreValue < 4)
{
directionToTarget = new Vector3(0, -1, 0);
rb.velocity = new Vector2(0, directionToTarget.y * moveSpeed);
}
}
else
{
GameObject.FindWithTag("ColouredBall Highress").SetActive(false);
}
}
void Update()
{
if (target != null)
{
if (ScoreScript.scoreValue > 10) //Determine when RedBall goes from going down in a straight line to following white ball
{
directionToTarget = (target.transform.position - transform.position).normalized;
rb.velocity = new Vector2(directionToTarget.x * moveSpeed,
directionToTarget.y * moveSpeed);
}
}
}
}
This is the class responsible for the spawning of the obstacles:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
/*
* ObjectPooler.Instance.SpawnFromPool("Ball", Posi, Quaternion.identity );
* ObjectPooler.Instance.SpawnFromPool("Star", Posi, Quaternion.identity );
*/
public class BallSpawnerControl : MonoBehaviour
{
public static bool spawnAllowed;
public static int TimerOn = 1;
public Transform[] spawnPoints;
// public GameObject[] interact;
int randomSpawnPoint, Interact;
int index = 1;
ObjectPooler objectPooler;
public static int LevelTrans = 1;
// Use this for initialization
public void Start()
{
objectPooler = ObjectPooler.Instance;
spawnAllowed = true;
InvokeRepeating("SpawnAInteract", 0f, 1f);
}
void SpawnAInteract()
{
// Change this if statement to only run 1 second after the transition to level 2 has been initiated
if (spawnAllowed)
{
if(ScoreScript.scoreValue > 4 && LevelTrans == 1)
{
StartCoroutine(LevelTransition());
}
else
{
if (index % 5 != 0)
{
randomSpawnPoint = Random.Range(0, spawnPoints.Length);
Interact = 1;
// var i = Instantiate(interact[Interact], spawnPoints[randomSpawnPoint].position, Quaternion.identity);
objectPooler.SpawnFromPool("Ball", spawnPoints[randomSpawnPoint].position, Quaternion.identity);
//randomSpawnPoint = Random.Range(0, spawnPoints.Length);
index++;
}
/*if (index % 5 != 0)
{
randomSpawnPoint = Random.Range(0, spawnPoints.Length);
objectPooler.SpawnFromPool("Clock", spawnPoints[randomSpawnPoint].position, Quaternion.identity);
} */
else
{
randomSpawnPoint = Random.Range(0, spawnPoints.Length);
Interact = 0;
// var i = Instantiate(interact[Interact], spawnPoints[randomSpawnPoint].position, Quaternion.identity);
objectPooler.SpawnFromPool("Star", spawnPoints[randomSpawnPoint].position, Quaternion.identity);
//if (TimerOn == 1)
//{
randomSpawnPoint = Random.Range(0, spawnPoints.Length);
objectPooler.SpawnFromPool("Clock", spawnPoints[randomSpawnPoint].position, Quaternion.identity);
randomSpawnPoint = Random.Range(0, spawnPoints.Length);
objectPooler.SpawnFromPool("Heart", spawnPoints[randomSpawnPoint].position, Quaternion.identity);
// TimerOn++;
// }
index++;
}
}
}
}
private IEnumerator LevelTransition()
{
yield return new WaitForSeconds(1f);
LevelTrans++;
Debug.Log( "Level Trans :" + LevelTrans);
}
}
Thanks in advance
This is the interface from which OnObjectspawn derives from:
using UnityEngine;
public interface IPooledObject
{
void OnObjectSpawn();
}

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.

unity moving an object to right/left side

I am new to unity and I am trying to make a simple task: touch an object and then release your touch. when you release, I want to check in which side of the screen you released your touch and then move the object to that side of the screen.
So if I am pressing on my object, then swiping my finger to thr giht side, the object will move left and same for the right side...
This is my code, attached to the game object, and for some reason the object is just going to the right side of the screen. and it do it immidietly even though I used Lerp.
void OnMouseUp()
{
Vector3 pos = Input.mousePosition;
Debug.Log("press off");
if (pos.x < Screen.width / 2)
{
transform.position = Vector3.Lerp(transform.position, new Vector3(0,0,0), 2f * Time.deltaTime);
}
else
{
transform.position = Vector3.Lerp(transform.position, new Vector3(Screen.width, 0, 0), 2f * Time.deltaTime);
}
}
thank you!
So After a lot of trying this worked out for me:
public float smoothing = 7f;
IEnumerator MoveCoroutine(Vector3 target)
{
while (Vector3.Distance(transform.position, target) > 0.05f)
{
transform.position = Vector3.Lerp(transform.position, target, smoothing * Time.deltaTime);
yield return null;
}
}
void OnMouseUp()
{
Plane p = new Plane(Camera.main.transform.forward, transform.position);
Ray r = Camera.main.ScreenPointToRay(Input.mousePosition);
float d;
if (p.Raycast(r, out d))
{
Vector3 target = r.GetPoint(d);
if (target.x > 0)
{
Debug.Log("right:" + target.x + " total: " + Screen.width);
target.x = 5;
target.y = 0;
}
else
{
Debug.Log("left:" + target.x + " total: " + Screen.width);
target.x = -5;
target.y = 0;
}
StartCoroutine(MoveCoroutine(target));
}
}
not sure what the Ray casting does, I would be glad if someone can explain.
You code is almost right. You just need to define target positions and have Lerp called each time in update function.
A simple solution is to define two empty objects as position targets and pass them as parameters to the function.
using UnityEngine;
using System.Collections;
public class ClickTest : MonoBehaviour {
public Transform posLeft;
public Transform posRight;
private Vector3 destPos;
void Setup()
{
// default target is init position
destPos = transform.position;
}
// Update is called once per frame
void Update () {
// Define target position
if (Input.GetMouseButtonUp (0)) {
Vector3 pos = Input.mousePosition;
Debug.Log("press off : "+pos+ " scren : "+Screen.width);
if (pos.x < Screen.width / 2)
{
Debug.Log("left");
destPos = posLeft.position;
}
else
{
Debug.Log("right");
destPos = posRight.position;
}
}
// update position to target
transform.position = Vector3.Lerp(transform.position, destPos, 2f * Time.deltaTime);
}
}
Screenshot with empty objects as parameters

Unity2D: Player continues to move after he respawns

Okay to be basic, I have a 2d top down click to move game with a small problem. You see I created my player to have three heart lives and that after you get hit by an object you lose a heart, once you lose a heart the player automatically re-spawn back to where he started. however I'm having problems with player's movement, as mentioned before to move my player you have to click around (click to move). When I click to a place and I get hit by an object my player does go back to where it started off at (which is what I want) but after it reset back to where it was in the beginning, my player would continue to move until it get to the destination (which is not want I want)
This is my player movement's script:
public class PlayerMovement : MonoBehaviour {
private Animator anim;
public float speed = 15f;
private Vector3 target;
public PlayerMovement playerMovementRef;
private bool touched;
void Start () {
target = transform.position;
anim = GetComponent<Animator> ();
}
void Update () {
if (Input.GetMouseButtonDown (0)) {
Vector3 mousePosition = Input.mousePosition;
mousePosition.z = 10; // distance from the camera
target = Camera.main.ScreenToWorldPoint(mousePosition);
target.z = transform.position.z;
}
transform.position = Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime);
var movementDirection = (target - transform.position).normalized;
if (movementDirection.x != 0 || movementDirection.y != 0) {
anim.SetBool ("walking", false);
anim.SetFloat("SpeedX", movementDirection.x);
anim.SetFloat("SpeedY", movementDirection.y);
anim.SetBool ("walking", true);
}
}
void FixedUpdate () {
float LastInputX = transform.position.x - target.x;
float LastInputY = transform.position.y - target.y;
if (touched) {
if (LastInputX != 0 || LastInputY != 0) {
anim.SetBool ("walking", true);
if (LastInputX < 0) {
anim.SetFloat ("LastMoveX", 1f);
} else if (LastInputX > 0) {
anim.SetFloat ("LastMoveX", -1f);
} else {
anim.SetFloat ("LastMoveX", 0f);
}
if (LastInputY > 0) {
anim.SetFloat ("LastMoveY", 1f);
} else if (LastInputY < 0) {
anim.SetFloat ("LastMoveY", -1f);
} else {
anim.SetFloat ("LastMoveY", 0f);
}
}
}else{
touched = false;
anim.SetBool ("walking", false);
}
}
}
And this is my Player's health script (this script respawns my player back to where it started after he is hit by an object):
public class PlayerHealth : MonoBehaviour {
//Stats
public int curHealth;
public int maxHealth = 3;
Vector3 startPosition;
void Start ()
{
curHealth = maxHealth;
startPosition = transform.position;
}
void Update ()
{
if (curHealth > maxHealth) {
curHealth = maxHealth;
}
if (curHealth <= 0) {
Die ();
}
}
void Die ()
{
//Restart
Application.LoadLevel (Application.loadedLevel);
}
public void Damage(int dmg)
{
curHealth -= dmg;
Reset();
}
void Reset()
{
transform.position = startPosition;
}
}
So when you first click a location that the player walk towards,
Input.GetMouseButtonDown (0) is true, so inside of the if statement you set mousePosition with the following line:
Vector3 mousePosition = Input.mousePosition;
and then you set the target equal to that with
target = Camera.main.ScreenToWorldPoint(mousePosition);
The problem is that outside of the if statement, but within the update method, you have
transform.position = Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime);
Where the player's postition is continually changed to move towards the target. This is causing the problem because update() is constantly being called, even after you respawn, and if you don't assign the target a different value, your player will still move towards target, which was set before the player dies.
So put simply, target is only set once when you click a place to move, but your character always moves towards target, even after he respawns. Inside of the Start() method, write something like PlayerMovement.target = startPosition
Keep in mind target will either have to be public or have a public getter and setter in order to be able to access it from your health class.