Lifecycle, Coroutine and deltaTime - c#

Unity: 2017.3.0f3
Use case : Move an object on click.
I often see Coroutine like the following to move object.
private IEnumerator MoveTo(Vector2 position){
while (/*stop condition*/)
{
float newX = speed * Time.deltaTime;
transform.position = /*with newX*/
yield return null; // <-----------
}
}
I think it should be:
private IEnumerator MoveTo(Vector2 position){
while (/*stop condition*/)
{
yield return null; // <-----------
float newX = speed * Time.deltaTime;
transform.position = /*with newX*/
}
}
I think that in most use case we have to wait the next frame to execute the move action otherwise we take into account the deltaTime that already passed before we managed to move the object.
What do you think ? Am I totally wrong ?
PS: I know that we can use Update() and co. for this kind of use case.

What do you think ? Am I totally wrong ?
Yes, you are wrong. The first one is more correct.
Think of it more like this:
You want to move object from one position to another, the first thing to do is to calculate the distance to move the object then move to that position and then wait for a frame. You do this over and over again until you reach the destination position. This is what the first code is doing.
The second code on the other hand is waiting for a frame first then moving the object. Why wait for a frame before starting the movement? What will happen is that the movement will not start when that function is called until next frame which will introduce a delay. Unless this is explicitly what you want, you shouldn't go with it.

Not wrong at all, but I guess it doesn't make a noticeable difference most of the time? Your second version would be more accurate in some sense because I think that is really the expected behavior, that the object is rendered once at position 0 before moving on. If the moving object is a user controlled object I still think the first method is better suited because it would give the fastest feedback/response to the user without any "delay". The second method would be better for animations where it is preferred that the starting position is rendered as well (especially if the object isn't even visible before the animation starts),

Related

My animations aren't playing when they should be

So Basically, I am creating a wandering ai character in unity using c# and the wandering is all working fine, but when the certain animations are supposed to play, they don't. I will include the code I am using to make this happen. I am also using an animator component on the model and the animations are all properly named and align with the animation in the animator's names and the animations are from mixamo. Any help is much appreciated because I am completely stuck!
void Update()
{
if (isWandering == false)
{
StartCoroutine(Wander());
}
if (isRotatingRight == true)
{
gameObject.GetComponent<Animator>().Play("idle");
transform.Rotate(transform.up * Time.deltaTime * rotSpeed);
}
if (isRotatingLeft == true)
{
gameObject.GetComponent<Animator>().Play("idle");
transform.Rotate(transform.up * Time.deltaTime * -rotSpeed);
}
if (isWalking == true)
{
gameObject.GetComponent<Animator>().Play("waalk");
transform.position += transform.forward * moveSpeed * Time.deltaTime;
}
}
Really consider using Animator parameters and transitions, it will save a lot of headache later on.
Anyways, regarding the question: Your code is in Update, which means it runs every frame.
That means every frame you're telling the Animator to play the state with that name, so every frame it starts the animation of that state over again. The result would be that your object would be stuck on the first frame of whatever animation.
Instead, you should call Play just once, when the desired conditions change.
Incidentally, that's one example of when it would be more convenient to use Animator parameters, since with transitions you are querying for specific conditions that take the animator from one state to the other, so you would get this "run just once" behaviour for free.
SORRY I FOUND THE ANSWER I WAS DOING EVERYTHING TO A MODEL THAT WASN'T RIGGED 😅 IT'S ALL GOOD NOW THANKS FOR THE HELP :)

I want to spawn my zombies in my game at a random location but I want to make sure to not spawn the inside the buildings

Currently in my script I am spawning in 125 zombies randomly all over my map however with my current code it just spawns them everywhere including inside the buildings. I want to keep the random spawns so i can't make exact spawn locations. Does anyone know how to block them from spawning inside buildings?
My current code for spawning them in (I am a noob so sorry if code is bad/messy)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ZombieRandomSpawn : MonoBehaviour
{
public GameObject ZK;
public int xPos;
public int zPos;
public int enemyCount;
// Start is called before the first frame update
void Start()
{
while (enemyCount < 125)
{
xPos = Random.Range(1, 165);
zPos = Random.Range(1, 215);
Instantiate(ZK, new Vector3(xPos, 0.3f, zPos), Quaternion.identity);
enemyCount = enemyCount + 1;
}
}
}
I would make an array of empty game objects that would be the possible spawn locations. Then place them anywhere you want, outside of the buildings. Then each time a zombie spawns, make it randomly select a spawn location from the array of locations that you made.
I would not use a SphereCast as suggested here!
As the name says it casts a sphere along a certain direction and checks if it hits anything on its way.
You probably rather would use an Physics.CheckSphere which checks for any colliders around a certain point within a certain radius without returning them since we are not interested in the hit Colliders anyway but only in the return value (bool).
In this specific use-case though it might not work as expected since in Start it might happen that not all physics related things are correctly initialized yet. I would wait until the first FixedUpdate call like e.g.
// Adjust this via the Inspector
[Tooltip("How far away from any collider do zombies have to spawn?")]
[SerializeField] private float zombySpawnDistanceThreashold = 0.3f;
// Yes, if you make Start return IEnumerator
// Unity automatically runs it as a Coroutine
private IEnumerator Start ()
{
// Wait for the first physics Update
yield return new WaitForFixedUpdate ();
// We don't actually care about the hits
// We only need to know IF we hit anything at all
// But this is way more efficient then everytime allocating a new array
var hits = new Collider[1];
while (enemyCount < 125)
{
// I assume you don't need to have exact INT values but can use any FLOAT value between them
// If that's the case make sure to use the FLOAT version here
var xPos = Random.Range(1f, 165f);
var zPos = Random.Range(1f, 215f);
var spawnPos = new Vector3(xPos, 0.3f, zPos);
// If we hit something we have to continue and pick a new random position
// Note: You might want/need to ignore the floor layer if your radius is too big since it would count as hit
if(Physics.OverlapSphereNonAlloc(spawnPos, zombySpawnDistanceThreashold, hits) > 0) continue;
Instantiate(ZK, spawnPos, Quaternion.identity);
// (Not sure if needed) Resimulate the physics to take the newly added zomby collider into account
Physics.Simulate(0.00001f);
enemyCount++;
}
}
As an alternative to skipping the spawn in case if a hit you could also use Physics.OverlapSphereNonAlloc and then use Physics.ComputePenetration in order to get a direction and distance of where and how far you have to move the zomby in order to push it out of the hit collider.
For an easy approach, I would consider looking into spherecast. Before spawning a zombie, cast a sphere and if it hits a building then try another spot to spawn. There is the potential for this to take a lot of computational time as it is randomly picking points and checking if it is valid. There is the case where it continually randomly picks points inside of buildings.
A more solid approach would be to predefined the bounds of where your buildings are, then only random pick places outside of these ranges. It also heavily depends on how large your building is or how many buildings you have. It is a rather vague question so more detail can help give a more exact answer.

Run function next frame (Not in update()) Unity

I am trying to run a block of code a certain amount of seconds after a 2D Collision. Currently, when the collision occurs it calls a function that loops over itself until a certain time is reached. The time is calculated using a time variable and adding Time.deltaTime each iteration.
This is the function
public void springBack(Collider2D collision, float timeCount) {
if (timeCount > springReloadTime * 10) {
Debug.Log("Springing Back!");
timeCount = 0f;
} else {
timeCount = timeCount + Time.deltaTime;
springBack(collision, timeCount);
}
}
The issue occurs when I run this. Everything happens in one frame right after the collision instead of after a few seconds. If I could call the springBack(collision, timeCount); function on the next frame, this would work. When I looked it up, some people suggested using yield return null;, but since this isn't a loop it gives me this error here:
The body of 'playerMovement.springBack(Collider2D, float)' cannot be an iterator block because 'void' is not an iterator interface type
Is there another way to force it to run on the next frame? I could call it from the update() loop, but I think I can have multiple instances of this function running without it inferring with each other. If you need the values of some of the public variables (like springReloadTime), please ask.

How can I change the time that it takes to an animation to run in Unity scripting C#?

I have the player, which is a cube and I want it to "jump" to a chosen empty object position.
I managed to move the player from it's original place with vector3.MoveTowards(), but at the same time I want to play an animation that shows how the cube jumps to the empty object position.
The problem here is that the empty object position will change so the distance from the cube to the empty object will be different. I believe I need to change the time that it takes the animation to complete, so it would pause and after that let the cube move just in a straight line.
I want the animation to take a longer or shorter time to run, given that the empty object position will always change...
You can use StartCoroutine in this case.
IEnumerator animated() {
// Code here
yield return new WaitUntil (() => stopanimated == true);
// Run the code here
}
Or you can
IEnumerator animated() {
// Code here
yield return new WaitForSeconds (1); // How much second to wait before execute the next line code.
// Run code here
}
And how to call it use :
StartCoroutine (Animated ());
For detail documentation coroutine here :
https://docs.unity3d.com/ScriptReference/MonoBehaviour.StartCoroutine.html
This StartCoroutine is use to pause a code for some time before it execute.
And
To change the speed animated :
See this post :
http://answers.unity3d.com/questions/950205/how-to-change-speed-of-animation-in-c.html

Set gameObject active Unity C#

A game object has force applied to it in the positive direction, then after some time it has a force applied to it in the negative direction.
If the force is applied in the negative direction, this means game over and I want to display a totally different gameObject which is the game over gameObject called icetextureONfile. My method is not working I get error "type 'UnityEngine.GameObject' does not contain a definition for icetextureONfile". I am having a hard time refe
public void FixedUpdate() {
// No action happened yet
gameObject.icetextureONfile.SetActive (false);
// Increase the kick timer
kickTimer += Time.fixedDeltaTime;
// If the next kick time has came
if (nextKick < kickTimer) {
// Schedule the kick back corresponding to the current kick
nextKickBacks.Enqueue (nextKick + 100f);
// Apply the kick force
rb.AddForce (transform.up * thrust, ForceMode.Impulse);
// Plan the next kick
nextKick = kickTimer + Random.Range (MinKickTime, MaxKickTime);
}
// If there are more kick backs to go, and the time of the closest one has came
if (0 < nextKickBacks.Count) {
if (nextKickBacks.Peek () < kickTimer) {
// Apply the kick back force
rb.AddForce (-transform.up * thrust, ForceMode.Impulse);
// Game object was kicked down, set active game over object
gameObject.icetextureONfile.SetActive (true);
// Dequeue the used kick back time
nextKickBacks.Dequeue ();
}
}
}
If your wanting to deactivate one and then activate the other you could just add this to the class make sure its not inside the function
public GameObject iceTexture;
then drag and drop that object into the spot shown in the script in unity called iceTexture. Then just make sure you deactivate the object that the script is attached to and activate the iceTexture object.
gameObject.SetActive(false);
iceTexture.SetActive(true);
This page might help you.
This syntax worked for me
GameObject.Find ("icetextureONfile").SetActive(false);
As opposed to what is found in the Unity docs
gameObject.SetActive(false);
The first worked for me, the second did not. I am not a great programmer, so maybe it is just a matter of context.
Edit
It is commonly known that it is difficult to SetActive(true) after the object has already been SetActive(false). This is because the attached script becomes deactivated too. See Unity forums for details on why this is a nightmare.
To overcome this I have chosen a different route that accomplishes the same thing.
I set the size of the object to 0 until I needed it.
GameObject.Find ("icetextureONfile").transform.localScale = new Vector3(0, 0, 0);
GameObject.Find ("icetextureONfile").transform.localScale = new Vector3(0.02f, 0.02f, 0.02f);
Note that 0.02 is the size of my object in particular and may not be the exact same size as yours.

Categories