Skip to main content
GameDev.net gamedev.net
🔒 Locked

Creating a modular engine design

Started by spiffgq Dec 12, 2002 at 10:04 PM 16 replies 500+ views
Original Post
spiffgq
spiffgq
In a follow-up to my last post, I''d like to get some ideas on the best way to implement a game engine with a very modular approach. My idea is to use shared libraries as the modules. This is not a new or revolutionary idea by any means; it is used all the time in regular programs and it is used in games for things like graphics APIs. For example, the game could define and abstract base class like "Enemy". The module writer would sub-class the enemy class creating a child class like "Soldier" which would implement the enemy class interface. Then, the module is compiled into a shared object (aka DLL) and loaded during runtime. There are many technical hurdles to over come with this approach. For example, loading classes from shared objects at run time requires a little bit of tweaking (using a loading function or proxy class, etc). I know this works well on UNIX systems, but I don''t know if it is possible on Windows or Mac systems. Also, I don''t know if this would be fast enough. Is this method used in any commercial games? What are some other advantages and disadvantages of this method? What other methods would you suggest when trying to implement a module system? Thanks for your help.
SpiffGQ
ApochPiQ
ApochPiQ
Four words.


Virtual functions are slow.




I''ve gone the subclassing route, and let me tell you, it absolutely sucks for high-performance game development. There''s just no way to build around that complex of an OOP model and make it run fast. Converting a sample blurb of code from OOP to standard C gained me a 3-4x speed boost without changing any details of implementation. So unless your games are extremely fine tuned, and you can afford the overhead of subclassing and such, OOP and related models are not going to do the job.

On the other hand, I''ve had good luck with pure function calls into DLLs. Usually there is only the small decrease caused by forcing Windows to jump across memory spaces to make the calls to the DLL, but if you scheme well, these decreases are virtually impossible to detect. The real key here is to design a set of functions well, similar to a subclassing structure, but without the evils of actual COM overhead.


Personally I recommend using OOP desing principles to create a structure that behaves like OOP, and is as flexible, but without the processing cost. I''ve done this many times with great success, but for non-performance-critical work I always opt for true OOP.

zppz
zppz
interesting... i have also gone the subclassing route but would be hard pressed to notice any slowdown at all.

does it only happen when you dynamically load from a dll? (which I''m not doing)
Oluseyi
Oluseyi
Consider a data-driven approach (essentially a data-is-code philosophy). It''s very similar to using an embedded high-level scripting language to define complex interaction objects and only using low-level code for game "infrastructure." This is important because neither an "Enemy" or "Soldier" are part of the game engine. It then reduces the complexity of implementing the game engine, requiring dynamic behavior only for swappable/upgradable components for the long run.
spiffgq
spiffgq
quote:
Original post by Oluseyi
Consider a data-driven approach (essentially a data-is-code philosophy). It''s very similar to using an embedded high-level scripting language to define complex interaction objects and only using low-level code for game "infrastructure." This is important because neither an "Enemy" or "Soldier" are part of the game engine. It then reduces the complexity of implementing the game engine, requiring dynamic behavior only for swappable/upgradable components for the long run.


This is intriguing, but I''m not sure I understand you. Can you please elaborate?

quote:
Original post by ApochPiQ
Four words.


Virtual functions are slow.




I''ve gone the subclassing route, and let me tell you, it absolutely sucks for high-performance game development. There''s just no way to build around that complex of an OOP model and make it run fast. Converting a sample blurb of code from OOP to standard C gained me a 3-4x speed boost without changing any details of implementation. So unless your games are extremely fine tuned, and you can afford the overhead of subclassing and such, OOP and related models are not going to do the job.

On the other hand, I''ve had good luck with pure function calls into DLLs. Usually there is only the small decrease caused by forcing Windows to jump across memory spaces to make the calls to the DLL, but if you scheme well, these decreases are virtually impossible to detect. The real key here is to design a set of functions well, similar to a subclassing structure, but without the evils of actual COM overhead.


Personally I recommend using OOP desing principles to create a structure that behaves like OOP, and is as flexible, but without the processing cost. I''ve done this many times with great success, but for non-performance-critical work I always opt for true OOP.


I know virtual functions are slow, but I was not aware they were that slow. You suggest reimplementing the idea using structs and functions rather than classes. Is that correct? I was considering doing that. It would work just as well. But the rest of my program is OO, so I was trying to stay on that path.

SpiffGQ
stodge
stodge
They can''t be "that" slow; I mean the UT2003 and Torque engines are pretty damn quick and they use sub-classing among other things.
---------------------http://www.stodge.net
Poontardis
Poontardis
quote:
Original post by spiffgq

I know virtual functions are slow, but I was not aware they were that slow. You suggest reimplementing the idea using structs and functions rather than classes. Is that correct? I was considering doing that. It would work just as well. But the rest of my program is OO, so I was trying to stay on that path.




Why would you move to using structs rather than classes? They''re effectively the same thing. All you''d want to do to avoid the virtual function overhead is write your classes without using virtual functions.

Calls to virtual functions aren''t massively slower than non-virtual calls anyway, you only have to watch if you have virtual functions in the core of your engine that get called many, many times per frame. Something like pEntity->Update(); for 20 monsters every frame is going to make zero impact by being virtual, whereas pVertex->Render(); being virtual would kill you.
Use virtual functions correctly and they''re great.
Wildfire
Wildfire
Why exactly are virtual function calls slower then normal function calls? Can't the compiler "sort that out" as in: Include the v-function into the calling class rather then leaving it in the original class and calling it?

And I agree with Poontardis. In C++ (at least Visual C++) classes and structs are exactly the same thing. The only difference being that classes are private by default whereas structs are public. They can inherit, have member function, private members etc. etc.

[edited by - Wildfire on December 13, 2002 12:51:19 PM]
How do I set my laser printer n stun? o
Cold_Steel
Cold_Steel
I completely agree with Oluseyi.
I would definitely go with the scripting language. That''s what I''m working on right now for my engine. Effectively, I will use the scripting language to define enemy behavior. I will have a generic enemy class that defines certain general characteristics of an enemy (Attack, move, etc.) In order to define specific enemies the enemy class will use the script for specific enemy attributes. For example. For example, the soldier may attack agressively and not worry about his own safety, whereas a wizard might be more defensive and use spells.

The enemy class has no idea about the specifics of any one enemy, it just knows how to process the script. I would suggest not hard coding specific enemies into your program, it can really bloat your program, and make it hard to debug. The scripting language will be easy to debug, and will not add any size to your program, everything is just stored in an external script. Very elegant solution, and you don''t need to worry about inheritance.
______"Man is born free, and everywhere he is in chains" - J.J. Rousseau
SabreMan
SabreMan
quote:
Original post by Wildfire
Why exactly are virtual function calls slower then normal function calls?

Because they don''t do the same thing ! It is *very silly* to compare virtual function calls with non-virtual function calls. The whole point of a virtual is that you don''t decide the types of it''s implicit first argument until runtime. To do that without virtual requires you to implement some mechanism, such as an array of function pointers, and manually perform the type-switch. If you do that, then you''ve coded what the compiler could generate for you. If you don''t need the runtime despatch, then you don''t use virtual.

Usually, when people complain that virtual is slow it''s because they don''t know how to use it correctly.
Wildfire
Wildfire
Thanks for the answer SabreMan. However "it is very unknowing" might get the point across without offending people

One thing that confuses me is that I've got to put 'virtual' in front of functions I want to overload in inherited classes in Visual... So, are these 'virtual function calls' or does overloading them make it a regular function call again?

[edited by - Wildfire on December 13, 2002 1:08:12 PM]
How do I set my laser printer n stun? o
spiffgq
spiffgq
quote:
Original post by Cold_Steel
I completely agree with Oluseyi.
I would definitely go with the scripting language. That''s what I''m working on right now for my engine. Effectively, I will use the scripting language to define enemy behavior. I will have a generic enemy class that defines certain general characteristics of an enemy (Attack, move, etc.) In order to define specific enemies the enemy class will use the script for specific enemy attributes. For example. For example, the soldier may attack agressively and not worry about his own safety, whereas a wizard might be more defensive and use spells.

The enemy class has no idea about the specifics of any one enemy, it just knows how to process the script. I would suggest not hard coding specific enemies into your program, it can really bloat your program, and make it hard to debug. The scripting language will be easy to debug, and will not add any size to your program, everything is just stored in an external script. Very elegant solution, and you don''t need to worry about inheritance.


I think I understand now. You suggest forgoing the dynamic objects entirely in favor of scripts. I should create a general class that executes the scripts.

I suppose that is an idea, but that would require me figuring out a scripting language and embedding it, etc. That sounds like a lot more work than just using C++ as a kind of low level pseudo-scripting language. Having the modules in dynamic objects would prevent code bloat (only the currently desired objects will be loaded).

If I decided to use your suggestion, what scripting language would you recommend?

SpiffGQ
SabreMan
SabreMan
quote:
Original post by Wildfire
One thing that confuses me is that I''ve got to put ''virtual'' in front of functions I want to overload in inherited classes in Visual...

You mean override. Overloading is something different.
quote:

So, are these ''virtual function calls'' or does overloading them make it a regular function call again?

When you put virtual in front of a function, it stays virtual for all derived classed. I.e. a class inherits the "virtualness" of parent functions.
Wildfire
Wildfire
Sorry, it''s just that it''s called "überladen" here (overload). I agree that this is a bad term (I don''t like it...) but somehow I got used to it...
(always reminds me of putting too much weight onto something)
How do I set my laser printer n stun? o
SabreMan
SabreMan
Overloading means something else specific to C++. Overriding is what happens when you re-implement a virtual function inherited from a base class. Overriding is what happens when you provide more than one function in the same scope, each with different signatures.
Wildfire
Wildfire
Thanks again... looks like the english language is a bit more distinctive here... because in German it''s called the same in both cases (At least I''ve never heard someone make a difference between these two).

By the way in case someone''s interested I''ve found this document about virtual function calls.
TestCode
It deals with some benchmarks that compare different implementations of C++ classes (PDF document).
How do I set my laser printer n stun? o
SabreMan
SabreMan
quote:
Original post by Wildfire
Thanks again... looks like the english language is a bit more distinctive here... because in German it''s called the same in both cases

I''m very skeptical about that. There has to be two different terms because they are two different things. You *have to* be able to distinguish them.
owl
owl
quote:
Original post by ApochPiQ
Four words.
Virtual functions are slow.


I've read that virtual functions cost is typically less than adding an extra parameter to a function


The cost is a memory read before every call to a virtual function (to get the object's vtable pointer) and a second memory read (to get the function pointer from the vtable


[edited by - xaxa on December 13, 2002 8:14:40 PM]
[size="2"]I like the Walrus best.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.