this script is supposed to be spawning an object every 2 seconds or so, but, it seems to be insta-spawning a million endlessly instead. I'll be fixing them further in making them get destroyed once off-screen so that's not an issue. But what I do need to do is have it properly delay executing
InvokeRepeating()
Any help would be appreciated. Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnManager : MonoBehaviour
{
public GameObject obstaclePrefab;
private Vector3 spawnPOS = new Vector3(25, 0, 0);
private float startDelay = 2;
private float repeatRate = 2;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
{
InvokeRepeating("SpawnObstacle", startDelay, repeatRate);
}
}
void SpawnObstacle()
{
Instantiate(obstaclePrefab, spawnPOS, obstaclePrefab.transform.rotation);
}
}
Doesn't seem to matter what I put in as repeatRate, 2 and 20 seem similar. Although just 2 should be fine... it was a few days ago the first time I put this together.
You are calling InvokeRepeating from Update. That will start a new timer every frame. You even have a comment in there that says:
// Update is called once per frame
It's true!
Presumably your updates are occurring 60-ish times per second. So I would expect that after the initial 2-second delay that 120-ish objects would be created. Which is neither insta-spawning nor is it a "million". (I'm a bit picky about reporting the details of things accurately.) But the timers will remain active and continue to spawn new instances, and the total number will grow quadratically (the number of objects will be proportional to elapsed time squared) so I'm sure it seems like a million.
Likely you intended the InvokeRepeating call to be in your Start method, not the Update method. That way you only start one repeating timer.
Related
The game is simple, your Player has a running animation and must jump to dodge obstacles, but isn't moving. The obstacles and background are.
My gut instinct is that it is out of scope somehow or is perhaps overflowing - the score increment was increasing far beyond what I had anticipated when I had the AddScore() inside of the Destroy(gameObject); if condition instead of its own function.
However, at this point I am very confused why it isn't working. As a bonus, I cannot get Audio to play from the second commented bit (Value cannot be null.) As for why that happens, no idea. I definitely have the source I have it attached to to the prefab that is spawned, and said spawn should trigger that sound to play when it passes under the player when they jump, I originally thought that there was an issue where the object was deleted before it could reference its audio source but I am unsure.
Edit: I am going to leave the 'bonus' issue above even though I literally fixed it as I typed this up. (I had to add an Audio Source component to the prefab I was spawning.)
I still, for the life of me, cannot get an integer to go above 1 and print to the console. It might be driving me a little insane. Please help friends. I have googled a ton and none of the suggestions from other comments fixed my issue.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveLeft : MonoBehaviour
{
private float speed = 30;
private PlayerController playerControllerScript;
private float leftBound = -15;
private float scoreZone = -3;
public AudioClip scoreSound;
private int score = 0;
private AudioSource playerAudio;
// Start is called before the first frame update
void Start()
{
playerControllerScript = GameObject.Find("Player").GetComponent<PlayerController>();
playerAudio = GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
// Moves objects as long as it is not game over.
if (playerControllerScript.gameOver == false)
{
transform.Translate(Vector3.left * Time.deltaTime * speed);
}
//Assigns scoring system, buggy. Commented out sound because of errors.
if (transform.position.x < scoreZone && gameObject.CompareTag("Obstacle"))
{
//playerAudio.PlayOneShot(scoreSound, 1);
}
//Destroy object out of bounds
if (transform.position.x < leftBound && gameObject.CompareTag("Obstacle"))
{
Destroy(gameObject);
AddScore();
}
}
void AddScore()
{
//Score goes to 1.
score++;
//prints 1 in console but does not increase over 1.
Debug.Log("Score! Your Score is" + score);
Debug.Log(score);
}
}
Tried: Numerous changes to configuration of ++, x = x +1, x++, x +=, etc. No idea. Am lost.
This is an issue caused by the local member int. You have an object, which has been created and this MoveLeft component is attached to it. You then destroy this object on collision, and therefore this component as well. You’ll have added one to the local instance int score, but then you destroy the component and lose this local instance.
I suspect what you thought was happening is that all the scripts/components shared the same variable values. That would only be true if you if you made the int score member a static int score, which means the member is the same shared amongst all instances of this component.
Instead of making score static, you might want to have a “Game Manager” that exposes public functions to allow you to increment and retrieve the current score.
In my game, i am trying to make a function that if the player gets more than 10 stars, the star spawner would not spawn stars for the next 24 hours. How do i go about this when i cannot use the unity systems function without affecting Random fucntion that spawns the star?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Timers;
public class StarSpawner : MonoBehaviour {
public Transform[] spawnPoints;
public GameObject[] blockPrefab;
public float timeBetweenWaves = 1f;
private float timeToSpawn = 2f;
// Start is called before the first frame update
void Spawn()
{
Instantiate(blockPrefab[Random.Range(0, blockPrefab.Length)], spawnPoints[Random.Range(0,
spawnPoints.Length)]);
}
// Update is called once per frame
void Update()
{
if (Time.time >= timeToSpawn)
{
Spawn();
timeToSpawn = Time.time + timeBetweenWaves;
}
}
I once had a similar problem and then "saved" the current time data, i.e. date and time in PlayerPrefs. I then queried at certain intervals to see if the saved time was longer than the time period ago.
This makes the system work even when the game is closed.
Is perhaps not the most elegant way, but it works.
I'm an absolute newbie in Unity, and I'm trying to create a 2D game that requires a scrolling background that accelerates every 10 seconds. I'm having trouble getting the code to work
I've tried to set up a Coroutine, but it seems to call the function every frame, instead of every 10 seconds
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScrollingBackground : MonoBehaviour
{
public float bgSpeed = 5;
public Renderer bgRend;
public float increment = 2f;
private void Start()
{
StartCoroutine(Accelerate());
}
void Update()
{
bgRend.material.mainTextureOffset += new Vector2(0f, bgSpeed * Time.deltaTime);
StartCoroutine(Accelerate());
}
private IEnumerator Accelerate()
{
while (true)
{
bgSpeed = increment * Time.deltaTime;
yield return new WaitForSeconds(10f);
Debug.Log("Getting Faster!");
Debug.Log("OnCoroutine: " + (int)Time.time);
}
}
}
Not only is the background speed going very slowly (only up to around 0.3 and stuck), I can't seem to make this work. Thank you for you help !
First, you should only start your coroutine once, such as in Start - instead, you're starting it every frame, so the speed is going to get stuck.
Second, is your texture scaled at all? This may affect how it looks when you're adjusting the offset directly, and thus why it appears to be stuck at 0.3 even though your increment is 2f here.
Finally, note that deltaTime is the time since the last frame, so using to adjust your speed rather than adding a constant is going to produce strange results dependent on performance. It's useful when applying the speed to movement, but not the acceleration based on this game logic.
It is calling your function every frame because you start the coroutine inside of Update. See Update scripting reference.
I've a 145 BPM sound with 137 milliseconds offset and I'm trying to sync a really fast animation with it... I was trying this way:
public class ActiveEpilepsy : MonoBehaviour {
public GameObject Epilepsy; //GameObject that carries the animation
float delay = 0.2f; //a fair time to disable the Epilepsy after the animation runs before active again
void Start()
{
InvokeRepeating("activeEpilepsy", 0.137f, 2.416f); //second parameter is the offset of the music and the third is the bpm/60;
}
void activeEpilepsy()
{
while(delay >= 0)
{
Epilepsy.SetActive(true);
delay -= Time.deltaTime;
}
Epilepsy.SetActive(false);
}
}
but doesn't activate the GameObject at all and returns A BEAUTIFUL CRASH in my Unity... Also, people tell that I shouldn't use strings to call methods (Invoke example), so what is wrong with my code and how can I make this works without using an Invoke?
Problem happens here:
void activeEpilepsy()
{
while(delay <= 0)
{
Epilepsy.SetActive(true);
delay -= Time.deltaTime;
}
Epilepsy.SetActive(false);
}
You have a while loop that will run as long as delay is less than 0. And you are decreasing that value inside. So either, delay is more than 0 and the loop does not enter. Or it is lower or equal to 0 and it will never leave.
Since delay is 0.2f, the loop is useless and the object deactivates right away. You need a coroutine or another invoke there:
void activeEpilepsy()
{
Epilepsy.SetActive(true);
Invoke("Reset", this.delay);
}
void Reset(){Epilepsy.SetActive(false);}
That is it for the inactive object problem. The crash is probably elsewhere, I can't see anything running wrong here.
For the method as string, it's not about calling with string, it's about how it is handled. I think (to be confirmed) Invoke uses reflection, so it calls on the assembly manifest to figure out if the method exists. This would be slow. If Unity was smart enough to add the method to a dictionary then calling by string has little to no impact.
I'm going to make a rhythm game.
music playing time is variable which change by music playing in millisecond
List is a float list which have the time(float second) I filled , let me to access it and use for compare with music playing time to instance object .
when list time = music time then instant object.
but sometimes will missing(not found value in list but value is really exist in list).
Here is my assumption.
I think List.indexOf performance are not good and thread have some delay.
Music playing time change every millisecond , a little delay from unity or others will cause missing(doesn't entering if statement)
I don't know if it's correct.
Could any one help me.
Here is my code.
IEnumerator Execute(MethodDelegate Start,MethodDelegate Stop)
{
while (true) {
int res = result.IndexOf ((float)System.Math.Round (GameObject.Find ("Music").GetComponent<AudioSource> ().time, DigitalAdjust)-TimeAdjust);
if (res!=-1) {
if (res == result.Count-2) {
Stop.Invoke ();
print ("CoroutineStop");
StopCoroutine (_Execute);
}
//execute
num=Positoin[res];
print (res);
Start.Invoke();
}
yield return null;
}
}
Thanks.
Chances are that you are correct. You might miss some if statement because you don't match the millisecond exactly. Here are some things that could help:
It you game reaches 60 FPS (the usual rate for a smooth rendering), each frame will take around 16 milliseconds. If you have events that must trigger at exact milliseconds you will miss some because your Execute function calls are separated by around 16ms (a coroutine is called once per frame).
A solution to this is remember the last time the Execute function was called and check everything in between:
private float _lastTime;
IEnumerator Execute(MethodDelegate Start,MethodDelegate Stop)
{
while (true) {
// here your code must check all the event between _lastTime and Time.time
var lastTimeMilliseconds = (int)(_lastTime*1000);
var currentTimeMilliseconds = (int)(Time.time*1000);
for(int time = lastTimeMilliseconds+1; time <= currentTimeMillisedons; time++)
{
// let's say last frame was at time 1000ms
// This frame occurs at time 1016ms
// we have to check your list for events that have occured since last frame (at time 1001, 1002, 1003, ...)
// so we have a for loop starting at 1001 until 1016 and check the list
int res = result.IndexOf ((float)System.Math.Round (time, DigitalAdjust)-TimeAdjust);
if (res!=-1)
{
if (res == result.Count-2)
{
Stop.Invoke ();
print ("CoroutineStop");
StopCoroutine (_Execute);
}
//execute
num=Positoin[res];
print (res);
Start.Invoke();
}
}
// At the end, remember the time:
_lastTime = Time.time;
yield return null;
}
}
Check the Time class, you also have access to Time.deltaTime to know the time elapsed between two frames, if that helps.
EDIT:
As you requested in comment, I added some bit of code from your example to explain better this idea. Note that I don't know what your variables do so you will have to adapt. For instance, Time.time gives that time since app start. You will likely need to adapt this use the time since you started the audio
Another important thing:
GameObject.Find must look in all the objects. It is really slow and shouldn't be used every frame
GetComponent looks for all your scripts and is slow as well. It shouldn't be used every frame.
Instead, do this:
private AudioSource _audioSource;
private void Start()
{
_audioSource = GameObject.Find ("Music").GetComponent<AudioSource> ();
}
This will retrieve the source only once. Then in your code you simply call
_audioSource.time;