I am going insane. For the life of me I cannot work out why the following code is causing Unity to freeze up as soon as I press play. It's an otherwise empty project with the script attached to an empty gameobject. In the console, nothing appears, not even the initial Debug.Log("Step 1");
using UnityEngine;
public class Reels : MonoBehaviour
{ void Start()
{
Debug.Log("Step 1");
TestFunction(1f);
}
private void TestFunction(float duration)
{
float endTimer = Time.time + duration;
Debug.Log("Step 2 " + endTimer);
while (Time.time < endTimer)
{
Debug.Log("Step 3");
}
}
void Update()
{
}
}
Please save my sanity.
Time.time is a read only field that stores the time at the beginning of the frame, and the way you wrote that code, the frame never ends, because you'll never get out of that loop.
Related
I have this simulation where the "Hunter" scans the area with the scannereyes object inside the hunter for an object called "Apple" and whenever it sees the apple its supposed to hunt after it. This is the same with other hunters from the same prefab as there are more hunters on the same platform.
The scanner eyes is supposed to send the data of the apple's position so the hunter can run for it. Ive tried putting the found apple object into static but that would resolve into every hunter hunting for the same apple so I tried instead doing this method:
Target = GetComponent<ScannerEyes>().foundobject.transform;
And instead of giving the Hunter the data it needed the whole engine just crashes whenever it tries to compile the C sharp code.
Yes everything is well updated and so is my computer. I have a strong enough computer to compile.
//In the Hunter CS file
void Update()
{
if (reproducerate < 1)
{
if (GameObject.FindGameObjectsWithTag("Food") != null ) //All apple objects have the tag "Food"
{
Target = GetComponent<ScannerEyes>().foundobject.transform;
directiontolookat = (Target.position - transform.position).normalized;
if ((transform.position - Target.position).magnitude > Distans)
{
transform.Translate(directiontolookat * Time.deltaTime * Speed);
}
}
}
}
public void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "Food")
{
reproducerate += 1;
Destroy(collision.gameObject);
Debug.Log(reproducerate);
}
}
//In the ScannerEyes CS file:
public bool found = false;
public GameObject foundobject;
void Update()
{
//This is only for the scanning sphere to grow overtime cuz thats how scanners work
if (currentsize != maxsize && !found) {
transform.localScale = new Vector3(currentsize, currentsize, currentsize);
currentsize += scanningspeed;
}
if (found)
{
transform.localScale = new Vector3(0f, 0f, 0f);
}
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Food")
{
Debug.Log("WE FOUND IT");
foundobject = other.gameObject;
Debug.Log("found at" + foundobject.transform.position);
found = true;
}
}
//In the Food behaviour CS file (This is just to make it spawn in the world between -4, 4)
private GameObject foodobject;
public GameObject Foodprefab;
private float Xspawncoords;
private float Zspawncoords;
void Start()
{
Debug.Log("Food is working");
for (int i = 0; i < 2; i++){
Xspawncoords = Random.Range(-4, 4);
Zspawncoords = Random.Range(-4, 4);
Debug.Log(Xspawncoords + " and " + Zspawncoords);
foodobject = Instantiate(Foodprefab,
new Vector3(Xspawncoords, 1.4f, Zspawncoords),
Quaternion.identity);
foodobject.name = "AppleObject";
}
}
I don't understand if the engine crashes when you start the game or when you just save the scripts.
As far as I can see the Hunter checks that there is at least one GameObject with the "food" tag on it, but you are doing this in the Update() method.
This is very bad for performance since you are calling it every frame, and it's pretty expensive as it's constantly checking for every object in the scene.
You can try removing that call and move it in the Start() method, that would call the method only once!
I've searched around and couldn't quite find the answer. I have two scenes, one with a play button which starts the next scene (the game) and the other scene is the game. Which has a spawner script which spawns random patterns of obsticles. Which can be seen here.
public class Spawner : MonoBehaviour {
public GameObject[] obstaclePatterns;
private float timeBtwSpawn;
public float startTimeBtwSpawn;
public float decreaseTime;
public float minTime = 0.55f;
private void Update()
{
if (timeBtwSpawn <= 0)
{
int rand = Random.Range(0, obstaclePatterns.Length);
Instantiate(obstaclePatterns[rand], transform.position, Quaternion.identity);
timeBtwSpawn = startTimeBtwSpawn;
if (startTimeBtwSpawn > minTime) {
startTimeBtwSpawn -= decreaseTime;
}
}
else {
timeBtwSpawn -= Time.deltaTime;
}
}}
I would like to after the play button is pressed and the game is started there be a delay for 1 second before the spawner begins spawning. I'm not sure how to do that. Any help would be appreciated.
You can use Unity's Start function as a coroutine directly.
private bool _canStart;
private IEnumerator Start()
{
yield return new WaitForSeconds(whatyouwant);
_canStart = true;
}
private void Update()
{
if(!_canStart) return;
whatyouwant
}
if you want to have a routine that starts after a specific amount of time since the scene was loaded you can use Time.timeSinceLevelLoad this variable holds the time in seconds since the last level(scene) was loaded
So you can either create a script that activates your spawner script or add an additional check to your spawner script
You should set timeBtwSpawn before start updating of Spawner:
timeBtwSpawn = 1; // seconds
I am calling a Coroutine shown below, which is attached to a DontDestroyOnLoad objects that persists across scenes.
IEnumerator InitMaxScore()
{
Debug.Log("will wait for some time");
yield return new WaitForSeconds(1);
GameObject[] coins = GameObject.FindGameObjectsWithTag("Coin");
Debug.Log("coins length == " + coins.Length);
if (coins.Length > 0)
{
maxScoreInitialized = true;
maxScore = score + coins.Length * 10;
foreach (GameObject healthPickup in GameObject.FindGameObjectsWithTag("Health"))
{
maxScore += healthPickup.GetComponent<Pickups>().pointsForLifePickup;
}
Debug.Log("maxScore inti == " + maxScore);
}
yield return null;
}
This Coroutine is called in the OnLevelWasLoaded event of the said gameobject which is set to DontDestroyOnLoad on awake as shown below.
private void Awake()
{
int numGameSessions = FindObjectsOfType<GameSession>().Length;
if (numGameSessions > 1)
{
Destroy(gameObject);
}
else
{
DifficultyManagement.setDifficulty(Difficulty.One); // start the game with diff one always
DontDestroyOnLoad(this.gameObject);
}
}
While the log "will wait for some time" in the Coroutine is getting printed, Debug.Log("coins length == " + coins.Length) is not getting printed all the times. I am certainly not destroying the said gameobject for the entire duration of my game that might have caused the Coroutine to behave this way. The behaviour is not consistent either, sometimes it works, sometimes it does not, and I am like why can't you make up your mind.
I have been banging my head on this for a long time and could not seem to fix this, any leads would be appreciated to lift my mental block :/
OnLevelWasLoaded is deprecated, consider using sceneLoaded event:
using UnityEngine.SceneManagement;
private void Awake()
{
int numGameSessions = FindObjectsOfType<GameSession>().Length;
if (numGameSessions > 1)
{
Destroy(gameObject);
}
else
{
DifficultyManagement.setDifficulty(Difficulty.One); // start the game with diff one always
DontDestroyOnLoad(this.gameObject);
SceneManager.sceneLoaded += (scene, mode) => StartCoroutine(InitMaxScore());
}
}
First off, I am quite new to scripting so there's probably going to be a few flaws in my script.
So basically, I've made a script for the power up, but once my shot or the player touches the power up coin the fire rate does increase however it won't go back to the normal fire rate after 5 seconds... I have no idea what might be the cause, any advice would be helpful!
using UnityEngine;
using System.Collections;
public class FireRatePowerUp : MonoBehaviour {
private bool isPowerUp = false;
private float powerUpTime = 5.0f;
private PlayerShoot playerShoot;
private void Start()
{
playerShoot = PlayerShoot.FindObjectOfType<PlayerShoot>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "Player" || collision.gameObject.tag == "Projectile")
{
StartCoroutine(PowerUpTime());
isPowerUp = true;
Destroy(gameObject);
if (collision.gameObject.tag == "Projectile")
{
Destroy(collision.gameObject);
}
}
}
IEnumerator PowerUpTime()
{
playerShoot.fireRate -= 0.13f;
yield return new WaitForSeconds(powerUpTime);
playerShoot.fireRate += 0.13f;
}
}
I think the issue here is that you're destroying the gameobject this script is attached to (the coin) and by so doing, the script itself is destroyed, therefor its code, coroutine or otherwise won't execute.
StartCoroutine(PowerUpTime());
isPowerUp = true;
Destroy(gameObject); //oops, our script has been destroyed :(
You would have to do this very differently, basically moving the bulk of the code to the PlayerShoot class.
Something like this (this being in PlayerShoot.cs)
public void ActivatePowerupFireRate(float time, float amt) {
StartCoroutine(DoActivatePowerupFireRate(time, amt));
}
public IEnumerator ActivatePowerupFireRate(float time, float amt) {
fireRate -= amt;
yield return WaitForSeconds(time);
fireRate += amt;
}
IEumerator is definately one of the ways you can solve this issue.
However I'm not a fan of them here's my solution if you have a timer in game.
public int timePassed = 0;
public int gotPowerUp = 0;
void Start(){
InvokeRepeating("Timer", 0f, 1.0f);
//Starting at 0 seconds, every second call Timer function.
}
void Timer(){
timePassed++; // +1 second.
}
That way when you obtained the powerup you can set gotPowerUp = timePassed. So you have the exact time when powerup is activated.
then you do something like
if( (gotPowerUp + 5) == timePassed ){
//5 seconds have passed.
//deactivate powerup here
}
I'm working on an AI script in Unity 5 mostly constructed of coroutines to move around an AI object in my game. It actually works pretty well and is frame independent that way. I'm trying to avoid cluttering the Update function of the class.
However, I'm trying to create a function called wander, where the AI hangs around and randomly chooses a couple of Vector3s in the area of the waypoint and travel to each of them. After that the AI should go to the next waypoint and do the same thing into infinity basically. Collision detection and all that sort is for later parts. I want to get the navigating part fixed first.
void Start(){
agent = GetComponent<NavMeshAgent> ();
collisionRange = GetComponent<CapsuleCollider> ();
collisionRange.radius = detectionRadius;
if (waypoints.Length > 0) {
StartCoroutine (patrol ());
} else {
StartCoroutine (idle (idleTime));
}
}
IEnumerator patrol(){
agent.SetDestination(waypoints[waypointIndex].position);
Debug.Log ("Patrol started, moving to " + agent.destination);
while (agent.pathPending) {
Debug.Log ("not having path");
yield return null;
}
Debug.Log ("I have a path, distance is " + agent.remainingDistance);
while (float.Epsilon < agent.remainingDistance) {
//Debug.Log ("Moving...");
yield return null;
}
StartCoroutine (nextWaypoint ());
}
IEnumerator idle(int time){
Debug.Log ("Idleing for "+ time + " seconds");
agent.Stop ();
yield return new WaitForSeconds(time);
if(waypoints.Length > 2){
agent.Resume ();
StartCoroutine(patrol());
}
}
IEnumerator wander(){
agent.Stop ();
Debug.Log ("Going to look around here for a while.");
Vector3[] points = new Vector3[wanderPoints];
for (int i = 0; i < wanderPoints; i++) {
agent.Stop ();
Vector3 point = Random.insideUnitSphere * wanderRadius;
point.y = transform.position.y;
points [i] = point;
}
agent.ResetPath ();
agent.SetDestination(points[0]);
agent.Resume ();
Debug.Log ("point: " + points [0]);
Debug.Log ("Destination: " + agent.destination);
while (float.Epsilon < agent.remainingDistance) {
Debug.Log ("Moving...");
yield return null;
}
//StartCoroutine(patrol());
yield return null;
}
IEnumerator nextWaypoint(){
Debug.Log ("Arrived at my waypoint at " + transform.position);
if (waypointIndex < waypoints.Length -1) {
waypointIndex +=1;
} else {
waypointIndex = 0;
}
StartCoroutine(wander ());
yield return null;
}
If I swap the wander function with the idle function in nextWaypoint, everything works as expected, but this bit will never work:
agent.ResetPath ();
agent.SetDestination(points[0]);
agent.Resume ();
Debug.Log ("point: " + points [0]);
Debug.Log ("Destination: " + agent.destination);
This is a bit of test code (manually setting only 1 position to go point[0], but it never will travel to that destination. SetDestination will never get updated to the points I want to set them. I've tried calculating paths (NavMeshPath) up front and everything but the destination path will not change or reset weirdly enough. I've also had the while (float.Epsilon < agent.remainingDistance) loop in the wander function as well, but with no luck since it will remain in that loop forever since there's no path to go to.
I might be missing something here, or my logic is wrong in this case. Hopefully someone could give me a little push or maybe some extra debugging options, because I have no idea why the destination doesn't get updated in my wander function.
Consider using triggers on waypoints instead, as this will be even less taxing for the system. Below is an example that will allow you to have a variable number of waypoints easily, by getting all WayPoint type children from a parent transform. The only Coroutine that is used is for idle as you said you didn't want your Update function cluttered.
I've also exposed a couple of additional variables, including a min and max idle time, and a patrol chance, which should be set to a value less than or equal to 1, representing a percentage chance of patrolling vs idle. You can also choose a min and max number of patrol points for the agent to navigate to.
In RandomActivity you can also see that there is error handling for infinitely idle (no WayPoint children under parent). The agent will also not include the current Waypoint in it's list of points to navigate to, and will not navigate to a point already navigated to during it's current patrol (every time an element is added to m_targets, it's removed from m_availableTargets).
AgentController.cs
[RequireComponent(typeof(NavMeshAgent))]
public class AgentController : MonoBehaviour
{
[SerializeField]
private Transform m_waypointParent;
[SerializeField]
private float m_minIdle;
[SerializeField]
private float m_maxIdle;
[SerializeField]
private float m_patrolChance;
[SerializeField]
private int m_minPatrolPoints;
[SerializeField]
private int m_maxPatrolPoints;
private Waypoint[] m_waypoints;
private List<Waypoint> m_availableTargets;
private List<Waypoint> m_targets;
private Waypoint m_tempWaypoint;
private int m_currentTargetIndex;
private NavMeshAgent m_navMeshAgent;
public void Start()
{
m_waypoints = m_waypointParent.GetComponentsInChildren<Waypoint>();
m_targets = new List<Waypoint>();
m_navMeshAgent = GetComponent<NavMeshAgent>();
RandomActivity();
}
private void RandomActivity()
{
if (m_waypoints.Length == 0)
{
Debug.Log("Enemy will idle forever (no waypoints found)");
StartCoroutine(Idle(Random.Range(m_minIdle, m_maxIdle)));
return;
}
if(Random.Range(0f, 1f) <= m_patrolChance)
{
//Available waypoints
m_availableTargets = new List<Waypoint>(m_waypoints);
//Remove currentpoint
if(m_targets.Count > 0)
m_availableTargets.Remove(m_targets[m_targets.Count - 1]);
//Reset list
m_targets.Clear();
m_currentTargetIndex = -1;
//Add patrol points
for (int i = 0; i < Random.Range(m_minPatrolPoints, m_maxPatrolPoints + 1); i++)
{
m_tempWaypoint = m_availableTargets[Random.Range(0, m_availableTargets.Count)];
m_targets.Add(m_tempWaypoint);
m_availableTargets.Remove(m_tempWaypoint);
}
NextWaypoint(null);
}
else
StartCoroutine(Idle(Random.Range(m_minIdle, m_maxIdle)));
}
public void NextWaypoint(Waypoint p_waypoint)
{
//Collided with current waypoint target?
if ((m_currentTargetIndex == -1) || (p_waypoint == m_targets[m_currentTargetIndex]))
{
m_currentTargetIndex++;
if (m_currentTargetIndex == m_targets.Count)
RandomActivity();
else
{
Debug.Log("Target: " + (m_currentTargetIndex + 1) + "/" + m_targets.Count + " (" + m_targets[m_currentTargetIndex].transform.position + ")");
m_navMeshAgent.SetDestination(m_targets[m_currentTargetIndex].transform.position);
}
}
}
private IEnumerator Idle(float p_time)
{
Debug.Log("Idling for " + p_time + "s");
yield return new WaitForSeconds(p_time);
RandomActivity();
}
}
Note that for this to work, I have created a tag called Enemy so as to easily differentiate between enemies and any other tiggers in your game.
WayPoint.cs
[RequireComponent(typeof(BoxCollider))]
public class Waypoint : MonoBehaviour
{
public void OnTriggerEnter(Collider p_collider)
{
if (p_collider.tag == "Enemy")
p_collider.GetComponent<AgentController>().NextWaypoint(this);
}
}
I know this is an old post but hope this helps someone.