Alright, so I'm using a coroutine to wait 6 seconds before executing a method to change a float in an animator, really simple stuff. My issue is that something in this script is causing my unity editor to completely lock up when I place it on a gameobject, and I don't know why. I don't think I have any infinite loops going, but I'm not sure. Anyone have any ideas? thx ahead of time.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class handanimatordock : MonoBehaviour
{
public Animator hand;
private float Blendfloat;
bool ChangeHand = false;
private float timefloat = 0.0f;
// Start is called before the first frame update
void Start()
{
StartCoroutine(waiter());
}
// Update is called once per frame
void Update()
{
}
public void changeHands()
{
if (ChangeHand == false)
{
ChangeHand = true;
while (Blendfloat != 1.0f)
{
Blendfloat = Blendfloat + 0.01f;
hand.SetFloat("Blend", Blendfloat);
}
}
else
{
ChangeHand = false;
while (Blendfloat != 0.0f)
{
Blendfloat = Blendfloat - 0.01f;
hand.SetFloat("Blend", Blendfloat);
}
}
}
IEnumerator waiter()
{
//Wait for 4 seconds
yield return new WaitForSeconds(6);
changeHands();
StartCoroutine(waiter());
}
}
You probably have an infinite loop in
while (Blendfloat != 1.0f)
{
Blendfloat = Blendfloat + 0.01f;
hand.SetFloat("Blend", Blendfloat);
}
Never directly compare two float values using == or !=.
Due to floating point impression a value like
10f * 0.1f
might end up being 1.000000001 or 0.99999999 though logically you would expect exactly 1. So your condition is probably never false!
Usually you rather give it a certain range like e.g.
while(Mathf.Abs(Blendfloat - 1) > certainThreshold)
Unity has for that Mathf.Approximately
while(!Mathf.Approximately(Blendfloat, 1f)
which basically equals comparing to a threshold of Mathf.Epsilon
which(Math.Abs(Blendfloat - 1) > Mathf.Epsilon)
Note that anyway what you have right now will execute the entire loop in one single frame.
If you really wanted it to fade over time you need to do one iteration per frame in e.g. a Coroutine!
Related
So in my game you are a sphere rolling down ramps, but if you go too fast and crash into a ledge the game freezes and the console gets spammed with the first error in the title followed by the second one. I managed to find what line of code caused this problem, here's that script:
public class FöljaBoll : MonoBehaviour
{
public GameObject player;
public float yOffset;
public float zOffset;
Transform playerPosition;
Transform cameraPosition;
// Start is called before the first frame update
void Start()
{
playerPosition = player.GetComponent<Transform>();
cameraPosition = gameObject.GetComponent<Transform>();
}
// Update is called once per frame
void Update()
{
if (playerPosition == null)
{
return;
}
cameraPosition.position = playerPosition.position + new Vector3(0, +yOffset, -zOffset);
}
private void FixedUpdate()
{
if (player == null)
{
return;
}
else
{
float speed = player.GetComponent<Rigidbody>().velocity.z + 6;
float targetFOV = (float)Math.Log(speed, 1.1) + 34;
GetComponent<Camera>().fieldOfView = Mathf.Lerp(GetComponent<Camera>().fieldOfView, targetFOV, (float)0.1);
}
}
}
So what it does is it sets the FOV based on the player speed through a logarithmic function in order for it to not get out of control at high speeds, but for some reason this part (I commented it out and wasn't able to recreate the errors):
GetComponent<Camera>().fieldOfView = Mathf.Lerp(GetComponent<Camera>().fieldOfView, targetFOV, (float)0.1);
triggers the error messages. If anyone could help me out here that would be awesome, thanks in advance! :)
I solved it myself by setting the bounciness of the Physics Material attached to both the ramps and the sphere to 0. This post: https://answers.unity.com/questions/9985/limiting-rigidbody-velocity.html said that bounciness combined with high velocities can cause problems and that seems to have been the problem.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FirePistol : MonoBehaviour {
public GameObject TheGun;
public GameObject MuzzleFlash;
public AudioSource GunFire;
public bool IsFiring = false;
void Update () {
if (Input.GetButtonDown("Fire1"))
{
if (IsFiring == false)
{
StartCoroutine(FiringPistol());
}
}
}
IEnumerator FiringPistol ()
{
IsFiring = true;
TheGun.GetComponent<Animation>().Play("PistolShot");
MuzzleFlash.SetActive(true);
MuzzleFlash.GetComponent<Animation>().Play("MuzzleAnim");
GunFire.Play();
yield return new WaitForSeconds(0.5f);
IsFiring = false;
}
}
I am writing a gun mechanic And
I wonder why we need
yield return new WaitForSeconds(0.5f); .What is the difference without this command .It's really unnecessary to write this code coz the time is short .Afterall , will it be an error like scene crash after i deleting this code ?Any help is greatly appreciated !
In general: Every method returning IEnumerator has to contain at least one yield statement. In Unity you have to use StartCoroutine to run an IEnumerator as Coroutine.
In your specific case: So you can delay your code by 0.5 seconds!
It is short but 0.5 is about 30 frames!
Someone using e.g. something like AutoClicker could jam the fire key each frame so he would cause significantly more damage then someone playing "normal" (due to physical limitations of your keyboard and finger ;) )
You are just avoiding that and limit down firing to a maximum of 2x per second.
In general - as usual - there are multiple ways to achieve that and you could go without Coroutines entirely but it makes coding so much cleaner and easier to maintain then doing everything in Update!
As some alternative examples for simple delays as here you could also either do a simple timer in Update
private float timer;
void Update ()
{
if(timer > 0)
{
// reduce the timer by time passed since last frame
timer -= Time.deltaTime;
}
else
{
if(Input.GetButtonDown("Fire1"))
{
FiringPistol();
timer = 0.5f;
}
}
}
void FiringPistol()
{
TheGun.GetComponent<Animation>().Play("PistolShot");
MuzzleFlash.SetActive(true);
MuzzleFlash.GetComponent<Animation>().Play("MuzzleAnim");
GunFire.Play();
}
or you can also use Invoke with a given delay.
bool canFire;
void Update ()
{
if (Input.GetButtonDown("Fire1") && canFire)
{
FiringPistol();
}
}
void FiringPistol()
{
TheGun.GetComponent<Animation>().Play("PistolShot");
MuzzleFlash.SetActive(true);
MuzzleFlash.GetComponent<Animation>().Play("MuzzleAnim");
GunFire.Play();
Invoke(nameof(AfterCooldown), 0.5f);
}
void AfterCooldown()
{
canFire = true;
}
In general btw you should store the Animation references to not use GetComponent over and over again:
// if possible already reference these via the Inspector
[SerializeField] private Animation theGunAnimation;
[SerializeField] private Animation muzzleFlashAnimation;
private void Awake()
{
// as fallback get them on runtime
// since this is a fallback and in best case you already referenced these via the Inspector
// we can save a bit of resources and use GetComponent
// only in the case the fields are not already set
// otherwise we can skip using GetComponent as we already have a reference
if(!theGunAnimation) theGunAnimation = TheGun.GetComponent<Animation>();
if(!muzzleFlashAnimation) muzzleFlashAnimation = MuzzleFlash.GetComponent<Animation>();
}
then later you reuse them
theGunAnimation.Play("PistolShot");
MuzzleFlash.SetActive(true);
muzzleFlashAnimation.Play("MuzzleAnim");
GunFire.Play();
I am learning Unity from a Swift SpriteKit background where moving a sprite's x Position is as straight forward as an running an action as below:
let moveLeft = SKAction.moveToX(self.frame.width/5, duration: 1.0)
let delayAction = SKAction.waitForDuration(1.0)
let handSequence = SKAction.sequence([delayAction, moveLeft])
sprite.runAction(handSequence)
I would like to know an equivalent or similar way of moving a sprite to a specific position for a specific duration (say, a second) with a delay that doesn't have to be called in the update function.
gjttt1's answer is close but is missing important functions and the use of WaitForSeconds() for moving GameObject is unacceptable. You should use combination of Lerp, Coroutine and Time.deltaTime. You must understand these stuff to be able to do animation from Script in Unity.
public GameObject objectectA;
public GameObject objectectB;
void Start()
{
StartCoroutine(moveToX(objectectA.transform, objectectB.transform.position, 1.0f));
}
bool isMoving = false;
IEnumerator moveToX(Transform fromPosition, Vector3 toPosition, float duration)
{
//Make sure there is only one instance of this function running
if (isMoving)
{
yield break; ///exit if this is still running
}
isMoving = true;
float counter = 0;
//Get the current position of the object to be moved
Vector3 startPos = fromPosition.position;
while (counter < duration)
{
counter += Time.deltaTime;
fromPosition.position = Vector3.Lerp(startPos, toPosition, counter / duration);
yield return null;
}
isMoving = false;
}
Similar Question: SKAction.scaleXTo
The answer of git1 is good but there is another solution if you do not want to use couritines.
You can use InvokeRepeating to repeatedly trigger a function.
float duration; //duration of movement
float durationTime; //this will be the value used to check if Time.time passed the current duration set
void Start()
{
StartMovement();
}
void StartMovement()
{
InvokeRepeating("MovementFunction", Time.deltaTime, Time.deltaTime); //Time.deltaTime is the time passed between two frames
durationTime = Time.time + duration; //This is how long the invoke will repeat
}
void MovementFunction()
{
if(durationTime > Time.time)
{
//Movement
}
else
{
CancelInvoke("MovementFunction"); //Stop the invoking of this function
return;
}
}
You can use co-routines to do this. To do this, create a function that returns type IEnumerator and include a loop to do what you want:
private IEnumerator foo()
{
while(yourCondition) //for example check if two seconds has passed
{
//move the player on a per frame basis.
yeild return null;
}
}
Then you can call it by using StartCoroutine(foo())
This calls the function every frame but it picks up where it left off last time. So in this example it stops at yield return null on one frame and then starts again on the next: thus it repeats the code in the while loop every frame.
If you want to pause for a certain amount of time then you can use yield return WaitForSeconds(3) to wait for 3 seconds. You can also yield return other co-routines! This means the current routine will pause and run a second coroutine and then pick up again once the second co-routine has finished.
I recommend checking the docs as they do a far superior job of explaining this than I could here
I am learning Unity from a Swift SpriteKit background where moving a sprite's x Position is as straight forward as an running an action as below:
let moveLeft = SKAction.moveToX(self.frame.width/5, duration: 1.0)
let delayAction = SKAction.waitForDuration(1.0)
let handSequence = SKAction.sequence([delayAction, moveLeft])
sprite.runAction(handSequence)
I would like to know an equivalent or similar way of moving a sprite to a specific position for a specific duration (say, a second) with a delay that doesn't have to be called in the update function.
gjttt1's answer is close but is missing important functions and the use of WaitForSeconds() for moving GameObject is unacceptable. You should use combination of Lerp, Coroutine and Time.deltaTime. You must understand these stuff to be able to do animation from Script in Unity.
public GameObject objectectA;
public GameObject objectectB;
void Start()
{
StartCoroutine(moveToX(objectectA.transform, objectectB.transform.position, 1.0f));
}
bool isMoving = false;
IEnumerator moveToX(Transform fromPosition, Vector3 toPosition, float duration)
{
//Make sure there is only one instance of this function running
if (isMoving)
{
yield break; ///exit if this is still running
}
isMoving = true;
float counter = 0;
//Get the current position of the object to be moved
Vector3 startPos = fromPosition.position;
while (counter < duration)
{
counter += Time.deltaTime;
fromPosition.position = Vector3.Lerp(startPos, toPosition, counter / duration);
yield return null;
}
isMoving = false;
}
Similar Question: SKAction.scaleXTo
The answer of git1 is good but there is another solution if you do not want to use couritines.
You can use InvokeRepeating to repeatedly trigger a function.
float duration; //duration of movement
float durationTime; //this will be the value used to check if Time.time passed the current duration set
void Start()
{
StartMovement();
}
void StartMovement()
{
InvokeRepeating("MovementFunction", Time.deltaTime, Time.deltaTime); //Time.deltaTime is the time passed between two frames
durationTime = Time.time + duration; //This is how long the invoke will repeat
}
void MovementFunction()
{
if(durationTime > Time.time)
{
//Movement
}
else
{
CancelInvoke("MovementFunction"); //Stop the invoking of this function
return;
}
}
You can use co-routines to do this. To do this, create a function that returns type IEnumerator and include a loop to do what you want:
private IEnumerator foo()
{
while(yourCondition) //for example check if two seconds has passed
{
//move the player on a per frame basis.
yeild return null;
}
}
Then you can call it by using StartCoroutine(foo())
This calls the function every frame but it picks up where it left off last time. So in this example it stops at yield return null on one frame and then starts again on the next: thus it repeats the code in the while loop every frame.
If you want to pause for a certain amount of time then you can use yield return WaitForSeconds(3) to wait for 3 seconds. You can also yield return other co-routines! This means the current routine will pause and run a second coroutine and then pick up again once the second co-routine has finished.
I recommend checking the docs as they do a far superior job of explaining this than I could here
Im trying to get a slow motion on the game im making but it looks realy laggy. Im using the FPS controller from the standard assets given by unity.I attached this script to it :
function Update ()
{
if (Input.GetKeyDown ("q"))
{
Time.timeScale = 0.5;
}
if (Input.GetKeyDown ("e"))
{
Time.timeScale = 2.0;
}
if (Input.GetKeyDown ("t"))
{
Time.timeScale = 1.0;
}
}
I know this is a similar question to this one http://answers.unity3d.com/questions/39279/how-to-get-smooth-slow-motion.html
but the fixes given on that question won't work.I tried adding a rigidbody to it and putting the Interpolation to Interpolate but it doesn't do anything.(I removed the tick from "Use Gravity" because the character started flying).Im new in unity and scripting so please go easy on me.
Thank you.
You should change Time.fixedDeltaTime as well:
function Update () {
if (Input.GetKeyDown ("q")){
Time.timeScale = 0.5;
Time.fixedDeltaTime = 0.02F * Time.timeScale;
}
}
To return it to normal motion again you should call these two lines
Time.timeScale = 1;
Time.fixedDeltaTime = 0.02F * Time.timeScale;
You just need to call SetTimeScale Method as target timescale as argument :)
using UnityEngine;
public class TimeScaleSmooth : MonoBehaviour
{
public float targetTimeScale = 1;
public float speed = 1;
void Update()
{
Time.timeScale = Mathf.MoveTowards(Time.timeScale, targetTimeScale,
Time.unscaledDeltaTime * speed);
//0.02 is default fixedDeltaTime
Time.fixedDeltaTime = timeScale * 0.02f;
//FOR TEST
if (Input.GetKeyDown(KeyCode.A))
{
SetTimeScale(1);
}
if (Input.GetKeyDown(KeyCode.S))
{
SetTimeScale(0.2f);
}
}
public void SetTimeScale(float timeScale)
{
targetTimeScale = timeScale;
}
}
Note that we used Time.unscaledDeltaTime which is independent of timescale.
You can tweak transition time using speed.
This is actually insanely easy. In fact, you don't even have to code anything! Just go into every rigidbody in the scene, and change the interpolation mode to "interpolate" or "extrapolate". They mean very different things, but both have the end result of perfectly smooth slow-motion.