Welcome, Guest. Please Login or Register.  • Help
SMF Underground
+ SHMUP-DEV » SHMUP DEV FORUMS » Assistance
|-+ Stage Design, Enemy objects and logic

Pages: [1]   Go Down
0 Members and 1 Guest are viewing this topic. Topic Tools  
Read February 16, 2011, 08:27:44 AM #0
Tukun

Stage Design, Enemy objects and logic

I'd first like to thank the community for helping me get this far.
For the most part, my engine is almost complete. I've successfully implemented a point system, player controls and animation, stage layout and objects within it, camera and matrix settings, text and display, sound and volume control, bullet and enemy controls, structures, functions, and with the community's help- bullet managment and pixel-perfect animation quality.

But before I can move on any further, there's one last thing I have to implement before moving on to actually scripting my game.
-events.

There's a very special kind of logic I'm trying to implement in the scripting development. I want each of my "enemy" objects to be called just once for them to play out their entire behavior. That is, I call them in a function or of the sort just once, anywhere within the script, and they play out their entire behavior in that one call over a given time.
But that's the problem. I have no means of stretching their behavior over several frames. What I'm looking for is a sort of yielding, which is similar to using return in functions. What differentiates yielding from function returns is that at a later point we can reenter the thread and carry on where we left off. When you exit a function scope using return the scope is destroyed and we cannot reenter it, e.g., The reason for this is because, as an example, have an "enemy" object spawn, move then wait a few frames, shoot some bullets all while waiting several more frames, and then move out of the screen- a function that marks it "dead" is automatically called when it's out of view.
I also need a way to implement "enemy" object behavior, so I can good control over exactly what they do. There's a game engine already out there called Danmakufu that does this in scripting quite well. Its structure looks like this.
Code:
script_enemy someenemy{
@Initialize{ ///this is run once, as soon as the thing spawns
loop(50){yield;} ///wait 50 frames
shootstuff; ///this is not in effect until 50 frames later
loop(100){yield;}///wait 100 frames
enemy_SetAngle(0); ///this is not set until 150 frames after the thing spawned
enemy_SetSpeed(2); ///this too
}

@MainLoop{ ///this loop runs every frame
yield;
///the yield tells it to, "pause for this frame,
///and proceed to other pending functions/tasks
///in our case, the functions called @Initialize
}
@Finalize{//this runs once upon the enemy's death
explode();
}
task shootstuff()
////this function makes this enemy object shoot
//60 bullets in a circle, every 20 frames, 40 times over.
//any "yields" outside this thread have no effect on this one, and
//this thread continues its execution next frame, along with
//any other task/function called within @Initialize brackets
{
let angle=0;
loop(40){
loop(60){
CreateShotA(GetX,GetY,1,angle,RED01);
angle+=360/60;
}
yield(20);
}
}

task explode()///this function makes 40 bullets spawn in a circle upon this
///object's "death".
{ let angle=0;
loop(40){
CreateShotA(GetX,GetY,1,angle,BLUE02);
angle+=360/40;
}
}
}

And implementing/calling this enemy is also quite easy.
Code:
CreateEnemyFromFile("someenemy",xcordinate,ycordinate,spawnspeed,spawndirection,somevaluetopasstoit);

I don't expect it to be this easy of course, but it's quite tricky getting the layout to be similar to it. I want to avoid putting things into my infinite loop, because if I will, they'll do it every frame.

Code:
void render_one_frame(){ ///function called in my infinite loop
        d3ddev->BeginScene();   
        d3ddev->Clear(NULL,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,255),1,0);   
        d3ddev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);   


stage_events(); ///stage stuff goes here. Only 1 stage, but lots of
///bullets, objects, camera movements, scripting stuff in here.
///how can I stop it from starting at the beginning every frame?
///I NEED a yield function that allows me to resume where I left off
///c++ is the best language to program games in with an iron-fist control, right?
//so why is there no incantations and coroutines in c?

render_livingobjects(); //similar to render_bullets
render_staticobjects(); //special effects 2D and 3D
render_background();    //3d Background
render_cameracontrols();
  render_bullets();       //drawprimitives and stuff goes here
render_text();          //point system

        d3ddev->EndScene(); 
        d3ddev->Present(NULL,NULL,NULL,NULL);
framecount++;         ///framecount goes up once a frame
}

I feel like I'm in a bind.
Offline  
Read February 16, 2011, 04:11:33 PM #1
motorherp

Re: Stage Design, Enemy objects and logic

Of the top of my head there's several possibilities you could explore.  One would be to use parametric equations in your behaviour functions.  Each entity would call its associated behaviour function each frame passing in the time that entity has been active for which would act as the paramter for your equation.  The equation would then return the current state (position, velocity, etc) for that entity.  A popular method for controlling entity movement using this kind of scheme is by using splines.  Take a look in the tutorial section, I've written an article in there all about spline motion.  You could use similar parametric equations to control lots of different aspects of your entities.  You could even combine this with time envolopes in your behaviour functions for switching between equations and building up more complex behaviours.  You could use function pointers or polymorphism to keep behaviours and entities seperated from each other so you can then mix and match for flexibility.

If you wanted to implement something closer to your Danmakufu example, then you'd probably have to use multi-threading.  Each behaviour would run in its own thread seperate from the main game thread such that you can use wait functions in your behaviours without disturbing the flow of your main game loop.  This would require that you ensure your entity accessors/functions are thread safe and also that you take care to not allow your behaviours to alter entity parameters at critical periods of your main game loop which could cause funny bugs/artifacts such as changing your entity position between effect rendering passes for example.  Its all do-able but you just have to be more careful since threading issues can be pretty random and hard to track down and debug.



Offline  
Read February 16, 2011, 08:26:27 PM #2
Tukun

Re: Stage Design, Enemy objects and logic

So multithreading is the best way to tackle at this? I guess it would make much more sense to use that, given that I can suspend threads but not functions. I have little knowledge of using threads altogether though. I'll have to look into that. Thanks for introducing the idea to me.
I made a new topic here on the subject because I don't like the idea of splining and polymorphism. It seems to make things more complicated mathematically, in my personal opinion. Having to make complex equations for simple motions seems like a big hassle, and manipulating it even more so. For now, it's also beyond my scope.
The reason I want to use multithreading-, as you put it, is for ease of access to behavior. I like Danmakufu's style of handling it. I can easily make complex movements and bullet patterns for any given object. Controlling its entire scope of execution is as easy as counting how much yielding I want. I'm guessing a thread equivalent is something like thread_suspend().
But there aren't many good multithreading tutorials out there, especially in the purpose for games. If you manage to find one, please post a link for it here. I'll be studying more about multithreading in the meantime.
Thanks again for bringing up the idea.
Offline  
Read February 16, 2011, 09:20:08 PM #3
motorherp

Re: Stage Design, Enemy objects and logic

It's hard to say what the 'best' way is, there's many options and it depends on how you intend to use the system.  In my mind there's really two issues here which are how you actualy define the motion of your entites and secondly how you schedual events for those entities such as changing motion or firing bullets or whatever.  Using motion equations has the benefit of reducing the demands on your schedualer whilst also producing more natural looking motions since they dont require scripting, but its not necessary to go down that route, it really depends on the type of motions you wish to produce.  As for the schedualing side of things, multi-threading could give you a nice solution but it really depends on the number of entity behaviours you intend to have running simultaneously.  If you want to have many at once then it probably wont be the best solution since thread context switches can be pretty slow, as well as being slow to create and destroy and being fairly resource heavy, so if you have many it could cause a big drain.  You could try and counteract that to a certain degree by using thread sleeping rather than your own timer based wait function to pause execution between events in that thread, that way if you only used these threads to occationaly schedual events and not for any kind of regular update logic then most will be suspended at any one time which will stop them switching with each other all the time.  That isn't really viable if you want accuratly timed events though since sleeping a thread means you relinquish control over when exactly it is resumed which is now under the control of the OS after the sleep time is over and can be delayed.  Therefore in the case where you want many simultaneous behaviours with accurate timing then you'd really have to implement your own timeline class to manage your behaviours at what they should be doing at any one time in which case there's no reason to have it in a seperate thread, instead your timeline/schedualer/behaviour manager whatever might as well live in your main loop.

PS:  Just in case that wasn't clear, if you wrote your own timeline class this would be something along the lines of maintaining an array of events were each event has a timestamp, a function pointer to the code to be executed at that time, and an entity pointer to define which entity to apply that function to.  The schedualer would keep track of the global time and would be updated once per frame.  During the update, if the tracked time passes a timestamp of one of its held events then it would execute that event.

« Last Edit: February 16, 2011, 09:40:36 PM by motorherp »

Offline  
Read February 17, 2011, 06:49:51 AM #4
Hornet600S

Re: Stage Design, Enemy objects and logic

Essentially you already took the right route, your scripting engine needs a "yield" command. To implement that you basically have to mimic what an OS does when implementing multithreading:

You have to store the current script-engine-state and then leave and return that state to the caller inside some struct. In the next frame you call the script engine and pass it that struct again. The engine sets its internal state according to the saved one you passed it and continues execution after the last command (which was the yield).

An OS would store/restore things like CPU registers, stack pointer, program counter, you would store/restore things like line-nr, function-name, content of local variables, whatever.

Such a state-struct could look similar to this (just pseudo-code, as always):

Code:
struct LocalVariableState {
  unsigned int var_name_hash;
  Variant value;
};
struct ScriptState {
  unsigned int command_index;
  unsigned int function_name_hash;
  LocalVariableState *var_states;
  unsigned int nr_of_var_states;
};

Each entity gets the ability to store such a state:

Code:
class GameEntity {
  private:
    ScriptState script_state;
    Script *script;
    ....
  public:
    ....
    void UpdatePerFrame(void);
};

And then you call it this way:

Code:
void GameEntity::UpdatePerFrame(void)
{
  if(script) {
    script->RunByState(script_state);
  }
}

The script engine's RunByState-method would look similar to this:

Code:
void Script::RunByState(ScriptState &p_script_state)
{
  RestoreState(p_script_state);
  Continue();
  StoreState(p_script_state);
}

I'd not do real multithreading to implement such a logic, that's like breaking a fly on the wheel.
Multithreading isn't particularly useful for such things and not really efficient. I mean:
you don't want to run things in parallel, you just want to stop and restart something at a very well defined point.

Of course you may use multi-threading, but in a totally different way:
You could use it to update multiple entities at the same time, for example (thread A updates entities 0-100, thread B updates entities 101-200). Of course you shouldn't use more threads than the number of hardware-threads your CPU supports.
And of course this does only work if there are no dependencies between the entities.

Btw.: check out LUA's coroutines, that's exactly what you want.


"When the Amiga came out, everyone at Apple was scared as hell."
(Jean-Louis Gassée - Head of Macintosh development at Apple)
Offline  
Read February 17, 2011, 09:35:15 AM #5
motorherp

Re: Stage Design, Enemy objects and logic

Interesting Hornet, I've not seen something like that before, just how easy is it to extract and restore local variables and continue execution mid function like you suggest and does that have any issues?

Anyway, like Hornet says, multi-threading is a bit of a red herring, it was just the first thing that popped into my head which would allow all your behaviour to run in one function stretched over time so I thought it was worth exploring.  However if your needs are small enough, for example you're making a boss-rush game and thus you only need to control the behaviour of one entity which could be quite complex, then I'd say go for it with multi-threading.  Chances are on most hardware these days there will be a spare hardware thread to take it since you're not currently using multi-threading for anything else and it would save you having to implement some form of schedualar or yielding.  Like discussed though, for larger scale needs its not really a viable option.

Secondly, I guess this is where my opinion might differ from others.  The suggestions I've been making have been ways you could go about getting the results you want without the need for yield functionality.  In my opinion, code isn't meant to yield and restore like this and if your design requires that you must yield and restore then its usually indicative that your design could be improved.  For example take the following functionality you might want to employ:

Entity::Run()
{
  FuncA();
  Yield(600);
  FuncB();
}

Using an event schedualar like I discussed at the bottom of my last post, this would become something like:

Entity::Run()
{
  FuncA();
  EventSchedualer::AddEvent(600, &FuncB, this);
}

Here the event schedualer will take care of calling FuncB on the entity after 600 time units have passed giving you the same results without the need to break the code flow.  I'm sure there's pros and cons to both techniques though, I'm just not personaly comfortable with the idea of trying to bend the code flow.

PS:  For clarity, what exactly are you talking about when you mention scripting?  I'm a bit confused whether you intend to do all this in c++ or employ a full scripting engine with external scripts (or maybe a bit of both)?.  Usualy the term scripting would mean the latter but the way you talk about it makes me think the former.

« Last Edit: February 17, 2011, 09:45:22 AM by motorherp »

Offline  
Read February 17, 2011, 10:01:21 AM #6
Hornet600S

Re: Stage Design, Enemy objects and logic

@motorherp
Quote
just how easy is it to extract and restore local variables and continue execution mid function like you suggest and does that have any issues?
If that's easy or a hazzle depends on the quality of your scripting-engine's code and if you planned for that feature Smiley
For example, if you compile your script to some pseudo-assembly (like JavaVM code) then (re)storing the current state might be as simple as (re)storing some virtual registers and the virtual machine's stack content (= local variables, internal return adresses etc).

If you grow wild with internal objects on the heap etc. it gets more complex, of course. But in general it shouldn't be too difficult to implement it, at the end it just boils down to be able to copy/overwrite the important internals.
To ease integration you should design your script-engine for such things in the beginning, for example by stuffing the vital internal state info into some isolated class:
Code:
  class ScriptState {
    ...
    // make it "transferable"
    ScriptState& operator=(const ScriptState &p_src);
  };
 
  class Script : public ScriptState {
  };
 
  // or
 
  class Script {
    ScriptState state;
  };

Quote
In my opinion, code isn't meant to yield and restore like this and if your design requires that you must yield and restore then its usually indicative that your design could be improved.
I agree. Especially going the event-way is something to strongly recommend, last not least because it allows for very easy integration of communication between entities.
The yield-thingy is nevertheless an interesting feature, and if you can integrate it into your scripting system, then do it. It has the benefit to make the script-writing easier, I mean, the script's code flow is more serial.


"When the Amiga came out, everyone at Apple was scared as hell."
(Jean-Louis Gassée - Head of Macintosh development at Apple)
Offline  
Read February 18, 2011, 07:54:21 PM #7
Tukun

Re: Stage Design, Enemy objects and logic

Thank you hornet for the useful input.
For clarity, what exactly are you talking about when you mention scripting?  I'm a bit confused whether you intend to do all this in c++ or employ a full scripting engine with external scripts (or maybe a bit of both)?.  Usualy the term scripting would mean the latter but the way you talk about it makes me think the former.
For the moment, I want to hardcode everything into C++. Later when I'm familiar with it, I want to move onto scripting and have the object behaviors/definitions done externally. The problem, as implied, is actually doing things over time. I'm not sure how to tackle that without putting if(framenumber==whatever) statements everywhere, which is why I'm so obsessed about the idea of yield that Danmakufu so successfully does. Being able to delay object creations and function executions without having to redo everything from scratch. Getting these objects to inherit behaviors is also very important to me for ease of access and manipulation of what I want and when these objects to do [stuff].
Here's an example of Danmakufu's stage script. The stage script is a multipurpose document used to spawn enemy objects and bullets. The enemy objects themselves run seperate scripts written especially for them that defines their behavior. And more than one object can run the same script. So I'm able to make several enemies of the same type in the stage.
Code:
script_stage_main{
@Initialize{
intro();
while(stage_start==0){yield;} //delay everything below until the player selects "start"
CreateEnemyFromFile("Behavior1.txt",xpos,ypos,valuetopass,sprite2);
loop(400){yield;} ///delay everything below by 400 frames.
CreateEnemyFromFile("Behavior 4.txt",xpos,ypox,valuetopass,sprite4);
CreateEnemyFromFile("Behavior 4.txt",xpos,ypox,valuetopass,sprite4); ///spawned at the same fame as the above
loop(100){yield;}///suspend the next functions by 100 frames.
CreateEnemyFromFile("Behavior 1.txt",xpos,ypos,valuetopass,sprite1);
///it doesn't matter if the last object inherits the same document as the first.
///it runs from the beginning of the script, the two objects are practically separate
}
@MainLoop{
DoThisStuffEveryFrame(); ///self explanatory
}
@Finalize{
if(lives==0){ask_continues();}
}
}

The definitions of the objects' behaviors is similar.
Code:
[Behavior 4.txt document]
script_enemy_main{
let angle=SomeMathFunction();
@Initilaize{
loop(50){yield;} ///wait a few frames before doing the executions below
stuff();
morestuff(); ///executed the same frame as the above despite the above function having yields within it
}
@MainLoop{
dothiseveryframe();
}
@Finalize{//runs when the object's "life" reaches zero or when it leaves the screen
explode();
}

function stuff(){
while(player=alive){shootbulletatplayer(); yield} //shoot a bullet at the player every frame
}

function morestuff(){
///motion functions here.
///function stuff()'s yielding does not affect the execution procedure of this thread
}
function explode(){bulletseverywhere();}
}

But how can I get object index 1 to inherit the behaviors stored in the given document?
Here's what I have. The struct for my objects are similar to that of the bullets. They are made in an array, and can be reused like bullet can. The reason is, as you may have guessed, because I'll be using numerous objects at any given time.
Code:
struct LIVEOBJECT{

bool alive;
float xpos;
float ypos;
float direction;
float speed;
int idnum;
OBJDATA objdata; //stuff like sprites/texturs and UV coordinates stored here

}

And just like the bullets struct, this struct has its own vertex buffer, DrawPrimitive function, all that small stuff.


Now, here's the function called in my infinite loop that renders a single frame.
Code:

void render_one_frame(){


        d3ddev->BeginScene();  
        d3ddev->Clear(NULL,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,255),1,0);  
        d3ddev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);  

events(); ///I want to avoid putting things here because they'll be done every frame
///if I do so. I need some sort of yield function that delays function executions I call
///throughout the game-time so I don't have to make an if-then statement
///every time I don't want my object's functions to be done every frame.
///this is the obstical that's preventing me from advancing.
render_3D_background();  ///this is done.
render_cameracontrols(); ///this is done.
render_livingobjects();  ///similar to render_bullets. Objects inherit behaviors written in a document.
render_staticobjects();  ///special effects. Similar to render_bullets and render_livingobjects
  render_bullets(); ///bullets drawn here.
render_text(); ///point system. This is done.

        d3ddev->EndScene(); 
        d3ddev->Present(NULL,NULL,NULL,NULL);
framecount++; ///framecount goes up by 1 every frame.
}


If I wanted to oh, say.. make a bullet spawn every 20 frames, I don't want to have to do this in my events() function.
Code:
{static framen=0;
if(framen==20){CreateShot(X,Y,whatever); framen=0;}
framen++;
}

Of course, I'd LOVE to be able to do this...
Code:
///events function is called once, outside my infinite loop.
Sleep(1000); //delay a second.

for(int i=0; i<20; i++){
for(int i=0; i<60; i++){
CreateShot()///shoot 60 bullets in a circle
}
Sleep(MilisecondsPerFrame); ///wait x-frames, so it shoots 60 bullets, 20 times over, x-frames waiting time every round.
}

////nothing else happens beyond this point.
////since events() is called once, and outside my infinite loop, this would
////be done in chronological order
///if it was this easy, making a yield function would be as easy as defining it
///int nextframe=framecount+1;
///while(nextframe!=framecount){sleep(1);} ///not correct, but get the idea..

Unfortunately, the Sleep function delays ALL processes, not just the processes executed by the thread it's called in like yield does.
It's ridiculous. I want to have a timeline that revolves around yield.



I know it's possible to use the infinite game loop to do the things I want when I want by using a timer or framecounter, but it gets obnoxiously tedious having if(framecountnumber==whatever) statements everywhere to do it. Not to mention the "stage" code that handles everything from camera motion to enemy spawns will be incredibly long to begin with, and having it in the middle of an infinite loop with if-then statements everywhere that tells what happens when is so obsurd it's laughable.
« Last Edit: February 19, 2011, 05:09:45 AM by Tukun »
Offline  
Read February 19, 2011, 03:25:24 PM #8
motorherp

Re: Stage Design, Enemy objects and logic

To be honest I dont really see why you're so resistant to using your own timers and counters.  If the issue is that you dont want to write out these implementations everytime then you should wrap those implementations into a function or simple class which you can re-use when you need it.  You should really be trying to do those kind of things anyway to keep your code clean and easy to refactor.  Really the only tedious thing here is that you wish to hardcode every event in your game, but having a yield function isn't going to help you there.

Also consider what you would have to do to implement yield functionality (this is something you would have to implement yourself, C++ as far as I'm aware doesn't have any built in yield capabilities).  Firstly you'd have to have a way to store the current function position and local data as Hornet describes.  Then secondly, say you wanted to yield for 'x' frames, then some other code or manager would need to run each frame to track the frames passed since the original code was yielded and then handle its restoration at the correct frame.  This other code therefore would itself require some form of counter or timer, so really you've not saved yourself needing a counter by implementing yield, you've simply moved that counter else where whilst also increasing the complexity of your code and the amount of work needed to be done by the cpu since you now have to handle local data caching and restoration.

Also one other thing I've noticed is that you seem intent to script your entire game inside one function in a procedural manner.  One thing that will help you to find better solutions to these problems is to get familiar with object orientated programming.  OOP is really the main strength of C++ and so you really shouldn't ignore it.  For example you could create a behaviour class along the lines of:

Code:
class BehaviourBase
{
   Entity* m_pEntitiy;

   virtual void Update() = 0;
};

class LinearMoveBehaviour : public BehaviourClass
{
   float m_xSpeed;
   float m_ySpeed;

   void Update()
   {
      m_pEntity->m_pos.x += m_xSpeed;
      m_pEntity->m_pos.y += m_ySpeed;
   } 
};

class AccelerateMoveBehaviour : public BehaviourClass
{
   float m_xSpeed;
   float m_ySpeed;
   float m_xAcc;
   float m_yAcc;

   void Update()
   {
      m_xSpeed += m_xAcc;
      m_ySpeed += m_yAcc;

      m_pEntity->m_pos.x += m_xSpeed;
      m_pEntity->m_pos.y += m_ySpeed;
   } 
};

You'd create all the types of behvaiours you needed as inheritted classes of the BehaviourBase class like I've done above.  You'd then have a behaviour manager which had an array of behaviour pointers which you add all your behaviours too and is resposible for calling update on all those behaviours each frame.

That's just a suggsetion, of course feel free to to implement your entities and behaviours etc how you see fit for your game, but hopefully you can see how breaking things down into objects like this is much more flexible and readable than trying to script all your events into a single gigantic linear function.


Offline  
Read February 19, 2011, 09:23:07 PM #9
Tukun

Re: Stage Design, Enemy objects and logic

But I already have everything as objects. Forgive my vague code up there, I was just trying to make it short to get the main idea across.
But the problem isn't object managment. Nothing can be more comfortable in C++ than using objects, and I have that part done. What my probem is the implementation of all the events in one, linear function.
It seams there's no way around it other than to multithread, and to have that thread give up its timeslice so all the updating and graphics can get done. Since Sleep() is the only way to stop function process, it seams I'll have to resort to that method.
If someone has a better idea, please do tell.
Offline  
Read February 19, 2011, 10:00:10 PM #10
motorherp

Re: Stage Design, Enemy objects and logic

...the implementation of all the events in one, linear function.

Thats your problem right there and the point I've been trying to get across.  Simply 'dont' try to implement all your events into one linear function, that's the wrong way to go about it.  Treat your events or behaviours as their own objects.  Either have each entity manage its own behaviour events which internaly track time/frames so they know when they need to do something, or have a manager which contains these events and triggers these events when they're needed like I discussed earlier when talking about implementing an event schedualer.
« Last Edit: February 19, 2011, 10:03:30 PM by motorherp »

Offline  
Read March 03, 2011, 02:56:12 AM #11
Tukun

Re: Stage Design, Enemy objects and logic

Alright, so after looking throughout my code, I've figured that it's best to have the threads within the infinite loop. Here's the order things will get down. All of these will be within the infinite loop, and threads will give up their time slice before finishing up, so the entire game won't be over within one frame. Leaving technical directx stuff out, here's the order.

Code:
infinite_game_loop
{

Stage_events();
entity_events();
special_effect_events();
bullet_events();

render_entities();
render_bullets();
render_background();
camerasettings();

}


All _events functions create their own thread (the first time they are called) for the given object. However, whenever a yield(); function is implemented within that thread, that thread gives up the rest of its timeslice until it is called again in the infinite loop.

For example, lets say I spawn an entity in the Stage_events.  The object is called by a SpawnEnemy(parameters) function, and when a yield(); is called in Stage_events, Stage_events gives up the rest of its timeslice. Now, entity_events will have a manager for all objects that are entities. An entityobject with index of 0 (assuming 0 is already "dead") will be assigned a thread, given by SpawnEnemy(parameters). It will run that thread until a yield(); is implemented, and move on to the next object, that too with its own thread to do its tasks. This repeats until all of the object do what they need to for one frame (I.e. fire a bullet, movement, whatever implemented in the thread they're assigned). This happens with all _events. The Render_  functions will do the actual drawing of them. As in, setting the vertices in the right positions and stuff, and DirectX stuff.


Is this effective? Is this sort of thing possible? I'd need to implement something like a pointer to a thread. Or maybe passing a thread? If it is, it'd make assigning lots of entities the same assignment without conflicting with one another. Simply calling them and special effects in stage_events. What would be the best way about doing it? is Boost threads effective? Or Lua? Or what?
« Last Edit: March 03, 2011, 02:58:26 AM by Tukun »
Offline  
Read March 04, 2011, 06:33:17 AM #12
LorenzoGatti

Re: Stage Design, Enemy objects and logic

Making a thread sleep is a very crude way to wait, and making a thread for every enemy and bullet object is horribly expensive. Moreover, unpredictability of thread scheduling could easily alter the simulation and cause horrible bugs.
I think you don't actually need these threads: you can have a queue of events that are scheduled to happen in future frames, containing commands that can be executed globally (e.g. "spawn a fighter at 125,55") or in the context of an entity (e.g. "set waypoint to halfway between my leader and the player"). In the latter case all data that would be local to the thread can be part of normal entity data or stored in appropriate memento objects attached to the entity.
Events would schedule other events at appropriate times, based on the actual time reference of the game (frames) and not on real time; for some categories of entities and events the scheduling could be implicit (for example, all starships apply thrust every frame)
Offline  
Read March 04, 2011, 07:59:18 AM #13
Hornet600S

Re: Stage Design, Enemy objects and logic

I second LorenzoGatti in every aspect.
Like I said somewhere above:
Quote
I'd not do real multithreading to implement such a logic, that's like breaking a fly on the wheel.
Multithreading isn't particularly useful for such things and not really efficient. I mean:
you don't want to run things in parallel, you just want to stop and restart something at a very well defined point.
Threads are useful for heavy parallel processing (and/or to keep a GUI responsive). And regarding efficiency: every thread that isn't accompanied by something like a CPU core is most likely one thread too much.

If you really want to stop processing of an entity in the middle of a script you should implement the yield functionality like outlined above (virtual machine state (re)storing) or simply chose LUA for your scripting and use its coroutines.

But even better would be to implement an event system like LorenzoGatti and motorherp said.


"When the Amiga came out, everyone at Apple was scared as hell."
(Jean-Louis Gassée - Head of Macintosh development at Apple)
Offline  
Read March 04, 2011, 12:47:12 PM #14
Tukun

Re: Stage Design, Enemy objects and logic

Making a thread sleep is a very crude way to wait, and making a thread for every enemy and bullet object is horribly expensive. Moreover, unpredictability of thread scheduling could easily alter the simulation and cause horrible bugs.

But I'm not going to actually try to Sleep() threads. The CPU can do a lot in 1 millisecond. I guess I shouldn't call it multithreading, because it will only work with one thread at a time.
I don't think I explained it in enough detail...

What I'm trying to do, is set up multiple threads, but only have it work on one thread at a time. For example, it first jumps to stage_events thread, runs that. Once a  yield is called, and it jumps out and back to the game loop. This leaves the rest of stage_events processing to the next frame.
Then the object_events is called. This is actually a function. The threads are in the structurs for the objects. It searches for "living" objects, and each one that is marked alive, runs its own "thread". Again, when a yield is called, it jumps out of that thread and leaves the rest of the processing for the next frame like the stage_events. It does this for every object. Once the next frame comes up, it goes back to stage_events, and attempts to finish where it left off.

I know running parallel threads at once is very shakey, because when I did a few tests, I noticed that it's difficult to get events to sync. That's why I'm trying to simply "jump" from thread to thread, working with small bit of each thread at a time. But they're not really running parallel, so the term "multithreading" is a bit misused, huh? Anyway, that's what I wanted to know, if this sort of thing is possible and effective. What I'm worried about, is when it "jumps out" of the thread, if it will be like using a return in functions, where the rest of the scope is destroyed and it starts over when called to run again.
you can have a queue of events that are scheduled to happen in future frames, containing commands that can be executed globally (e.g. "spawn a fighter at 125,55") or in the context of an entity (e.g. "set waypoint to halfway between my leader and the player").

This is exactly the sort of thing that I want to avoid. I find having to work with a global timeframe very tedious and unnecessary. It's essentially the same as putting- if(framenumber) then{whateverhappens} everywhere I want an event to happen. I call this sort of programming of events "perspective-oriented". By scheduling events from the perspective of the first frame and setting future events through that. What I'm looking for is "progressive-oriented", meaning I won't have to worry about things like framenumber. When an event happens, that event becomes the new "zero-frame perspective", which is why I want yielding so much.

Lets say I want to fire 30 bullets in a circle, ten times (one circle of bullets each frame) This is how I want to write it.
Code:
///firebullet prototype(x-cord,y-cord,angle,speed)
///moveto(x-cord,y-cord)
SomeThreadForSomeObject{
moveto(50,50);
for(int i=0; i<31; i++){yield();} ///yield for that many frames
attack();///initializes the thread.
for(int i=0; i<60; i++){yield();} ///yield again for that much
suicide();
}

///thread for attack(). It's sort of a function, but I don't know how to properly do this...
void attack()
{  float ang=0;
for(int times=0; times<10; times++) ///happens 10 times
{
    for(int i=0; i<30; i++)
       {firebullet(100,100,ang,1.5);
        ang+=360/30;
       }
yield();} ///finish the rest next frame (each frame)
}


If I were to do this your way, I'd have to do it like this. Assume EventsForObject() is in an infinite loop.

Code:
///moveto(frame_number,x-cord,y-cord);
///firebullet(frame_number,x-cord,y-cord,angle,speed);
EventsForObject()
{static int framenum=0;
moveto(0,50,50);
attack(31);
suicide(61);
}

void attack(int whatframe)
{static int delaythis=whatframe;
while(delaythis>0)
{delaythis--; return;}

static int framenumber=0;
float ang=0;

for(static int howmanytimes=10; howmanytimes>0; howmanytimes--)
{
    for(int circle=30; circle>0; circle--)
       {
       firebullet(framenumber+whatframe,100,100,ang,1.5);
       ang+=360/30;
       }
}
framenumber++;
}



As you can see, not having to worry about whether declaring integers as statics or not, frame numbers, and knowing that the function tries to start from the beginning every frame makes things a lot more simple. By working progressively, I don't have to worry about any of it because every frame is my reference frame. All I have to do is put in a yield, and I move with the flow of the program. I find it much more simple than to have to worry about what happens on what frame.
« Last Edit: March 04, 2011, 01:06:13 PM by Tukun »
Offline  
Read March 04, 2011, 03:19:11 PM #15
Hornet600S

Re: Stage Design, Enemy objects and logic

Quote
But I'm not going to actually try to Sleep() threads.
Yes, you are. That's what your yielding does essentially.

Quote
it will only work with one thread at a time
Yes, and that's exactly the reason why you should not use threads. Because you're misusing them. You should implement the sequential behaviour you want without threads and without wasting OS resources needlessly.

Simply put:
if you don't want parallelism then don't use threads.

You use threads just because of one reason: to abuse the thread's private stack and the fact that the OS (re)stores it for you when (de)activating the thread. Instead of implementing your own state (re)storing.

You should definitely not do that. Not just because it's a bad habbit. You are also adding many sources for headaches to your program without need (for example: don't wonder if your prog behaves weird if you try to use OpenGL or similar APIs from within your thread...).
And while your 200 threads maybe still run on a fresh multi-core machine (I actually don't know) I guarantee you that it won't run on the iPhone or the Android you maybe want to port it to.

Have to repeat it Wink
if you don't want parallelism then don't use threads.

If you don't like the event approach then go for LUA coroutines. There you got a working yield() without threads.


"When the Amiga came out, everyone at Apple was scared as hell."
(Jean-Louis Gassée - Head of Macintosh development at Apple)
Offline  
Read March 04, 2011, 03:26:20 PM #16
Tukun

Re: Stage Design, Enemy objects and logic

So how does LUA implement it?
I've been looking at its source code (which is done in c).
But this coroutine thing is hard to find.... the code is all chaotic, I'm having a hard time following it.
« Last Edit: March 04, 2011, 03:32:10 PM by Tukun »
Offline  
Read March 04, 2011, 03:35:36 PM #17
Hornet600S

Re: Stage Design, Enemy objects and logic

I did not look into LUA sources, I can't tell you how it is implemented exactly.
But I already outlined the general idea somewhere above:

Essentially you want nothing more than to store your script-engine's current state when it encounters a yield() command.
Then later, in the next frame or so, you want to continue the script after that yield() command. That again means nothing else than to "inject" the previously stored state back into the script engine.

It's basically just a matter of being able to copy the important script-engine's internals from and to some safe memory, nothing more. No need for threads.

Alternatively (depends on your script-engine's design) you can also carry around one little script-engine per entity. Then you just need yield() to return to the calling C function and some continue-function to continue after the yield again (since the entity carries the script-engine plus its state that should be rather trivial).

« Last Edit: March 04, 2011, 03:40:02 PM by Hornet600S »

"When the Amiga came out, everyone at Apple was scared as hell."
(Jean-Louis Gassée - Head of Macintosh development at Apple)
Offline  
Read April 06, 2011, 01:20:06 AM #18
Tukun

Re: Stage Design, Enemy objects and logic

I took a close look into it, and found the source code from the engine I was looking for.
By the looks of it, he didn't even use LUA. Everything was done in C++ original code.
But I'm having a hard time following it. It looks as if though the tasks are object, managed by managment objects, which are managed by managment object managers. It's really confusing.
Can someone debrief it for me?
Offline  
Read April 08, 2011, 10:11:41 AM #19
Arpeggiodragon

Re: Stage Design, Enemy objects and logic

I've been using the angelscript library for handling scripts: http://www.angelcode.com/angelscript/ I don't know how it compares to LUA in terms of speed, (I imagine it could be up to 2x slower in places perhaps, though maybe not) but it makes running multiple script contexts easy as pie. There's no need to use threads, you just create a simple class that creates and reuses previous script contexts which your script object instances will use. I don't really even notice much of a performance hit from this until I have about 500 enemy scripts or so running "at the same time". Plus it is strongly, statically typed which is a big plus for me.

Sorry, don't know much about the LUA concurrency thing.
Offline  
Read April 17, 2011, 11:04:38 PM #20
the2bears

Re: Stage Design, Enemy objects and logic

It's been said better already by others, but there's simply no reason to use multiple threads in a shmup.  The best advice is: don't! Wink

Bill


the2bears - the indie shmup blog
Offline  
Read April 29, 2011, 06:01:37 PM #21
Hornet600S

Re: Stage Design, Enemy objects and logic

I recently found myself in need to program something like yield() manually in C. If you think about it it's really nothing, as you'll see. It boils down to a switch-case-block and a small per-object state-info-structure in its most simple form.

Here is how your little script from the first post would look like if done in plain C:
Code:
/*
This is your original snippet:

@Initialize{ ///this is run once, as soon as the thing spawns
loop(50){yield;} ///wait 50 frames
shootstuff; ///this is not in effect until 50 frames later
loop(100){yield;}///wait 100 frames
enemy_SetAngle(0); ///this is not set until 150 frames after the thing spawned
enemy_SetSpeed(2); ///this too
}
*/

/*
And this is how it could look like in plain C(++)
*/

struct EnemyStateInfo {
  unsigned int yield_position;
  unsigned int yield_counter;
  EnemyStateInfo(void) : yield_position(0),yield_counter(0) {}
};

void EnemyActivity(EnemyStateInfo &p_enemy_state_info)
{
  switch(p_enemy_state_info.yield_position) {
    case 0:
      // loop(50){yield;} ///wait 50 frames
      p_enemy_state_info.yield_position++; // now points to case 1
      p_enemy_state_info.yield_counter=0;  // reset loop counter to 0
    case 1:
      while(p_enemy_state_info.yield_counter++<50) {
        return;
      }
      p_enemy_state_info.yield_position++; // now points to case 2
    case 2:
      // shootstuff ; ///this is not in effect until 50 frames later
      shootstuff();
      p_enemy_state_info.yield_position++; // now points to case 3
      return;
    case 3:
      // loop(100){yield;}///wait 100 frames
      p_enemy_state_info.yield_position++; // now points to case 4
      p_enemy_state_info.yield_counter=0; // reset loop counter to 0
    case 4:
      while(p_enemy_state_info.yield_counter++<100) {
        return;
      }
      p_enemy_state_info.yield_position++; // now points to case 5
    case 5:
      enemy_SetAngle(0);
      enemy_SetAngle(2);
      p_enemy_state_info.yield_position++; // processing ended, point to default case (do nothing)
    default:
      break;
  }
}

Got it? The idea is to store the program-counter inside a struct and to continue processing where the program-counter points to the next time you call the function. Here in this case the program-counter is nothing else than a variable used in a switch-statement. Note how it is incremented inside the function if you want the program flow to advance and how it is not incremented if you want to "pause".

And stuff we want to survive through multiple function calls (in this case the loop-counter in the for-loop) is also stored inside that struct. Actually that's it.

I hope this clarifies things a bit more. It should demonstrate that no threads and no magic is needed to get the desired yield() functionality.
« Last Edit: April 30, 2011, 10:00:57 AM by Hornet600S »

"When the Amiga came out, everyone at Apple was scared as hell."
(Jean-Louis Gassée - Head of Macintosh development at Apple)
Offline  
Read May 02, 2011, 11:13:14 AM #22
VectorVanDoom

Re: Stage Design, Enemy objects and logic

i have no idea what the last line in the struct EnemyStateInfo is supposed to be, never seen that in C. i got missing specifier-quallifier-list on that struct, with gcc. are u sure u didnt sneak in some C++, where the type declaration is missing? Tongue
Offline  
Read May 02, 2011, 12:06:13 PM #23
Hornet600S

Re: Stage Design, Enemy objects and logic

You are right, it's not "plain" C, let's say it is C+ (also note the // comments), that's why I wrote C(++) inside the source-code above (but forgot to write the same in the other text...) Smiley
That last line is a normal constructor (C++: struct == class with default public) with its initialization list. In that case it's more or less equivalent to
Code:
EnemyStateInfo l_info;
// the following is done automatically during instanciation
l_info.yield_position=0;
l_info.yield_counter=0;
Quote
where the type declaration is missing?
C++ is as strong typed as C, there is no way to declare a variable without type (and let's praise Lord Stroustrup for that Smiley ).
Whenever I write such small snippets it's most likely near-to-real pseudo-code and/or a C/C++ mixture.
« Last Edit: May 03, 2011, 06:25:04 AM by Hornet600S »

"When the Amiga came out, everyone at Apple was scared as hell."
(Jean-Louis Gassée - Head of Macintosh development at Apple)
Offline  
Read March 09, 2012, 09:12:55 PM #24
Tukun

Re: Stage Design, Enemy objects and logic

Well, after a bunch of hair-pulling, typing, and coffee I'm back.
Oh, and thanks Hornet, for advising me off a dangerous and stupid path.

Anyway, since I couldn't do what I initially wanted to-  as Hornet said to me earlier, misuse threads to manipulate the stack in a way it wasn't meant to, I've figured the only way to implement what I wanted to without having any sort of counter to keep track of yields is to built my own system.

So I did just that. I made my own virtual machine. Oh, and the reason I went so far instead of simply using a counter is because I didn't want to be limited by the maximum number of whatever data type I'm using. For example, if I had an infinite loop in which there was a yield; call inside the loop - this would only allow me yield X number of times, which is very very bad for me.

Anyway, the problem is resolved and I'm posting this for anyone who was in a similar problem I was so they'll find a quick solution to this tedious problem. And, this may help other people who have already made games but want to implement more complex behavior for any given object, like say- player, or the player's bullets, or enemies or their bullets, or lights, or whatever. This will allow acute definition of behavior for said objects in chronological programming and eliminates counters along the way.

I've implemented threads by environment arithmatic. An environment is a struct that executes an array of codes, one code at a time, that was made by the parser. The code itself is never changed and is only written once, and it has crucial information like (push back the stack), assignments, function/task calls or (yield)s. Here, the environments are the virtual machine's threads and behave like such, so bare with me when I say "threads", I mean these especially made structs.
The virtual machine is fairly simple. It implements microthreading via task calls. Whereas in a function, the thread's flow of control goes straight to the function environment (suspending the calling thread's flow of control doing so until a return; is called or it reaches end-of-code).  Whereas in a task, a new thread is inserted in the calling thread's index + 1. The current thread index is incremented, because it's a task -call-, and like a function, the flow of control is brought to the task thread. Yielding a task suspends that thread by decrementing the current thread index. If it's at 0, it's then equal to the thread size -1.
Here's a small diagram so you can get the idea...

Oh, and a little source code.
The first is a dice game written in script in the first very short text document, the second is a simple function to get the cosine/sine.

Of course, it has c-like syntax, so function calls, PEMDAS, trig functions, if/else statements, and arrays are all supported. Pass-by-reference are handled via passing arrays, and instead of char/double/bool, I've implemented an abstract data type "let". (i.e. let a="String"; let b=5;  let c=true;)
It's fairly lightweight, and executes a very reasonable number of codes over time. So now, all I have to do is add game objects this abstract data type.
This means it'll be a polymorphic class that inherets all things a base class inherets. (a struct with an x-cord, y-cord, direction, angle, etc) and several things that may be specific for that class (a bullet class has an additional pointer to the texture it uses, width, height, and so on..)


So here's the question I originally came here for. It's actually more of a DirectX problem than anything..
If I'm going to have multiple bullets, each with a different size/texture/blend type, will I have to have a DrawPrimitive() for each bullet? There WILL be lots of bullets. At least a hundred at any given time, in addition to background sprites, players, and so on. I want to say "Yes", but it seems rather heavy, and I'm not so sure. What's a good way to arrange all of it efficiently, and lightweightly?
Offline  
Read March 26, 2012, 07:20:49 PM #25
Hornet600S

Re: Stage Design, Enemy objects and logic

If I'm going to have multiple bullets, each with a different size/texture/blend type, will I have to have a DrawPrimitive() for each bullet?

- Size doesn't matter... It's just different coordinates, no need to make multiple drawing calls due to that.

- You should merge textures (texture atlas) to avoid texture changes. A texture change means another drawing call (there are 3D texture tricks to overcome that, but forget that).

- You should sort your bullets by material (= by shader, texture atlas, blending mode). Then build a nice big vertex list of all the bullets sharing the same material and draw those at once. Then onto the next batch.


"When the Amiga came out, everyone at Apple was scared as hell."
(Jean-Louis Gassée - Head of Macintosh development at Apple)
Offline  
Pages: [1]   Go Up
Jump to:  

Page created in 0.127 seconds with 19 queries.