(Shmup) step-threading and stage/entity events

Started by
12 comments, last by kiwibonga 13 years, 1 month ago
I'd like to thank the community for helping me get this far. But I've come to a critical problem that I just can't solve on my own yet.
My tools are C++ (Visual Studio Express Edition) and DirectX.

I'm trying to implement event-oriented design to actually make things happen in my game.
Things like schedulers, frame numbers, and other similar scopes are exhaustingly tedious. I don't want to write if-then statements if(framenumber) do{whatever} for every single thing I want something to happen. That is a ridiculous way make entities in my game to do stuff like shoot patterns of bullets. It's also very inefficient, because everything is run in an infinite game loop. And the more things I have scheduled for those entities to execute at some point in time, the more if-then statements it'll have to check to be true every frame and therefore the lower my framerate will be because it must check it at every single game loop.
Not to mention, assigning tasks to entities will also be very tedious.


Here's what I have.
-structure for my entities (the player, enemies, bosses, etc)
-structure for my bullets
-a manager to update things like each entity and bullet
-DirectX ready to render every single thing
-a game loop to put the stuff in and execute every frame. Here's what it looks like.
void infinitite_gameloop()
{

Stage(); //where I call functions like CreateEnemy(where,speed,dir,direction_to_task_list)
Entity_manager(); //updates all entities, directx stuff....
special_effect_manager(); //other stuff
Bullet_manager() //similar to entity_manager. Updates all bullet's coordinates, directx stuff...
Background_manager();
camerasettings();

}




I have my game running and ready to execute. The only problem, though, is assigning tasks for the entities/bullets to execute. The thing is, though, I want to organize it in a very special way... event-oriented rather than frame-number oriented. I want each entity to be independent, so to do that, I made them them all in one array. This way, the object in the first element of the array can have completely different data (like x-y coordinates) opposed to other objects. You can imagine how the manager will update things like movement- it increments the index and calculates new x/y coordinates based on speed and direction.
Anyway, I have a function to make an entity to show on the screen, its speed-direction-coordinates all part of the argument list. All it does is look for an 'inactive' element in the entity-objects array, and mark it active and set the data into the object's inheritence. But that isn't the problem. The problem, is to have the object have a unique behavior set as well. For example, the object in the first element of the array will fire bullets toward the player, while the object in the fifth element will shoot somewhere else. Assigning tasks for the object to follow over the frames. The two should be doing different things.

The problem is, it seems impossible to have an object do a portion of its assigned function. I want to have all of its tasks all bundled up together and have it execute it over a duration of several hundred frames. And that's where I have my problem.Since it's executing everything once per frame, it's impossible to have that object to do just a portion of its job. It must execute everything for the manager to move on to updating the next object. If I had the manager have an object run its function that's inherited by its structure (or set for it somewhere in the gameloop), It will have each entity do EVERYTHING (like movement, firing all its bullets, and suicide) all in one frame. I figured I would use a the return function in windows whenever I want to delay later tasks by one frame, but unfortunately the scope is destroyed, and the next loop it will start the function anew instead of where it left off. What I need is some sort of coroutine yielding. It executes its tasks in the function, goes back to the game loop, and resumes everything when it's called next frame. And so I got a new idea...multithreading. Except only one thread will be run at any one time.
Having the entities to inherit their own thread, but they will not run until until the manager tells them to. When a yield function is called, that thread will give up the rest of its timeslice, and the CPU leave the scope and back to the manager to work with the next object, every object until all of them have done what they need to for that frame. Next loop around, when the manager is called, the CPU will go back and resume that thread from where it left off. So it basically jumps thread to thread every frame. Since it's an infinite loop, it'll come back around to that thread next frame, every frame.
This way, I can schedule all events with a simple yield function, that makes the CPU jump thread to thread until every object does what it needs to do for that frame.

In the end, I want to schedule tasks for enemies like this....
By the way, EVERY object will have a task list like this. But what the list will be will depend on what I give the parameters when I "activate" the object.
main_enemy_task{ //this is called once per frame for every object. Everytime a yield is called here, the timeslice for the thread will be switched to the previous thread.
move(x,y); //frame relative to this object, it starts to move over time to these x-y coordinates
yield(50); ///yields 50 frames
firebullets(); ///starts to fire bullets at frame 50
move(x,y); ///moves toward X-Y over time, starting at frame 50
yield(100); ///yields 100 frames
suicide(); ///the object does a suicide effect over time starting at frame 150
}

task firebullets{ int angle=0; //fires 30 bullets in a circle every frame. this is a thread
///when running in this thread, the timeslice for main_enemy_task is suspended until this thread is yielded
while(TRUE)
{
for(int bullet=0; bullet<30; bullet++)
{
fireshot(GetX(),GetY(),2,angle,RED); //x,y,speed,angle,color. GetX&GetY are functions to get the current enemy's x&y coordinates
}
yield; }//end of while loop. The yield will return CPU back to main_enemy_task, but the timeslice for this thread will be resumed next frame.
}






If this was confusing, here's a simple example of what I'm trying to do. In a console application, it would look like this.


//thread for object 1
obj1{ //main() thread timeslice is suspended until this returns, just as a normal function would
cout<<1<<endl;
cout<<2<<endl;
yield;
cout<<8<<endl;
cout<<9<<endl;
cout<<10<<endl;
}

//thread for object 2
obj2{
cout<<3<<endl;
cout<<4<<endl;
cout<<5<<endl;
yield;
cout<<6<<endl;
cout<<7<<endl;
}

void main(){

obj1();
obj2();
obj2();
obj1();

cin.get();
}


This would ouput 1 2 3 4 5 6 7 8 9 10, each number on a new line in that order.
As you can see, everytime a yield is called, the thread gives up the rest of its timeslice to the thread that called it (in this case, main(), and resumes what it's doing when called again.

I heard LUA does something similar with its coroutines, but I don't know how to implement it. Moreover, how can I declare threads in structuresso I can assign tasks in objects to entities?
I'll be watching this thread all month.
Advertisement
I don't know if threads or coroutines would be the best way to go about this -- the (main) reason you'd want to use them is for performance, if you don't have enough processing power to run all scripts you need to run within a single frame, but I don't think that's an issue for you.

I implemented a sequence system in the engine/framework I'm working on, perhaps it could help you think of a simpler solution to your problem.

I created a base class called "SeqObj" (sequence object). All SeqObjs have a virtual member function called "ping()" which could also have been called "execute()". The body of the function is designed to run a single action.

For instance, the SeqCountdown SeqObj has an int counter member variable, and every call to ping(), it decrements that internal counter. When it gets to 0, SeqCountdown sets its "bool alive" flag to false to indicate that its work is done and it can be destroyed in a later cleanup step of the current frame.

A Sequencer is an object that can hold a stack of SeqObjs. Every frame, it runs the ping() function of the first SeqObj in its stack. If after running it, the SeqObj is marked as dead, it moves on to the next SeqObj on the list. If the SeqObj is still alive (if it still has stuff to do), the Sequencer stops iterating through its queue immediately -- until the next frame.

This is what allows you to delay execution of upcoming sequential actions.

Examples of other sequence objects:

SeqMessage -- this carries an arbitrary functor and an arbitrary list of arguments (heavily templated); when ping is called, the SeqMessage calls the desired member function on the desired object with the desired arguments, and dies.
SeqFlush -- this has a counter, and a target screen position; when ping is called, the SeqFlush moves the desired object towards the desired screen position and decrements the counter. The goal is to move the target object to a given position in a fixed number of frames.

So, to deal a card in my little blackjack demo, I first spawn a hidden sprite for the card, then I push a SeqMessage to a sequencer that calls the card sprite's unhide() function. Then, I push a SeqFlush that will move the card to the appropriate position (player's hand) in 10 frames. And finally, I push a SeqCountdown for 20 frames to the end of that, so that there's a delay in between deals.

The fun thing about this is that Sequencer is also a SeqObj, so you can actually have an entire tree of sequences within sequences. You can also set a flag on Sequencers so that they run "concurrently" (i.e. more than one sequencer is run in one frame).

So in the first phase of my Blackjack game loop, I just call:

deck->giveCard(dealer);
deck->giveCard(player);
deck->giveCard(dealer);
deck->giveCard(player);

giveCard basically sets up a sequencer that does what I outlined above.

Over the next few frames, the sequencers magically advance through each SeqObj in sequence until they're all done with their job of spawning and moving cards.

Similarly, your game entities could hold a stack of sequential or concurrent actions that they should perform each frame, and have a manager keep track of them and provide them with their "heartbeat."

If you don't use frame-based timing, all you have to do is pass the delta time to the ping()/execute() function of your sequence objects and have them deal with that.

Every frame, it runs the ping() function of the first SeqObj in its stack. If after running it, the SeqObj is marked as dead, it moves on to the next SeqObj on the list. If the SeqObj is still alive (if it still has stuff to do), the Sequencer stops iterating through its queue immediately -- until the next frame.


I'm barely following this. Can you please give me an eample, especially with that last sentence?
"Sequencer stops iterating through its queue immediately -- until the next frame."
How would you get it to stop iterating through it's queue without suspending the thread? Can you give me a diagram, so I can visually see how it works?
Because from what I know, if it's a function, it must do all of its ques for that object in one frame before moving on to the next object. If I'd try to use return, the scope will be destroyed and it will try to start from the very beginning next time the queue is called. This makes it impossible to make multiple object to run simultaniously like I want.

And by "doing all of its queues", I mean ALL of them. Since I'm making a shmup, the queues will be "move, shoot bullets, suicide". It's not the processing power that's the problem, it's making multiple objects doing these things over time simultaneously. Suppose I have 5 objects as entities. And to make things simple, these objects have the same objective- the queues I exampled above. The first entity is called and queued move, while the second entity hasn't been called yet. Then the first entity will then shoot bullets at a later frame, just when the second entity is called. The first entity then suicides, while the second entity shoots bullets, and a third entity is called and starts to move.

My problem is that I can't exit the scope of queues of the first entity before it's officially "dead". If I exit the scope in a function using return, the scope is returned and it starts from the beginning next frame. This makes it impossible, which is why I suggested to myself to simply make threads and use coroutines to iterate and simply suspend them- just so those scopes don't get destroyed and so I can move on from where I left off when I return to it.

It's a simple concept. Suppose this...

somefunction()
{
int a=44;
for (int i=30; i>0; i--)
{
a++;
yield; //if this were a return, a would be 45 every frame, and would never even get to the scope below
}
a=100;
}


There may be a time when I will want to change values like this in my game. If I wanted to display variable a every frame in the above example, somefunction would be in an infinite loop. For the first 30 frames/loops, a will increment itself once every frame. It will then be set to 100 after.

By the looks of it, it looks like you return to your sequencer after you increment the frame for that object and have it queue again after marking that object inactive. Is that right?
But still, just to be sure, I'd like to see a diagram. That will definitely help.
I'm not great at diagrams, but here's some pseudocode.

This would be a member function of your game object's base class:


function runThisObjectsSequence()
{

for each(sequence_object in sequence)
{
sequence_object->execute();
if(sequence_object is still alive)
{
break; // break out of the loop, because this sequence object will need to run again next frame
}
}
}


And you would have your game loop call that function (for every object) every frame.

When I say stop iterating through the queue, I mean break out of the loop and skip the rest of the objects in the sequence -- it's not the time for them to execute yet.

For a concrete example, let's say you push three sequence objects to a spaceship's sequence queue:

1. Move to position 500,10 @ 3 pixels/sec
2. Shoot bullet at player
3. Self-destruct and explode

The sequencer for that spaceship, which is set to run every frame, will do the following:

- Start iterating through the list, find first sequence object (move)
- Execute the sequence object's function -- the ship moves by 3 pixels towards 500,10
- Check if the sequence object is still alive -- yes it is, because the ship is not yet at 500,10, so the sequence object has not yet marked itself for deletion -- in that case, break out of the loop and ignore the rest of the sequence objects for this frame (you don't want to execute them yet because the ship is not done with its first action)
- Every frame after that, the same process repeats until the ship has reached 500,10, at which point the sequence object will mark itself for deletion (alive == false)
- When the sequencer sees this, it moves on to the next sequence object in the queue immediately (spawns a bullet, etc) instead of breaking out of its loop
- Since this is an instantaneous event, that sequence object immediately marks itself for deletion.
- The sequencer then sees the self-destruct sequence object. When it first runs, it could add an explosion animated sprite to the screen and play a sound
- The self-destruct sequence object remains alive until the entire explosion animation has played out.
- When the self-destruct sequence object is run for the last time (animation is done), it can mark the ship, and itself, for deletion
- Sequence is done.

This is basically mimicking threading. Instead of relying on the computer's threading to time the execution of your loops, you create small units of code designed to run quickly over one frame.
That's ingenious. But what if I wanted to have two (or three, or ten) things done at the same time per entity, per frame?
Like lets say, moving and shooting bullets, while displaying text over it?

Suppose this I want to set the behaviors for the entities like this....
This is psuedo-code similar to Danmakufu's, but it's very similar to what I want to do.

script_enemy_main{

Initialize/// runs only once during the entitiy's lifecycle. "Reset" when the object is reused to make a new entity later.
{ ///frame 0 start
SetLife(1000); //1000 life points at frame 0
Move(50,100); //moves at frame 0 to 50x,100y, over time
yield(50); ///yields for 50 frames
FireShot(Get_X(),Get_Y(),1.5,GetAngleToPlayer(),RED); //shoots a bullet from entitie's coords toward the player at frame 50. This is done only once.
Move(20,60); //moves at frame 50, over time to 20x, 60y
yield(30); ///yields for 30 frames
ShootPattern1; ///makes a new task. Specified below. Starts at frame 80
ShootPattern2; ///similer to last task, the two tasks run simultaniously.

yield(100); //yield 100 frames. The tasks continue executing during these frames
suicide(); //kills the entity and marks it "dead" at frame 180
}

EveryLoop ///runs every frame
{
}


task ShootPattern1 ///shoots 30 bullets in a circle every 4 frames
{
while(TRUE)
{ int angle=0; ///in degrees
for(int bullets=30; bullets>0; bullets--)
{
FireShot(Get_X(),Get_Y(),1.5,angle,GetAngleToPlayer(),BLUE);
angle+=360/30;
}
yield(4);
}
}



task ShootPattern2 ////shoots a big bullet toward the player every 10 frames
{
while(TRUE)
{
FireShot(Get_X(),Get_Y(),3,GetAngleToPlayer(),BIGWHITE); //function to fire the shot
yield(10); ///wait ten frames before doing resuming this infinite loop
}
}

}


Now at this point, my guess is- I'm going to have to have some sort of task list for the sequence manager to sort through?
How would I go about making new tasks like this when I make behaviors for my objects? I'll be changing things alot as I code them, and I need a quick and easy way to do it.
Will your method work for it?
You can create another type of "sequencer" that runs all sequence objects in its queue "concurrently" (runs all of them once every frame until they're empty).

If you design the system so that you can attach "concurrent sequencers" and "sequential sequencers" to each other as you please, you can create many different code paths that can run concurrently and asynchronously.

Also, nothing prevents sequence objects themselves from creating more sequences. A behavior could be a constantly running sequence object that makes decisions based on calculations and pushes those decisions to its own subsequence on an as-needed basis (i.e. every time the sequence queue is empty, make a new decision and repopulate it).

But how would I go about assigning those tasks to entities?
The entities are all in one array, and all of them are objects with data like X and Y coordinates.
Would the sequencer be inherited each object/entity? Or would there be one sequencer to do all of the work for all of them?

I should probably tell you, I'll be spawning those entities the same way, and assigning them tasks in a simple function, like so....


CreateEnemy(X-cord,Y-cord,life_points, behavior,graphic);
yield(40); //yields 40 frames
CreateEnemy(differentX,differentY,different_life_points,different_behavior,different_graphic);


These functions will take a random entity from within the array, one that's marked "dead", and bring it to "life" with the assigned data, behavior, and color/image.
Things like behavior will be in detail very similar to the script_enemy_main task I showed in my last post.
How would I go about assigning that behavior to said entity?

I'm barely following this. Please allow me to try to break it down. Correct me where I'm wrong.
The sequencer manages the tasks by looking through them in a queue.
The actual function (ex. Move(); ) will have its own sequencer that consists of a loop that sets the entity's new x-y coordinates every loop. If I'm not mistaken, a break; somewhere within that loop will be equivilent to a yield, right? Because it will come around to finishing that sequence next time the entity will get focus in the next loop, it will proceed.

But here's the part I'm confused about.

- Check if the sequence object is still alive -- yes it is, because the ship is not yet at 500,10, so the sequence object has not yet marked itself for deletion -- in that case, break out of the loop and ignore the rest of the sequence objects for this frame (you don't want to execute them yet because the ship is not done with its first action)

This only explains how to make the bits of sequences themselves to be executed over time. The Move() is called, it's executed over time. But suppose I have two (or three) Move() sequences. What if I want those sequences to be called at different times? Suppose at frame 50, I want the entity to move to (30,20) over time, but at frame 150, I want it to move again at (100,130) over time? And it takes 30 frames for it to get to one position to the other? How would I go about setting those sequences to do it at their respective times? Keep in mind, I want to make it possible to keep it as simple as

main_enemy_task{ ///frame zero
Move(30,20); ///there is a sequencer to actually move it, right? Suppose it takes it 30 frames to move it to this location. This is done at frame zero
ShootPattern1()// this would also have a sequencer, right? This is also done at frame zero
yield(100); //by frame 30, the entity has has reached its destination, and Move() would disable itself. right?
//however this means the entity will be "sitting" for 70 frames. Since there's no Move() to tell it to move. ShootPattern1() is never disable itself, so it'll execute every frame.
Move(100,130); //There is another sequencer to move it. The entity starts moving at frame 150.
yield(100); //yield for 100 more frames
suicide(); //a sequencer to "kill" the entity will start executing at frame 250.
}


The object may be reused, too. So later on, it may be assigned a new sequence of sequences. How would i assign that?
Please answer the every question, especially the first two, those are important.
Also, thanks for the help. I'm starting to get it, but the idea of sequences for sequencers are confusing.
My worry is that it will try to execute the sequences from the beginning. I don't want Move(30,20); to be called every frame because main_enemy_task is an instruction for an object which is in an infinite loop.

But how would I go about assigning those tasks to entities?
The entities are all in one array, and all of them are objects with data like X and Y coordinates.
Would the sequencer be inherited each object/entity? Or would there be one sequencer to do all of the work for all of them?


You could give each entity a sequencer (as a member variable of the base class). Then your main game loop can run each entity's sequencer every frame, for instance. Personally, I have a centralized handler in my scene manager that keeps track of all sequencers in the scene -- that way only objects that actually have a sequence to execute are considered.


I should probably tell you, I'll be spawning those entities the same way, and assigning them tasks in a simple function, like so....


CreateEnemy(X-cord,Y-cord,life_points, behavior,graphic);
yield(40); //yields 40 frames
CreateEnemy(differentX,differentY,different_life_points,different_behavior,different_graphic);


These functions will take a random entity from within the array, one that's marked "dead", and bring it to "life" with the assigned data, behavior, and color/image.
Things like behavior will be in detail very similar to the script_enemy_main task I showed in my last post.
How would I go about assigning that behavior to said entity?
[/quote]

A behavior would simply be a function that decides what sequential actions to push to the sequence. So create a function that pushes a list of sequence objects to a sequence, and when you want to assign that behavior to an entity, just call the function so it automatically takes care of generating the proper sequence. If you want a repeating behavior, you can tack on a sequence object at the end of the sequence that calls that function again once all sequential actions have been performed, or even calls a decision-making function that will decide what behavior to select next.

This can be done using any message-passing (event handling) mechanism. Personally, I created C++ templates for member function pointers and arbitrary arguments so that it's very simple to call arbitrary functions on specific objects using just one type of sequence object (that way, I don't need to create a different type of sequence object for every possible action; just member functions).

If you need to interrupt the behavior, you can simply clear the sequence queue.


I'm barely following this. Please allow me to try to break it down. Correct me where I'm wrong.
The sequencer manages the tasks by looking through them in a queue.[/quote]

Yes.


The actual function (ex. Move(); ) will have its own sequencer that consists of a loop that sets the entity's new x-y coordinates every loop. If I'm not mistaken, a break; somewhere within that loop will be equivilent to a yield, right? Because it will come around to finishing that sequence next time the entity will get focus in the next loop, it will proceed.[/quote]

Yes, pretty much equivalent to a 1 frame yield.


But here's the part I'm confused about.

- Check if the sequence object is still alive -- yes it is, because the ship is not yet at 500,10, so the sequence object has not yet marked itself for deletion -- in that case, break out of the loop and ignore the rest of the sequence objects for this frame (you don't want to execute them yet because the ship is not done with its first action)

This only explains how to make the bits of sequences themselves to be executed over time. The Move() is called, it's executed over time. But suppose I have two (or three) Move() sequences. What if I want those sequences to be called at different times? Suppose at frame 50, I want the entity to move to (30,20) over time, but at frame 150, I want it to move again at (100,130) over time? And it takes 30 frames for it to get to one position to the other? How would I go about setting those sequences to do it at their respective times? Keep in mind, I want to make it possible to keep it as simple as

main_enemy_task{ ///frame zero
Move(30,20); ///there is a sequencer to actually move it, right? Suppose it takes it 30 frames to move it to this location. This is done at frame zero
ShootPattern1()// this would also have a sequencer, right? This is also done at frame zero
yield(100); //by frame 30, the entity has has reached its destination, and Move() would disable itself. right?
//however this means the entity will be "sitting" for 70 frames. Since there's no Move() to tell it to move. ShootPattern1() is never disable itself, so it'll execute every frame.
Move(100,130); //There is another sequencer to move it. The entity starts moving at frame 150.
yield(100); //yield for 100 more frames
suicide(); //a sequencer to "kill" the entity will start executing at frame 250.
}

[/quote]

I mentioned a SeqCountdown object I used -- probably the simplest way to do it. If you want to wait 10 frames, set the counter to 10, and make the countdown sequence object decrement that counter every frame until it finishes -- when it reaches that point, it marks itself dead and the sequencer moves on to the next item in the list. It's like a "spacer" of sorts.

In your example, since the object moves and fires bullets while moving, you'll have to have two separate sequences that are executed concurrently; one that moves, one that shoots. Or you could create a moves-while-shooting sequence object -- keep a counter of the frames since movement started, and if the frame is a multiple of, say, 10, generate a bullet.

You could also have a sequence object that can hold two sub-sequences. Every time it's executed, it also executes those two sequences under it. That sequence object could self-destruct once either one of the sequences is finished. It could contain both the moving sequence and the forever-repeating shooting sequence, and as soon as the moving sequence is finished, it "kills" the shooting sequence too, and then marks itself for deletion, allowing the structure that tracks it to move on to another sequence...


The object may be reused, too. So later on, it may be assigned a new sequence of sequences. How would i assign that?
Please answer the every question, especially the first two, those are important.
Also, thanks for the help. I'm starting to get it, but the idea of sequences for sequencers are confusing.
My worry is that it will try to execute the sequences from the beginning. I don't want Move(30,20); to be called every frame because main_enemy_task is an instruction for an object which is in an infinite loop.
[/quote]

I think the answer is the same as for the first question -- a member function that pushes a predefined list of sequential actions to a sequence.
I think you're trying way to hard to solve your problem. It's a neat idea, but there's a much easier way to solve it. In my Shump (Named Alpha and omega, links below), I have a simple scripting system that setups up my levels, enemies, weapons, sounds, and backgrounds. The Level script file basically lays out which enemies, backgrounds, and sounds/music come into the game based on elapsed time.

When a stage starts, I open the level file and store off in a sorted list the enemies as they appear in the game. This information includes the X/Y coords, what weapons the enemies have, the strength, initial velocity, etc.

Each game loop, I check if it's time to release an enemy (or background, sounds, upgrade object, etc.) If it is, i add that enemy to the list.

Each enemy has a special function called each frame. For what you're trying to do, you could assign one of those functions that gets run every frame, and check the time it's been on screen to see if you need to do something special:


void EnemyFunction(tEnemy *pEnemy)
{
uint_32 u32ElapsedTime;
tWeapon *pWeapon;

/* If enemy is new, init some values */
if (pEnemy->bIsNew == TRUE)
{
pEnemy->bIsNew = FALSE;
pEnemy->StartTime = GetCurrentTime();
}

/* check what the enemy should be doing based on time on screen */
if (EnemyIsWithinTime(pEnemy, BEGIN_HUNT_TIME))
{
/* move towards the player, based on it's speed */
MoveEnemyTowards(pEnemy, GetPlayerShip()->GetLocation());
}
else if() // whatever
{}

/* update the weapons on the enemy, loop through them all and fire if time */
LOOP_LIST(pWeapon, pEnemy->pWeaponList)
{
if (pWeapon->IsTimeToFire())
{
/* shoot towards players */
ShootWeaponTowards(pWeapon, pEnemy->GetLocation(), GetPlayerShip()->GetLocation());
}
}
}


Just a thought.

Here's the links to my code(Src) and exectuable (Exec) for my shump. I wrote it back in 2000-2001, but it's still good IMO.
Shump Src

Shump Exec

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

You could give each entity a sequencer (as a member variable of the base class). Then your main game loop can run each entity's sequencer every frame, for instance.


So is the sequencer an object? A function? What exactly is it?

ENTITY arrayofentities[1000]; //probably won't be using more than that at any given time


struct arrayofentities
{
float x,y,speed,direction;
bool enabled;
VERTEXDATA vertices;
SEQUENCER sequence; ///or would it be void sequence(); ??
};


struct SEQUENCER
{
void Move(posX,posY,weight,initial_speed);
}


This is just a quick thing I made in a few minutes, but where would I go from here? And how would Move() function definition look like?
It's supposed to do things over time. How would the loop for it look like?

This topic is closed to new replies.

Advertisement