I've been searching like a bloodhound through forum archives for some working source code of a particle explosion. Does anyone think they could point me towards a tutorial or just provide some code directly? I'll keep searching but I think human's understand what I'm looking for better than the search algorithm does.
Create a particle struct or class containing x, y, vel, dir, color. Array or vector containing the amount of particles you want in the explosion.\ When explosion happens For each particle give particle random dir give particle a vel After explosion happens For each particle update particles position (calculate xS and yS from vel and dir)
Blargmob's basic approach is good, though I'd recommend that you dispense with the array and just simply maintain a big list of "graphical objects" in your program, all of which can have individual velocity, acceleration, lifetimes, and so on.
In my recent projects, having every sprite/etc. on the screen be considered a "Particle" and treated the same has simplified rendering and updating significantly, at the expense of slightly more memory and CPU usage (that is not an issue at all on a PC built this century).
If you use the hardware accelerated approach, blargmob's method is better, since you don't have to switch textures between each particle, leading to enormous speed benefits.
You could look at the sources of these demos I guess.
That's assuming all of your particles in the system are the same texture, and your "big list" isn't optimized to provide similar benefits.
In my case, neither of those assumptions are true, but I agree, mileage may vary.
I appreciate your replies, but I have to admit that I don't know what you are all talking about.
I tried the sources of the demos you provided a link to, but they are all way to complex for me to understand with my limited knowledge of this particular aspect of programming.
Is it difficult to write the code for a simple explosion? I learn best from working code. I heard that there are simple examples somewhere, but I haven't been able to locate them.
Thanks again for all your help so far.
I learn best from working code.
I think you'd learn even better by trying. Write some thoughts down on how you think it would be done and then try to code it yourself. When you get stuck at this point, then come here and show your work.
I have to admit that I don't know what you are all talking about.
First learn what we say. Then try to code it.
Thanks for the advice, but I'm still going to go with learning by example.
I believe the phrase goes something like, "Why reinvent the wheel?" 
I'd really appreciate it if someone would post something that I could learn from. I want to learn. I want to.........LEARN!!! :p
Try something like this.
| 1 | //define a particle structure |
| 2 | typedef struct particle { |
| 3 | //we'll need an x and y position for each particle |
| 4 | double x, y; |
| 5 | //the particles are going to be moving, |
| 6 | //so here's x velocity and y velocity |
| 7 | double xvel, yvel; |
| 8 | //let's also keep track of the old |
| 9 | //x and y positions so we can draw the |
| 10 | //particles as lines instead of just points |
| 11 | double oldx, oldy; |
| 12 | }; |
| 13 | //create a vector of pointers to particles (kind of like an array) |
| 14 | vector<particle*> particles; |
| 15 | /* |
| 16 | explode: a function to make a particle explosion |
| 17 | parameters: |
| 18 | x: the horizontal position of the explosion's center |
| 19 | y: the vertical position of the explosion's center |
| 20 | num: number of particles in the explosion |
| 21 | */ |
| 22 | void explode(double x, double y, int num) { |
| 23 | //initializes the random number generator with the current time as the seed |
| 24 | srand(time(0)); |
| 25 | //for loop to create num particles |
| 26 | for(int i=0; i<num; i++) { |
| 27 | //allocates memory for our new particle |
| 28 | //note that this is a pointer to a particle, not the particle itself |
| 29 | particle* p = (particle*)malloc(sizeof(particle)); |
| 30 | //set x, y, oldx, and oldy to the center of the explosion |
| 31 | (*p).x = (*p).oldx = x; |
| 32 | (*p).y = (*p).oldy = y; |
| 33 | //set xvel and yvel to random numbers between -4 and 4 |
| 34 | (*p).xvel=rand()%4-rand()%4; |
| 35 | (*p).yvel=rand()%4-rand()%4; |
| 36 | //add the particle pointer to our vector |
| 37 | particles.push_back(p); |
| 38 | } |
| 39 | } |
| 40 | /* |
| 41 | stepParticles: a function to step the particles through one frame of movement |
| 42 | parameters: none |
| 43 | notes: |
| 44 | -call this function every time you go through your game loop. |
| 45 | -remember that particles is a vector of pointers, so you have to dereference them! |
| 46 | */ |
| 47 | void stepParticles(){ |
| 48 | //iterate through our vector of particle pointers |
| 49 | //particles.size() gets the total number of elements in the vector |
| 50 | for(int i=0; i<particles.size(); i++){ |
| 51 | //set oldx and oldy to the current values of x and y |
| 52 | (*particles.at(i)).oldx=(*particles.at(i)).x; |
| 53 | (*particles.at(i)).oldy=(*particles.at(i)).y; |
| 54 | //now we add xvel and yvel to x and y so the particles move |
| 55 | (*particles.at(i)).x+=(*particles.at(i)).xvel; |
| 56 | (*particles.at(i)).y+=(*particles.at(i)).yvel; |
| 57 | } |
| 58 | } |
| 59 | /* |
| 60 | drawParticles: a function to draw the particles on the screen |
| 61 | parameters: none |
| 62 | notes: call this function every time you go through your game loop. |
| 63 | */ |
| 64 | void drawParticles() { |
| 65 | //iterate through the vector like last time |
| 66 | for(int i=0; i<particles.size(); i++){ |
| 67 | //you'll have to have a BITMAP named buffer for this to work, |
| 68 | //assuming you're double buffering |
| 69 | line(buffer, |
| 70 | (*particles.at(i)).x, (*particles.at(i)).y, |
| 71 | (*particles.at(i)).oldx, (*particles.at(i)).oldy, |
| 72 | makecol(255,127,0)); |
| 73 | //it spans many lines to make it easier to read |
| 74 | } |
| 75 | } |
| 76 | //an example of your game loop |
| 77 | while(!key[KEY_ESC]){ |
| 78 | clear_keybuf(); |
| 79 | if(key[KEY_SPACE]){ |
| 80 | srand(time(0)); |
| 81 | //make explosion at random place on the screen |
| 82 | explode(rand()%SCREEN_W, rand()%SCREEN_H, 20); |
| 83 | } |
| 84 | clear(buffer); |
| 85 | stepParticles(); |
| 86 | drawParticles(); |
| 87 | blit(buffer,screen,0,0,0,0,buffer->w,buffer->h); |
| 88 | rest(20); |
| 89 | } |
I didn't test it, but that should get you started. If you want images attached to your particles, you may not need oldx or oldy, they're just for drawing the line.
Correct me if I'm missing anything, but I think this code needs to include allegro.h, ctime, math.h, and vector.
Let us know how the learning goes!
And a good way to learn is by doing... Blargmob gave you an algorithm you could implement in your very first reply. Translating it to code shouldn't be too difficult if you just try. Here's an example based on his algorithm (untested, uncompiled) :
| 1 | // |
| 2 | |
| 3 | #include <vector> |
| 4 | #include <cmath> |
| 5 | #include <allegro.h> |
| 6 | |
| 7 | |
| 8 | class Particle { |
| 9 | public : |
| 10 | int x,y;// display position |
| 11 | float fx,fy;// accurate position |
| 12 | float fvx,fvy;// component velocities |
| 13 | float fspeed;// absolute speed |
| 14 | float direction;// angle of travel in radians |
| 15 | int color; |
| 16 | unsigned int num_updates_allowed; |
| 17 | unsigned int updates_left; |
| 18 | |
| 19 | Particle() : x(0) , y(0) , fx(0.0f) , fy(0.0f) , |
| 20 | fspeed(0.0f) , direction(0.0f) , color(makecol(0,0,0)) , num_updates_allowed(0) {} |
| 21 | |
| 22 | void SetPersistence(unsigned int num_updates) { |
| 23 | num_updates_allowed = num_updates; |
| 24 | updates_left = num_updates_allowed; |
| 25 | } |
| 26 | |
| 27 | void SetColor(int newcolor) {color = newcolor;} |
| 28 | |
| 29 | void SetPos(float xpos , float ypos) { |
| 30 | fx = xpos; |
| 31 | fy = ypos; |
| 32 | x = (int)fx; |
| 33 | y = (int)fy; |
| 34 | } |
| 35 | |
| 36 | void SetMovement(float speed , float angle_radians) { |
| 37 | if (speed < 0.0f) {speed = -speed;} |
| 38 | fspeed = speed; |
| 39 | direction = angle_radians; |
| 40 | fvx = fspeed*cos(angle_radians); |
| 41 | fvy = fspeed*sin(angle_radians); |
| 42 | } |
| 43 | |
| 44 | unsigned int UpdatePos() { |
| 45 | if (updates_left) { |
| 46 | fx += fvx; |
| 47 | fy += fvy; |
| 48 | x = (int)fx; |
| 49 | y = (int)fy; |
| 50 | --updates_left; |
| 51 | } |
| 52 | return updates_left; |
| 53 | } |
| 54 | |
| 55 | void Draw(BITMAP* bmp) { |
| 56 | putpixel(bmp , x , y , color); |
| 57 | } |
| 58 | }; |
| 59 | |
| 60 | class Explosion { |
| 61 | public : |
| 62 | std::vector<Particle*> particles; |
| 63 | unsigned int particle_count; |
| 64 | |
| 65 | Explosion(unsigned int num_particles , int ex , int ey) { |
| 66 | Particle* p = NULL; |
| 67 | float basespeed = 5.0f; |
| 68 | float pspeed = 0.0f; |
| 69 | float angle = 0.0f; |
| 70 | particle_count = 0; |
| 71 | for (unsigned int i = 0 ; i < num_particles ; ++i) { |
| 72 | p = new Particle; |
| 73 | if (p) { |
| 74 | p->SetPos((float)ex + 0.5f , (float)ey + 0.5f); |
| 75 | pspeed = basespeed + (float)((rand() % 5) - 2); |
| 76 | angle = ((float)(rand() % 3600))/10.0f; |
| 77 | angle = angle*(M_PI/180.0f) |
| 78 | p->SetMovement(pspeed , angle); |
| 79 | |
| 80 | p->SetPersistence(125 + rand()%50); |
| 81 | p->SetColor(makecol(128 + rand()%128 , 128 + rand()%128 , 64)); |
| 82 | |
| 83 | particles.pushback(p); |
| 84 | ++particle_count; |
| 85 | } |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | ~Explosion() { |
| 90 | std::vector<Particle*>::iterator it; |
| 91 | for (it = particles.begin() ; it != particles.end() ; ) { |
| 92 | if (*it) {delete (*it);} |
| 93 | } |
| 94 | particles.clear(); |
| 95 | } |
| 96 | |
| 97 | unsigned int UpdateExplosion() { |
| 98 | std::vector<Particle*>::iterator it; |
| 99 | for (it = particles.begin() ; it != particles.end() ; ) { |
| 100 | if ((*it)->UpdatePos()) {// check the return value of Particle::UPdatePos to |
| 101 | // see if there are any updates left for this particle |
| 102 | ++it; |
| 103 | } else { |
| 104 | delete *it; |
| 105 | it = particles.erase(it); |
| 106 | --particle_count; |
| 107 | } |
| 108 | } |
| 109 | return particle_count; |
| 110 | } |
| 111 | |
| 112 | void Draw(BITMAP* bmp) { |
| 113 | std::vector<Particle*>::iterator it; |
| 114 | for (it = particles.begin() ; it != particles.end() ; ) { |
| 115 | (*it)->Draw(bmp); |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | private : |
| 120 | |
| 121 | }; |
| 122 | |
| 123 | |
| 124 | // |
And a quick example of usage :
| 1 | unsigned int num_particles = 100; |
| 2 | Explosion* exp = new Explosion(num_particles , SCREEN_W/2 , SCREEN_H/2); |
| 3 | if (!exp) {return 0;} |
| 4 | |
| 5 | clear_keybuf();readkey(); |
| 6 | |
| 7 | bool stop = false; |
| 8 | while (!stop) { |
| 9 | clear_to_color(buffer , makecol(0,0,0)); |
| 10 | exp->Draw(buffer); |
| 11 | blit(buffer , screen , 0 , 0 , 0 , 0 buffer->w , buffer->h); |
| 12 | if (exp->Update() == 0) { |
| 13 | stop = true; |
| 14 | } else { |
| 15 | rest(25); |
| 16 | } |
| 17 | } |
| 18 | |
| 19 | delete exp; |
| 20 | clear_keybuf();readkey(); |
| 21 | |
| 22 | return 0; |
(*p).x = (*p).oldx = x;
perhaps going p->x = p->oldx = x; would actually be clearer.
Edit:
Memory allocations are quite expensive, so you could use an array. If the array is full and you need to insert a new particle, you can over-write the oldest.
Particles lend themselves to C++, with a pixel class, but I did my example in C. You could easily roll this up into a class, but I am in a C mood. (The code, binary and code::blocks project is attached.)
| 1 | #include <allegro.h> |
| 2 | |
| 3 | typedef struct |
| 4 | { |
| 5 | float x, y; |
| 6 | float dx, dy; |
| 7 | unsigned int alive; |
| 8 | }PARTICLE; |
| 9 | |
| 10 | void particle_init (PARTICLE *p); |
| 11 | PARTICLE particle_create(float _x, float _y); |
| 12 | void particle_update(PARTICLE *p); |
| 13 | void particle_render(PARTICLE p, BITMAP *buffer); |
| 14 | |
| 15 | void manager_init (PARTICLE *particle, int count); |
| 16 | void manager_update(PARTICLE *particle, int count); |
| 17 | void manager_render(PARTICLE *particle, int count, BITMAP *buffer); |
| 18 | void manager_insert(PARTICLE *particle, int count, PARTICLE p); |
| 19 | |
| 20 | |
| 21 | volatile int logic_counter = 0; |
| 22 | void logic_counter_handler() |
| 23 | { |
| 24 | logic_counter++; |
| 25 | } |
| 26 | END_OF_FUNCTION(logic_counter_handler) |
| 27 | |
| 28 | |
| 29 | |
| 30 | #define MAX_PARTICLES 256 |
| 31 | |
| 32 | int main(int argc, char **argv) |
| 33 | { |
| 34 | PARTICLE particle[MAX_PARTICLES]; |
| 35 | PARTICLE p; |
| 36 | int quitting = 0; |
| 37 | |
| 38 | if (allegro_init() != 0) |
| 39 | { |
| 40 | allegro_message("Unable to initilize allegro. :("); |
| 41 | return 1; |
| 42 | } |
| 43 | |
| 44 | if (install_keyboard() != 0) |
| 45 | { |
| 46 | allegro_message("Unable to install keyboard driver. :("); |
| 47 | return 1; |
| 48 | } |
| 49 | |
| 50 | if (install_mouse() == -1) |
| 51 | { |
| 52 | allegro_message("Unable to install mouse driver. :("); |
| 53 | return 1; |
| 54 | } |
| 55 | |
| 56 | if (install_timer() != 0) |
| 57 | { |
| 58 | allegro_message("Unable to install timer driver. :("); |
| 59 | return 1; |
| 60 | } |
| 61 | |
| 62 | LOCK_VARIABLE(logic_counter); |
| 63 | LOCK_FUNCTION(logic_counter_handler); |
| 64 | |
| 65 | set_color_depth(32); |
| 66 | if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 800, 600, 0, 0) != 0) |
| 67 | { |
| 68 | allegro_message("Unable to set gfx mode. :(\n(%s)", allegro_error); |
| 69 | return 1; |
| 70 | } |
| 71 | |
| 72 | BITMAP *buffer = create_bitmap(SCREEN_W, SCREEN_H); |
| 73 | if (buffer == NULL) |
| 74 | { |
| 75 | allegro_message("Unable to create back buffer. :("); |
| 76 | return 1; |
| 77 | } |
| 78 | |
| 79 | show_mouse(screen); |
| 80 | |
| 81 | manager_init(particle, MAX_PARTICLES); |
| 82 | |
| 83 | if (install_int(logic_counter_handler, 33) != 0) |
| 84 | { |
| 85 | allegro_message("Unable to install logic timer. :("); |
| 86 | quitting = 1; |
| 87 | } |
| 88 | logic_counter = 0; |
| 89 | while(!quitting) |
| 90 | { |
| 91 | |
| 92 | if (logic_counter > 0) |
| 93 | { |
| 94 | if (mouse_b & 1) |
| 95 | { |
| 96 | p = particle_create(mouse_x, mouse_y); |
| 97 | manager_insert(particle, MAX_PARTICLES, p); |
| 98 | } |
| 99 | manager_update(particle, MAX_PARTICLES); |
| 100 | logic_counter--; |
| 101 | } |
| 102 | |
| 103 | |
| 104 | clear_bitmap(buffer); |
| 105 | manager_render(particle, MAX_PARTICLES, buffer); |
| 106 | |
| 107 | |
| 108 | scare_mouse(); |
| 109 | blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H); |
| 110 | unscare_mouse(); |
| 111 | |
| 112 | if (key[KEY_ESC]) |
| 113 | { |
| 114 | quitting = 1; |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | |
| 119 | show_mouse(NULL); |
| 120 | destroy_bitmap(buffer); |
| 121 | |
| 122 | return 0; |
| 123 | } |
| 124 | END_OF_MAIN() |
| 125 | |
| 126 | void particle_init(PARTICLE *p) |
| 127 | { |
| 128 | p->x = 0; |
| 129 | p->y = 0; |
| 130 | p->dx = 0; |
| 131 | p->dy = 0; |
| 132 | p->alive = 0; |
| 133 | } |
| 134 | |
| 135 | PARTICLE particle_create(float _x, float _y) |
| 136 | { |
| 137 | PARTICLE p; |
| 138 | p.x = _x; |
| 139 | p.y = _y; |
| 140 | p.dx = ((float)((rand() % 101) - 50)) / 10; |
| 141 | p.dy = ((float)((rand() % 101) - 50)) / 10; |
| 142 | p.alive = 255; |
| 143 | return p; |
| 144 | } |
| 145 | |
| 146 | void particle_update(PARTICLE *p) |
| 147 | { |
| 148 | if (p->alive > 0) |
| 149 | { |
| 150 | p->dy += .1; |
| 151 | p->x += p->dx; |
| 152 | p->y += p->dy; |
| 153 | p->alive--; |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | void particle_render(PARTICLE p, BITMAP *buffer) |
| 158 | { |
| 159 | if (p.alive > 0) |
| 160 | { |
| 161 | putpixel(buffer, p.x, p.y, makecol(p.alive, p.alive, p.alive)); |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | void manager_init(PARTICLE *particle, int count) |
| 166 | { |
| 167 | int c; |
| 168 | for (c = 0; c < count; c++) |
| 169 | { |
| 170 | particle_init(&particle[c]); |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | void manager_update(PARTICLE *particle, int count) |
| 175 | { |
| 176 | int c; |
| 177 | for (c = 0; c < count; c++) |
| 178 | { |
| 179 | particle_update(&particle[c]); |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | void manager_render(PARTICLE *particle, int count, BITMAP *buffer) |
| 184 | { |
| 185 | int c; |
| 186 | for (c = 0; c < count; c++) |
| 187 | { |
| 188 | particle_render(particle[c], buffer); |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | void manager_insert(PARTICLE *particle, int count, PARTICLE p) |
| 193 | { |
| 194 | int c; |
| 195 | int oldest = 0; |
| 196 | for (c = 0; c < count; c++) |
| 197 | { |
| 198 | if (particle[c].alive == 0) |
| 199 | { |
| 200 | particle[c] = p; |
| 201 | return; |
| 202 | } |
| 203 | if (particle[c].alive < particle[oldest].alive) |
| 204 | oldest = c; |
| 205 | } |
| 206 | particle[oldest] = p; |
| 207 | } |
malloc(sizeof(particle));
Why use malloc when you're using c++, why use doubles? you don't seem to have the ability to end the life of a particle, or group particles together as distinct units, or reuse the memory for new particles.
Why use malloc when you're using c++, why use doubles? you don't seem to have the ability to end the life of a particle, or group particles together as distinct units, or reuse the memory for new particles.
Because it's a simple example, I didn't include anything for deleting particles, but you could easily give each particle a lifetime and do something like this:
| 1 | //a function to remove particles that have died |
| 2 | void removeDeadParticles() { |
| 3 | //set up a vector of particles that are still alive |
| 4 | vector<particle*> alive; |
| 5 | for(int i=0; i<particles.size();i++){ |
| 6 | //decrement each particle's life |
| 7 | (*particles.at(i)).life--; |
| 8 | if((*particles.at(i)).life<=0){ |
| 9 | //if the particle is dead, free the memory |
| 10 | free(particles.at(i)); |
| 11 | }else{ |
| 12 | //if the particle is still alive, put it in the alive vector |
| 13 | alive.push_back(particles.at(i)); |
| 14 | } |
| 15 | } |
| 16 | //change the contents of vector particles to the contents of vector alive |
| 17 | particles.assign(alive.begin(),alive.end()); |
| 18 | } |
What would you recommend for my example other than malloc? I'm still working with my first project that uses structures, so I'm still quite new to the various uses of malloc, new, free, and delete. You should be able to malloc or new a structure, right?
As for the doubles, feel free to use floats. That would take up less memory, right?
The thing is new and delete are part of C++, that is memory allocation and freeing are part of the language, when in C (malloc and free), they are provided by the system library.
When you use new it calls that class constructor if there is one, and delete calls the destructor. If new for some reason can't allocate the memory, the default action is to throw an exception, where malloc just returns a NULL pointer.
You can use malloc to allocate an area in memory for a struct, but you can't really use it to create a class on the heap.
(AFAIK you can use malloc, then manually call the constructor, then manually call the destructor before freeing it, but you are really asking for trouble if you go down that path.)
Edit:
and please for the love of whatever deity you follow, use -> to dereference your pointers
I think there's a little miscommunication in this thread... as to the skill of the original poster (Op).
Not taking in account sound and keyboard input.. this is how I (self taught game dev) learned:
1-Create a program that draws a rectangle.
2-Then try to change it's color.
3-Then try to change it's color rapidly with a loop.
4-Once you've got that then try to move it.
5-Then try to make it bounce off the edges of the screen.
6-Then fiddle around with random ideas like "What if I make it change color everytime it bounces?"
7-Then make an array of rectangles so you have 20 or more bouncing off the screen.
8-That should give you enough working knowledge to write your own particle explosion, starfields, etc.
I'm curious, which step is the OP at? :p
What is the specific task the OP is having trouble with?
Idealius: your example fails. It took me less than a day to learn how to bounce rectangles around on the screen. Ok, I used circles instead of rectangles, but it's the same thing. A particle explosion is much different.
You're probably right as I learned on Qbasic long before Allegro ever existed..
And computers were less mainstream back then, too.
I guess I'm just wondering if the OP has a basic understanding of arrays. Once you have that a particle explosion should be easy to create.
Of course creating, tracking, and deleting it as an object is another layer of complexity..
kudos to circles =)
I learned in QBASIC too!
My biggest problem with a particle explosion was always the random direction thing. I could make things go up, down, left, right, or at a 45 degree angle to any of those, but it wasn't until just recently that I learned how to make things go in any direction. I still have trouble with particle effects, though.
Oh cool. I guess I just assumed you started on Allegro.
Yeah I think I bridged that gap by using float's instead of int's for the particle's coords and deltas. And of course knowing how to make a random value that can be negative OR positive helped, too..
Then there are more complex ways to do it, by giving the particles' starting points based on the shape of a circle so when they travel the same basic circular plot is maintained.. like fireworks.
..using vectors (not the class)..etc. Ah memories. Thanks..
I began working with 2d environments a little over 2 weeks ago. Before that, it was all text, learning for and while loops, arrays, etc, etc, all the basics. I'm still not that familiar with implementing physics for objects in 2d. I made a bouncy ball program but I'm pretty sure I cheated.
I'd say I'm probably around step 5.
I'm going to try out some of the examples posted here, although I can already tell that it may be difficult for me to understand. I'll post back later with how I did.
Thanks for all the replies!
I woke up this morning and was like, "Whoa! 17 replies!!" 
----------
I can't seem to figure out how to add a post right after my last post, so I'm just going to use the "minus bar" and edit this post. 
Schyfis, I tried out your code and I threw together a working program below (oh yeah, I don't know how to put it in a cool code window). I'm a little confused about it though, mainly this line:
particle* p = (particle*)malloc(sizeof(particle));
This allocates memory for each particle in the vector, am I right? But, why is it necessary to do this? Is it because particle p has been declared as a pointer? And if so, why not just directly declare p as not a pointer? Full working program below:
#include <allegro.h>
#include <ctime>
#include <math.h>
#include <vector>
using namespace std;
BITMAP* buffer;
//define a particle structure
typedef struct particle {
//we'll need an x and y position for each particle
int x, y;
//the particles are going to be moving,
//so here's x velocity and y velocity
int xvel, yvel;
//let's also keep track of the old
//x and y positions so we can draw the
//particles as lines instead of just points
int oldx, oldy;
};
//create a vector of pointers to particles (kind of like an array)
vector <particle*> particles;
/*
explode: a function to make a particle explosion
parameters:
x: the horizontal position of the explosion's center
y: the vertical position of the explosion's center
num: number of particles in the explosion
*/
void explode(int x, int y, int num) {
//initializes the random number generator with the current time as the seed
srand(time(0));
//for loop to create num particles
for(int i=0; i<num; i++) {
//allocates memory for our new particle
//note that this is a pointer to a particle, not the particle itself
particle* p = (particle*)malloc(sizeof(particle));
//set x, y, oldx, and oldy to the center of the explosion
(*p).x = (*p).oldx = x;
(*p).y = (*p).oldy = y;
//set xvel and yvel to random numbers between -4 and 4
(*p).xvel=rand()%64-rand()%64;
(*p).yvel=rand()%64-rand()%64;
// (*p).xvel=-1;
// (*p).yvel=0;
//add the particle pointer to our vector
particles.push_back(p);
}
}
/*
stepParticles: a function to step the particles through one frame of movement
parameters: none
notes:
-call this function every time you go through your game loop.
-remember that particles is a vector of pointers, so you have to dereference them!
*/
void stepParticles(){
//iterate through our vector of particle pointers
//particles.size() gets the total number of elements in the vector
for(int i=0; i<particles.size(); i++){
//set oldx and oldy to the current values of x and y
(*particles.at(i)).oldx=(*particles.at(i)).x;
(*particles.at(i)).oldy=(*particles.at(i)).y;
//now we add xvel and yvel to x and y so the particles move
(*particles.at(i)).x+=(*particles.at(i)).xvel;
(*particles.at(i)).y+=(*particles.at(i)).yvel;
}
}
/*
drawParticles: a function to draw the particles on the screen
parameters: none
notes: call this function every time you go through your game loop.
*/
void drawParticles() {
//iterate through the vector like last time
for(int i=0; i<particles.size(); i++){
//you'll have to have a BITMAP named buffer for this to work,
//assuming you're double buffering
line(buffer,
(*particles.at(i)).x, (*particles.at(i)).y,
(*particles.at(i)).oldx, (*particles.at(i)).oldy,
makecol(255,127,0));
//it spans many lines to make it easier to read
}
}
int main(){
allegro_init();
install_keyboard();
set_gfx_mode(GFX_AUTODETECT_WINDOWED,640,480,0,0);
set_color_depth(32);
buffer = create_bitmap(640,480);
// particle.x = 200;
// particle.y = 200;
// particle.xvel = 5;
// particle.yvel = 5;
//an example of your game loop
while(!key[KEY_ESC]){
clear_keybuf();
if(key[KEY_SPACE]){
srand(time(0));
//make explosion at random place on the screen
//explode(rand()%SCREEN_W, rand()%SCREEN_H, 5);
explode(300,240,100);
}
clear(buffer);
stepParticles();
drawParticles();
blit(buffer,screen,0,0,0,0,buffer->w,buffer->h);
rest(20);
}
}
END_OF_MAIN();
To put code into the codebox just put [code] and [/code] around it.
The particles are declared as a pointer so they can be allocated on the heap(correct me if I'm wrong). This way they can be deleted. The stack is what items are declared on if you don't allocate memory, and it is hard to delete them off of it. The stack will also go out of scope. Ex:
int* return_stack(void) { int number = 10; //declare on stack, valid now return &number; //return address of number }; int main() { int* p_num; p_num = return_stack(); //number goes out of scope, printf("%d",*p_num); //causing p_num to be some random value }
The heap is where memory that was allocated by malloc or new resides. It doesn't go out of scope Ex:
int* return_stack(void) { int* number = (int*)malloc(sizeof(int)); //declare on heap, valid now *number = 10; return number; //return address pointed to by number }; int main() { int* p_num; p_num = return_stack(); //number doesn't go out of scope, printf("%d",*p_num); //causing it to stay 10 //deallocate it (forgot the code) }
Always be sure to deallocate memory or there will be a memory leak.
Thanks someone972, for showing me how to do the code thing. 
By the way, what's a memory leak?
I'll play around with the particles for a while a post back if I run into more trouble with it. Thanks for all the help everyone!
Memory leak occurs when allocated memory is not freed when you're done with it.
Example relevant is when you keep creating more particles but never free any, then the old particles stay in memory, and when you have reused the the old pointers to point to the new particles you have no way to access the old particles, they have leaked out of your control.
By the way, what's a memory leak?
Whenever you allocate memory, either using malloc (plain C) or new (C++), you are putting your hands around a bit of memory that can't be used for anything else. It is good practice to deallocate anything you allocate, either using free (plain C) or delete (C++). Most OS (that is, I believe it is the OS that does this), deallocates memory on the program's exit. However, it's still good practice to do it yourself, and if you are allocating memory for things that only exist for a moment in your program, you are wasting memory and this can cause the entire computer to run out.
Thanks for the explanations. I understand memory leaks now.
I managed to make a little explosion too. I'm quite proud of it. I'd probably use it in combination with an animation of an explosion in the center.
| 1 | #include <math.h> |
| 2 | #include <allegro.h> |
| 3 | |
| 4 | BITMAP* buffer; |
| 5 | |
| 6 | struct Particle_Type |
| 7 | { |
| 8 | double x; |
| 9 | double y; |
| 10 | double speed; |
| 11 | double angle; |
| 12 | int life; |
| 13 | }Particles[150]; |
| 14 | |
| 15 | double Max_Angle = 2 * 3.14159; |
| 16 | |
| 17 | void Draw_Stuff() |
| 18 | { |
| 19 | clear_to_color(buffer, makecol(0, 0, 0)); |
| 20 | for(int i = 0; i < 150; i++) |
| 21 | { |
| 22 | if(Particles<i>.life) |
| 23 | { |
| 24 | putpixel(buffer, Particles<i>.x, Particles<i>.y, makecol(255, 255, 255)); |
| 25 | } |
| 26 | } |
| 27 | blit(buffer, screen, 0, 0, 0, 0, 640, 480); |
| 28 | } |
| 29 | |
| 30 | int main() |
| 31 | { |
| 32 | allegro_init(); |
| 33 | install_keyboard(); |
| 34 | set_color_depth(16); |
| 35 | set_gfx_mode( GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0); |
| 36 | |
| 37 | buffer = create_bitmap(640, 480); |
| 38 | |
| 39 | while(!key[KEY_ESC]) |
| 40 | { |
| 41 | if(key[KEY_SPACE]) |
| 42 | { |
| 43 | for(int i = 0; i < 150; i++) |
| 44 | { |
| 45 | Particles<i>.x = 320.0; |
| 46 | Particles<i>.y = 240.0; |
| 47 | Particles<i>.speed = rand() % 10 + 5; |
| 48 | Particles<i>.angle = rand() % 360 + 1; |
| 49 | Particles<i>.angle = Particles<i>.angle * 3.14159 / 180; |
| 50 | Particles<i>.life = rand() % 10 + 10; |
| 51 | } |
| 52 | } |
| 53 | for(int i = 0; i < 150; i++) |
| 54 | { |
| 55 | if(Particles<i>.life > 0) |
| 56 | { |
| 57 | Particles<i>.x += Particles<i>.speed * cos(Particles<i>.angle); |
| 58 | Particles<i>.y += Particles<i>.speed * sin(Particles<i>.angle); |
| 59 | Particles<i>.life--; |
| 60 | } |
| 61 | } |
| 62 | Draw_Stuff(); |
| 63 | rest(50); |
| 64 | } |
| 65 | destroy_bitmap(buffer); |
| 66 | allegro_exit(); |
| 67 | } |
| 68 | END_OF_MAIN() |
I get this warning whenever I compile, I know it's because I'm passing doubles to putpixel() but I don't know how to fix it.
[Warning] passing 'double' for converting 2 of 'void putpixel(BITMAP*, int, int, int)'
That is an awesome particle explosion!
Also, the code is easy to understand even without the blue little helpy helpers.
I make my code easy to understand because the more compleximacated stuff makes me think. And I'm too damn tired to think right now.
Neil: It runs too fast on my q6600 quad core. It seems even if you didn't program it for multi core systems, X gets its own core as well
SILENCE!
I was too lazy to implement real timing... would it work right if I used timing instead of rest()? If I had it in a game I'd do it properly.
Heh, I'm just bugging you.
I get this warning whenever I compile, I know it's because I'm passing doubles to putpixel() but I don't know how to fix it.
[Warning] passing 'double' for converting 2 of 'void putpixel(BITMAP*, int, int, int)'
C++ uses strong static typing, which means that you need to pass arguments with the correct types.
Some implicit conversions are allowed (i.e., you can assign a value of type A to a variable of type B), but if the conversion can lead to loss of precision (e.g. double -> int), the compiler throws a warning. You need to explicitly cast to tell the compiler that you know what you're doing:
Note that this produces a sub-pixel inaccuracy around 0, because the default cast rounds towards 0 (negative numbers round up, positive ones round down). Objects crossing the x or y axis will experience a tiny glitch. You can use libm's floor() function to avoid this:
C++ uses strong static typing
Type system war!!!! :-)
C++ has a weak static type system, due to static_cast, reinterpret_cast and C-style casts.
And what if those casts didn't exist? I figure much worse...