Creating and deleting objects with/in clock

edited February 2011 in Help request
Well, I want to create objects in random place and random time. I'm using one main clock, which calls function for creating objects.

In this function, I create object and clock for it, and after one clock I want this object to be deleted.

Problem is, that application runs for few seconds, create and delete few objects and then crash.

My code with some comments for bette explanation.
orxSTATUS orxFASTCALL Game::Algorithm(){
		//create main clock
	orxCLOCK *clockScena = orxClock_Create(1, orxCLOCK_TYPE_SECOND);
	
	orxClock_Register(clockScena, Game::Create, NULL, orxMODULE_ID_MAIN, orxCLOCK_PRIORITY_NORMAL);
	
	return orxSTATUS_SUCCESS;
}

void orxFASTCALL Game::Create(const orxCLOCK_INFO* clockInfo, void* context){
	if (pocetKrtku < MAX) { //if there already isn't 3 objects
		orxOBJECT *krtek;
		
		int position = Tools::RandomNumber(9) + 1;
		
			//create object with entered positon (select vector with switch inside)
		krtek = Krtek::Create(position); //Create function returns orxOBJECT so I can work with it
		
		orxCLOCK *krtekClock = orxClock_Create(2, orxCLOCK_TYPE_SECOND); //create lock for just created object
		
		orxClock_Register(krtekClock, Game::SmazKrtka, krtek, orxMODULE_ID_MAIN, orxCLOCK_PRIORITY_NORMAL); //register it
	
		pocetKrtku++; //Number of objects increased
		
		orxLOG("Krtek %s vytvořen", orxObject_GetName(krtek)); //Object was created
	}
	
	return;
}

void orxFASTCALL Game::SmazKrtka(const orxCLOCK_INFO* clockInfo, void* context){
	orxOBJECT* krtek = orxOBJECT(context);
	orxCLOCK *clock = orxClock_GetFromInfo(clockInfo);
	orxClock_Unregister(clock, NULL); //unregister this clock
	orxClock_Delete(clock); //delete it
	orxObject_Delete(krtek); //then delete object
	pocetKrtku--; //Number of objects decreased
	
	orxLOG("Krtek %s smazán", orxObject_GetName(krtek)); //Object was deleted
}


Maybe is my logic completely wrong as I have no experience with game creating and ORX, but I would really appreciate if someone will be able to help me :blush:

Comments

  • edited February 2011
    Well, the issue here is that you try to delete a clock while in its callback and that won't work (delete will return orxSTATUS_FAILURE). So your clock will continue to run, but the next time it calls the callback, your object has already been deleted, so you're accessing a dangling pointer -> crash.

    I'm not sure why you want to use that many clocks here. You can register more than one callback on a single clock (actually as many as you want). Furthermore I think you're trying to emulate a timer with a clock. Clocks are here for frequent jobs that will be updated throughout your entire game (like ticking your objects, your UI, etc...).
    Most of the time, reusing the core clock that already exists is far enough.
    If you want to have a function called only a couple of times in the future, use orxClock_AddTimer() / orxClock_AddGlobalTimer() that will do exactly this. This will actually wait for the time you give it and use the clock's tick size as a resolution for this time. So if you have a clock ticked 10 times per second and, admitting you create your time on the tick, if you ask a timer for 0.15s, it'll actually be called at 0.20s, on the second tick from now.

    Now, in your particular case, random position is done automatically in config: if you specify a list of positions or boundaries using the operator ~, each time a new object gets created it'll have a random position.

    If you want to maintain a permanent number of objects, I suggest using a spawner, otherwise you can simply listen to the orxOBJECT_EVENT_DELETE to update your counter and given a random delay for your orxClock_AddGlobalTimer().

    Here's an example:
    config:
    [MyObject]
    Position = (0, 0, 0) # (100, 0, 0) # ...
    LifeTime = 1 ~ 3; <= Each object will leave between 1 and 3 seconds
    
    [MyMainObject]
    Spawner = MySpawner
    
    [MySpawner]
    Object = MyObject
    WaveSize = 1; <= Here we specify we only create only one object every time we spawn
    WaveDelay = 1 ~ 5; <= random delay between 1 and 5 seconds between two spawn tries
    ActiveObject = 3; <= Never have more than 3 objects alive at once
    
    code:
    orxObject_CreateFromConfig("MyMainObject");
    

    And that's all. You'll get objects of type 'MyObject' created every 1 to 5 seconds, never more than 3 at once and they'll leave for 1 to 3 seconds.

    You can find more helpful fields for config in the CreationTemplate.ini (and SettingsTemplate.ini) files.

    Hope this helps. If not let me know what part is obscure. :)

    EDIT: Careful as the first time I wrote the post I used the config attribute TotalObject (total absolute number of spawned objects) instead of ActiveObject (max number of objects alive at once). :)
  • edited February 2011
    Wow, thats awesome, still more in love with siplicity and functionality of ORX!

    Configurating random spawning of object only in config is really cool BUT what if I need to speed up object spawning and shorten time before object is deleted (speed up whole game)?

    (I know, I'm quite sticky :blush: )
  • edited February 2011
    Ahah no worries, I'm here for that and I'm sure this info will help other people too. :)

    Well, you can interact with your spawner directly in code. All you have to do is get a pointer to it. Again you have more than one option to your convenience:
    - create the spawner manually and set it manually on the parent object, but you'll be responsible of deleting it after unlinking it from your parent object, too much code for a lazy guy like me ;)
    - listen to the event orxSPAWNER_EVENT_CREATE to get the pointer to the spawner (only with latest svn version :))
    - or my personal favourite:
    orxOBJECT *pstParentObject = orxObject_CreateFromConfig("MyMainObject");
    orxSPAWNER *pstSpawner = orxOBJECT_GET_STRUCTURE(pstParentObject, SPAWNER);
    

    You can now change the spawner as much as you want, with functions such as orxSpawner_SetWaveDelay().

    EDIT: Just for the sake of being exhaustive, you could go with more convoluted solutions such as adding a clock in config to your main object (the one that also contains the spawner), get that in the same way I get the spawner and apply a time modifier on it which will make the parent object act slower or fast depending on your time modified (it'll affect its animation, spawner, physics and even sound! ;) ). But again, it's a convoluted solution. :)
  • edited February 2011
    Well, thank you for exhaustive answers. I'll try it out today.
  • edited February 2011
    Found two little problems.

    1) is there a way (I know there is) to protect creating objects at same place? As I have defined 9 positions, there's pretty big chance to create object at same place as previous object.

    2) is there a way, how to connect spawner with clock? If I create spawner and connect objct with a clock like this, it's just object when I get it from context
    orxOBJECT *spawner = orxObject_CreateFromConfig("SpawnerObject");
    orxCLOCK *clockScena = orxClock_Create(0.5f, orxCLOCK_TYPE_USER);
    orxClock_Register(clockScena, Game::Update, spawner, orxMODULE_ID_MAIN, orxCLOCK_PRIORITY_NORMAL);
    
    And if I use
    orxSPAWNER *spawner = orxSpawner_CreateFromConfig("SpawnerObject");
    
    it doesn't spawn objects. :huh: I want to use it with clock so I can speed it up if I want to.

    And at last, am I right, that if I want to interact with spawner like when object dissapears after it's time, I have to use event handlers?
  • edited February 2011
    Kenn wrote:
    Found two little problems.

    1) is there a way (I know there is) to protect creating objects at same place? As I have defined 9 positions, there's pretty big chance to create object at same place as previous object.

    Yes, you're right, you'll have to come with your own system to prevent this. Like maintaining your own array of free positions, listening to the orxOBJECT_EVENT_CREATE event and set the created object at a free position (while removing it from the list). Then when the object is deleted (orxOBJECT_EVENT_DELETE) you can mark that position as free again. There's an in-place dynamic container in orx called orxBANK (which is a memory chunk bank).
    2) is there a way, how to connect spawner with clock? If I create spawner and connect objct with a clock like this, it's just object when I get it from context
    orxOBJECT *spawner = orxObject_CreateFromConfig("SpawnerObject");
    orxCLOCK *clockScena = orxClock_Create(0.5f, orxCLOCK_TYPE_USER);
    orxClock_Register(clockScena, Game::Update, spawner, orxMODULE_ID_MAIN, orxCLOCK_PRIORITY_NORMAL);
    
    And if I use
    orxSPAWNER *spawner = orxSpawner_CreateFromConfig("SpawnerObject");
    
    it doesn't spawn objects. :huh: I want to use it with clock so I can speed it up if I want to.

    Not directly. The only way to connect them easily is through a container orxOBJECT. Otherwise you can use a spawner directly but it won't do wave synchronization by itself.
    And at last, am I right, that if I want to interact with spawner like when object dissapears after it's time, I have to use event handlers?
    Yes, events are the most easy way to get notified about everything happening in orx's world: spawners, objects, sounds, FXs, anims, etc...
  • edited February 2011
    I was quite busy today, so I only sat for half and our and tried some magic with orxBANK, but I have problem even with this :unsure:

    I probably can create it well, but how to insert some data into it as there are no set functions or operators defined?
    orxBANK *bank = orxBank_Create(9, sizeof(orxVECTOR), orxBANK_KU32_FLAG_NONE, orxMEMORY_TYPE_MAIN);
    
    orxVECTOR *vector;
    orxVector_Set(vector, -160, -180, 0);
    

    My point is, to store vector in bank.
  • edited February 2011
    Ah yes, sorry, didn't explain how sorting and containers work in orx. As the most important consideration behind them is performance (both in term of memory fragmentation and processing time), they behave like what you can typically find in OS or in old 16bit era code and not like STL-like modern containers which are primarily built for convenience and ease of use.

    So in orx, the only memory container is orxBANK. One bank can only hold structures of the same size and will guarantee that all allocated memory will be in-place (allowing you to safely keep pointer on them, which you couldn't do on a STL vector for example, as reallocation will happen when you outgrow the current capacity). The size of a segment, in your case 9, means that everytime we run out of space, we'll allocate a new segment of this size (unless the bank is explicitly created with a non-extendable flag). If there's enough room, no memory allocation will take place, it will simply use the first empty slot (a compact bit-map is used for keeping track of available slots in each segment).
    In your case, you should do this:
    orxVECTOR *vector = (orxVECTOR *)orxBank_Allocate(bank);
    // And when you're done with it and want to release it
    orxBank_Free(bank, vector);
    

    Now if you need actual sorting you have two options in orx: linked list or trees. They are very similar to the linux kernel lists (there's a post about it here): you need to insert a node in your own structure that will be used to store info about its position in the list/tree. If you only need your structure to be part of only one list/tree at a time, I'd recommend putting it as first field so that you can cast it directly instead of having to use the offsetoff macro.
    Example:
    struct MyVector
    {
      orxLINKLIST_NODE stNode;
      orxVECTOR vValue;
    };
    
    static orxLINKLIST stVectorList;
    
    [...]
    
    // Clears the list prior to the first use
    orxMemory_Zero(&stVectorList, sizeof(orxLINKLIST));
    
    struct MyVector v;
    
    // You can use all the orxLinkList_* functions on them.
    orxLinkList_AddEnd(&stVectorList, &v.stNode);
    etc...
    

    And, of course, you can use STL/boost or any kind of containers if you want, there's no restriction! I was simply pointing out how things were stored and ordered in orx for a good trade-off between memory fragmentation and performances.

    Hope this helps! :)
Sign In or Register to comment.