Jump to content
  • Advertisement

Archived

This topic is now archived and is closed to further replies.

RuneLancer

Improving a basic scripting engine...

This topic is 5210 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've set up my game to use a simple scripting engine, however I get mixed results. While the engine performs exactly like I want it to, it's slow. Very slow. First, I write stuff in a .txt. I have a program that'll compile everything in the .txt to binary (converting instructions into opcodes, converting parameters, etc..). All events and whatever are in the same file: events.dat. So I could have various scripts in the same file. To keep track of what's where, I have a header that provides information on finding the start of a given event. In my game, when I want to run an event, I call LoadEvent(int EventID) which basically gets the starting position of the event and creates a pointer to it. Since I load the file into RAM (no, I don't read directly from it; that would be quite a performance hit ), I can just move my pointer around from instruction to instruction. Then, I call the DoEvent() function over and over until it returns a non-zero value. DoEvent() is basically a switch statement. It checks what opcode the event pointer points to, basically, and does whatever that particular instruction's meant to do. If there are parameters, they're read by advancing the pointer accordingly. By the time the instruction has finished executing, the pointer has advanced to the next instruction and I can just call the function again (DoEvent()) to continue with the script. This works really well: my game's prelude runs solely off of a script and it looks very nice. Problem is, it does this v.e.r.y. s.l.o.w.l.y... I ran into serious problems when trying to implant a "fade" instruction that would fade, say, a picture over the course of 'x' miliseconds: at first I thought the instruction was broken. Then I realized that, since I was increasing the alpha by 1 every frame and then tried with 20, the instruction would halt after 1000 ms (as it should) but only fade 3-4 steps. I've also seen certain problems related to positioning certain elements, such as a window or text. With the latter, supposing I were to set the text and then place it where I want it, I would see the text display itself in the upper-left corner briefly, then it would snap where it should on the screen (I coded a work-around that allows me to lock the display and stop updating until I tell it to). Any suggestions on implanting a fast yet simple script parser in a game? I don't feel like learning a whole new language or API, I'm just looking for tips on getting this to run decently. Edit: Here's a sample of what the parser looks like...
int DoEvent()
{
	unsigned int P_A, P_B, P_C, P_D, P_E, i;

	do
	{
		switch(EventDat[EventPointer])
		{
			/* End						*/ 
			case 0:
				return 0;
				break;
			/* Move Label				*/ 
			case 1: P_A = Event[++EventPointer]; P_B = Event[++EventPointer]; P_C = Event[++EventPointer];
				MyLabel[P_A].SetPos(P_B, P_C);
				EventPointer++;
				break;
			/* Set Label				*/ 
			case 2: P_A = Event[++EventPointer]; P_B = Event[++EventPointer];
				MyLabel[P_A].SetText(hInst, P_B, FontTable);
				EventPointer++;
				break;
...
			/* UNKNOWN					*/ 
			default:
				return 0;
		}
	} while(f_Locked);
	for(i = 0; i < MAXPICT  ; i++) MyPict  .Render();
	for(i = 0; i < MAXWINDOW; i++) MyWindow[i].Render();
	for(i = 0; i < MAXLABEL ; i++) MyLabel [i].Render();
	return 1;
}
 
[edited by - RuneLancer on April 13, 2004 2:17:31 PM]

Share this post


Link to post
Share on other sites
Advertisement
As far as I can see, this should work fine. What I do not quite get, though, is why you put rendering functions inside the DoEvent (at the bottom of your function).

What might be interesting is doing a profiler run. This should tell you more about where the bottlenecks are.

The switch-statement shouldn't be what you worry about...

Profile your code and you'll see where most CPU time goes ...

[edit]


/* Move Label*/

case 1: P_A = Event[++EventPointer]; P_B = Event[++EventPointer]; P_C = Event[++EventPointer];

MyLabel[P_A].SetPos(P_B, P_C);

EventPointer++;

break;


Something that came into my mind on a second look is the following: you might gain a slight speedup by defining P_A and so on as static variables (but that shouldn't make much difference).

Please don't get it wrong, I do NOT intend to start a flame war on coding style! I've always been told not to use statements with side-effects (P_A=Event[++EventPointer] but rather to split them up into seperate instructions:


EventPointer++;
P_A=Event[EventPointer];


But that's only a point of readability.

What do you use that f_locked for, where does it come from, and where does it get modified? Is it for forcing the script to execute in one run?

[/edit]


Indeterminatus

--si tacuisses, philosophus mansisses--

[edited by - Indeterminatus on April 13, 2004 2:48:21 PM]

Share this post


Link to post
Share on other sites
Your loop makes entirely no sense. You never execute the code at the bottom that I can see.

You''d be better off making a virtual machine. And use enums for your opcodes, that hardcoded stuff is nasty.

If you are looking for an idea how to make a script language you can take a look at my project. Warning, it is funky, but it performs well. If you are using MSVC6 you will need STL-port, the MS STL is a pile of crap.

http://xano.sourceforge.net/cull.html

Share this post


Link to post
Share on other sites
Don''t use scripting for things that need to go fast. Scripting should be high-level, fading is low-level. Implement a function that fades and a fade-opcode.

Good use of scripting:

if enemy.hits(wall) then
enemy.turnAround()
end if

Bad use of scripting:

if enemy.x + enemy.direction between wall.left and wall.right then
enemy.direction = -enemy.direction
end if

You write scripts in a scripting language, you don''t program.

---
tommy online: http://users.pandora.be/tommycarlier

Share this post


Link to post
Share on other sites
@Indeterminatus
f_locked is used for a command not represented in my sample code (I trimmed out stuff for the sake of readability). It locks the script loop. This is basically a fix for the slow speed I end up with: remember how I mentionned that text display problem, for instance? This allows me to perform multiple instructions before I update the display. Not exactly kosher, but it works. ;P

@PeterTarkus
Do you mean the three for() loops? Then you're probably blind, with all due respect. There's nothing preventing that code from being executed. Not even remotely. Though if the f_locked flag is what gets you confused, read my reply to Indeterminatus for an explanation on that one. Believe me, my display would never update if the code that renders the text, windows, and pictures wouldn't get executed.

@Tommy Carlier
There's very little to no programming going on. The scripting in my game is only to display basic events. For instance...



I set the text and the textlabel's position, the window's position and size, I tell it which picture to load, and then just wait until a few seconds have been elapsed before proceeding. This is all handled by C++ code; the scripting bit just tells it the very basics of what to do. For instance...


Lock
LoadPict 0, "002"
MovePict 0, 256, 176
SetLabel 0, 202
Unlock
WaitTicks 5000


Same goes with fading: I just tell it "Ok, dude, like... start the fade process (lol, wtf?)" with a single instruction. The script only sets things in motion, it doesn't outright dictate how to do everything.

--

Obviously, I'll be using more complex stuff for in-game events such as cutscenes. Any suggestions on what should be done for these? Hardcoding the event sounds like it'd be the fastest way to do this, but also the least manageable when it comes to updating stuff. And obviously, scripting isn't the way to go if I want to acheive that sort of performance. So... what should I do?

[edited by - RuneLancer on April 13, 2004 4:01:37 PM]

Share this post


Link to post
Share on other sites
Hrm, hate to do this but, bump!

I really need some way of doing this, and short of hardcoding it I can''t think of any methods. Scripting seems to be viewed as a bad thing for this sort of work (and tests indicate it to be too slow) and hardcoding would be very, very troublesome and ugly...

Share this post


Link to post
Share on other sites
Nobody can suggest something for this? At all?

I''m sure someone knows by what kind of system these kinds of things are handled in a game...

Share this post


Link to post
Share on other sites
I''m curious about this part:


for(i = 0; i < MAXPICT ; i++) MyPict [i].Render();
for(i = 0; i < MAXWINDOW; i++) MyWindow[i].Render();
for(i = 0; i < MAXLABEL ; i++) MyLabel [i].Render();


What is the value of MAXPICT, etc.? I imagine that you have some array of MAXPICT items with only a few of those items being used. Do you really want to render them all every frame? It would make more sense to me to only render items that are currently in use.

Other than that, it''s hard to say without seeing more of your code.

Also, after seeing your screenshot I have to say that I''d be glad to help you out for a chance to work with an artist like that...

Tony

Share this post


Link to post
Share on other sites
Obviously, MAXobject are constants representing the max amount of possible objects in existence of these types. For instance, the max amount of windows which can be created. I could (and probably should) create these dynamically instead of having a static amount of them, but meh.

The render method of each object exits right away if the object in question isn't in use, so it won't try to render, say, 8 picture resources if only 1 is in use.

Though what I'm mainly concerned with is how to do this, not how to speed this up. Apparently, from the previous posts, a scripting engine isn't the right way to handle this sort of thing. I could do the obvious and hardcode something in a finite-state machine that would switch states depending on what storyline event is being handled, but this is not only difficult to quickly modify, added bloat to the code, and reduced legibility, it also means I can't just write a quick and dirty tool to help me speed up the creation of in-game events.

So if scripting's out, short of hardcoding stuff, there has to be some other, better way of doing this sort of thing? Or would a proper rewrite/optimization of my parser be enough anyways? I can't quite see how I could make the parser much faster than it currently is (noticeably) to be honest, if only to just remove the rendering methods I call, but that would rather defeat the purpose of having events, wouldn't it?

Edit: Me? An artist? lol, I wish. Thanks for the compliment, but I can't take undue credit: I didn't draw that. I made the font and the window graphics, but the picture's taken from an SNES RPG called "Record of Lodoss War."

[edited by - RuneLancer on April 15, 2004 2:35:10 AM]

Share this post


Link to post
Share on other sites
I can''t see why a scripted method would be bad. Sounds like a good idea to me but then I am a totall noob. Are you sure its the scripting that taking so long and not the drawing part? From what I see the script part looks fine.

Share this post


Link to post
Share on other sites

  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!