Lots of modules(for gameobject controllers)

Started by
5 comments, last by Deyja 17 years, 6 months ago
Hi. Are there are any performance penalties by using several modules? I'm using AS for gamescripting and the idea is to have a controller for every gameobject. Since the controller would have some defined variables that would be unique for each gameobject I thought that maybe I could put each controller in it's own Module. The downside is that if I have 100 gameobjects using the "SpinController" I would end up with 100 copies of the same code in 100 different modules. (Sounds like a waste of memory to me.) This is what the SpinController could look like...

/// SpinController ///

int numRotations;
float currentRotation;

void Update(GameObject@ go, float dt)
{
  go.GetSceneNode("MainNode").Yaw(45.0*dt);

  currentRotation += 45.0*dt;

  if(currentRotation > 360.0)
  {
    numRotations += 1;
    currentRotation = 0.0;
  }

}




An alternative would be to let the GameObject handle these variables so that the Controller work against those instead, that way I would only need one single SpinController. Something like this...

/// SpinController ///

void Update(GameObject@ go, float dt)
{
  go.GetSceneNode("MainNode").Yaw(45.0*dt);

  float currentRotation = go.Properties.GetFloat("CurrentRotation");
  int numRotations = go.Properties.GetInt("NumRotations");

  currentRotation += 45.0*dt;

  if(currentRotation > 360.0)
  {
    numRotations += 1;
    currentRotation = 0.0;
  }

  go.Properties.SetFloat("CurrentRotation",currentRotations);
  go.Properties.SetInt("NumRotations",numRotations);

}




These "Properties" that I store in the GameObject is already implemented and would work, but I'm afraid that could affect the performance a bit since these Properties was more meant for "once in a while" state changes etc... Do anyone cope with these kind of things and have any advice or suggestions? //Sweenie
<-Sweenie->
Advertisement
I use the first technigue myself. (I actually have a rather large library that I'm -> <- this close to releasing that handles that)

Yeah, it's a bit of a waste though. A remember Witchlord saying at one point that he was going to seperate the global script variables from the engine; that would allow scripts to be shared and still have unigue global data per entity. It would be perfect.

How the scriptengine behaves with many modules I don't know. I've not stress tested the system with more than 50 unigue modules.
Quote:Yeah, it's a bit of a waste though. A remember Witchlord saying at one point that he was going to seperate the global script variables from the engine; that would allow scripts to be shared and still have unigue global data per entity. It would be perfect.


That would be awesome, let's hope that will happen.

In the meantime I will probably continue with the second alternative.

When it comes to creation of GameObjects I do use several modules.
My GameObjects are componentbased and to simplify the creationprocess I've created some template functions that take an empty gameobject and a custom keyvaluepair table as parameters. The template functions are each put inside their own module which is named the same as their source file.

Like this...
// ==========================================================// SimpleMesh Template// ==========================================================void Setup(GameObject@ go, Table params){	if( not params.HasString("meshfile") )	{	   Print("Missing required param <meshfile> for GameObject <" + go.GetName() + ">");	   Print("Using default 'cube.mesh' instead");	   params.SetString("meshfile","cube.mesh");	}	SceneNode@ node = go.AddSceneNode("MainNode");	if( params.HasVector3("position") )	  node.SetPosition( params.GetVector3("position") );	Entity@ ent = go.AddEntity("Mesh", params.GetString("meshfile") );	ent.AttachToSceneNode(node);	if( params.HasString("material") )	  ent.SetMaterial( params.GetString("material") );	else	  ent.SetMaterial("BaseWhite");}


And to create an instance of a "SimpleMesh" GameObject I do like this...
// ==========================================================// Main script// ==========================================================void Main(){	Table params;	params.SetString( "meshfile", "monkey.mesh" );	params.SetString( "material", "GreenSkin" );	params.SetVector3( "position", Vector3(0,0,90) );	GoMgr.CreateGameObject("Monkey","SimpleMesh.as",params);}


The second parameter "SimpleMesh.as" is actually the module name in which I call the function Setup()

This way I can define any kind of gameobject, from a simple rendered mesh to more advanced gameobjects like a physics-driven vehicles. Passing a collection of parameters allow me create loads of different gameobjects with just a slight modification of the creation parameters.

For example to create another monkey but with another material I just add two lines to the code above...
// ==========================================================// Main script// ==========================================================void Main(){	Table params;	params.SetString( "meshfile", "monkey.mesh" );	params.SetString( "material", "GreenSkin" );	params.SetVector3( "position", Vector3(0,0,90) );	GoMgr.CreateGameObject("Monkey","SimpleMesh.as",params);	params.SetString( "material", "BlueSkin" );	GoMgr.CreateGameObject("SecondMonkey","SimpleMesh.as",params);}


It works so far... :)
<-Sweenie->
Besides the memory waste, I cannot think of any direct performance impacts with using multiple modules.

I do indeed intend to change the way script code and global variables are stored, so that applications can use the memory resources in a better way. Perhaps I'll start on this after releasing the next stable version.

It'll be something like this:

- A module will hold script code and shared global variables.

- A context will reference a module, and hold the non-shared global variables.

- A thread will execute code in a context.

Typically each game object will have it's own context. The threads will be pooled and only allocated when a script function is executed.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

That's awesome. One thing jumps out at me, though. I'd hate to have to figure out wether a global is shared or not, and call different functions depending. It would be perfect if a shared global could still be set with the exact same function call as a non-shared global. Your proposed system is almost exactly what I've implemented in my own wrapper code.

Sweenie; your property system seems to suffer from one flaw: it's totally un-generic. How easy is it to add new types that can be stored in a property? I've written a library purposly for that sort of thing. I'd like atleast one person besides me to use it before I release it.

Here's some code samples.

//Registering the type 'string'#include "autobind.h"namespace{	sil::script_string_type StringFactory(unsigned int length, const char *s)	{		return sil::script_string_type(s,length);	}};REGISTER_COMPLETE_BASIC_TYPE(string,sil::script_string_type);silREGISTER_ANY_FUNCTIONS(string,sil::script_string_type);REGISTER_TYPE_BEHAVIOR(string,asBEHAVE_ADD_ASSIGN,"string& op_addassign(const string&)",asFUNCTION((Wrap::AddAssign<sil::script_string_type&,sil::script_string_type,sil::script_string_type>)), asCALL_CDECL_OBJLAST);REGISTER_BEHAVIOR(asBEHAVE_EQUAL,"bool op_equal(const string&,const string&)",asFUNCTION((Wrap::Equal<sil::script_string_type,sil::script_string_type>)), asCALL_CDECL);REGISTER_BEHAVIOR(asBEHAVE_NOTEQUAL,"bool op_notequal(const string&, const string&)",asFUNCTION((Wrap::NotEqual<sil::script_string_type,sil::script_string_type>)), asCALL_CDECL);REGISTER_BEHAVIOR(asBEHAVE_ADD,"string op_add(const string&, const string&)",asFUNCTION((Wrap::Add<sil::script_string_type,sil::script_string_type,sil::script_string_type>)), asCALL_CDECL);REGISTER_TYPE_BEHAVIOR(string,asBEHAVE_INDEX,"uint8& op_index(uint)",asFUNCTION((Wrap::Index<char&,sil::script_string_type,unsigned int>)), asCALL_CDECL_OBJLAST);REGISTER_METHOD(string, "uint length()", asMETHOD(sil::script_string_type,size), asCALL_THISCALL);REGISTER_STRING_FACTORY(string,asFUNCTION(StringFactory),asCALL_CDECL);


//Loading, compiling, and executing a script//    ...and bits of the unit testing framework. ^.^std::string script = 	"string test(int16& unused)"	"{"	"	unused = 42;"	"	return \"hello world\"; "	"}";		sil::Initialize(&filesource,&outstream,0);	Preprocessor::define("this __THIS");		sil::Any test_create_any = sil::create_any("int16");	test(test_create_any.is_of_type<short>(),"Create any int16");	sil::Script test_script(script,"script");	test(test_script.good(),"Create and parse script");	sil::Any test_script_result;	int error_code = test_script.execute_function(test_script_result,"test",silMAKE_ANY_LIST(test_create_any),10.0);	if (test(error_code == 0,"Execute script function 1"))	{		if (test(test_script_result.is_of_type<sil::script_string_type>(),": returns string"))			test(test_script_result.reference<sil::script_string_type>() == "hello world",": : value is \"hello world\"");		test(test_create_any.reference<short>() == 42,": modified reference parameter");	}
So, is Sil some kind of helper system to simplify the process of binding classes to the script?
<-Sweenie->
Yes. It also adds abstractions for scripts and threads, and hides the details of setting script globals and calling script functions (with parameters and return values).

This topic is closed to new replies.

Advertisement