Let's say I have two scripts:
- SpawnManager
- Enemy
In SpawnManager
, I have the function SpawnEnemyWave
that should instantiate 3 enemies, if the random number generator is lower than 5, then one of them should have a higher movement speed, the other shouldn't move at all.
In SpawnManager
:
bool toughEnemy = true;
int waveNumber = 3;
float randomNumber = Random.Range(0, 10);
void Start()
{
SpawnEnemyWave(waveNumber);
}
void SpawnEnemyWave(int enemiesToSpawn)
{
float randomNumber = Random.Range(0, 10);
print(randomNumber);
for (int i = 0; i < enemiesToSpawn; i )
{
if ((randomNumber < 5) && toughEnemy)
{
print("Tough");
Instantiate(enemyPrefab, GenerateSpawnPosition(), enemyPrefab.transform.rotation);
toughEnemy = false; //I make sure there is only one tough enemy per wave
}
else
{
print("Weak");
Instantiate(enemyPrefab, GenerateSpawnPosition(), enemyPrefab.transform.rotation);
}
}
}
In Enemy
, I'm checking if the toughEnemy
variable is set to true to modify the enemy speed before the instantiation, I put those if statements in the start function because I think than when an enemy is instantiated is when it is called.
void Start()
{
spawnManager = GameObject.Find("Spawn Manager").GetComponent<SpawnManager>();
if (spawnManager.toughEnemy)
{
speed = 1;
print("Speed " speed);
}
else
{
speed = 0;
print("Speed " speed);
}
}
The problem is, when the random number is 0 in the logs i see this...
- random number:0
- Tough (the i in the for loop is 0)
- Weak (the i in the for loop is 1)
- Weak (the i in the for loop is 2)
- speed 0
- speed 0
- speed 0
And what I was expecting was something like below, because I'm modifying the variable in the SpawnManager
script first before instantiating the enemy.
- random number:0
- Tough (the i in the for loop is 0)
- speed 1
- Weak (the i in the for loop is 1)
- speed 0
- Weak (the i in the for loop is 2)
- speed 0
What am I missing here?
CodePudding user response:
Timing. You’re partly correct thinking that Start
will be called when the object is instantiated. But, it will be called when the next frame starts. In your current loop, you’re setting up the objects to be instantiated, then you set the toughEnemy
to true. When the next frame starts, all the enemies think that a tough enemy has been set, and the output you see is correct.
If you want the manager to control the enemies, I’d personally include something like a Setup
method, called by the manager. For example, in the Enemy
script:
public bool isSetup { get; private set; }
public bool isTough { get; private set; }
void Setup(bool tough)
{
if ( isSetup ) return;
isSetup = true;
isTough = tough;
speed = tough ? 1 : 0;
}
Then, when you instantiate the enemy in your manager, do something like:
for (int i=0; i<enemiesToSpawn; i )
{
var enemy = Instantiate( enemyPrefab, GenerateSpawnPosition(), enemyPrefab.transform.rotation );
var e = enemy.GetComponent<Enemy> ( );
if ((randomNumber < 5) && toughEnemy)
{
print("Tough");
toughEnemy = false; //I make sure there is only one tough enemy per wave
e.Setup(true);
}
else
{
print("Weak");
e.Setup(false);
}
}
Here’s the life cycle of a ‘frame’
Notice that the Update
method processing occurs about in the middle of the frame lifecycle. You’re Instantiating 3 objects in the same Update method. THEN … the frame ends, and at the beginning of the next frame, the Start
event for each of the new objects is triggered.
CodePudding user response:
I would do this by putting the toughEnemy flag in the Enemy script itself. I would then add a SetTough() method to Enemy, which I would call from SpawnManager via GetComponent() after the Enemy is instantiated.