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.