Advertisement

Passing an object from AngelScript to C++

Started by September 18, 2009 02:29 AM
10 comments, last by WitchLord 15 years, 2 months ago
Is there a way to pass an object from a class that is created in AngelScript to the C++ side? The C++ side doesn't know about this class. I've look through the manual and I found asIScriptObject, so I think that it's possible to do that with C++ not knowing about the class in AS but look at the object as a general script object asIScriptObject. However, I could not find a way to do it. I've tried register a function "int createCollision( int, GameActor ∈ )" But then an error occured, telling me that the configuration is invalid and GameActor is not a data type. Any help is appreciated :) Thanks!
You can see an example of using an asIScriptObject pointer in this thread.
Advertisement
Thank you Sicrane. That was a really useful topic.

I've read through the topic and it seems like the only way to pass the object from script back to the application is to register a class in the application first.

For example, in order to pass ScriptFoo you have to create CFoo, and register ScriptFoo with CFoo properties, size, etc. ( Or as in your example, register FooInner with property and methods of Foo )

Am I understanding this correctly? Also, is there any other way to do this without registering the base class for script class first?

Thank you in advance :)
The easiest way to do it is with interfaces. Example:

  // Register an interface  engine->RegisterInterface("MyIntf");  // Register a function that takes a handle of the interface  engine->RegisterGlobalFunction("void func(MyIntf @)", asFUNCTION(func),   asCALL_CDECL);


The function is implemented like this:

  void func(asIScriptObject *obj)  {    // Access the properties of the script object    // Release the handle before returning (unless we're storing it)    if( obj )       obj->Release();  }


The script class would need to implement this interface. As we didn't specify any methods for the interface, then this is really easy:

  // The AngelScript class must inherit from the interface  class MyClass : MyIntf  {  }  // Passing the class to the application function is also easy  void main()  {    MyClass cl;    // Pass the script class instance to the application function    func(cl);  }


Another option is to use the generic container add-on, CScriptAny. In this case the script might look like this:

  void main()  {    MyClass cl;    func(any(@cl));  }


The third option, is to use the variable argument type directly. You can look at the CScriptAny add-on for example on how it works.

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

If you have an asIScriptObject * you can call methods on it by first using GetObjectType() to get the asIObjectType * for the object, then call GetMethodIdByDecl() on that to get the id for the method. Prepare a asIScriptContext with that id. Fill the asIScriptContext with the appropriate object and arguments and call Execute() on the context. If you want to just access properties you can use GetAddressOfProperty() on the asIScriptObject.
Thank you both of you! I've managed to store the script object inside the application class now.

However, new problem arise when I tried to pass that script object reference back to the scripting side.

Basically, what I'm trying to do is having a collisionShapeObject in the application side. The collisionShape must know which script object it belongs to.
During collision detection, if two collisionShape collided, it will store the pointer to the other collisionShape's owner and send that to the scripting side so that it can manipulate the other item.

For example, collisionShape A collide with B. A will send the pointer of its owner to B, and viceversa. The owner of A on the scripting side will then get the collided object from this function

GamePlayer @target = cast<GamePlayer@>( getCurrentCollidedTarget( collisionId ) );if( target !is null ){	        //Do something with the collided player.	target.z += 1;				}



I got this far, but the problem is that the changes doesn't seems to affect the target at all. I've tried printing out the value, and the value changes. But the actual target player on the screen doesn't seems to be affect by the change. It's like, the target object handle point to a different object or something.

To help you see the problem better, I'll paste some code related to passing pointers here.

//CPP Sideint createCollision( int type, asIScriptObject *obj ){        //Create a new collisionShape and return the id to the scripting side.	eCollisionType colType = eCollisionType(type);	int id = GPF::GetASCollisionManager()->createCollision( colType );	AngelCollision *tmp = GPF::GetASCollisionManager()->get(id);        //Set owner of the collisionShape	tmp->setOwner( obj );        obj->AddRef();	return id;}asIScriptObject* getCurrentCollidedTarget( int id ){	AngelCollision* tmp = GPF::GetASCollisionManager()->get(id);	return tmp->target;}// The script side should call this right after it is done with the collided // target.void resetCurrentCollidedName( int id ){	AngelCollision* tmp = GPF::GetASCollisionManager()->get(id);	tmp->target->Release();	tmp->targetName = NULL;	}
Advertisement
How do you pass the original object to the application? Make sure you pass it by reference or by handle and not by value. If you pass it by value the application would receive a copy of the object.

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

I call the "createCollision" like this

 //Circle is an enum for collisionShape collisionId = createCollision( Circle, @this );


Which will return an integer of the collisionShape id in the application side. You can see the cpp code of createCollision in my earlier post.

I believe this is how I pass a reference from script to application. Or am I doing it wrong?

Here is how I register the function

// An interface that will be inherit by all game actor on the script side.r = AngelEngine->RegisterInterface("Collidable");r = AngelEngine->RegisterGlobalFunction("int createCollision( int ,Collidable @)", asFUNCTION(createCollision), asCALL_CDECL); assert( r >= 0 );
You're doing it correctly. I suppose you do it the same way for the target, right?

You're not updating the reference counter correctly though. The createCollision function should not increase the reference counter of the object, since it already received ownership of the reference in the parameter. The getCurrentCollidedTarget function must increase the reference counter of the object it returns. Manual: Object handles.

I'm not sure the incorrect handling of the reference counter is causing the problem you're noticing, but it would for sure cause other memory problems, such as memory leaks and memory access violations.

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

Thank you for the correction! Though that isn't the source of the current problem, I've found where the problem is. It was the mistake on my part in the script where it update the collisionShape. I forgot to update the collisionShape with an id of 0. Was using something like, if ( collisionId > 0 ) when it should be if (collisionId > -1) *sigh*

Anyway, thank you very much for your help :) Really appreciate it!

EDIT : I have another question about interaction between scripting and C++ :O

Is it possible to call a global script function from c++ class, and passing 'this' pointer as an argument? Basically, something like this


//Script
void attack(GamePlayer @owner){    // CreateBullet( owner, x, y )    // This should create a reference type object GameBullet on the C++ side.    GameBullet @bullet = CreateBullet(owner, 0, 0);    bullet.speed = 5    bullet.direction = random(360);}


//CPP Side
void GamePlayer::attack(){    CallScriptFunction( "PlayerModule", "void attack(this)");}



Is this possible? If so, could you please tell me how? I have an idea that I should register GamePlayer as a reference type, but I'm not sure if I can just pass 'this' into the script function call.

I hope I didn't ask too many questions! :(

[Edited by - hima on September 18, 2009 11:46:46 PM]

This topic is closed to new replies.

Advertisement