[SOLVED] Spawn entities along bezier curve

Whether you're a newbie or an experienced programmer, any questions, help, or just talk of any language will be welcomed here.

Moderator: Coders of Rage

Post Reply
User avatar
jaybee
Chaos Rift Newbie
Chaos Rift Newbie
Posts: 27
Joined: Thu Jan 13, 2011 12:53 pm
Current Project: 2d platformer
Favorite Gaming Platforms: NES, SNES, Genesis, DOS
Programming Language of Choice: C++
Location: Running Springs, CA

[SOLVED] Spawn entities along bezier curve

Post by jaybee »

So over the past couple of months I've been trying to get my feet wet by writing a little shmup in C++/SDL. Things have been going pretty well up until this point and I've been able to implement a scrolling background, a player ship with a specified number of lives, explosion animations for enemies and for the player, 3 different weapon tiers, a basic HUD, etc...

The problem is I find my code becoming increasingly disorganized and at times somewhat "hackish". Lately I've been spending more time just trying to figure out how to design my classes so I can more easily accommodate things like a text file parser for level creation, ability for enemies and the player ship to take more than 1 hit before explosion, a scoring system, and so on. I really want to keep things simple seeing as though this is my first real game. Maybe I've gotten in over my head but I've gotten much farther than I thought I would.

Right now I'm trying to figure out a basic way to spawn waves of enemies. What I have so far is an enemy factory class with a generate wave member function. The class itself contains a std::vector that stores the enemies and a single formation is hard coded into the function. I KNOW there is a much better way of doing this, but I'm fairly new to this and the solution is eluding me as it stands. Below is a VERY basic start, but I am pretty sure I want to stay away from this way of doing it.

Code: Select all

void Enemy_Factory::generateWave()
{
        //I know the Enemy constructor is quite cryptic, this is what I meant by "hackish"
        //below is a basic guide to the arguments it is taking
        //Enemy(pointer to sdl surface, imageX, imageY, width, heigh, R, G, B, bitmap file, screenX, screenY, movement speed)
        enemyList.push_back(new Enemy(m_graphics, 0, 0, 51, 46,
                                      255, 0, 255,
                                      "enemies.bmp", 0,
                                      -50, m_maxSpeed));

        enemyList.push_back(new Enemy(m_graphics, 0, 0, 51, 46,
                                      255, 0, 255,
                                      "enemies.bmp", 150,
                                      -100, m_maxSpeed));

        enemyList.push_back(new Enemy(m_graphics, 0, 0, 51, 46,
                                      255, 0, 255,
                                      "enemies.bmp", 300,
                                      -150, m_maxSpeed));

        enemyList.push_back(new Enemy(m_graphics, 0, 0, 51, 46,
                                      255, 0, 255,
                                      "enemies.bmp", 450,
                                      -100, m_maxSpeed));

        enemyList.push_back(new Enemy(m_graphics, 0, 0, 51, 46,
                                      255, 0, 255,
                                      "enemies.bmp", 600,
                                      -50, m_maxSpeed));
}
So i'm wondering, what is a better way of doing this? Or should I hard code each of enemy formation that I want and have them triggered by a text file that contains level definitions? I know these are some fairly basic things I'm dealing with but I'm having trouble wrapping my mind around the best way to implement this. Any help would be appreciated.
Last edited by jaybee on Mon Jan 24, 2011 12:06 am, edited 1 time in total.
"Do I really have to spell this out? What if a bunch of punk kids go into the woods with a bullet proof vest and strap it on a grizzly bear? Then what have you got? Invincible bears. Is that what you want? Invincible bears running around; raping your churches and burning your women?"
User avatar
GroundUpEngine
Chaos Rift Devotee
Chaos Rift Devotee
Posts: 835
Joined: Sun Nov 08, 2009 2:01 pm
Current Project: mixture
Favorite Gaming Platforms: PC
Programming Language of Choice: C++
Location: UK

Re: Adventures in n00b development

Post by GroundUpEngine »

If my code ever looks messy or unorganized, then i tweak it!

e.g. Getting rid of writing common variables, to stop yourself from coding same parameters over and over. Wrap your code up neatly so you can do implement like this ;)
jaybee wrote:

Code: Select all

void Enemy_Factory::generateWave(int howMany)
{
  for(int i = 0; i < howMany; i++)
  {       
    vec2 position = ...; // generate random vector
    enemyList.push_back(new Enemy(position));
  }
}
User avatar
dandymcgee
ES Beta Backer
ES Beta Backer
Posts: 4709
Joined: Tue Apr 29, 2008 3:24 pm
Current Project: https://github.com/dbechrd/RicoTech
Favorite Gaming Platforms: NES, Sega Genesis, PS2, PC
Programming Language of Choice: C
Location: San Francisco
Contact:

Re: Adventures in n00b development

Post by dandymcgee »

Yes, as GroundUpEngine said all of those parameters should be given a default value in the Enemy constructor so that you only need to specify them when you'd like something other than the default.

Also, some of those parameters even seem redundant.
1. You can get the image width and height from the SDL surface.
2. Why do you need both an SDL surface and a bitmap?
3. R, G, & B I'm guessing are the values for your color keying, no? This could be condensed into an SDL_Color variable or defaulted to magenta as I don't see other color being used.
Falco Girgis wrote:It is imperative that I can broadcast my narcissistic commit strings to the Twitter! Tweet Tweet, bitches! :twisted:
D-e-X
Chaos Rift Newbie
Chaos Rift Newbie
Posts: 39
Joined: Tue Dec 28, 2010 6:49 pm

Re: Adventures in n00b development

Post by D-e-X »

also like GroundUpEngine wrote in his example, make a class or data structure that represents coordinates (vec2, vec2f or something like that) instead.
I remember when I used to be into nostalgia.
User avatar
jaybee
Chaos Rift Newbie
Chaos Rift Newbie
Posts: 27
Joined: Thu Jan 13, 2011 12:53 pm
Current Project: 2d platformer
Favorite Gaming Platforms: NES, SNES, Genesis, DOS
Programming Language of Choice: C++
Location: Running Springs, CA

Re: Adventures in n00b development

Post by jaybee »

Thanks for the quick reply. Really helpful info. My first idea was similar to what you had put groundupengine, but I'm not sure how I would go about placing them in particular formations within a for loop like that. Most likely some basic math would do the trick. Again, I know these are relatively simple concepts, so bear with me.

I'm really wanting to keep this simple as this is my first game. Wrapping it up like you have put it would be really helpful.
D-e-X wrote:also like GroundUpEngine wrote in his example, make a class or data structure that represents coordinates (vec2, vec2f or something like that) instead.
This is an interesting idea and most likely the type of solution that would work best for me. Can you elaborate on this? I'm not quite sure what a class representing coordinates would look like. Would it simply return ints that would be fed into the Enemy(int position) as per GroundUp's example?
"Do I really have to spell this out? What if a bunch of punk kids go into the woods with a bullet proof vest and strap it on a grizzly bear? Then what have you got? Invincible bears. Is that what you want? Invincible bears running around; raping your churches and burning your women?"
User avatar
thejahooli
Chaos Rift Junior
Chaos Rift Junior
Posts: 265
Joined: Fri Feb 20, 2009 7:45 pm
Location: London, England

Re: Adventures in n00b development

Post by thejahooli »

jaybee wrote:I'm not quite sure what a class representing coordinates would look like.
Mine looks like this

Code: Select all

struct Vector2
{
int x, y;
}
But you could add in additional functions if you really want.
Also you can create additional classes such as 'Vector2f' which would have 'x' and 'y' as floats instead of ints.
I'll make your software hardware.
qpHalcy0n
Respected Programmer
Respected Programmer
Posts: 387
Joined: Fri Dec 19, 2008 3:33 pm
Location: Dallas
Contact:

Re: Adventures in n00b development

Post by qpHalcy0n »

What you could do for the placement is form a line against which the units will be placed. Break it up into perhaps 5 control points along the line. Displace the control points in some direction in some random length away from the line. The control points could also be hard coded into some static array, in a file, or what have you.

You can then use some polynomial function like a cubic or bi-cubic to interpolate along the line the displacement away from it where the units would be spawned. This will net you a more flowing and natural looking placement.

One particular type of polynomial that flows rather nicely is the Bezier curve, which follows the form:
P(t) = P0(1 - t)^3 + P1 * 3(1 - t)^2 * t + P2 * 3(1 - t) * t^2 + P3 * t^3

Where P0, P1, P2, and P3 are all control points (P0 being the initial point, P3 being the terminator) and t being some time (between 0 and 1) between P0 and P3.
And if you go the route of coding the control points in a file or in a static array, then you can also pre-generate all of the control points according to the above equation. This should give you "random enough".

This is the method I used in a tower defence game that a friend and I had developed.
User avatar
jaybee
Chaos Rift Newbie
Chaos Rift Newbie
Posts: 27
Joined: Thu Jan 13, 2011 12:53 pm
Current Project: 2d platformer
Favorite Gaming Platforms: NES, SNES, Genesis, DOS
Programming Language of Choice: C++
Location: Running Springs, CA

Re: Adventures in n00b development

Post by jaybee »

dandymcgee wrote:Yes, as GroundUpEngine said all of those parameters should be given a default value in the Enemy constructor so that you only need to specify them when you'd like something other than the default.

Also, some of those parameters even seem redundant.
1. You can get the image width and height from the SDL surface.
2. Why do you need both an SDL surface and a bitmap?
3. R, G, & B I'm guessing are the values for your color keying, no? This could be condensed into an SDL_Color variable or defaulted to magenta as I don't see other color being used.
1. Yeah, I definitely see having default values. Right now I don't have a separate class for each enemy type, but I'm working on that.

2. I actually misspoke in the comment. The bitmap is just the file name of the bitmap containing the enemies. The SDL surface is a pointer to the graphics object.

3. I will definitely make an SDL_Color variable, good idea.
qpHalcy0n wrote: You can then use some polynomial function like a cubic or bi-cubic to interpolate along the line the displacement away from it where the units would be spawned. This will net you a more flowing and natural looking placement.

One particular type of polynomial that flows rather nicely is the Bezier curve, which follows the form:
P(t) = P0(1 - t)^3 + P1 * 3(1 - t)^2 * t + P2 * 3(1 - t) * t^2 + P3 * t^3

Where P0, P1, P2, and P3 are all control points (P0 being the initial point, P3 being the terminator) and t being some time (between 0 and 1) between P0 and P3.
My math skills are fairly non existent as it has been years since I have taken any math. I'm assuming I would iterate through that equation for both the x and y of each entity that I intend to spawn, given t. What I am unsure about is what t actually represents in relation to the enemies position on the screen. I know you said t represents some "time" between P0 and P3, however, I'm not exactly sure what is meant by that.
"Do I really have to spell this out? What if a bunch of punk kids go into the woods with a bullet proof vest and strap it on a grizzly bear? Then what have you got? Invincible bears. Is that what you want? Invincible bears running around; raping your churches and burning your women?"
qpHalcy0n
Respected Programmer
Respected Programmer
Posts: 387
Joined: Fri Dec 19, 2008 3:33 pm
Location: Dallas
Contact:

Re: Adventures in n00b development

Post by qpHalcy0n »

You got it ;] x and y are both bound by that equation.

So if for example you wanted to spawn 25 enemies along said path. The time interval is 1/25, if you want 30 enemies then the time interval is 1/30. Enemy 0 starts at P0. Enemy 1 follows at time = 1/25. Enemy 2 follows at time = 2/25, Enemy 3 follows at time 3/25, and so on... (To eliminate any source of confusion 1/25, 2/25, and 3/25 indicate fractions).

You can even arrange the control points so they form a box, then the spawn path will be a nice flowing curve around the control points of the box, so it does not look so sporadic. It works well for line formations/attack formations and things like this.

The code might look like so:

Code: Select all

// Here, (ndx, ndy), cpa, cpb, (ndxPrev, ndyPrev) represent four control points 
// cpa and cpb represented by a 2-tuple vector type, ndx, ndy, ndxPrev, ndyPrev by x and y scalars.
float t = i / (float)m_iLineDivisions;
x = (ndx * powf(1.0f - t, 3.0f)) + (cpa.x * 3.0f * pow(1.0f - t, 2.0f) * t) + (cpb.x * 3.0f * (1.0f - t) * pow(t, 2.0f)) + (ndxPrev * pow(t, 3.0f));
y = (ndy * powf(1.0f - t, 3.0f)) + (cpa.y * 3.0f * pow(1.0f - t, 2.0f) * t) + (cpb.y * 3.0f * (1.0f - t) * pow(t, 2.0f)) + (ndyPrev * pow(t, 3.0f));
Last edited by qpHalcy0n on Mon Jan 24, 2011 12:05 am, edited 1 time in total.
User avatar
jaybee
Chaos Rift Newbie
Chaos Rift Newbie
Posts: 27
Joined: Thu Jan 13, 2011 12:53 pm
Current Project: 2d platformer
Favorite Gaming Platforms: NES, SNES, Genesis, DOS
Programming Language of Choice: C++
Location: Running Springs, CA

Re: Adventures in n00b development

Post by jaybee »

qpHalcy0n wrote:You got it ;] x and y are both bound by that equation.

So if for example you wanted to spawn 25 enemies along said path. The time interval is 1/25, if you want 30 enemies then the time interval is 1/30. Enemy 0 starts at P0. Enemy 1 follows at time = 1/25. Enemy 2 follows at time = 2/25, Enemy 3 follows at time 3/25, and so on... (To eliminate any source of confusion 1/25, 2/25, and 3/25 indicate fractions).

You can even arrange the control points so they form a box, then the spawn path will be a nice flowing curve around the control points of the box, so it does not look so sporadic. It works well for line formations/attack formations and things like this.
Yes this is exactly what I was looking for! :worship:

All of you guys have been extremely helpful.
"Do I really have to spell this out? What if a bunch of punk kids go into the woods with a bullet proof vest and strap it on a grizzly bear? Then what have you got? Invincible bears. Is that what you want? Invincible bears running around; raping your churches and burning your women?"
Post Reply