Advertisement

Calling a classes method not working.

Started by March 12, 2008 05:59 PM
5 comments, last by 39ster 16 years, 8 months ago
Im using angel script in my game and it allows people to create their own npcs within the game. To do this each NPC they create gets it own class and whenever they add an NPC into the level it uses CreateScriptObject to create an instance of the NPC class. I've got it to compile and create instances of NPCs but when i try to call a method (the npcs are event orientated) it wont work (but it still return >= 0). Heres the code for the Npc class:
CNpcClass* loadNpc(const CString& fName, asIScriptEngine* engine)
{
    CString className = removeFileExt(fName);
    CByteStream codeStream;
    if(codeStream.loadFile(fName.text()))
        return buildNpc(className, codeStream.text(), engine);
    return NULL;
}

CNpcClass* buildNpc(const CString& className, const CString& code, asIScriptEngine* engine)
{
    CString scriptCode;
    scriptCode << "class " << className << "\n";
    scriptCode << "{\n";
    scriptCode << "float x;\n";
    scriptCode << "float y;\n";
    scriptCode << code << "\n";
    scriptCode << "}\n";
    printf("Code: %s\n", scriptCode.text());
    if(engine->AddScriptSection("npcs", className.text(), scriptCode.text(), scriptCode.length()) >= 0)
    {
        if(engine->Build("npcs") >= 0)
            return new CNpcClass(className, engine->GetTypeIdByDecl("npcs", className.text()), engine);
    }
    return NULL;
}


CNpcClass::CNpcClass(const CString& className, int typeId, asIScriptEngine* engine)
{
    name = className;
    this->typeId = typeId;

    methodIds[evOnCreate] = engine->GetMethodIDByName(typeId, "onCreate");
    methodIds[evOnDraw] = engine->GetMethodIDByName(typeId, "onDraw");
    methodIds[evOnExploded] = engine->GetMethodIDByName(typeId, "onExploded");
    methodIds[evOnSwordCollision] = engine->GetMethodIDByName(typeId, "onSwordCollision");
    methodIds[evOnArrowCollision] = engine->GetMethodIDByName(typeId, "onArrowCollision");
    methodIds[evOnWarpCollision] = engine->GetMethodIDByName(typeId, "onWarpCollision");
    methodIds[evOnPlayerCollision] = engine->GetMethodIDByName(typeId, "onPlayerCollision");
    methodIds[evOnPlayerChats] = engine->GetMethodIDByName(typeId, "onPlayerChats");
    methodIds[evOnPlayerEnters] = engine->GetMethodIDByName(typeId, "onPlayerEnters");
    methodIds[evOnPlayerLeaves] = engine->GetMethodIDByName(typeId, "onPlayerLeaves");
    methodIds[evOnPlayerDies] = engine->GetMethodIDByName(typeId, "onPlayerDies");
    methodIds[evOnPlayerSpawns] = engine->GetMethodIDByName(typeId, "onPlayerSpawns");
    methodIds[evOnPlayerHurt] = engine->GetMethodIDByName(typeId, "onPlayerHurt");
    methodIds[evOnTimer] = engine->GetMethodIDByName(typeId, "onTimer");

    for(int i = 0; i < eventCount; ++i)
        printf("Event: %i:%i\n", i, methodIds);

}
int CNpcClass::getMethodId(unsigned int eventId) const {
    if(eventId < eventCount)
        return methodIds[eventId];
    else return -1;
}

int CNpcClass::getTypeId() const {
    return typeId;
}
And heres the code for the npcs:
CNpc::CNpc(float pX, float pY, CNpcClass* npcClass, asIScriptEngine* engine)
{
    context = engine->CreateContext();
    this->npcClass = npcClass;
    instance = (asIScriptStruct*)engine->CreateScriptObject(npcClass->getTypeId());
    x = (float*)instance->GetPropertyPointer(0);
    y = (float*)instance->GetPropertyPointer(1);


    printf("X: %f\n"
        "Y: %f\n", *x, *y);
    *x = pX;
    *y = pY;


}

bool CNpc::callEvent(unsigned int eventId)
{
    int methodId = npcClass->getMethodId(eventId);
    if(methodId >= 0)
    {
        context->SetObject((void*)instance);
        if(context->Prepare(methodId) >= 0)
            return context->Execute() >= 0;
    }
    return false;
}
Whenever i call "callEvent" it returns true but nothing happens. Also heres the angel script code. The script is called bomb.txt:
bomb()
{
  x = 64;
  y = 96;
}

void onCreate()
{
  print("test");
}
And the output:
Code: class bomb
{
float x;
float y;
bomb()
{
  x = 64;
  y = 96;
}

void onCreate()
{
  print("test");

}
}

Event: 0:57
Event: 1:-6
Event: 2:-6
Event: 3:-6
Event: 4:-6
Event: 5:-6
Event: 6:-6
Event: 7:-6
Event: 8:-6
Event: 9:-6
Event: 10:-6
Event: 11:-6
Event: 12:-6
Event: 13:-6
X: 64.000000
Y: 96.000000

Also i know the method isnt being called and its not just a glitch in print because i tried an infinite loop and it didnt freeze. npcs[0]->callEvent(evOnCreate) Also the constructor for the NPC works. It prints the correct x, y values in CNpc::CNpc()
Your problem is here:

bool CNpc::callEvent(unsigned int eventId){    int methodId = npcClass->getMethodId(eventId);    if(methodId >= 0)    {        context->SetObject((void*)instance);        if(context->Prepare(methodId) >= 0)            return context->Execute() >= 0;    }    return false;}


You need to call Prepare() before you call SetObject(). Prepare() resets the state of the context and prepares for a new execution, in this the object you set before the call is also cleared.

Also, you're not checking the result of Execute() properly. You need to compare with asEXECUTION_FINISHED, not >= 0. In fact, you're most likely receiving the return code asEXECUTION_EXCEPTION, and context->GetExceptionString() will tell you that you're having a 'null pointer access' in the script (due to the object not being properly set).

Regards,
Andreas

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

Advertisement
Thankyou for the quick reply! Yes that worked and i will check the result more carefully now.

Two more questions:
-Can i set the arguments before prepare?
-Would i create a context for every npc instance?
No, arguments must also be set after the call to Prepare(). Prepare() is what tells the context what arguments to expect.

You can use a single context for all your NPCs. It will save you a lot of memory as each context holds it own stack memory (by default 1024 bytes, but dynamically growing as needed).

You only need multiple contexts if you have long running scripts, that need to be suspended so that other scripts get a chance to run as well.

By the way, I like your structure for storing the NPC classes. Maybe I'll write a tutorial with something like this.

Regards,
Andreas

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

Thanks for the help. The script library is really fast. I did a test with 10,000 npcs in the level all executing this script every frame (the game is meant to run at 40fps/sec but i removed the FPS limiting):
void onDraw(){  for(int i = 0; i < 10; ++i)    depth = -10;}

And i get 80 fps/sec (while still drawing over 1200 16x16 tiles and animating the player). When the games done there probably wont be more than 10 npcs per level.
I'm actually quite surprised with the numbers, as there's definitely a lot more that I can do to improve the performance of AngelScript.

What CPU do you have?

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

Advertisement
http://img146.imageshack.us/img146/9298/testmg5.jpg


Thats 3000 npcs using this script:

void onDraw(){  for(int i = 0; i < 1; ++i)  {    depth = random(10) - 5;    x = random(640);    y = random(480);    drawSprite(x, y);  }}
Theres also no 3D acceleration for the drawing (using SDL) majority of FPS loss is from drawing.

In Game Maker (which has its own scripting language) i get 33 fps and Game Maker uses 3d Acceleration. When i let game maker draw the objects for me (instead of using code similar to the above) i get 70-80 fps. And thats without drawing 1200 16x16 tiles and animating the player.

[Edited by - 39ster on March 14, 2008 11:55:22 PM]

This topic is closed to new replies.

Advertisement