[SOLVED] Variable Wave Delays

edited November 2011 in Help request
There's one spawner in my game:
[Spawner]
Object = Enemy
WaveSize = 1
WaveDelay = 0.5
ObjectSpeed = (-150, 0, 0)
CleanOnDelete = True

[EnemySpawner]
Spawner = Spawner


I'd like the spawner to spit out say 3 enemies then decrease the wave delay and increase the object speed. Then once that wave is done, repeat the same after spitting out say 5 enemies.

I tried to accomplish this in my Update function:
...
void orxFASTCALL StandAlone::Update(const orxCLOCK_INFO* clockInfo, void* context)
{
	for(orxOBJECT *pstObj = orxOBJECT(orxStructure_GetFirst(orxSTRUCTURE_ID_OBJECT)); pstObj != orxNULL; pstObj = orxOBJECT(orxStructure_GetNext(pstObj)))
	{
		std::string ObjectName = orxObject_GetName(pstObj);
		if((ObjectName.compare("EnemySpawner") == 0))
		{
			orxSPAWNER* pstSpawner = orxOBJECT_GET_STRUCTURE(pstObj, SPAWNER);
			if((totalSpawned == 3) {
				orxSpawner_SetWaveDelay(pstSpawner, 0.5);}
		}
	}
}

On object create of type "enemy" I increase the totalSpawned counter. This just produces 3 enemies and stops.

Comments

  • edited November 2011
    Hi,

    Are you sure that your spawner description is like you mentionned here ?

    Just by looking your code it seems it will be working (spawner shouldn't be stopped), or I didn't see the mistake.

    But, I don't understand why you set the WaveDelay to 0.5 because it's alreay the value stored in your config.

    If there's only 3 spawned objects, maybe you have a "TotalObject" properties in your spawner description (in the .ini) ?

    If you comment you C code, what happens ? Only 3 spawn or more ?

    Suggestion : why you didn't keep a reference on your spawner instead of parsing all object to get it ? It will be more efficient.

    One remarks more, instead of counting spawned object with your totalSpawned var, I suggest you to use the orxSpawner_GetTotalObjectCounter, I think it's what you want and it's more clean.

    Cheers !
  • edited November 2011
    But, I don't understand why you set the WaveDelay to 0.5 because it's alreay the value stored in your config.
    Oh that's because I was fiddling around with it and never changed it back to WaveDelay = 1 when I c&p'd.
    If there's only 3 spawned objects, maybe you have a "TotalObject" properties in your spawner description (in the .ini) ? If you comment you C code, what happens ? Only 3 spawn or more ?
    Nope. There are no TotalObject properties anywhere.

    Commenting bits out links the problem to: orxSpawner_SetWaveDelay(pstSpawner, 0.5); When the code hits this in Update() the spawner stops altogether.
    why you didn't keep a reference on your spawner instead of parsing all object to get it ? It will be more efficient. One remarks more, instead of counting spawned object with your totalSpawned var, I suggest you to use the orxSpawner_GetTotalObjectCounter, I think it's what you want and it's more clean.
    You're absolutely right!
  • edited November 2011
    Hmm,

    Have you got a "ActiveObject = 3" maybe ? This means that the spawner stop spawning until your enemies die.
    Commenting bits out links the problem to: orxSpawner_SetWaveDelay(pstSpawner, 0.5); When the code hits this in Update() the spawner stops altogether.

    Because my english sucks a little, I'm not sure to understand. Spawner stop when commenting the orxSpawner_SetWaveDelay(...) or not ?
    If yes, try to change your 0.5 to orx2F(0.5). I think it will not solve the problem, but you can try to modify that.
  • edited November 2011
    Pas de changement.

    It's really this line; I can't seem to set any of the Spawner properties:
    orxSpawner_SetWaveDelay(pstSpawner, 0.5);

    I'm doing this simpler code for testing and my spawner doesn't give me any enemies at all:
    void orxFASTCALL StandAlone::Update(const orxCLOCK_INFO* clockInfo, void* context) {
    orxOBJECT* pstObject = GetObjectByName("EnemySpawner");
    orxSPAWNER* pstSpawner = orxOBJECT_GET_STRUCTURE(pstObject, SPAWNER); 	
    orxSpawner_SetWaveDelay(pstSpawner, orx2F(0.5));}
    


    Spawner in my .ini is still just:
    [Spawner]
    Object = Enemy
    WaveSize = 1
    WaveDelay = 1.5
    ObjectSpeed = (-80, 0, 0)
    CleanOnDelete = True

    [EnemySpawner]
    Spawner = Spawner
  • edited November 2011
    Oh, hahaha so silly!

    Update() was being called wayyy too fast so it was resetting itself over and over. I'm going to move this to the Spawner event handler instead.

    4:00a me isn't the brightest crayon in the box sometimes.
  • edited November 2011
    Aaaahhhhhh !!

    Sure ! It was so easy ... I didn't see that !
  • edited November 2011
    The easiest things are the easiest to miss. Thanks for your help anyway!
  • edited November 2011
    I see you already got the assistance of the "roi du spawner", but I still have a trick up my sleeve. :)

    There's a much more efficient way of doing what you want. There are many spawner events to which you can listen and act upon the spawner directly.
    One of them is orxSPAWNER_EVENT_SPAWN, which is fired everytime an object is spawned. It also contains both the spawned object and the spawner, so no need to crawl through all your objects to update your spawner, it's right there. If you were to create objects "Enemy" from anywhere else, this also has the advantage that you could track them separately, on a per spawner basis.

    You can also listen to orxSPAWNER_EVENT_WAVE_STOP, if you wanted to act whenever a wave is over (useful if you decide to spawn more than 1 Enemy per wave).

    Lastly, it you set a TotalObject limit on your spawner, no need anymore to track the number of spawned objects manually.

    In config, you setup your TotalLimit to 3. In your code you listen to the orxSPAWNER_EVENT_EMPTY, whenever you hit that event, you can then set your spawner limit to something else, update the wave delay and then reset it (orxSpawner_Reset()).

    If you're very lazy like me, you can also add the AutoReset property that will call the reset for you, and instead of the empty event, you can listen to orxSPAWNER_EVENT_RESET.

    The event approach has the advantage of not crawling through all the objects in your Update and only do the bare minimum for your spawner logic.

    Something like:
    Config:
    
    [Spawner]
    Object = Enemy
    WaveSize = 1
    WaveDelay = 1.5
    ObjectSpeed = (-80, 0, 0)
    CleanOnDelete = True
    AutoReset = true
    
    
    ; Now some user-only config for your spawner logic
    
    StageList = 3 # 2 # 0; All the different spawner stages, the 0 means we don't set any more stages after that
    DelayList = 1.0 # 0.5 # 0.2; When reaching 3 objects-> Delay = 1.0, 2 more objects -> 0.5, etc...
    SpeedList = (-100, 0, 0) # (-120, 0, 0) # (-150, 0, 0); Same logic for the speeds
    
    [EnemySpawner]
    Spawner = Spawner
    
    In code:
    
    You have to keep an Index somewhere in your code for your spawner, let's assume it's global here and called u32StageIndex
    
    orxSTATUS orxFASTCALL SpawnerEventHandler(const orxEVENT *_pstEvent)
    {
      if((_pstEvent->eType == orxEVENT_TYPE_SPAWNER)
      && (_pstEvent->eID == orxSPAWNER_EVENT_RESET))
      {
        orxVECTOR vTemp;
        orxSPAWNER *pstSpawner = orxSPAWNER(_pstEvent->hSender);
    
        orxConfig_PushSection(orxSpawner_GetName(pstSpawner));
    
        // Updates spawner
        orxSpawner_SetTotalObjectLimit(pstSpawner, orxConfig_GetListU32("StageList", u32StageIndex));
        orxSpawner_SetWaveDelay(pstSpawner, orxConfig_GetListFloat("DelayList", u32StageIndex));
        orxSpawner_SetObjectSpeed(pstSpawner, orxConfig_GetListVector("SpeedList", u32StageIndex, &vTemp));
    
        // Updates stage index
        u32StageIndex++;
    
        orxConfig_PopSection();
      }
    
      return orxSTATUS_SUCCESS;
    }
    
    Note that orxSpawner_SetTotalObjectLimit() with 0 actually removes the limit, so no more empty/auto-reset will happen.
Sign In or Register to comment.