Does diddy have an Object Pooler?
Monkey Forums/Monkey Programming/Does diddy have an Object Pooler?
| ||
If so, how do I use it? I'm constantly wondering how to deal with large amounts of object creation. Samah/therevills: if diddy does not have something like this can one be implemented and tutoriaslised, please? Thank You! |
| ||
I implemented one for my MonkeyTouch project, and I'm planning on generalising it and adding it to Diddy. Given the way it works, what will probably happen is that you'll need to make your class implement IPoolable and then implement a property that returns whether the item is active (basically just store a boolean field). The pool will purge any inactive items, and shuffle the rest around so that the active objects will always be sequential (even if they're unordered). This way, an enumerator will not need to skip over inactive items. Should be soon, hopefully! |
| ||
That's good news, Samah! Looking forward to the update! |
| ||
Done, go break it. ;) [monkeycode]Class Foo Implements IPoolable Private ' your own field, since IPoolable is an interface Field activeInPool:Bool = False Public ' the pool reads this property when purging Method ActiveInPool:Bool() Property Return activeInPool End ' you should write to this property before purging Method ActiveInPool:Void(activeInPool:Bool) Property Self.activeInPool = activeInPool End ' called by GetObject() Method InitFromPool:Void(arg:Object = Null) ' optionally do stuff with arg End ' called when the object is purged Method PurgedFromPool:Void() End End ' create the pool, with 1000 instances of Foo Local pool:Pool<Foo> = New Pool<Foo>(1000) ' get one object Local hello:Foo = pool.GetObject() ' optionally pass an arg here ' do some stuff with hello ' finished with that instance, set active to false hello.ActiveInPool = False ' object will be purged on the next Sort() or EachIn ' or you can manually purge with pool.Purge()[/monkeycode] Creating a pool will instantiate a bunch of objects automatically. Call GetObject() to get one instance, which increases pool.ActiveCount. If the pool is full, it will return Null. When a purge is called, it shuffles object references such that the active instances are contiguous from 0 until ActiveCount, then updates ActiveCount. An EachIn will first call a purge. Objects can be retrieved during an EachIn as it will not affect the current loop, but a purge will throw a ConcurrentModificationException if any objects were purged. This is to prevent objects from being looped twice, or skipped entirely. To use the sorting feature, you will need to either create a Comparator, or implement IComparable. They work the same way as sorting for ArrayList. |
| ||
You are of course beautiful! :) |
| ||
So have you broken it yet? ;) I'll make an example once I've got this MonkeyTouch stuff out of the way (I'm way past my due date...) |
| ||
Samah, how would I integrate this with a bullet class? I've tried, it compiles and does not throw errors but my implementation is wrong somewhere as nothing seems to show up while everything else does. Oh! Samah! Are you the Sama.Van from the Unity Boards? 3d Artist like I've never seen? |
| ||
Oh! Samah! Are you the Sama.Van from the Unity Boards? 3d Artist like I've never seen? Nope, though I wish I had artistic skills... XD Can you show me a snippet of your code? Here's an example (I haven't tested it): [monkeycode]' define pool Field bulletPool:Pool<Bullet> = New Pool<Bullet>(1000) ' add some test bullets (normally you'd add one of these when something fires) For Local i:Int = 0 Until 100 Local bullet:Bullet = bulletPool.GetObject() If bullet Then bullet.image = someImage bullet.x = Rnd(0, DeviceWidth()) bullet.y = Rnd(0, DeviceHeight()) End Next ' render bullets (EachIn only loops on active bullets, not all 1000) For Local bullet:Bullet = EachIn(bulletPool) bullet.Render() Next Class Bullet Implements IPoolable Private Field activeInPool:Bool Public Field x#, y# Field image:Image Method ActiveInPool:Bool() Property Return activeInPool End Method ActiveInPool:Void(activeInPool:Bool) Property Self.activeInPool = activeInPool End Method InitFromPool:Void(arg:Object = Null) End Method PurgedFromPool:Void() End Method Render:Void() DrawImage(image, x, y) End End[/monkeycode] |
| ||
Hi, If all you want is a way to create/destroy objects, something like this might be enough: Class Pool<T> Method New( initialCapacity:Int=10 ) For Local i:=0 Until initialCapacity _pool.Push New T Next End Method Allocate:T() If _pool.IsEmpty() Return New T Return _pool.Pop() End Method Free:Void( t:T ) _pool.Push t End Private Field _pool:=New Stack<T> End Sample usage... Global bulletPool:=New Pool<Bullet>( 1000 ) Function CreateBullet:Bullet( x,y,etc ) Local bullet:=bulletPool.Allocate() bullet.Create x,y,etc Return bullet End Function DestroyBullet:Void( bullet:Bullet ) bullet.Destroy bulletPool.Free bullet end |
| ||
A simple pool like you've described is fine if all you want to do is reuse objects, but generally with a Bullet class you would want to loop through them so that you can call Update and/or Render on them. That means you would need to store those Allocate()d references in a list or array somewhere else. The point of the Diddy Pool class is to allow reuse of objects, and to keep their order sequential without having to do any array shifting when one of the elements becomes inactive. |
| ||
I'd be inclined to use a separate container for 'active' bullets etc - keeps memory management completely separate, and removes the need for the activeInPool stuff. |
| ||
What kind of container though? If you use a List you have object manipulation (nodes), if you use a Stack or an array, you'll need to mess around with loops when removing items from the middle of the container. |
| ||
Thanks for your example, Samah. I have got it working nice. On a side note are the particles in the particle system pooled also or are they mobile efficient already as they are? |
| ||
The particle system doesn't have a Particle class, it uses parallel float arrays. In my experience in writing particle systems, I've found that 20 arrays of 10000 elements is generally faster and more memory efficient than 10000 objects of 20 fields each. |
| ||
Thanks for the info! Just tried blasting it with 100's of particles and it holds its own great. :) |