FixedUpdate slightly fired up even if scene is paused with Time.timeScale = 0f - c#

I'm a newbie in unity & c# . The FixedUpdate function does some Rigidbody action (here pushing the cube over the z axis)
However the scene works fine in development (cube starts at z = 0 )
The problem is in build , the cube starts at certain distance means z = 5 or 6
According to my understanding , I believe this is caused due to FixedUpdate being fired for some milli seconds before it notice Time.timeScale = 0f in PauseGame function
And when it notice this it behave as expected.
But then on Restart function when being called up (called by a button) using SceneManager.LoadScene function cube starts with z= 0 in build.
clip at development and clip with bug at build
where did I go wrong, thanks in advance.
image of bug at build, cube starts at certain position
image of development which working fine as expected cube starts at z = 0
public class GameController : MonoBehaviour
{
public GameObject tapToStart;
private void Start()
{
tapToStart.SetActive(true);
PauseGame();
}
private void Update()
{
StartGame();
}
public void Restart()
{
SceneManager.LoadScene("Game");
}
public void PauseGame()
{
Time.timeScale = 0f;
}
public void StartGame()
{
tapToStart.SetActive(false);
Time.timeScale = 1f;
}
}
public class PlayerScript : MonoBehaviour
{
public new Rigidbody rigidbody;
public float force;
private void Update(){}
private void FixedUpdate()
{
rigidbody.AddForce(0, 0, force * Time.deltaTime);
}
}

You are correct! FixedUpdate is called at relatively'fixed' intervals. It doesn't matter if something is done in your other code and you need it to get updated immediately. FixedUpdate will just keep pressing on at a a regular interval.
You can verify your suspicions by placing the line:
rigidbody.AddForce(0, 0, force * Time.deltaTime);
Inside your Update() function.
Now, onto the solution. I suggest that create place the following code in your currently empty Update() function:
if(Time.timeScale = 0f){
rigidbody.velocity = Vector3.zero;
rigidbody.angularVelocity = Vector3.zero;
}
Now, as soon as you press the pause button, it will be detected on the next frame of Update() and the object will be frozen. This will likely occur even before FixedUpdate is called. Of course, if you want to 'restore' the velocity and angular velocity when you unpause, you will need to store that data, and then set it back up when you unpause the game.

The issue has been solved .. I can't pause on start,
need to create a Boolean in PlayerScript.
public class PlayerScript : MonoBehaviour
{
public new Rigidbody rigidbody;
public float force;
public bool isGameStart; // this Boolean
private void Update(){}
private void FixedUpdate()
{
if (isGameStart)
{
rigidbody.AddForce(0, 0, force * Time.deltaTime);
}
}
}
and using that bool value , we need to use rigidbody component.
and in StartGame function in GameController we must assign true to that bool value
public class GameController : MonoBehaviour
{
public GameObject tapToStart;
public PlayerScript playerScript;
private void Start()
{
tapToStart.SetActive(true);
PauseGame();
}
private void Update()
{
StartGame();
}
public void Restart()
{
SceneManager.LoadScene("Game");
}
public void PauseGame()
{
Time.timeScale = 0f;
}
public void StartGame()
{
tapToStart.SetActive(false);
Time.timeScale = 1f;
playerScript.isGameStart = true;
}
}
this worked for me , this solution was given by a Youtuber "Unity city".. credits to him.

Related

Unity Input.GetKeyDown() event frequently missed

I'm working on a test project to experiment with Rigidbody in Unity. I worked on horizontal movement and jump actions, but I have a problem. Input.GetKeyDown() seems to not catch my key down event most of the time. I tried looking at possible solutions that suggested catching key inputs in Update() and corresponding with Rigidbody interactions with FixedUpdate(). When I tried this, I saw little to no improvement. Here is the script I'm working on right now:
public class PlayerScript : MonoBehaviour
{
[SerializeField] private float jumpConstant = 5.0f;
[SerializeField] private int walkSpeed = 10;
private bool jumpDown;
private float horizontalInput;
private Rigidbody rbComponent;
// Start is called before the first frame update
void Start()
{
rbComponent = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
CheckIfJumpKeyPressed();
GetHorizontalInput();
}
void FixedUpdate()
{
JumpIfKeyPressed();
MoveHorizontal();
}
void CheckIfJumpKeyPressed()
{
jumpDown = Input.GetKeyDown(KeyCode.Space);
}
void JumpIfKeyPressed()
{
if (jumpDown)
{
jumpDown = false;
rbComponent.AddForce(Vector3.up * jumpConstant, ForceMode.VelocityChange);
Debug.Log("Jumped!");
}
}
void GetHorizontalInput()
{
horizontalInput = Input.GetAxis("Horizontal");
}
void MoveHorizontal()
{
rbComponent.velocity = new Vector3(horizontalInput * walkSpeed, rbComponent.velocity.y, 0);
}
}
Thank you in advance.
You are overwriting your input if an extra frame occurs between your input and the physics frame. You should make sure a lack of input does not overwrite a detected input:
void CheckIfJumpKeyPressed()
{
jumpDown |= Input.GetKeyDown(KeyCode.Space);
}
or, equivalently:
void CheckIfJumpKeyPressed()
{
if (!jumpDown)
{
jumpDown = Input.GetKeyDown(KeyCode.Space);
}
}
Or even:
void CheckIfJumpKeyPressed()
{
if (Input.GetKeyDown(KeyCode.Space))
{
jumpDown = true;
}
}
Whichever you find most readable.

State machine for a game object in Unity doesn't seem to be working. Instead of changing from "stagger" to "idle", it just keeps moving

So I'm developing my first game in Unity currently (following a series by Mister Taft on YouTube called "Make a game like Zelda using Unity and C#" and just finished the 19th video).
I've currently got a "Log" enemy and am working on a knockback feature. I've followed the way that it is approached in the series is by creating a state machine that changes the Log's state to "stagger" for a couple of seconds, then return it to "idle" so it can change itself to "walk."
Whenever I hit the Log, however, it gets stuck in the stagger state and drifts without stopping until it hits a collider (note it doesn't change back to idle/walk even when it does hit the collider). If I can hit the Log correctly, sometimes, the player will actually also drift in the opposite direction.
Relevant code:
public class Log : Enemy {
private Rigidbody2D myRigidBody;
public Transform target;
public float chaseRadius;
public float attackRadius;
public Transform homePosition;
public Animator anim;
// Use this for initialization
void Start () {
currentState = EnemyState.idle;
myRigidBody = GetComponent<Rigidbody2D> ();
anim = GetComponent<Animator> ();
target = GameObject.FindWithTag ("Player").transform;
}
// FixedUpdate is called by physics.
void FixedUpdate () {
CheckDistance ();
}
//Log finds and walks towards Player.
void CheckDistance(){
if (Vector3.Distance (target.position, transform.position) <= chaseRadius && Vector3.Distance (target.position, transform.position) > attackRadius
&& currentState != EnemyState.stagger)
{
Vector3 temp = Vector3.MoveTowards (transform.position, target.position, moveSpeed * Time.deltaTime);
myRigidBody.MovePosition (temp);
ChangeState (EnemyState.walk);
}
}
private void ChangeState(EnemyState newState){
if (currentState != newState)
{
currentState = newState;
}
}
}
public class Knockback : MonoBehaviour {
public float thrust;
public float knockTime;
private void OnTriggerEnter2D(Collider2D other){
if (other.gameObject.CompareTag ("breakable"))
{
other.GetComponent<Pot>().Smash();
}
if(other.gameObject.CompareTag("enemy"))
{
Rigidbody2D enemy = other.GetComponent<Rigidbody2D>();
if (enemy != null)
{
enemy.GetComponent<Enemy> ().currentState = EnemyState.stagger;
Vector2 difference = enemy.transform.position - transform.position;
difference = difference.normalized * thrust;
enemy.AddForce (difference, ForceMode2D.Impulse);
StartCoroutine (KnockCo (enemy));
}
}
}
private IEnumerator KnockCo(Rigidbody2D enemy){
if (enemy != null)
{
yield return new WaitForSeconds (knockTime);
enemy.velocity = Vector2.zero;
enemy.GetComponent<Enemy>().currentState = EnemyState.idle;
}
}
}
public enum EnemyState{
idle,
walk,
attack,
stagger
}
public class Enemy : MonoBehaviour {
public EnemyState currentState;
public int enemyHealth;
public string enemyName;
public int baseAttack;
public float moveSpeed;
}
I've tried to add an else statement after the void CheckDistance in Log.cs, but that resulted in the Log having jittery movement and jumping back along its path. I'm at a loss. Any help would be greatly appreciated!

my variable doesn't change even that in the log its say the variable change [unity]

I have 1 script to PlayerMovement and one for powerUp I the power-up code I reference player movement to change the speed and change the bool named timer to true and I write that in log and when I touch the paper the speed doesn't change and the timer don't turn to true but in the log, its say that is yes
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
private float TargetPos;
public float Speed;
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.position = new Vector2(TargetPos, transform.position.y);
}
public void right()
{
TargetPos = transform.position.x + Speed * Time.deltaTime;
}
public void left()
{
TargetPos = transform.position.x - Speed * Time.deltaTime;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Powrups : MonoBehaviour
{
public PlayerMovement pm;
public float PowerUpActiveTime;
public float StartPowerUpActiveTime;
public float peperSpeed;
float NormalSpeed;
bool timer;
bool timerover;
private void OnTriggerEnter2D(Collider2D col)
{
if (col.name == "peper")
{
pm.Speed = peperSpeed;
timer = true;
Debug.Log("timerOn");
Debug.Log(pm.Speed);
Debug.Log(timer);
}
}
private void Update()
{
while(timer)
{
GameObject Pause = GameObject.Find("Pause");
PauseScript pausescript = Pause.GetComponent<PauseScript>();
if (!pausescript.pause)
{
PowerUpActiveTime -= Time.deltaTime;
if(PowerUpActiveTime <= 0 )
{
timerover = true;
}
if (timerover)
{
timer = false;
}
}
}
if (timerover)
{
PowerUpActiveTime = StartPowerUpActiveTime;
pm.Speed = NormalSpeed;
}
}
private void Start()
{
PowerUpActiveTime = StartPowerUpActiveTime;
timerover = false;
NormalSpeed = pm.Speed;
}
}
Your mistake is that while loop.
You are lucky that until now you probably have tested this always while not being in pause mode ;)
This while would completely freeze your app and the entire Unity Editor!
In general be extremely careful with while loops and nested conditions like here, where the exit condition might never be fulfilled!
What happens currently is that you are not in pause mode so this while loop gets activated and runs until timer is set to false .. completely within one single frame. That is the reason why to you it seems that the value is never true.
What you rather want anyway is that code block be executed once per frame.
And in particular in a frame based application like Unity also have some performance impacts in mind.
You shouldn't use Find and GetComponent repeatedly within Update but store and re-use the results.
So your code should rather be
// If possible already drag this in via the Inspector
[SerializeField] private PauseScript _pauseScript;
private void Start()
{
PowerUpActiveTime = StartPowerUpActiveTime;
timerover = false;
NormalSpeed = pm.Speed;
// Get this ONCE as fallback on runtime
if(!_pauseScript)
{
_pauseScript = GameObject.Find("Pause"). GetComponent<PauseScript>();
// Or simply use
//_pauseScript = FindObjectOfType<_pauseScript>();
}
}
private void Update()
{
if(timer)
{
if (!_pauseScript.pause)
{
PowerUpActiveTime -= Time.deltaTime;
if(PowerUpActiveTime <= 0 )
{
timer = false:
PowerUpActiveTime = StartPowerUpActiveTime;
pm.Speed = NormalSpeed;
}
}
}
}
Besides all that, you should rather not let an external power-up control your player values. I would rather go the other way round and have your player object have a component which checks into which power-up items you run and react to it accordingly.
So your power-up itself would actually only be a trigger without any clue if or how exactly the player will be influenced by it.

Unity 2018 script to make panel scroll up wont work if I go to the game scene before the credits scene

I am working on a game in Unity3D (what else would I be making) and so far I have the Main menu scene, the game scene and the Credits scene.
There is a script I made (shown below) That will make the panel holding the names scroll up that works fine if I select the credits scene from the main menu. But here is the problem. If I go to the game first and then go back to the main menu rand select credits nothing happens. Any ideas?
using UnityEngine;
using System.Collections;
public class ScrollCredits : MonoBehaviour
{
public GameObject Canvas;
public int speed = 1;
public string level;
private void Start()
{
Canvas.transform.Translate(Vector3.up * Time.deltaTime * speed);
StartCoroutine(waitFor());
}
private void Update()
{
}
IEnumerator waitFor()
{
yield return new WaitForSeconds (69);
Application.LoadLevel(level);
}
}
You moved your Translate inside the Start(), it won't work that way.
Only StartCoroutine should be in Start(), like this :
public GameObject canvas;
public float speed = 0.1f;
public string sceneName;
public float timer;
private void Start()
{
StartCoroutine(WaitFor());
}
private void Update()
{
canvas.transform.Translate(Vector3.right * Time.deltaTime * speed);
}
IEnumerator WaitFor()
{
yield return new WaitForSeconds (timer);
SceneManager.LoadScene(sceneName);
}
Note : I changed LoadLevel to SceneManager.LoadScene because it's deprecated, and will be removed in the future.

How to change a boolean from another script C#

I have 2 script, playerMachanics and enemyBehavior. My enemyBehavior has a boolean that when the boolean is true it moves away from the player. Instead i'm getting the error: "object reference not set to an instance of an object".
I'm sure it means the script can't find the component but i can't quite figure out what's wrong.
public class enemyBehavior : MonoBehaviour
{
public bool evade = false;
public GameObject Player;
public float movementSpeed = 4;
// Start is called before the first frame update
void Start()
{
Player = GameObject.FindGameObjectWithTag("Player");
}
// Update is called once per frame
void Update()
{
transform.LookAt(Player.transform);
transform.position += transform.forward * movementSpeed * Time.deltaTime;
if (evade == true)
{
movementSpeed = -4;
}
}
}
public class playerMechanics : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
void OnCollisionEnter(Collision collision)
{
enemyBehavior evade = gameObject.GetComponent<enemyBehavior>();
if (collision.gameObject.name == "coin")
{
Destroy(collision.gameObject);
enemyBehavior script = GetComponent<enemyBehavior>();
script.evade = script.evade == true;
}
}
}
I expected that the movementSpeed would go to -4 but now i'm just getting an error.
Calling getComponent by itself will look for the component attached to the parent object of the script, which is the player in this case I think. So it will always return null.
Add
Public GameObject enemy;
to the playerMechanics class and then go into the designer and drag the game object that has the enemyBehavior script attached into it. There are several problems with the onCollisionEnter method. Something like this
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.name == "coin")
{
Destroy(collision.gameObject);
enemyBehavior script = enemy.GetComponent<enemyBehavior>();
script.evade = false;
}
}
should get you going in the right direction.
Is the enemy behavior on the player object? See here
enemyBehavior evade = gameObject.GetComponent<enemyBehavior>();
and here
enemyBehavior script = GetComponent<enemyBehavior>();
You need to implement a way to track which enemy instance you are grabbing. Do this by making a variable to hold the enemy script, or by using a singleton on the enemy script (if there is one enemy).
Variable:
public enemyBehaviour enemy;
Singleton:
(enemyBehaviour)
public static enemyBehaviour instance = null;
private static readonly object padLock = new object();
void Awake(){
lock(padLock){
if(instance == null)
instance = this;
}
}
(player)
enemyBehaviour.instance.evade = false;
Look up singletons if you want to learn more.
If I'm right, I think your game mechanics works like this: The player's objective is to collect coins. When they collect one, the enemy near it will come and evade the player.
If that's the case, you should use this:
public class enemyBehavior : MonoBehaviour
{
public bool evade = false;
public GameObject Player;
public float movementSpeed = 4;
void Start()
{
Player = GameObject.FindGameObjectWithTag("Player");
}
void Update()
{
transform.LookAt(Player.transform);
transform.position += transform.forward * movementSpeed * Time.deltaTime;
if (evade)
{
movementSpeed = -4;
}
}
}
public class playerMechanics : MonoBehaviour
{
[SerializeField] enemyBehvaior enemy;
void OnCollisionEnter(Collision collision)
{
if (collision.collider.name == "coin")
{
Destroy(collision.collider.gameObject);
enemy.evade = true;
}
}
}
In your code, you wrote 'collision.gameObject.' This refers to the object the script is attached to. If you want to reference to the object that we hit, use 'collision.collider'.
'[SerializeField]' is a unity attribute, which is used to make a field show up in the inspector without making it public.
Just a heads up, if you're using 2D, make sure the method is signatured 'OnCollisionEnter2D(Collision2D collision)'.
I hope I answered your question. :)

Categories

Resources