Hey, I'm trying to get an array based on the "auto defragmenting array"
Here's the method, (a quote from ImaginaryHuman):
One thing you can try is what I call a `auto-defragmenting array`. You keep a counter of how many items are stored in the array - ie defined particles. This starts at 0. When you add an item you add it at the counter position and then add 1 to the counter. When you remove/kill an item, instead of trying to wipe it out or delete it you copy the item at position `Count-1` to overwrite the item you are deleting, and then subtract 1 from Count. In other words you're taking the item from the top end of the defined items and moving it to fill the gap created by the item you want to remove. The benefit of this is you then can loop from 0 to Count-1 and you'll know that there are no gaps and you don't have to detect dead objects. You really want to avoid having to skip over stuff if you can. One downside is that because you are defragmenting the space and keeping it compact, any kind of `sort order` goes out the window and your particles become somewhat random order. Presumably since particles move all over the place you probably don't usually care that this happens. The only issue might be that the user sees one particle suddenly jump in front of another.
This will be used in my particle engine to (hopefuly) boost speeds. My particle engine is mathematicly updated in C++, and basicly, feeds info into blitzmax to draw the particles. I've taken a whack at this, and it's causing some really wierd behavior.
C++ code, save as "cParticle.cpp" in the same dir as the bmx source.
bmax code:
I'm almost 100% certain the problem isnt with the C++ code, as it worked fine with a TList version of this code, it's something to do with the bmax side of things.
Thanks in advance for any help!
Here's the method, (a quote from ImaginaryHuman):
One thing you can try is what I call a `auto-defragmenting array`. You keep a counter of how many items are stored in the array - ie defined particles. This starts at 0. When you add an item you add it at the counter position and then add 1 to the counter. When you remove/kill an item, instead of trying to wipe it out or delete it you copy the item at position `Count-1` to overwrite the item you are deleting, and then subtract 1 from Count. In other words you're taking the item from the top end of the defined items and moving it to fill the gap created by the item you want to remove. The benefit of this is you then can loop from 0 to Count-1 and you'll know that there are no gaps and you don't have to detect dead objects. You really want to avoid having to skip over stuff if you can. One downside is that because you are defragmenting the space and keeping it compact, any kind of `sort order` goes out the window and your particles become somewhat random order. Presumably since particles move all over the place you probably don't usually care that this happens. The only issue might be that the user sees one particle suddenly jump in front of another.
This will be used in my particle engine to (hopefuly) boost speeds. My particle engine is mathematicly updated in C++, and basicly, feeds info into blitzmax to draw the particles. I've taken a whack at this, and it's causing some really wierd behavior.
C++ code, save as "cParticle.cpp" in the same dir as the bmx source.
#include <math.h> #define DEGREE_TO_RADIAN 0.0174532925199432957692369076848861 //Functions to grab a Radian sin/cos not in degrees. double Sin2( double ang ){ return sin( ang*DEGREE_TO_RADIAN ); } double Cos2( double ang ){ return cos( ang*DEGREE_TO_RADIAN ); } // All the stuff that can be accessed in bmax extern "C" { // our particle class class cParticle { public: // all the variables associated with a particle float x; float y; float a; int r; float dx; float dy; float da; int maxdistance; float speed; int dir; int fade; int z; // constructor (AKA Create()) cParticle(float, float, int, float, int, bool, bool, int); // destructor have not found a use yet ~cParticle(); //Update particle void Update(float, float, float, float); }; // Our class functions redefined: // constuctor cParticle::cParticle(float _x, float _y, int _dir = 0, float _speed = 0, int frames = 1, bool _fade = false, bool _autorot = false, int _z = 0) { z = _z; x = _x; y = _y; speed = _speed; dir = _dir; dx = (Sin2(dir) * speed); dy = (-Cos2(dir) * speed); da = 1.0 / frames; a = 1.0; maxdistance = frames; if(dir > 0&&_autorot > 0){ r = dir; } if(_fade > 0) { fade = true; } } //destructor cParticle::~cParticle() { // code for destructor } void cParticle::Update(float ax,float ay,float fx, float fy) { dx = dx+fx; dy = dy+fy; x=x+dx+ax; y=y+dy+ay; if( fade == true ) { a=a-da; } maxdistance--; } // mainsteam functions: // Wrap function to create a particle cParticle *cCreateParticle(float _x, float _y, int _dir = 0, float _speed = 0, int frames = 1, bool _fade = false, bool _autorot = false, int _z = 0) { cParticle *p; // particle that we will return p = new cParticle(_x, _y, _dir, _speed, frames, _fade, _autorot, _z); // basicly shove the arguments of this function into a constructor return p; } //Wrap function to delete a particle void cDeleteParticle(cParticle *p) { if (p != 0) { delete p; } } //Wrap function to update a particle void cUpdateParticle(cParticle *p, float ax, float ay, float fx, float fy){ if (p != 0){ p->Update(ax, ay, fx, fy); } } //functions to get various states of a particle float cPX(cParticle *p) { if (p != 0){ return p->x; } } float cPY(cParticle *p) { if (p != 0){ return p->y; } } float cPA(cParticle *p) { if (p != 0){ return p->a; } } int cPR(cParticle *p) { if (p != 0){ return p->r; } } int cPLife(cParticle *p) { if (p != 0){ return p->maxdistance; } } }
bmax code:
Import "cParticles.cpp" 'Wrap functions from the particle C++ code Extern "C" Function cCreateParticle:Byte Ptr(_x:Float, _y:Float, _dir:Int = 0, _speed:Float = 0, frames:Int = 1, _fade:Int = False, _autorot:Int = False, _z:Int = 0) Function cUpdateParticle(p:Byte Ptr, ax:Float, ay:Float, fx:Float, fy:Float) Function cDeleteParticle(p:Byte Ptr) Function cPX:Float(p:Byte Ptr) Function cPY:Float(p:Byte Ptr) Function cPA:Float(p:Byte Ptr) Function cPR:Int(p:Byte Ptr) Function cPLife:Int(p:Byte Ptr) End Extern 'TParticle type, basicly a wrapper w/ drawing functions for the class counterpart. Global ParticleList:TParticle[10000] Global pCount:Int Type TParticle Field img:TImage 'particle img... Field cBuddy:Byte Ptr 'the handle for our c++ counterpart. Field z:Int 'an 'ID' that we call on to give us seperate drawing abilities Field pos:Int 'position within the particle array Method Create:TParticle(_x:Float, _y:Float, _img:TImage, _dir:Int = 0, _speed:Float = 0, frames:Int = 1, _fade:Int = False, _autorot:Int = False, _z:Int = 0) 'create a "c" particle and give it's handle to the cbuddy variable cBuddy = cCreateParticle(_x, _y, _dir, _speed, frames, _fade, _autorot, _z) 'Add some given arguments to the object img = _img z = _z alive = True 'Add this object to the particle list pos = pCount ParticleList[pCount] = Self pCount:+1 End Method Method Update(addx:Float, addy:Float, forcex:Float=0, forcey:Float=0) 'Update the "c" particle if the cparticle is real If cBuddy <> Null cUpdateParticle(cBuddy, addx, addy, forcex, forcey) 'Grab the new x/y positions of the "c" particle(so we only have to use this function once per loop) Local x:Float = cPX(cBuddy) Local y:Float = cPY(cBuddy) 'check for conditions for destroying a particle If cPLife(cBuddy) = 0 Or x > GraphicsWidth()+ImageWidth(img) Or x < 0-ImageWidth(img) Or y > GraphicsHeight()+ImageHeight(img) Or y < 0-ImageHeight(img) Destroy() Return End If 'Draw the given img at the "c" Particle's locations SetAlpha cPA(cBuddy) SetRotation cPR(cBuddy) DrawImage img,x,y SetAlpha 1 SetRotation 0 End If End Method Method Destroy() If cBuddy <> Null cDeleteParticle(cBuddy) 'destroy C++ counterpart if the cparticle is real ParticleList[pos] = ParticleList[pCount-1] ' copy newest particle onto dead particle pCount:-1 ' drop count by 1 End Method End Type Rem bbdoc: Particle update routine about: Updates particles at said Z level, you can add a optional force routine for things such as gravity. returns: Nothing EndRem Function UpdateParticlesZ(z:Int = 0, addx:Float = 0, addy:Float = 0, forcex:Float = 0, forcey:Float = 0) For Local i:Int = 0 Until pCount If ParticleList[i].z = z ParticleList[i].Update(addx, addy, forcex, forcey) End If Next End Function Rem bbdoc: Particle creator about: Creates a particle at said point with said attributes returns: Nothing EndRem Function EmitParticle(_x:Float, _y:Float, image:TImage, frames:Int, fade:Int = True, dir:Int = 0, speed:Float = 0, ar:Int = False, _z:Int = 0) Local part:TParticle = New TParticle.Create(_x, _y, image, dir, speed, frames, fade, ar, _z) End Function '----------------------------------------------------------------------------------- 'main program '----------------------------------------------------------------------------------- Graphics 800,600 SetBlend LIGHTBLEND AutoMidHandle True Local dot:TImage = CreateImage(10,10) DrawOval 0,0,10,10 GrabImage dot,0,0 While Not KeyHit(KEY_ESCAPE) Cls If MouseHit(1) EmitParticle(MouseX(), MouseY(), dot, 240)'ParticleExplosion(MouseX(),MouseY(),dot,50,80,2) UpdateParticlesZ() DrawText pCount,0,0 Flip 1 Wend End
I'm almost 100% certain the problem isnt with the C++ code, as it worked fine with a TList version of this code, it's something to do with the bmax side of things.
Thanks in advance for any help!