Unity program stopped responding because of a infinite while loop - c#

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.

Related

I added coyote time to my jump in my 2d platformer(where you can jump shortly after jumping off the ledge), but it's not working

All the settings are there and the code seems correct to me, but it's just not working.
This is the code that involves coyote time:
[SerializeField, Range(0f, 0.3f)] private float _coyoteTime = 0.2f;
private float coyoteCounter
private void FixedUpdate()
{
onGround = ground.GetOnGround();
velocity = body.velocity;
if(onGround && body.velocity.y == 0)
{
jumpPhase = 0;
coyoteCounter = _coyoteTime;
isJumping = false;
}
else
{
coyoteCounter -= Time.deltaTime;
}
private void JumpAction()
{
if(coyoteCounter > 0f || jumpPhase < maxAirJumps && isJumping)
{
if(isJumping)
{
jumpPhase += 1;
}
jumpBufferCounter = 0;
coyoteCounter = 0;
jumpSpeed = Mathf.Sqrt(-2f * Physics2D.gravity.y * jumpHeight);
isJumping = true;
if(velocity.y > 0f)
{
jumpSpeed = Mathf.Max(jumpSpeed - velocity.y, 0f);
}
velocity.y += jumpSpeed;
}
}
If someone could please explain to me what is going wrong, that would be greatly appreciated. Thanks!
First you don't need to use fixed update for this, your ground.GetOnGround is probably filled on FixedUpdate itself. FixedUpdate is mostly useful when you are dealing with AddForce for a certain amount of time not just one frame.
Second if you are using FixedUpdate use fixedDeltaTime instead of deltaTime.
This is the barebone of the effect, you can add multi jump to the logic yourself.
// Will set from checking the ground contact on update
bool IsOnGround = true;
bool WasOnGround = true;
bool LedgeFall = false;
bool IsJumping = false;
bool ThereIsGroundContact()
{
// Check if character is contact with ground with raycast or overlaps
}
void OnUpdate()
{
IsOnGround = ThereIsGroundContact();
if (IsOnGround && !WasOnGround)
{
OnLand();
}
// if falling from ledge
if (!IsOnGround && WasOnGround && velocity.y <= 0)
{
LedgeFall = true;
CoyoteTimer = 0;
}
if (LedgeFall){
CoyoteTimer += Time.deltaTime;
}
if (LedgeFall && CoyoteTimer >= _CoyoteTime){
LedgeFall = false;
}
// You can process jump input here
if ((IsOnGround || LedgeFall) && JumpInputPressed)
IsJumping = true;
WasOnGround = IsOnGround;
}
void OnLand()
{
LedgeFall = false;
IsJumping = false;
CoyoteTimer = 0;
}
Hope this helps

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;
}

How to delay movement in unity while animation plays

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;
}
}
}
}

How do I stop the player partly going through ground

I made a controller for a 2d character, I fixed the issue of the player jittering through the wall but I cant fix the player jittering through the ground. Got this code from multiple tutorials and tweaking stuff. If you have any tips that would be greatly appreciated I'm new to this stuff and working on my first game (had to ramble cuz I put too much code) thanks.
{
public float speed, height;
Rigidbody2D rb;
private bool horizontalRight = false;
private bool horizontalLeft = false;
private bool verticalMove = false;
private void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
if (Input.GetAxisRaw("Horizontal") > 0)
{
horizontalRight = true;
}
else if (Input.GetAxisRaw("Horizontal") < 0)
{
horizontalLeft = true;
}
if (Input.GetButton("Jump") && rb.velocity.y == 0)
{
verticalMove = true;
}
}
private void FixedUpdate()
{
if (horizontalRight == true)
{
transform.Translate(Vector2.right * Time.deltaTime * speed);
horizontalRight = false;
}
else if (horizontalLeft == true)
{
transform.Translate(Vector2.left * Time.deltaTime * speed);
horizontalLeft = false;
}
if (verticalMove == true)
{
rb.velocity = new Vector2(0, height);
verticalMove = false;
}
}
}
Activate continuous collision detection on your rigid bodies, it'll keep them from phasing through things so long as they're being moved via their velocity.
A typical jump in unity would look like:
void Jump()
{
if (_isGrounded == true)
{
rb.AddForce(Vector2.up * _jumpSpeed);
_isGrounded = false;
}
}
and in collision event
void OnCollisionEnter (Collision hit)
{
_isGrounded = true;
}
this would limit when you can use a jump.

How to deactivate bool var if nothing is pressed

I have a problem in my code. I have a sprite and when I click on it I rotate another object. That object i can move it left, right with the keyboard. The problem is that if you keep pressing LeftArrow and at the same time click to rotate it then the object continues to move to the left even if you do not click on anything.
private KeyboardEventSystem kBoard;
private Dictionary<KeyCode, bool> pressed = new Dictionary<KeyCode, bool>();
[ReadOnly] public bool moving;
[ReadOnly] public bool grounded = false;
void Start()
{
degeticaRb = GetComponent<Rigidbody2D>();
kBoard = gameObject.AddComponent<KeyboardEventSystem>();
kBoard.addKey(KeyCode.LeftArrow);
kBoard.addKey(KeyCode.RightArrow);
kBoard.addMap(KeyCode.A, KeyCode.LeftArrow);
kBoard.addMap(KeyCode.D, KeyCode.RightArrow);
pressed.Add(KeyCode.LeftArrow, false);
pressed.Add(KeyCode.RightArrow, false);
kBoard.KeyBoardEvent += KBoard_KeyBoardEvent;
}
private void KBoard_KeyBoardEvent(KeyboardEventType keyboardEventType, KeyCode keyCode)
{
if (!grounded)
return;
if (keyboardEventType == KeyboardEventType.DOWN)
pressed[keyCode] = true;
if (keyboardEventType == KeyboardEventType.UP)
{
moving = false;
pressed[keyCode] = false;
degeticaRb.angularVelocity = 0f;
degeticaRb.velocity = Vector3.zero;
}
}
public void DoRotation(int tIndex)
{
Vector3 rotAngle = transform.rotation.eulerAngles;
if (tIndex == 1)
{
rotAngle.z -= 90f;
transform.position = new Vector3(transform.position.x - 0.65f, transform.position.y);
}
else
{
rotAngle.z += 90f;
transform.position = new Vector3(transform.position.x + 0.65f, transform.position.y);
}
transform.eulerAngles = rotAngle;
}
private void FixedUpdate()
{
Collider2D[] colliders = Physics2D.OverlapCircleAll(Feet.position, 0.1f);
grounded = false;
foreach (Collider2D coll in colliders)
{
if (coll.tag == "wall")
{
grounded = true;
break;
}
}
if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.RightArrow))
{
moving = (pressed[KeyCode.LeftArrow] ^ pressed[KeyCode.RightArrow]);
if (moving && grounded)
{
moveAxis += pressed[KeyCode.RightArrow] ? accelaration : (accelaration * -1f);
moveAxis = Mathf.Clamp(moveAxis, -1, 1);
degeticaRb.velocity = new Vector2(moveAxis * moveSpeed, degeticaRb.velocity.y);
}
else
moveAxis = 0f;
}
else
moving = false;
DoMoveAnim();
}
So the bool variable moving remains active even if I do not press on anything. Help!
You need to manage your key states even if the object isn't grounded. The only things that care if it's grounded is setting moving = false and setting the velocities to zero:
private void KBoard_KeyBoardEvent(KeyboardEventType keyboardEventType, KeyCode keyCode)
{
if (keyboardEventType == KeyboardEventType.DOWN)
pressed[keyCode] = true;
if (keyboardEventType == KeyboardEventType.UP)
{
pressed[keyCode] = false;
if (grounded)
{
moving = false;
degeticaRb.angularVelocity = 0f;
degeticaRb.velocity = Vector3.zero;
}
}
}
Practically speaking, you may want to simply use Input.GetKey, Input.GetKeyUp and Input.GetKeyDown instead of managing the key state yourself.

Categories

Resources