Simple multithreading in scripts?
by Mark Rose · in Torque Game Builder · 07/25/2007 (12:56 pm) · 19 replies
Hi ya :)
I'm after a function in the scripts, like Sleep(time); that I can call, and then have the script continue from that location after the time passes. So, a lot like schedule(time,0,"function") but jumping half way through the function instead of starting again from the top.
It's so that I can do things like this
function GameRules()
{
while($WaitingForGameStart)
{
// some code in here
sleep(10);
}
// okay, waiting for game start has now been set to true, somewhere else in another script
for(%i=0; %i<100; %i++)
{
// fill up a power bar
sleep(10);
}
// okay, now the bar is filled
while(GetPlayerAliveCount() > 1)
{
// process game rules
sleep(1000);
}
// one player is left!
AnounceWinner(GetPlayerLeftAlive());
}
Okay, all of the above is all pseudo-code nonsense, and not any of my real scripts. But anyway, a function like that would be really really useful. I find it gets really tricky doing mulitple 'schedules' and splitting game logic into a hundred different functions. Is there a way to do that?
Would I be able to schedule the same function, but skip to a certain line somehow with the same local variable stack?
Many thanks :)
I'm after a function in the scripts, like Sleep(time); that I can call, and then have the script continue from that location after the time passes. So, a lot like schedule(time,0,"function") but jumping half way through the function instead of starting again from the top.
It's so that I can do things like this
function GameRules()
{
while($WaitingForGameStart)
{
// some code in here
sleep(10);
}
// okay, waiting for game start has now been set to true, somewhere else in another script
for(%i=0; %i<100; %i++)
{
// fill up a power bar
sleep(10);
}
// okay, now the bar is filled
while(GetPlayerAliveCount() > 1)
{
// process game rules
sleep(1000);
}
// one player is left!
AnounceWinner(GetPlayerLeftAlive());
}
Okay, all of the above is all pseudo-code nonsense, and not any of my real scripts. But anyway, a function like that would be really really useful. I find it gets really tricky doing mulitple 'schedules' and splitting game logic into a hundred different functions. Is there a way to do that?
Would I be able to schedule the same function, but skip to a certain line somehow with the same local variable stack?
Many thanks :)
#2
--Torque based games are best served when they are event driven, instead of timer driven. It's how the entire simulation works, and that's primarily for performance reasons.
--for a "sleep" function to work at any level of desired performance, it would be made up of a series of scheduled events. Instead of forcing a potentially performance hog system that only allows for timer based events, Torque exposes the scheduling of a simulation event directly via the schedule commmand.
--any sleep based system that relies on polling every object in the simulation that has any script attached would be much less performant, as well as much less scalable for large numbers of objects.
--timer based game design looks "better/easier/simpler" until you start become more complex in how your game works, and it quickly becomes almost amazingly difficult to design, troubleshoot, and manage. Yes, it's at least somewhat easier to implement, but the benefit gained there is commonly lost in other areas of the development lifecycle.
07/25/2007 (1:06 pm)
This discussion came up in a different thread, so I'll try to summarize the key points:--Torque based games are best served when they are event driven, instead of timer driven. It's how the entire simulation works, and that's primarily for performance reasons.
--for a "sleep" function to work at any level of desired performance, it would be made up of a series of scheduled events. Instead of forcing a potentially performance hog system that only allows for timer based events, Torque exposes the scheduling of a simulation event directly via the schedule commmand.
--any sleep based system that relies on polling every object in the simulation that has any script attached would be much less performant, as well as much less scalable for large numbers of objects.
--timer based game design looks "better/easier/simpler" until you start become more complex in how your game works, and it quickly becomes almost amazingly difficult to design, troubleshoot, and manage. Yes, it's at least somewhat easier to implement, but the benefit gained there is commonly lost in other areas of the development lifecycle.
#3
I started having a look at the "backtrace" function to see about getting a list of local variables and stuff. Backtrace seems to crash but that's probably just my utter misuse of it :)
I'm just finding that in my very simple game, that the logic for the main game is a bit of a pain and getting really messy with the constant use of Schedule. I'm trying to find a way around it :)
07/25/2007 (2:05 pm)
Thanks for the speedy response. I totally agree that polling each object is a bad thing. What I meant was for sleep(10) to be the same as schedule(10,ThisExactSameObject, "ThisExactSameFunction", "SetAllVariablesTheSame", "SkipToSameLine");I started having a look at the "backtrace" function to see about getting a list of local variables and stuff. Backtrace seems to crash but that's probably just my utter misuse of it :)
I'm just finding that in my very simple game, that the logic for the main game is a bit of a pain and getting really messy with the constant use of Schedule. I'm trying to find a way around it :)
#4
For example:
When designed from an event perspective, comes out something like:
or, your second example:
in an event based manner, would possibly look like:
Please note that these are for example purposes, to show design differences, and aren't going to "work" as presented here, but hopefully demonstrate the differences in design.
07/25/2007 (8:05 pm)
Well, it's fundamentally a design issue. Even with your relatively simple example, you are focusing on what's called "monolithic control", instead of event based situations.For example:
function GameRules()
{
while($WaitingForGameStart)
{
// some code in here
sleep(10);
}When designed from an event perspective, comes out something like:
$ReadyToStartGame = false;
$MaxPlayers = 2;
$PlayerReadyToStart[1] = false;
$PlayerReadyToStart[2] = false;
function displayPreStartStuff()
{
// display stuff (only needs to be displayed once)
}
function onGameReadyToStart()
{
// start up game
}
function onPlayerStatusChange(%player, %status)
{
// set this player number's status:
$PlayerReadyToStart[%player] = %status;
// check to see if all players are ready
%allReadyToStart = true;
for (%index = 1; %index <= $MaxPlayers; %index++)
{
if ($PlayerReadyToStart[%index] == false)
{
%allReadyToStart = false;
break;
}
}
if (%allReadyToStart == true)
{
onGameReadyToStart();
}
}or, your second example:
for(%i=0; %i<100; %i++)
{
// fill up a power bar
sleep(10);
}in an event based manner, would possibly look like:
$PowerBarLevel = 0;
function increasePower(%bar, %amount, %interval, %maxFull)
{
// check to make sure we should add more:
if (%bar.power >= %maxFull )
{
// we're full
return;
}
// add power
%bar.power += %amount;
// should we repeat?
if (%interval > 0)
{
schedule(%interval, 0, "increasePower", %bar, %amount, %interval, %maxFull);
}
}Please note that these are for example purposes, to show design differences, and aren't going to "work" as presented here, but hopefully demonstrate the differences in design.
#5
@Stephen
Sometimes "monolithic control" is simply better for some things, especially for cut scenes.
07/26/2007 (2:49 am)
Quote:schedule(10,ThisExactSameObject, "ThisExactSameFunction", "SetAllVariablesTheSame", "SkipToSameLine");I would be really interested in that, especially if a "skiptosameline" is possible. It would be a great way to emulate wait() without having to do obscures things.
@Stephen
Sometimes "monolithic control" is simply better for some things, especially for cut scenes.
#6
I guess you want to continue execution from a schedule command. In the event model you can implement the obejct response as separate function. There is no need to use 'sleep' or 'wait'...
07/26/2007 (3:00 am)
What do you mean by "skiptosameline"? I guess you want to continue execution from a schedule command. In the event model you can implement the obejct response as separate function. There is no need to use 'sleep' or 'wait'...
#7
In my example above, 26 lines of code became 56 lines of code, and was far harder to read at a glance what was happening, and even hard to make changes because you end up having to change multiple functions for the same effect (if you want to add something new in the middle, say).
So by "skiptosameline" what we really mean is "skip all the stuff in this function you've already done".
Cutscenes are a superb example.
function GameCharacter::WalkTo(%dest_obj, %radius)
{
while(t2dVectorDistance(%this,%dest_obj) > %radius)
{
// move one step towards dest_obj via navigation/phsyics etc
// ...
sleep(50);
}
}
function GameCharacter::MyCutScene()
{
%computer_desk = NameToID("ComputerDesk");
WalkTo(%computer_desk, 1); // have to be 1 meter away from the computer to hack
echo("I'm at the computer!");
sleep(1000);
echo("Hacking computer now");
sleep(15000);
echo("Hacking complete");
sleep(500);
echo("Moving on to computer 2");
%computer_desk = NameToID("ComputerDesk2");
WalkTo(%computer_desk, 1);
echo("I'm at the computer!");
sleep(1000);
echo("Hacking computer now");
// computer 2 is easier to hack, only takes 5 seconds
sleep(5000);
echo("Hacking complete, door open!");
%door = NameToID("LevelExit");
WalkTo(%door, 5); // 5 meters from the door is close enough
echo("Finished!);
}
So in the example of SkipToSameLine, what we're really 'faking' here, is sleep(1000) calls Schedule(1000, %this, "SameFunction") but skips to the line after the sleep(1000) somehow, with all local variables set to the same state. So the very first call to sleep(1000); in "MyCutScene" (forgetting the one in WalkTo), would actually return to the next line, and %computer_desk would have the correct data in etc.
Does that make sense? I'm having trouble articulating this :) Maybe Benjamin can explain better.
07/26/2007 (3:42 am)
What we basically want to be able to do, is simplify our code using 'monolithic control' (what a great phrase).In my example above, 26 lines of code became 56 lines of code, and was far harder to read at a glance what was happening, and even hard to make changes because you end up having to change multiple functions for the same effect (if you want to add something new in the middle, say).
So by "skiptosameline" what we really mean is "skip all the stuff in this function you've already done".
Cutscenes are a superb example.
function GameCharacter::WalkTo(%dest_obj, %radius)
{
while(t2dVectorDistance(%this,%dest_obj) > %radius)
{
// move one step towards dest_obj via navigation/phsyics etc
// ...
sleep(50);
}
}
function GameCharacter::MyCutScene()
{
%computer_desk = NameToID("ComputerDesk");
WalkTo(%computer_desk, 1); // have to be 1 meter away from the computer to hack
echo("I'm at the computer!");
sleep(1000);
echo("Hacking computer now");
sleep(15000);
echo("Hacking complete");
sleep(500);
echo("Moving on to computer 2");
%computer_desk = NameToID("ComputerDesk2");
WalkTo(%computer_desk, 1);
echo("I'm at the computer!");
sleep(1000);
echo("Hacking computer now");
// computer 2 is easier to hack, only takes 5 seconds
sleep(5000);
echo("Hacking complete, door open!");
%door = NameToID("LevelExit");
WalkTo(%door, 5); // 5 meters from the door is close enough
echo("Finished!);
}
So in the example of SkipToSameLine, what we're really 'faking' here, is sleep(1000) calls Schedule(1000, %this, "SameFunction") but skips to the line after the sleep(1000) somehow, with all local variables set to the same state. So the very first call to sleep(1000); in "MyCutScene" (forgetting the one in WalkTo), would actually return to the next line, and %computer_desk would have the correct data in etc.
Does that make sense? I'm having trouble articulating this :) Maybe Benjamin can explain better.
#8
07/26/2007 (5:36 am)
Quote:simplify our code using 'monolithic control'I get it. I think this isn't good way to simplify. Because the way how a script is called by the engine.
#9
And, I think that the solution Mark is suggesting is a very good one (using a modified schedule with the ability to skip lines), it wouldn't disturb the current TGB inner working and would do the job as expected in most essential cases. What is missing, to do it, is the ability to skips to the line after the wait/sleep command.
07/26/2007 (8:30 am)
I don't think it's the place to speak about script methodology. Schedule is a very good function on its own, but sometimes it just don't fit (cutscenes with the current stock schedule are a nightmare). That is the point.And, I think that the solution Mark is suggesting is a very good one (using a modified schedule with the ability to skip lines), it wouldn't disturb the current TGB inner working and would do the job as expected in most essential cases. What is missing, to do it, is the ability to skips to the line after the wait/sleep command.
#10
Now, that being said, you could poke into the console system, take a look at the torquescript parser, and probably be able to store the data you mention above, and basically implement a new torquescript statement called "wait", and do basically what you want to do. It's certainly not trivial, because you will need to work through issues with multiple waits on a single object, as well as come up with a way to poll the objects that have waits pending (unless you do it fully event driven, but then we're back to schedule again).
Or, you could take a macro approach--build a "CutSceneManager" object that allows scene objects to register for timeline participation, and you would provide a script to the CutSceneManager that delegates events off to various participants based on the timeline progression.
07/26/2007 (8:37 am)
I can see the benefit of what you are talking about given the relatively basic examples you are showing in a cutscene (and in fact agree that your request would be very useful), but I would also suggest that ultimately, the mechanism isn't going to be as beneficial as you would think. In the long run, my gut (educated guess) tells me that eventually you'll want to be able to visually edit a 'dope sheet' (think of a timeline of events that you can adjust at will, including overlapping two different effects, etc), and become strictly limited by a pure "wait()" implementation.Now, that being said, you could poke into the console system, take a look at the torquescript parser, and probably be able to store the data you mention above, and basically implement a new torquescript statement called "wait", and do basically what you want to do. It's certainly not trivial, because you will need to work through issues with multiple waits on a single object, as well as come up with a way to poll the objects that have waits pending (unless you do it fully event driven, but then we're back to schedule again).
Or, you could take a macro approach--build a "CutSceneManager" object that allows scene objects to register for timeline participation, and you would provide a script to the CutSceneManager that delegates events off to various participants based on the timeline progression.
#11
I think poking around the console system and parser is a good step. I tried last night and only got so far before having to go. I'm away on holiday for a fortnight too, so I won't be able to get to it very soon. If anyone else feels the urge that would be awesome.
As for multiple waits on the same script object... can I ask how that is any different from multiple schedules on the same object?
07/26/2007 (9:03 am)
Hmm, I'm not sure about the dope sheet thing. For me, just being able to write my code in a nicely flowing fashion, rather than scheduling individual functions would be brilliant. It especially becomes a nightmare when loops are concerned. Not only is it a bit of a pain to write, you have to keep track of all the schedule ID's in my case, so that I can clean up/stop the ones I want from happening, in case the level changes for any particular reason.I think poking around the console system and parser is a good step. I tried last night and only got so far before having to go. I'm away on holiday for a fortnight too, so I won't be able to get to it very soon. If anyone else feels the urge that would be awesome.
As for multiple waits on the same script object... can I ask how that is any different from multiple schedules on the same object?
#12
@Benjamin:
Do you have the source code version? i.e. if I suss this out, I can share the solution. Otherwise I'll hunt for some kind of hacky/macro style/script fix :)
07/26/2007 (9:06 am)
In fact Stephen, if you're able to point me in the direction of some code where I can get a nice list of all local variable names and values, that would be brilliant. Does such a thing exist?@Benjamin:
Do you have the source code version? i.e. if I suss this out, I can share the solution. Otherwise I'll hunt for some kind of hacky/macro style/script fix :)
#13
I'm going to give this one more go, and then stop trying to blast design theory at you guys :)
Unfortunately, event driven systems don't "flow", they react to events. User events, collision events, timer events, network events, simulation events, the list is pretty big, and it's all asynchronous, so trying to control "flow" is just going to get you into trouble as your complexity increases. For every benefit of a flow control based solution, you are going to run into 2 different/new problems that make the solution more complex, and ultimately less capable and performant.
For example, you mention having issues with keeping track of scheduled event ID's to clean up/stop. You will have exactly the same issue with a 'wait' concept, and in fact it will probably be more complex, when you factor in multiple wait points and durations on a single object. You will also be almost guaranteed to run into extremely difficult synchronization issues if/when you decide to go multi-player of any sort.
Regarding the underlying implementations, you will want to focus on understanding ConsoleObject and SimObject completely, and study the Sim:: and Console:: manager (singleton) classes and how they interact with the hierarchy classes. If you haven't set up dOxygen on your computer for html based source code navigation, I highly suggest you do so as well.
07/26/2007 (9:30 am)
Quote:
For me, just being able to write my code in a nicely flowing fashion, rather than scheduling individual functions would be brilliant.
I'm going to give this one more go, and then stop trying to blast design theory at you guys :)
Unfortunately, event driven systems don't "flow", they react to events. User events, collision events, timer events, network events, simulation events, the list is pretty big, and it's all asynchronous, so trying to control "flow" is just going to get you into trouble as your complexity increases. For every benefit of a flow control based solution, you are going to run into 2 different/new problems that make the solution more complex, and ultimately less capable and performant.
For example, you mention having issues with keeping track of scheduled event ID's to clean up/stop. You will have exactly the same issue with a 'wait' concept, and in fact it will probably be more complex, when you factor in multiple wait points and durations on a single object. You will also be almost guaranteed to run into extremely difficult synchronization issues if/when you decide to go multi-player of any sort.
Regarding the underlying implementations, you will want to focus on understanding ConsoleObject and SimObject completely, and study the Sim:: and Console:: manager (singleton) classes and how they interact with the hierarchy classes. If you haven't set up dOxygen on your computer for html based source code navigation, I highly suggest you do so as well.
#14
With regards to having the same issue with the wait command, I was going to be cunning about it and try and hide as much of the guts of what's happening as possible. For example, within one function, you'd only need one ID to keep track of. You can keep reusing the same variable for each wait command.
function Myfunc()
{
$ID = Wait(100);
echo("some stuff");
$ID = Wait(100000000);
}
calling Cancel($ID) will always work, no matter where abouts in MyFunc you got to.
I'm sure our "Wait(time)" function has it's limits, but for certain situations it's so much more powerful. I'm interested in quick turn around time of game logic, and being able to have certain functions pause mid script until some event happens, and then carry on.
Thanks for the info though! :)
I'll let you all know if/when I get something running!
07/26/2007 (9:38 am)
Cool cheers for the info on the Sim and Console stuff, I'll have a good look at all that when I get back :)With regards to having the same issue with the wait command, I was going to be cunning about it and try and hide as much of the guts of what's happening as possible. For example, within one function, you'd only need one ID to keep track of. You can keep reusing the same variable for each wait command.
function Myfunc()
{
$ID = Wait(100);
echo("some stuff");
$ID = Wait(100000000);
}
calling Cancel($ID) will always work, no matter where abouts in MyFunc you got to.
I'm sure our "Wait(time)" function has it's limits, but for certain situations it's so much more powerful. I'm interested in quick turn around time of game logic, and being able to have certain functions pause mid script until some event happens, and then carry on.
Thanks for the info though! :)
I'll let you all know if/when I get something running!
#15
The other way would be to create a little reinterpreter with Torque script, that, once you finished setting your function, would read your files and automatically replace every "wait" lines with big schedules and split functions (though you'll have to safe keep your simplified version of the scripts). It would be a little complicated, but not more than some big manager into source.
07/26/2007 (10:44 am)
Stephen, what you're suggesting seems overkill. And I don't own the source. I wondered if you could do something that would simplify everything for everyone (even for no-source proletarians). But it seems not. So, I'll do it myself. The good thing is that, unlike some camera system bug, I can workaround it.Quote:I'll hunt for some kind of hacky/macro style/script fix :)No need to ^^ here's the simplest script I made :
function cutsceneA(%time)
{
switch(%time)
{
case 1:
echo("How are you ?");
echo("*walking toward the other guy*");
schedule(200, 0, cutsceneA, %time++);
case 2:
echo("I'm cool");
echo("*shaking hand*");
schedule(200, 0, cutsceneA, %time++);
case 3:
echo("yep me too :)");
echo("*thumb up*");
schedule(1000, 0, cutsceneA, %time++);
}
}This is not great looking, but it does the trick. Though you'll have to increment the case each time you add a schedule, it is already a lot better than to split the function.The other way would be to create a little reinterpreter with Torque script, that, once you finished setting your function, would read your files and automatically replace every "wait" lines with big schedules and split functions (though you'll have to safe keep your simplified version of the scripts). It would be a little complicated, but not more than some big manager into source.
#16
In my opinion, your time would be much better spent learning to work with the existing system. Read existing code, learn how callbacks are used, arrange your data such that having to resume a function is not necessary. Most of the time, schedule is not necessary either ... and indeed, having too many schedules is generally a bad idea.
What I get from that statement is that you want event based programming, which is what you have. The problem is simply one of where the data is. Use objects for callbacks, store the data as members, and the whole nasty problem of requiring locals and waiting is gone. Alternatively (and probably better in most situations), pass the required data as arguments to the callback function.
Event based programming is just a different way of thinking. It's just as quick a turn around as any other method, provided you understand it. In fact, in Torque (and most other game engines) it's the fastest and most reliable turn around.
T.
07/26/2007 (1:04 pm)
To put this in perspective, implementing something like a "wait" function would require a major rewrite of the script interpreter. It would (probably) take me on the order of months to implement it, without any guarantee that it would actually work (I doubt that it will), and I know the console code like the back of my hand.In my opinion, your time would be much better spent learning to work with the existing system. Read existing code, learn how callbacks are used, arrange your data such that having to resume a function is not necessary. Most of the time, schedule is not necessary either ... and indeed, having too many schedules is generally a bad idea.
Quote:I'm sure our "Wait(time)" function has it's limits, but for certain situations it's so much more powerful. I'm interested in quick turn around time of game logic, and being able to have certain functions pause mid script until some event happens, and then carry on.
What I get from that statement is that you want event based programming, which is what you have. The problem is simply one of where the data is. Use objects for callbacks, store the data as members, and the whole nasty problem of requiring locals and waiting is gone. Alternatively (and probably better in most situations), pass the required data as arguments to the callback function.
Event based programming is just a different way of thinking. It's just as quick a turn around as any other method, provided you understand it. In fact, in Torque (and most other game engines) it's the fastest and most reliable turn around.
T.
#17
I don't think events really do the trick in every situation. I'm working on an RPG battle system. I need my characters shooting particles and playing animations over a set period of time. Maybe I could use a coroutine, but for now I have a huge command that keeps being rescheduled with different string "commands".
Here's a simplified example:
And my spell scripts look something like this ( Lua code )
07/26/2007 (9:37 pm)
Sounds like what you really want is a coroutine, like Lua has. They are pretty awesome. Every object in my game has a coroutine attached to it. I don't think events really do the trick in every situation. I'm working on an RPG battle system. I need my characters shooting particles and playing animations over a set period of time. Maybe I could use a coroutine, but for now I have a huge command that keeps being rescheduled with different string "commands".
Here's a simplified example:
function doSpellSequence(%object ){
%cmd = lua.docode( "return table.remove(this_spell, 1)"); //returns a string containing the command to do
if (%cmd !$= ""){
if (getSubStr( %cmd , 0 , 7 ) $= "FLOATUP" ){ //parameters: speed, time
%object.setconstantforcepolar( 360, getword( %cmd, 1 ), 0 ) ;
%time = getword( %cmd, 2 ); // Last word of the string should be the amount of time we want this command to run
}
if (getSubStr( %cmd , 0 , 10 ) $= "FLOAT_DOWN" ){ //parameters: speed, time
%object.setconstantforcepolar( 180, getword( %cmd, 1 ), 0 ) ;
%time = getword( %cmd, 2 );// Last word of the string should be the time we want this command to run
}
if (getSubStr( %cmd , 0 , 4 ) $= "STOP" ){ //parameters: time
%object.setatrest() ;
%object.stopConstantForce();
%object.setPhysicsSuppress(1);
%time = getword( %cmd, 1 );
}
if (getSubStr( %cmd , 0 , 14 ) $= "MOVE_TO_TARGET" ){ //params speed, targetID
echo( "MOVETOTARGET" SPC getword( %cmd, 2 ) );
//Elsewhere we use %object.onPositionTarget to call doSpellSequence again and continue this here loop
%object.moveto( %targetName.getposition(), getword( %cmd, 1 ) , 1, 1, 1, 1 );
%time = -1;//secret code to wait forever
}
if (%time $= -1){ // time -1 is secret code for "wait"..
echo( "WAITING FOR ANOTHER FUNCTION TO CONTINUE SPELL");
return;
}
if (%time !$= "" )
schedule( %time, 0, "dospellsequence", %object );
}else{
// There was no command string so we must be done with the animation, so move on
do_end_spell();
}
}//end functionAnd my spell scripts look something like this ( Lua code )
this_spell = {
'PLAYEFFECT magic_test1 1000',
'FLOATUP 15 1000',
'STOP 1000',
'STOPEFFECT 1',
'PLAY_EFFECT magic_test2 1000',
'STOP_EFFECT 1',
'SHOOT magic_test1 2 30',
'DAMAGE_TARGET 1 10'
'FLOATDOWN 10 3000',
}This works pretty well, but I'm open to suggestions on less cluttery, better performing ways to achieve this.
#18
@Joe:
Lua coroutines. Yes, that's the kind of thing we're talking. :)
@Benjamin:
Yep, that was my line of thinking for a purely script based solution. I might try and wrap the details up more so that you don't have to do too much fiddly details
@Tom:
Maybe callbacks are what I should be using more. I seem to be using schedule every time I need something timed. So, anything where I have to wait a second, or 10 seconds etc I'm using schedule. Is that bad?
It might be that I can just wrap up certain functionality to make what I need to do a bit simpler. Anyway, when I' get back from my hols I'll have more of a think of what kind of stuff I need, and see if there's an elegant script solution that doesn't involve splitting my game logic up into several functions :)
Cheers guys!
07/27/2007 (3:05 am)
I have to be quick, so apologies for not commenting on everything you guys have added :)@Joe:
Lua coroutines. Yes, that's the kind of thing we're talking. :)
@Benjamin:
Yep, that was my line of thinking for a purely script based solution. I might try and wrap the details up more so that you don't have to do too much fiddly details
@Tom:
Maybe callbacks are what I should be using more. I seem to be using schedule every time I need something timed. So, anything where I have to wait a second, or 10 seconds etc I'm using schedule. Is that bad?
It might be that I can just wrap up certain functionality to make what I need to do a bit simpler. Anyway, when I' get back from my hols I'll have more of a think of what kind of stuff I need, and see if there's an elegant script solution that doesn't involve splitting my game logic up into several functions :)
Cheers guys!
#19
By using the eval command, you could implement the coroutine method fairly simply:
07/27/2007 (1:40 pm)
I've been using a method pretty similar to Benjamin Grauer's, and it seems to work well. In fact, since it's done using one schedule at a time, it's easy to add a "cancel" command to be called, if, for instance, the user goes wants to return to the menu while your cutscene is running.By using the eval command, you could implement the coroutine method fairly simply:
function t2dSceneObject::runSpell(%THIS, %spell, %TARGET, %i, %lines)
{
if(%i $= "")
{
%lines = getRecordCount(%spell);
%i = 0;
}
if(%i < %lines)
{
%line = getRecord(%spell, %i);
%div = strstr(%line, "#");
%cmd = getSubStr(%line, 0, %div);
%time = getSubStr(%line, %div+1, strlen(%line));
eval(%cmd);
%THIS.schedule(%time, runSpell, %spell, %TARGET, %i+1, %lines);
}
}
$this_spell =
"%THIS.pfx = pfxPlay(\"magic_test1.eff\"); #1000\n
%THIS.setLinearVelocityY(-15); #1000\n
%THIS.setLinearVelocityY(0); #1000\n
pfxKill(%THIS.pfx); #1\n
%THIS.pfx = pfxPlay(\"magic_test2.eff\"); #1000\n
pfxKill(%THIS.pfx); #1\n
pfxShoot(\"magic_test1.eff\",%THIS.getPosition(),%TARGET.getPosition(),2); #30\n
%TARGET.takeDamage(1); #10\n
setLinearVelocityY(10); #3000";
Player1.runSpell($this_spell, $someEnemy);
Torque Owner Mark Rose