How to delay movement in unity while animation plays - c#

I am having difficulty delaying the movement of my character in Unity 2018.4, I suspect this is more of just a problem with my code as apposed to unity.
Edit:
I want to be able to hold down the right arrow key, have the character not move for 415ms, then be able to move for 580ms, after which he cannot move for 350ms (to let the animation finish playing)
I tried using countdown in a IE numerator to wait and then call my movement function but this results in my character not moving after the animation has played.
//What makes the character move, works fine if executed on its own in update
private void ExecuteMovement(float dir)
{
currentPosition = transform.position;
currentPosition.x += dir * Time.deltaTime * speed;
transform.position = currentPosition;
}
//Trying to use waitforseconds to delay the execution while the animation plays
private IEnumerator Movement(float dir)
{
yield return new WaitForSeconds(0.5f);
ExecuteMovement(dir);
}
void Update()
{
if (0 > Input.GetAxis("Horizontal"))
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 180, 0);
//starts animation
animator.SetBool("isSkipping", true);
//calls the movement function with the direction to move in
Movement(Input.GetAxis("Horizontal"));
}
else if (0 < Input.GetAxis("Horizontal"))
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 0, 0);
//starts animation
animator.SetBool("isSkipping", true);
//calls the movement function with the direction to move in
Movement(Input.GetAxis("Horizontal"));
}
}
I'm happy to try any alternatives to delaying movement of the character. The animation shows the character charging up to jump and then jumps. I want to only move while it is in the air which is approximately half a second into the animation.

You are trying to call your IEnumerator like a method.
Instead you have to start it as a Coroutine using StartCoroutine
StartCoroutine(Movement(Input.GetAxis("Horizontal")));
Saw your edit
I want to be able to hold down the right arrow key, have the character not move for 415ms, then be able to move for 580ms, after which he cannot move for 350ms (to let the animation finish playing)
until now so since you want a continues movement but still fully control the speed I would keep it in Update and use the coroutine only for controlling a flag. Coroutine is still way better to read/write and maintain. (Now it also allows direction switch midair .. not sure if you want this or not):
// control the ability to move with a simple flag
// instead of various states
public bool canMove;
// check if a jumping is already being animated/executed
// -> disallow a new jumping animation until it is finished
private bool canJump = true;
private void Update()
{
// avoid repeated API calls
var input = Input.GetAxis("Horizontal");
if (input < 0)
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 180, 0);
if(canJump)
{
//starts the movement
StartCoroutine(Jump());
}
}
else if (input > 0)
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 0, 0);
if(canJump)
{
//starts the movement
StartCoroutine(Jump());
}
}
// if can not move do nothing else
if(!canMove) return;
// execute the movement
transform.position += Vector3.right * input * speed * Time.deltaTime;
}
private IEnumerator Jump()
{
// disable jumping
canJump = false;
// disable move during animation
canMove = false;
//starts animation
animator.SetBool("isSkipping", true);
// not move for 415ms
yield return new WaitForSeconds(0.415f);
// enable move
canMove = true;
// be able to move for 580ms
yield return new WaitForSeconds(0.580f);
// disable move
canMove = false;
// cannot move for 350ms during end of animation
yield return new WaitForSeconds(0.350f);
// enable move again
canMove = true;
// re-enable jumping
canJump = true;
}

How about using timer in Update?
float timer = 0f;
void Update()
{
var dir = Input.GetAxis("Horizontal");
if (0 > dir)
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 180, 0);
//starts animation
animator.SetBool("isSkipping", true);
//calls the movement function with the direction to move in
timer +=Time.deltaTime;
if(timer >= 0.5){
ExecuteMovement(dir);
timer -=0.5f;
}
//Movement(Input.GetAxis("Horizontal"));
}
else if (0 < dir)
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 0, 0);
//starts animation
animator.SetBool("isSkipping", true);
//calls the movement function with the direction to move in
timer +=Time.deltaTime;
if(timer >= 0.5){
ExecuteMovement(dir);
timer -=0.5f;
}
//Movement(Input.GetAxis("Horizontal"));
}
}

I found a rather crude way of making this work after heavily modifying Gray_Rhino's answer to this question. I set up 3 stages of my jump animation, when continually moving one direction a timer will check to see how far into the animation it is and decide whether or not to allow movement input. the timer resets after the animation is finished or the character is told to move in the other direction.
bool facingRight;
int jumpPhase = 1;
float timer = 0f;
float dir;
void Update()
{
if (jumpPhase != 3)
{
dir = Input.GetAxis("Horizontal");
}
if (0 > dir || (jumpPhase == 3 && facingRight == true))
{
timer += Time.deltaTime;
if (facingRight != true)
{
transform.eulerAngles = new Vector3(0, 180, 0);
jumpPhase = 1;
timer = 0f;
facingRight = true;
}
else if (jumpPhase == 1)
{
//starts animation
animator.SetBool("isSkipping", true);
if (timer >= 0.415f)
{
jumpPhase = 2;
}
}
else if (jumpPhase == 2)
{
ExecuteMovement(dir);
if (timer >= 0.995f)
{
jumpPhase = 3;
}
}
if (jumpPhase == 3)
{
if (timer >= 1.5f)
{
jumpPhase = 1;
timer = 0f;
}
}
}
else if (0 < dir || (jumpPhase == 3 && facingRight != true))
{
timer += Time.deltaTime;
if (facingRight == true)
{
transform.eulerAngles = new Vector3(0, 0, 0);
jumpPhase = 1;
timer = 0f;
facingRight = false;
}
else if (jumpPhase == 1)
{
//starts animation
animator.SetBool("isSkipping", true);
if (timer >= 0.415f)
{
jumpPhase = 2;
}
}
else if (jumpPhase == 2)
{
ExecuteMovement(dir);
if (timer >= 0.995f)
{
jumpPhase = 3;
}
}
if (jumpPhase == 3)
{
if (timer >= 1.5f)
{
jumpPhase = 1;
timer = 0f;
}
}
}
}

Related

How to prevent any player Input if the object is moving

I am creating a clone of Billiards and I cannot work out how to stop the player from interacting with and launching the ball while it is still moving; below is my attempt. I have placed an if statement that checks whether the ball is moving on both the mouseDrag and mouseUp functions. I have also tried using isSleeping() but this caused the ball to not move at all.
If possible I would like to apply this method to all of the balls and not just the cue ball; so that all balls have to have stopped before any actions may happen. This is currently in my "player" script, if I should move it a GameManager script please let me know.
private void Update()
{
speed = rb.velocity.magnitude;
if (speed < 0.5)
{
rb.velocity = new Vector3(0, 0, 0);
}
}
void OnMouseDrag()
{
if (speed == 0)
{
mousePointB.GetComponent<SpriteRenderer>().enabled = true;
currDistance = Vector3.Distance(mousePointA.transform.position, transform.position);
if (currDistance <= 3f)
{
spaceLimit = currDistance;
}
else
{
spaceLimit = maxDistance;
}
shootPower = Mathf.Abs(spaceLimit) * shootPowervar;
Vector3 dimxy = mousePointA.transform.position - transform.position;
float difference = dimxy.magnitude;
mousePointB.transform.position = transform.position + ((dimxy / difference) * currDistance * -1);
mousePointB.transform.position = new Vector3(mousePointB.transform.position.x, mousePointB.transform.position.y, -0.8f);
shootDirection = Vector3.Normalize(mousePointA.transform.position - transform.position);
}
else
{
}
}
void OnMouseUp()
{
if (speed == 0)
{
mousePointB.GetComponent<SpriteRenderer>().enabled = false;
arrow.GetComponent<SpriteRenderer>().enabled = false;
circle.GetComponent<SpriteRenderer>().enabled = false;
Vector3 push = shootDirection * shootPower * -1;
GetComponent<Rigidbody2D>().AddForce(push, ForceMode2D.Impulse);
}
else
{
}
}
if your test speed == 0 is not functional, you could record the last position of the ball, and set a boolean isMoving = true, if actual position of ball is different thant the last, then record the lastposition and so on
Vector3 LastPosition;
bool isMoving;
void Update()
{
isMoving = BallPosition == LastPosition;
:
:
}
void OnMOuseDrag()
{
if(!isMoving) return;
}
void OnMOuseUp()
{
if(!isMoving) return;
}

Is there a way to manage object movement toggling depend on touch count?

I want to make my player object move right when a touch happens and move left on following touch and so on.
bool isTurn = true;
[SerializeField] float horizonMoveSpeed = 20f;
Vector3 targetPosWhenTrue;
Vector3 targetPosWhenFalse;
private void Start()
{
targetPosWhenTrue = new Vector3(3f, 0, 0);
targetPosWhenFalse = new Vector3(-3f, 0, 0);
}
private void Update()
{
if(Input.touchCount > 0)
{
if (isTurn == true)
{
transform.position += targetPosWhenTrue * Time.deltaTime * horizonMoveSpeed;
isTurn = false;
}
if (isTurn == false)
{
transform.position += targetPosWhenFalse * Time.deltaTime * horizonMoveSpeed;
isTurn = true;
}
}
}
There is no error message but it is not working. Does nothing.
Since update is called every frame, isTurn is being reset every single frame. It appears that nothing is happening because it's going by so fast. In order to achieve what you're looking for you're going to have to check the position of the target and set the isTurn variable in the else statement. This may not be exactly what you're looking for, but should get you started.
if(Input.touchCount > 0)
{
if (isTurn == true)
{
if(transform.position != targetPosWhenTrue)
transform.position += targetPosWhenTrue * Time.deltaTime * horizonMoveSpeed;
else
isTurn = false;
}
if (isTurn == false)
{
if(transform.position != targetPosWhenTrue)
transform.position += targetPosWhenFalse * Time.deltaTime * horizonMoveSpeed;
else
isTurn = true;
}
}

Unity program stopped responding because of a infinite while loop

Hello stack overflow users! I was creating a game in unity 2018.2.2f1. All went great, but i missplaced the order of code lines in the program --> which caused a infite while loop appearing. I didn't know it would be infinite so I runned the program. Now my unity window is unselectable when i click on the icon, and when i enter the window, everything is frozen. I would close the program trough task manager, but i didnt save my files and i can't even save the project. Is there any way for me to save the project when it is in this state? My unity didn't crash, it just froze.
Here is my FIXED c# code if that somehow helps (and i am in visual studio):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour {
public float speed = 10f;
private Vector3 velocity = new Vector3();
private string direction = "none";
private bool canMoveAgain = true;
private bool go = false;
// Use this for initialization
void Start () {
speed = 10f;
}
// Update is called once per frame
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.name == "wall (3)" && direction == "down")
{
go = false;
}
if (collision.collider.name == "wall (2)" && direction == "up")
{
go = false;
}
if (collision.collider.name == "wall" && direction == "left")
{
go = false;
}
if (collision.collider.name == "wall (1)" && direction == "right")
{
go = false;
}
}
void Update () {
if (Input.GetKeyDown(KeyCode.DownArrow) && canMoveAgain)
{
direction = "down";
go = true;
canMoveAgain = false;
while (go)
{
velocity = new Vector3(0,-1,0);
transform.position += velocity;
}
velocity = new Vector3(0, 0, 0);
direction = "none";
} else if (Input.GetKeyDown(KeyCode.UpArrow) && canMoveAgain)
{
direction = "up";
go = true;
canMoveAgain = false;
while (go)
{
velocity = new Vector3(0, +1, 0);
transform.position += velocity;
}
velocity = new Vector3(0, 0, 0);
direction = "none";
} else if (Input.GetKeyDown(KeyCode.LeftArrow) && canMoveAgain)
{
direction = "left";
go = true;
canMoveAgain = false;
while (go)
{
velocity = new Vector3(-1, 0, 0);
transform.position += velocity;
}
velocity = new Vector3(0, 0, 0);
direction = "none";
} else if (Input.GetKeyDown(KeyCode.RightArrow) && canMoveAgain)
{
direction = "right";
go = true;
canMoveAgain = false;
while (go)
{
velocity = new Vector3(+1, 0, 0);
transform.position += velocity;
}
velocity = new Vector3(0, 0, 0);
direction = "none";
}
}
}
You do
while (go)
{
velocity = new Vector3(+1, 0, 0);
transform.position += velocity;
}
but go is never set to false inside the loop.
OnCollisionEnter is never executed since the while is still being executed => freeze.
since Update is called each frame anyway you should rather move that part to a separate if(go) block after checking the Inputs ..
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.name == "wall (3)" && direction == "down"
|| collision.collider.name == "wall (2)" && direction == "up"
|| collision.collider.name == "wall" && direction == "left"
|| collision.collider.name == "wall (1)" && direction == "right")
{
go = false;
velocity = Vector3.zero;
direction = "none"
}
}
void Update ()
{
if (Input.GetKeyDown(KeyCode.DownArrow) && canMoveAgain)
{
direction = "down";
go = true;
canMoveAgain = false;
velocity = -Vector3.up;
}
else if (Input.GetKeyDown(KeyCode.UpArrow) && canMoveAgain)
{
direction = "up";
go = true;
canMoveAgain = false;
velocity = Vector3.up;
}
else if (Input.GetKeyDown(KeyCode.LeftArrow) && canMoveAgain)
{
direction = "left";
go = true;
canMoveAgain = false;
velocity = -Vector3.right;
}
else if (Input.GetKeyDown(KeyCode.RightArrow) && canMoveAgain)
{
direction = "right";
go = true;
canMoveAgain = false;
velocity = Vector3.right;
}
if(go)
{
transform.position += velocity;
}
}
Note that currently your code is also frame rate dependent. You might want to rather use something like
public float moveSpeed;
...
transform.position += velocity * moveSpeed * Time.deltaTime;
For the direction I would rather recommend an enum like
private enum MoveDirection
{
None,
Up,
Down,
Left,
Right
}
and use it e.g. like
direction = MoveDirection.Up;
and
if(direction == MoveDirection.Up)
which is more efficient than comparing strings.
Note: Typed on smartphone so no warranty but I hope the idea gets clear
First of all. Do not close Unity.
You got two things you can try to do.
Luckily your code has a backup when you press run.
It is inside the Temp folder. The Temp folder will be replaced if you press run again. Copy the project to somewhere safe.
Attach a Debugger, this can be either Visual Studio or MonoDevelop. Once they are attached to Unity just click pause. And you're free to save your project.
I hope you find this answer before it's too late.

Playing animation with swipe controls and transform.position problem Unity

I've set up my swipe controls to transform my player left/right/up/down and I've restricted the movement on 3 lines; -1, 0, 1. Everything works fine but the movement is not smooth at all and the player seems to be "teleporting" from one position to another. I wanted to smooth the movement by playing the animation but the result was that the animation was being played after the player has changed his position.
Is there any way to play the animation while the player is changing his position or a way to smooth the movement so it looks right ?
I've tried everything and now I'm stuck with the problem, please help
Here's my code
public class SwipeControls : MonoBehaviour {
public float speed = 5.0f;
private Vector3 startpos; // start position
private Vector3 endpos; //end position
public int pozioma = 0;
public int pionowa = 0;
Animator anim;
void Start() {
GetComponent<Animator>();
}
void Update() {
foreach (Touch touch in Input.touches) {
Vector3 newPosition;
if (touch.phase == TouchPhase.Began) {
startpos = touch.position;
endpos = touch.position;
}
if (touch.phase == TouchPhase.Moved) {
endpos = touch.position;
}
if (touch.phase == TouchPhase.Ended) {
newPosition = transform.position;
if (Mathf.Abs(startpos.y - endpos.y) > Mathf.Abs(startpos.x - endpos.x)) {
if ((startpos.y - endpos.y) > 100 && pionowa > -1) //swipe down
{
pionowa--;
newPosition.y -= speed;
transform.position = newPosition;
anim.SetTrigger("Flydown");
}
if ((startpos.y - endpos.y) < -100 && pionowa < 1) //swipe up
{
pionowa++;
newPosition.y += speed;
transform.position = newPosition;
anim.SetTrigger("Flyup");
}
}
else {
if ((startpos.x - endpos.x) > 100 && pozioma > -1) //swipe left
{
pozioma--;
newPosition.z -= speed;
transform.position = newPosition;
anim.SetTrigger("Flyleft");
}
}
if ((startpos.x - endpos.x) < -100 && pozioma < 1) //swipe right
{
pozioma++;
newPosition.z += speed;
transform.position = newPosition;
anim.SetTrigger("Flyright");
}
}
}
}
}
Modify your code instead of using just
transform.position = newPosition;
in all position use
transform.position = Vector3.Lerp(transform.position, newPosition, smooth * Time.deltaTime);
where smooth is float variable assign it required value may be 0.5f or whatever you want
Note : What it actually does is that it makes game object to move little based on smooth value, every frame
Note that we do not know your animator setup. If in those animation clips there is anywhere a keyframe for the position then the animator will always overrule anything done by scripts!
Instead of setting the new position immediately you could use a Coroutine with StartCoroutine like (also fixing some coding styles)
public class SwipeControls : MonoBehaviour
{
// Flag for ignoring input until current animation finished
private bool routineRunning;
// Configure in the Inspector distance to swipe
// in unity units
[SerializeField] private float swipeDistance = 5;
// configure this in the Inspector
// How long should the swiping take in seconds
[SerializeField] private float swipeDuration = 1;
private Vector3 _startpos; // start position
private Vector3 _endpos; //end position
public int pozioma = 0;
public int pionowa = 0;
private Animator _anim;
private void Start()
{
GetComponent<Animator>();
}
private void Update()
{
// Ignore any Input while a routine is running
if (routineRunning) return;
foreach (var touch in Input.touches)
{
switch (touch.phase)
{
case TouchPhase.Began:
_startpos = touch.position;
_endpos = touch.position;
break;
case TouchPhase.Moved:
_endpos = touch.position;
break;
case TouchPhase.Ended:
if (Mathf.Abs(_startpos.y - _endpos.y) > Mathf.Abs(_startpos.x - _endpos.x))
{
if (_startpos.y - _endpos.y > 100 && pionowa > -1) //swipe down
{
pionowa--;
StartCoroutine(MoveSmooth(Vector3.up * -1, swipeDuration));
_anim.SetTrigger("Flydown");
}
else if (_startpos.y - _endpos.y < -100 && pionowa < 1) //swipe up
{
pionowa++;
StartCoroutine(MoveSmooth(Vector3.up, swipeDuration));
_anim.SetTrigger("Flyup");
}
}
else
{
if (_startpos.x - _endpos.x > 100 && pozioma > -1) //swipe left
{
pozioma--;
StartCoroutine(MoveSmooth(Vector3.forward * -1, swipeDuration));
_anim.SetTrigger("Flyleft");
}
else if (_startpos.x - _endpos.x < -100 && pozioma < 1) //swipe right
{
pozioma++;
StartCoroutine(MoveSmooth(Vector3.forward, swipeDuration));
_anim.SetTrigger("Flyright");
}
}
break;
}
}
}
private IEnumerator MoveSmooth(Vector3 direction, float duration)
{
routineRunning = true;
var currentPosition = transform.localPosition;
var targetPosition = currentPosition + direction * swipeDistance;
var timePassed = 0f;
do
{
// Additionally add some ease in and out to the movement to get
// even smoother movement
var lerpFactor = Mathf.SmoothStep(0, 1, timePassed / duration);
// Interpolate the position between currentPosition and targetPosition
// using the factor between 0 and 1
transform.localPosition = Vector3.Lerp(currentPosition, targetPosition, lerpFactor);
// increase by time since last frame
timePassed += Time.deltaTime;
// let this frame render and go on from
// here in the next frame
yield return null;
} while (timePassed <= duration);
// To avoid over or undershooting in the end set a fixed value
transform.localPosition = targetPosition;
routineRunning = false;
}
}
I don't know why you are using y+= and z+= .. it seems to me that it meeans the Camera is facing left in your scene e.g. with rotation = 0, -90, 0 and e.g. position = 10, 0, 0 than this should be the result (Note I copied the same code into if(GetMouseButtonDown(0)) etc in order to simulate the touch on the PC.)

Everything in the scene except the player looks blurry when the camera is moving | Unity2D

I started off my game using a simple camera follow script which only followed my player on the x axis with an offset and and some restrictions
I recorded a video on my android to show what I mean. In the recording the issue is a bit exaggerated ( due to recording) and only viewable once the spikes enter the view. When not recording, the player animation is perfectly smooth. Here's the video
using UnityEngine;
public class MainCamera : MonoBehaviour {
public Transform target;
public float xOffset, xPosRestrictionMin, xPosRestrictionMax;
private float yPos, zPos;
void Start ()
{
yPos = transform.position.y;
zPos = transform.position.z;
}
void LateUpdate ()
{
transform.position = new Vector3(Mathf.Clamp(target.position.x + xOffset, xPosRestrictionMin, xPosRestrictionMax), yPos, zPos);
}
}
However when first running the game everything looked "jittery". Despite all my player physics being inside fixed update, input inside update and camera updates being inside lateupdate. I tried setting interpolate to extrapolate on the players rigidbody2d. Now the player would look and animate smoothly but everything else look blurry when the camera is moving. I thought that maybe my script or settings were at fault so I tried to turn off vsync, set target frame rate to 60 and when nothing worked I downloaded Cinemachine. Even with cinemachine instead of my own script it still looks blurry and I can't figure out why. Here's a simplified version of my controls.
private void Update()
{
//Handle touch input
if (Input.touchCount > 0)
{
foreach (Touch touch in Input.touches)
{
switch (touch.phase)
{
// Touchdown
case TouchPhase.Began:
if (onGround || !doubleJumped)
{
jump = true;
touchReleased = false;
if (onGround)
anim.SetBool("jump", true);
}
break;
//Touch up
case TouchPhase.Ended:
allowGlide = false;
anim.SetBool("glide", false);
if (rb.velocity.y > 0)
touchReleased = true;
break;
}
}
}
}
void FixedUpdate()
{
rb.velocity = new Vector2(newMoveSpeed, rb.velocity.y);
onGround = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
if (onGround && !jump)
{
gliding = false;
allowGlide = false;
anim.SetBool("glide", false);
anim.SetBool("jump", false);
doubleJumped = false;
}
//Slowly Accelerate if not at top speed and touching the ground
if (newMoveSpeed < moveSpeed && onGround)
newMoveSpeed += 0.0165f;
anim.speed = Mathf.Clamp(newMoveSpeed, 13, 18);
//Jumping
if (jump && onGround)
{
jump = false;
rb.velocity = new Vector2(rb.velocity.x, jumpHeight);
}
else if (jump && !doubleJumped && !onGround)
{
jump = false;
doubleJumped = true;
allowGlide = true;
rb.velocity = new Vector2(rb.velocity.x, jumpHeight);
}
//Add multiplier if falling down
if (rb.velocity.y < 0 && allowGlide)
{
anim.SetBool("glide", true);
if (!gliding)
{
rb.velocity -= Vector2.up * Physics2D.gravity.y;
gliding = true;
}
else
{
rb.velocity += Vector2.up * Physics2D.gravity.y * (glideMultiplier - 1) * Time.deltaTime;
}
}
else if (rb.velocity.y < 0)
{
rb.velocity += Vector2.up * Physics2D.gravity.y * (fallMultiplier - 1) * Time.deltaTime;
}
//Increase fall multiplier if touch is released mid jump
else if (rb.velocity.y > 0 && touchReleased)
{
rb.velocity += Vector2.up * Physics2D.gravity.y * (lowJumpMultiplier - 1) * Time.deltaTime;
}
else if (rb.velocity.y == 0)
{
return;
}
}
Thanks, any help or feedback is appreciated!
Have you checked the frame rate of your game running on device? I have a film/animation background (but new to Unity) and it looks to me like you have a frame rate issue. Although, if the recording software changed the graphics at all I'm not sure I am seeing the same thing you are. Why does your recording software do that? Have you tried using Rec? It works great for me.
Sorry for not posting this as a comment - my current rep won't allow me to.
Maybe i think this is causing issues with the recording in the device which maybe took some computation power and caused issues, does this same thing happens while just playing and not recording?

Categories

Resources