How to fix this unity 2d sidescroller movement/animation problem - unity3d

So I am creating my first game in unity and I am currently working on the left to right movement.
I move to the left, let go of the left arrow button -> idle animation.
Press right arrow and character moves to right, let go -> idle.
The problem is I have to wait for the idle animation before I can press to go to the other direction chosen. When I press left and same time right the character does not move.
What I would like to do is fe:
press left -> character goes left, and then
press right at the same time -> character goes right.
So the need to be able to press buttons at the same time, and the last key pressed dictates the movement/animation.
Animation code:
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
this.GetComponent<Animator>().SetInteger("pallotila", 1);
}
if (Input.GetKeyUp(KeyCode.LeftArrow))
{
this.GetComponent<Animator>().SetInteger("pallotila", 0);
}
if (Input.GetKeyDown(KeyCode.RightArrow))
{
this.GetComponent<Animator>().SetInteger("pallotila", 2);
}
if (Input.GetKeyUp(KeyCode.RightArrow))
{
this.GetComponent<Animator>().SetInteger("pallotila", 0);
}
Player movement code:
if (Input.GetKey(KeyCode.LeftArrow))
{
this.transform.position += Vector3.left * this.nopeus * Time.deltaTime;
this.transform.rotation = this.vasemmalle;
}
if (Input.GetKey(KeyCode.RightArrow))
{
this.transform.position += Vector3.right * this.nopeus * Time.deltaTime;
this.transform.rotation = this.oikealle;
}

Note that you should never use GetComponent in Update better do it once and reuse the reference.
//Here you store the Animator reference
private Animator animator;
private void Awake()
{
animator = GetComponent<Animator>();
}
I'ld also use a switch to define what should happen for which button in order to avoid having the same code over and over again.
private void SetLastPressed(KeyCode code)
{
int value = 0;
switch (code)
{
case KeyCode.None:
value = 0;
break;
case KeyCode.LeftArrow:
value = 1;
break;
case KeyCode.RightArrow:
value = 2;
break;
}
animator.SetInteger("pallotila", value);
lastPressed = code;
}
Simply store and check which button pressed last and make the Input checks exclusive using if-else.
// Here you store the last pressed key
private KeyCode lastPressed = KeyCode.None;
private void Update()
{
if (lastPressed != KeyCode.LeftArrow && Input.GetKeyDown(KeyCode.LeftArrow))
{
SetLastPressed(KeyCode.LeftArrow);
}
else if (lastPressed != KeyCode.RightArrow && Input.GetKeyDown(KeyCode.RightArrow))
{
SetLastPressed(KeyCode.RightArrow);
}
// If none of the keys is pressed reset
else if (lastPressed != KeyCode.None && !Input.GetKey(KeyCode.LeftArrow) && !Input.GetKey(KeyCode.RightArrow))
{
SetLastPressed(KeyCode.None);
}
// And if only one of them is released but the other one still pressed
//go on using that still pressed key again
else if (lastPressed != KeyCode.LeftArrow && Input.GetKeyUp(KeyCode.RightArrow) &&
Input.GetKey(KeyCode.LeftArrow))
{
SetLastPressed(KeyCode.LeftArrow);
}
else if (lastPressed != KeyCode.RightArrow && Input.GetKeyUp(KeyCode.LeftArrow) &&
Input.GetKey(KeyCode.RightArrow))
{
SetLastPressed(KeyCode.RightArrow);
}
For the movement you could simply reuse the lastPresses value than as well
if(lastPressed == KeyCode.LeftArrow)
{
transform.position += Vector3.left * nopeus * Time.deltaTime;
transform.rotation = vasemmalle;
}
else if (lastPressed == KeyCode.RightArrow)
{
transform.position += Vector3.right * nopeus * Time.deltaTime;
transform.rotation = oikealle;
}
Additionally you could/should use a List<KeyCode> to store the last presses. Everytime a button goes down add the button to the end of the list; everytime a button goes up remove it from the list
result => the last pressed button is always the last one in the list.
This makes the return to a previous button way more simple and flexible without adding/changing so much lines of code like
private List<KeyCode> lastPresses = new List<KeyCode>();
private KeyCode lastPressed = KeyCode.None;
private Animator animator;
private void Awake()
{
animator = GetComponent<Animator>();
}
private void SetLastPressed(KeyCode code)
{
int value = 0;
switch (code)
{
case KeyCode.None:
value = 0;
break;
case KeyCode.LeftArrow:
value = 1;
break;
case KeyCode.RightArrow:
value = 2;
break;
}
animator.SetInteger("pallotila", value);
lastPressed = code;
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
if (!lastPresses.Contains(KeyCode.LeftArrow)) lastPresses.Add(KeyCode.LeftArrow);
}
else if (Input.GetKeyUp(KeyCode.LeftArrow))
{
if (lastPresses.Contains(KeyCode.LeftArrow)) lastPresses.Remove(KeyCode.LeftArrow);
}
if (Input.GetKeyDown(KeyCode.RightArrow))
{
if (!lastPresses.Contains(KeyCode.RightArrow)) lastPresses.Add(KeyCode.RightArrow);
}
else if (Input.GetKeyUp(KeyCode.RightArrow))
{
if (lastPresses.Contains(KeyCode.RightArrow)) lastPresses.Remove(KeyCode.RightArrow);
}
var currentCode = lastPresses.Count > 0 ? lastPresses[lastPresses.Count - 1] : KeyCode.None;
if (currentCode != lastPressed) SetLastPressed(currentCode);
if (lastPressed == KeyCode.LeftArrow)
{
transform.position += Vector3.left * nopeus * Time.deltaTime;
transform.rotation = vasemmalle;
}
else if (lastPressed == KeyCode.RightArrow)
{
transform.position += Vector3.right * nopeus * Time.deltaTime;
transform.rotation = oikealle;
}
}

You can change the "Value" Variable to
animator.SetInteger("pallotila", 0);
break;
in the location of
value = 0;
break;
or change
int value;
to
int value = 0;
in the code by #derHugo

Related

Unity Mobile - How to make fluid swipe to move movement?

So I am working on a personal project, and I had this idea for a movement that I don't know to realize. So the project(game) is for phone, and what I want to, is to when I swap in a direction, that vector becomes its closest axis (ex: 70 degrees will be (0,1) while 35 will be (1,0)). So when I get a swipe, I want my character to move in that direction. MAIN ISSUE is I don't know how to make him dash to that location. I know how to make him TELEPORT, but not how to make him DASH. I tried going for Vector2.Lerp, but it is a bit buggy and feels like character does not react on time. Here is the script:
private void Update()
{
if (gameObject.GetComponent<OnDeath>().isDead == false)
{
if (trigger)
{
fractionOfWayThere += 0.1f;
transform.position=Vector2.Lerp(transform.position, finish, fractionOfWayThere);
isMoving = true;
}
if ((Vector2)transform.position == finish)
{
isMoving = false;
animator.SetBool("isMoving", false);
}
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
startPos = cam.ScreenToWorldPoint(touch.position);
}
if (touch.phase == TouchPhase.Ended)
{
endPos = cam.ScreenToWorldPoint(touch.position);
destination = endPos - startPos;
if (Mathf.Abs(destination.x) > Mathf.Abs(destination.y))
{
destination = new Vector2(Funkcija(destination.x), 0);
}
if (Mathf.Abs(destination.x) < Mathf.Abs(destination.y))
{
destination = new Vector2(0, Funkcija(destination.y));
}
if (Mathf.Abs(destination.x) == Mathf.Abs(destination.y))
{
destination = new Vector2(Funkcija(destination.x), 0);
}
finish = (Vector2)transform.position + destination.normalized;
if (Physics2D.OverlapCircle(finish, 0.1f, spike) != null)
{
if (finish.x < transform.position.x)
{
sr.flipX = true;
}
else if (finish.x > transform.position.x)
sr.flipX = false;
trigger = true;
fractionOfWayThere = 0;
isMoving = true;
animator.SetBool("isMoving", true);
}
else
{
finish = transform.position;
}
}
}
}
}
Most of the code is getting the swipe into its axis and flipping sprite, only thing regarding the movement is this:
if (trigger)
{
fractionOfWayThere += 0.1f;
transform.position=Vector2.Lerp(transform.position, finish, fractionOfWayThere);
isMoving = true;
}
Playtesting, this feels realy weird. I tried Vector2.MovePosition(), rb.AddForce() and rb.MovePosition. Any other way to do this, or even using those functionts I have not thought of? All feedback would be highly appreciated!
Lerp is a good Start, but it's linear. A Dash would typically start instantly and slow down over time.
to achieve framerate independent movement, you should use Time.deltaTime in Update. It's neglectable in FixedUpdate as these occur at (almost) fixed timesteps.
You can achieve that by adding an AnimationCurve to your code:
[...]
public AnimationCurve dashCurve = new AnimationCurve.Linear(0,0,1,1); // modify this in inspector!
[...]
if (trigger)
{
fractionOfWayThere += 0.5f * Time.deltaTime; // fractionOfWayThere takes 0.5 second to grow from 0 to 1.
transform.position = Vector2.Lerp(transform.position, finish, dashCurve.Evaluate(fractionOfWayThere));
isMoving = true;
}
Set up the AnimationCurve like this:

Unity - GetKeyDown and GetKey using the same KeyCode to get a delay on input if held down

I an trying to get the user input do two different behaviors with the same input key.
like this :
if (Input.GetKeyDown(KeyCode.D) || Input.GetKey(KeyCode.D))
Making a tetris game: The goal is, tapping "D" once, I want tetromino to move one world unit per tap. AND when holding down the same key "D" I want the block to move right continuously until it reaches the edge of the game board, without having to tap.
This sort of works with the code above but, the problem I have is that tapping once moves 2 or 3 world unit instead of once because there is no delay before unity realizes that I am holding the key down.
I would like unity to wait .5 seconds before activating "Input.GetKey(KeyCode.D)" so that I can keep the behavior "Input.GetKeyDown(KeyCode.D)"
Bottom line,
I want to be able to tap "D" to move one world unit per tap
I want the block to move continuously right until it reaches the edge of the game board if I hold down "D" but, only after holding it down for .5 seconds
How can I do this ?
Full code for the Tetromino.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Tetromino : MonoBehaviour {
//#####################################################################################################
//#####################################################################################################
float fallTimer = 0f; // timer counting the seconds to check if mino needs to fall
public float fallSpeed = 1f; // variable to determine how fast the mino needs to fall
public bool allowRotation = true;
public bool limitRotation = false;
//#####################################################################################################
//#####################################################################################################
// Use this for initialization
void Start () {
}
//#####################################################################################################
//#####################################################################################################
// Update is called once per frame
void Update ()
{
CheckUserInput(); // --------------------------- // Checks the user input every frames
FallBehavior(); // checks if the block needs to fall and increments the timer
}
//#####################################################################################################
//#####################################################################################################
void CheckUserInput()
{
if (Input.GetKeyDown(KeyCode.D)) // moves the mino to the right
{
transform.position += new Vector3(1,0,0);
if (CheckIsValidPosition()) // if minos is not in a valid position, the transform pushes the minos
{ // back to the left, to keep it inside the grid
}
else
{
transform.position += new Vector3(-1, 0, 0); // this counters the first attempt to move
}
}
else if (Input.GetKeyDown(KeyCode.A)) // moves the mino to the left
{
transform.position += new Vector3(-1, 0, 0);
if (CheckIsValidPosition())
{
}
else
{
transform.position += new Vector3(1, 0, 0);
}
}
else if (Input.GetKeyDown(KeyCode.W)) // rotates the mino
{
if (allowRotation)
{
if (limitRotation) //limited rotation ON, to prevent rotating outside the grid
{ // after the tetromino landed at the bottom
if (transform.rotation.eulerAngles.z >= 90)
{
transform.Rotate(0, 0, -90);
}
else
{
transform.Rotate(0, 0, 90);
}
}
else
{
transform.Rotate(0, 0, 90); // 90 degrees rotation on the mino
}
if (CheckIsValidPosition())
{
}
else
{
if (limitRotation)
{
if (transform.rotation.eulerAngles.z >= 90)
{
transform.Rotate(0, 0, -90);
}
else
{
transform.Rotate(0, 0, 90);
}
}
else
{
transform.Rotate(0, 0, -90);
}
}
}
}
else if (Input.GetKeyDown(KeyCode.S))
{
transform.position += new Vector3(0, -1, 0); // makes the mino go down when pressing
if (CheckIsValidPosition())
{
}
else
{
transform.position += new Vector3(0, 1, 0);
}
}
}
//#####################################################################################################
//#####################################################################################################
/// <summary>
/// Makes the block fall by 1 unit and checks how fast it needs to fall
/// </summary>
void FallBehavior()
{
if (Time.time - fallTimer >= fallSpeed) // on the first frame, Time.time = 0 & fallTimer = 0
// so 0 - 0 = 0, is it >= then fallSpeed = 1? no
// so the if statement does not exectute, block dont fall
// after 1 sec, Time.time = 1 & fallTimer = 0
// so 1 - 0 = 1, is it >= then fallSpeed = 1? yes
// so block falls after 1 sec, because we increment it
// in the if statment also
{
transform.position += new Vector3(0, -1, 0); // moves the mino down
fallTimer = Time.time; // Time.time check the time since game started and is assigned
} // to fallTimer so that the timer updates every frame
// when called in the Update method. fallTimer = 0, 1, 2, 3 ...
if (CheckIsValidPosition()) // also helps checking if the Y is invalid, which tells the game to spawn
{ // the next tetromino when Y is less <= to the bottom of the grid
}
else
{
transform.position += new Vector3(0, 1, 0);
enabled = false; // disables the current piece, because it is at the bottom. So that the controls are not still
// attached to the current piece, after the next one spawned
FindObjectOfType<Game>().SpawnNextTetromino(); // spawns the next tetromino after the last one reached the bottom
}
}
//#####################################################################################################
//#####################################################################################################
/// <summary>
/// check the position of the individual tiles of the minos (children of the prefab)
/
/// </summary>
/// <returns></returns>
bool CheckIsValidPosition()
{
foreach (Transform mino in transform)
{
Vector2 pos = FindObjectOfType<Game>().RoundingTheMinoPosition (mino.position);
if (FindObjectOfType<Game>().CheckIsInsideGrid(pos) == false)
{
return false;
}
}
return true;
}
}
Full code of Game.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Game : MonoBehaviour {
public static int gridWidth = 10; // fixed grid size varibles
public static int gridHeight = 20; // for the blocks to fall in
// the grid need to be in a 2d array and we want to store all the x and y values for each world unit of the grid
// so that we can know which point on the grid are beind occupied by tetrominos that fell in.
//
// the array is gonna store the transforms so we use "gridWidth" and "gridHeight" to define the size of the array.
public static Transform[,] grid = new Transform[gridWidth, gridHeight];
// Use this for initialization
void Start () {
SpawnNextTetromino(); // spawns the first tetromino in the game
}
// Update is called once per frame
void Update () {
}
public void SpawnNextTetromino() // the Resources folder is included when the game compiles, we placed our prefabs
{ // in "Assets\Resources\Prefabs" to allow instantiation in the code.
// we cast a gameobject -> "(GameObject)" to let "Instantiate" know what we want to instantiate.
GameObject nextTetromino = (GameObject)Instantiate(Resources.Load(GetRandomTetromino(), typeof(GameObject)), new Vector2(5.0f, 20.0f), Quaternion.identity);
}
//gonna pass in the mino position in this method to see
// if it is still in the grid
public bool CheckIsInsideGrid(Vector2 pos)
{
return ((int)pos.x >= 0 && (int)pos.x < gridWidth && (int)pos.y >= 0);
}
public Vector2 RoundingTheMinoPosition(Vector2 pos)
{
return new Vector2(Mathf.Round(pos.x), Mathf.Round(pos.y));
}
/// <summary>
/// Genreates a random int and assings a teromino prefab to the outcome
/// </summary>
/// <returns></returns>
string GetRandomTetromino()
{
int randomTetromino = Random.Range(1, 8); //
string randomTetrominoName = null;
switch (randomTetromino)
{
case 1:
randomTetrominoName = "Prefabs/Tetromino_T";
break;
case 2:
randomTetrominoName = "Prefabs/Tetromino_Long";
break;
case 3:
randomTetrominoName = "Prefabs/Tetromino_Square";
break;
case 4:
randomTetrominoName = "Prefabs/Tetromino_J";
break;
case 5:
randomTetrominoName = "Prefabs/Tetromino_L";
break;
case 6:
randomTetrominoName = "Prefabs/Tetromino_S";
break;
case 7:
randomTetrominoName = "Prefabs/Tetromino_Z";
break;
}
return randomTetrominoName;
}
}
Looks like I misunderstood the original question. You want to move on "D" key press only but move until your you have reached the Edge when the the "D" key is held down.You need a timer when the the key is held down and this can be done with Time.deltaTime. Check while the key is held down with Input.GetKey, and if the timer reaches the amount of value you think makes it a held down, then you know the key is held down.
Also, check when the key is released with Input.GetKeyUp(KeyCode.D). If the key is released but timer has not reached the value you think makes it a held down,then it's simply a key press. It's worth doing this in a coroutine function instead of the Update function to simplify it and also reduce the amount of variables required to do it.
const float timeToCountAsHeldDown = 0.3f;
float pressTimer = 0;
IEnumerator moveChecker()
{
while (true)
{
//Check when the D key is pressed
if (Input.GetKeyDown(KeyCode.D))
{
//Continue to check if it is still heldown and keep counting the how long
while (Input.GetKey(KeyCode.D))
{
//Start incrementing timer
pressTimer += Time.deltaTime;
//Check if this counts as being "Held Down"
if (pressTimer > timeToCountAsHeldDown)
{
//It a "key held down", call the OnKeyHeldDown function and wait for it to return
yield return OnKeyHeldDown();
//No need to continue checking for Input.GetKey(KeyCode.D). Break out of this whule loop
break;
}
//Wait for a frame
yield return null;
}
}
//Check if "D" key is released
if (Input.GetKeyUp(KeyCode.D))
{
//Check if we have not not reached the timer then it is only a key press
if (pressTimer < timeToCountAsHeldDown)
{
//It just a key press, call the OnKeyPressedOnly function and wait for it to return
yield return OnKeyPressedOnly();
}
//Reset timer to 0 for the next key press
pressTimer = 0f;
}
//Wait for a frame
yield return null;
}
}
IEnumerator OnKeyPressedOnly()
{
Debug.Log("D key was only Pressed");
//Move 1 unit only
transform.position += new Vector3(1, 0, 0);
yield return null;
}
IEnumerator OnKeyHeldDown()
{
Debug.LogWarning("D key is Held Down");
//Don't move for 0.5 seconds
yield return new WaitForSeconds(0.5f);
//Move 1 unit every frame until edge detection is reached!
while (!CheckIsValidPosition())
{
transform.position += new Vector3(1, 0, 0);
//Wait for a frame
yield return null;
}
}

unity 3d the jump is very short

when my pnj is running(with the right key pressed) and y tried jump the pnj jump. but little very, how could i solucionate this?
This is no a problem when i let to press the right button, but i want make a game without fails and i want jump while io am running, thanks¡
void Update () {
if (Input.GetKeyDown (KeyCode.Space)) {
if (corriendo) {
if (enSuelo || !dobleSalto) {
GetComponent<Rigidbody2D> ().velocity = new Vector2 (GetComponent<Rigidbody2D> ().velocity.x, 0);
GetComponent<Rigidbody2D>().AddForce(Vector2.up * fuerzaSalto);
if (!dobleSalto && !enSuelo){
dobleSalto=true;
}
}
corriendo = true;
}
}
if ((enSuelo || !dobleSalto) && Input.GetKeyDown (KeyCode.Space)) {
GetComponent<Rigidbody2D> ().velocity = new Vector2 (GetComponent<Rigidbody2D> ().velocity.x, 0);
GetComponent<Rigidbody2D>().AddForce(Vector2.up * fuerzaSalto);
if (!dobleSalto && !enSuelo){
dobleSalto=true;
}
}
if (Input.GetKey (KeyCode.RightArrow) && enSuelo==true) {
if (velocidad > 17) {
velocidad = 17;
}
GetComponent<Rigidbody2D> ().velocity = new Vector2 (GetComponent<Rigidbody2D> ().velocity.x, 0);
GetComponent <Rigidbody2D> ().AddForce (Vector2.right * velocidad);
velocidad++;
} else {
if (velocidad > 1) {
velocidad--;
} else {
velocidad = 0;
}
}
}
Try to add an extra variable in AddForce(Vector2.right * velocidad * jumpFactor)
There you can give to increFactor a value, so you can modify the length of the jump.

how to change from one coroutine to another when while condition is false

I have created two coroutines, one for moving my badGuy game objects right and another for moving them left (please see code below).
IEnumerator moveBadGuyLeft (Transform fromPosition, Vector3 toPosition, float duration, int newIndex) {
while (emptyPos.Contains(badGuyPos[newIndex])) { //how to switch to moveBadGuyRight when this condition is false
emptyPos.Add(badGuyPos[newIndex + 1]);
filledPos.Remove(badGuyPos[newIndex + 1]);
float counter = 0;
Vector3 startPos = fromPosition.position;
while (counter < duration)
{
counter += Time.deltaTime;
fromPosition.position = Vector3.Lerp(startPos, toPosition, counter / duration);
yield return null;
}
emptyPos.Remove(badGuyPos[newIndex]);
filledPos.Add(badGuyPos[newIndex]);
if (newIndex > 0) {
newIndex--;
}
startPos = toPosition;
toPosition = new Vector3 (badGuyPos[newIndex], startPos.y, startPos.z);
int waitInterval = Random.Range(3, 5);
yield return new WaitForSeconds(waitInterval);
}
}
IEnumerator moveBadGuyRight (Transform fromPosition, Vector3 toPosition, float duration, int newIndex) {
while (emptyPos.Contains(badGuyPos[newIndex])) { //how to switch to moveBadGuyLeft when this condition is false
emptyPos.Add(badGuyPos[newIndex - 1]);
filledPos.Remove(badGuyPos[newIndex - 1]);
float counter = 0;
Vector3 startPos = fromPosition.position;
while (counter < duration)
{
counter += Time.deltaTime;
fromPosition.position = Vector3.Lerp(startPos, toPosition, counter / duration);
yield return null;
}
emptyPos.Remove(badGuyPos[newIndex]);
filledPos.Add(badGuyPos[newIndex]);
if (newIndex > 0) {
newIndex++;
}
startPos = toPosition;
toPosition = new Vector3 (badGuyPos[newIndex], startPos.y, startPos.z);
int waitInterval = Random.Range(3, 5);
yield return new WaitForSeconds(waitInterval);
}
}
I am trying to move my badGuy objects left till they meet a filled position then move them right till they meet a filled position and so on, toggling between left and right. I know that if my condition
while (emptyPos.Contains(badGuyPos[newIndex]))
is false then I should change from one coroutine to the other or vice versa. How can I implement this changing between coroutines? Please see how I am calling the coroutine in the Start method below:
for (int i = 0; i < badGuys.Count; i++) {
if (badGuys [i].getBlockType () == BadGuySetup.BadGuyType.moving) {
int indexInBadGuyPos = badGuyPos.IndexOf(badGuys[i].getBadGuyGameObject().transform.position.x);
Vector3 targetPos = new Vector3(badGuyPos[indexInBadGuyPos - 1], badGuys[i]. getBadGuyGameObject().transform.position.y, 0.0f);
StartCoroutine(moveBadGuyLeft(badGuys [i]. getBadGuyGameObject().transform, targetPos, 1.0f, indexInBadGuyPos - 1));
}
}
UPDATE
I added the following to the if (newIndex) > 0 condition in moveBadGuyLeft and moveBadGuyRight:
in moveBadGuyLeft:
if (newIndex > 0) {
newIndex--;
if (!emptyPos.Contains(badGuyPos[newIndex])) {
isMovingLeft = false;
badGuyDestPos = new Vector3(badGuyPos[newIndex + 2], startPos.y, startPos.z );
badGuyNewIndex = newIndex + 2;
break;
}
}
in moveBadGuyRight:
if (newIndex > 0) {
newIndex++;
if (!emptyPos.Contains(badGuyPos[newIndex])) {
isMovingLeft = false;
badGuyDestPos = new Vector3(badGuyPos[newIndex - 2], startPos.y, startPos.z );
badGuyNewIndex = newIndex - 2;
break;
}
}
Then I created another coroutine which is suppose to change between the two coroutines:
IEnumerator movingBadGuys(Transform fromPosition, Vector3 toPosition, float duration, int newIndex) {
if(isMovingLeft){
yield return StartCoroutine (moveBadGuyLeft(fromPosition, toPosition, duration, newIndex));
}
else if(!isMovingLeft){
yield return StartCoroutine (moveBadGuyRight(fromPosition, toPosition, duration, newIndex));
}
}
Finally I updated looping through the badguys and moving them:
for (int i = 0; i < badGuys.Count; i++) {
if (badGuys [i].getBlockType () == BadGuySetup.BadGuyType.moving) {
int indexInBadGuyPos = badGuyPos.IndexOf(badGuys[i].getBadGuyGameObject().transform.position.x);
Vector3 targetPos = new Vector3(badGuyPos[indexInBadGuyPos - 1], badGuys[i]. getBadGuyGameObject().transform.position.y, 0.0f);
badGuyDestPos = targetPos;
badGuyMoveDuration = 1.0f;
badGuyNewIndex = indexInBadGuyPos - 1;
StartCoroutine(movingBadGuys(badGuys [i]. getBadGuyGameObject().transform, badGuyDestPos, badGuyMoveDuration, badGuyNewIndex));
}
}
But this is changes are not working. I am testing it with only two badGuys which always start moving left and they only move left once and stop though there are other empty positions for them to continue moving left.
Unfortunately, I think this is not a good way to use coroutines. A better approach would be to have a single coroutine "moveBadGuy" then have the BadGuy object have a move method, and some internal state variable. For example.
class BadGuy{
bool moveLeft = false;
public void move(){
if(moveLeft){
//move the guy left
}else{
//move right
}
}
}
Then your coroutine can do
public IEnumerator moveBadGuy(BadGuy guyToMove){
guyToMove.move();
}
Another solution involving a single coroutine would be to have a moveLeft function and a moveRight function. For example,
public IEnumerator moveBadGuy(){
if(shouldmoveleft){
moveLeft();
}else{
moveRight();
}
}
public void moveLeft(){
//logic for moving left
}
public void moveRight(){
//logic for moving right
}
I recommend that you dont depend on while , what you can do is use the ability of courotuines to yield another coroutine like this yield return StartCoroutine(myFunc());
I personally wouldn't give the responsibility to the Move[Right|Left] coroutine to stop themselves.
Since coroutines are just iterator blocks you can even abstract further, and let the parent coroutine drive their execution.
Here's an abstract example:
IEnumerator AlternateOnCondition(Func<bool> evaluateCondition,
IEnumerator firstAction,
IEnumerator secondAction)
{
while(true)
{
if(evaluateCondition())
{
if (firstAction.MoveNext())
yield return firstAction.Current;
}
else
{
if (firstAction.MoveNext())
yield return secondAction.Current;
}
yield return null;
}
}
This is just an example of course, but it should give you the idea how coroutine can be nested.

Lifting platforms is not working as it should

I would like to make some lifting platforms in my game, so if the platform went down, the characters can't go over it. I have written a script for it, but for some reason the "lifting up" is not working as intended. It won't go back to its starting place, but it will go a bit below. And for some reason it won't go smoothly to the place where it should, just "teleport" there and done. I thougt multiplying Time.deltaTime with a const will help, but it is the same.
Here is my code, any help would be appreciated:
public class LiftingPlatform : MonoBehaviour {
private Transform lift;
private bool isCanBeLifted;
private float timeToLift;
public float timeNeededToLift = 5f;
private Vector3 startPos;
private Vector3 downPos;
private Vector3 shouldPos;
private bool isDown;
public GameObject[] collidingWalls;
// Use this for initialization
void Start () {
lift = transform;
isCanBeLifted = true;
timeToLift = 0f;
isDown = false;
startPos = transform.position;
downPos = new Vector3(startPos.x, startPos.y - 5f, startPos.z);
}
// Update is called once per frame
void Update () {
timeToLift += Time.deltaTime;
if (timeToLift >= timeNeededToLift) {
if (isCanBeLifted) {
if (isDown) {
shouldPos = Vector3.Lerp(startPos, downPos, Time.deltaTime * 10);
lift.position = new Vector3(shouldPos.x, shouldPos.y, shouldPos.z);
isDown = true;
}
else if (!isDown) {
shouldPos = Vector3.Lerp(downPos, new Vector3(startPos.x, startPos.y, startPos.z), Time.deltaTime * 10);
lift.position = new Vector3(shouldPos.x, shouldPos.y, shouldPos.z);
isDown = false;
}
}
timeToLift = 0;
}
if (!isDown) {
for (int i = 0; i < collidingWalls.Length; i++) {
collidingWalls[i].SetActive(true);
}
}
else if (isDown) {
for (int i = 0; i < collidingWalls.Length; i++) {
collidingWalls[i].SetActive(false);
}
}
}
void OnTriggerEnter(Collider collider) {
if (collider.tag == "Player" || collider.tag == "Enemy") {
isCanBeLifted = false;
}
}
void OnTriggerExit(Collider collider) {
if (collider.tag == "Player" || collider.tag == "Enemy") {
isCanBeLifted = true;
}
}
}
These lifting platforms are a child of another Platforms object.
It doesn't look like you are updating the object's position every frame. You are only checking if the total time passed is greater than the time needed to lift, and then updating the position to a value that is dependent on the delta time (using the Vector3.Lerp function).
What I would do is in the update step, if timeToLift is greater then timeNeededToLift, subtract the latter from the former and invert the value of isDown. Then, in your Vector3.Lerp, make the third argument (timeToLift / timeNeededToLift) instead of (Time.deltaTime * 10). Can you try that and see if it works?
The third argument for Vector3.Lerp is the "blending factor" between the two vectors, 0 is the first vector, 1 is the second, and 0.5 is in between. If the total time is greater than the time needed to lift, but the delta time is not greater than 1, it will get the position of the platform using a blending factor of less than 1, resulting in a platform that didn't move fully.