Archive 17/01/2023.

Angelscript inheritance

thebluefish

I’ve been scratching my head over this one for a few days, thought you guys might have a better idea.

Essentially I want to define a standard interface that can be then inherited from in Angelscript classes. For example, say I have a card game like Magic The Gathering, my base class might look like:

class Card { public: virtual void PreDrawPhase() = 0; virtual void PostDrawPhase() = 0; // etc.... };

Then I want to be able to define each card in Angelscript such that the cards can be called from C++, but the logic is handled by the script itself. How would I go about defining the behavior?

friesencr

Do you need to pass data generated from c++? Why does the c++ code call it? There are lots of looser coupled options like subscribing to events and using node vars data bags.

thebluefish

Think of my example from before. Say I have a hundred cards and each one does various effects. Instead of hard-coding the effect in C++, I can let the script define the effect that’s done. So I could then call from C++:

// in pseudo-code foreach(card in player.cards) { if(card->PreDrawPhase()) { player->DrawCard(); } card->PostDrawPhase(); }

and in my card’s script:

[code]bool PreDrawPhase()
{
if(player.cards.count > 7)
return false; // Don’t draw a card

return true;

}

void PostDrawPhase()
{
//Discard another player’s card because we can
}[/code]

weitjong

Have you checked the ScriptInstance documentation? I think it may fit your use case well. You should be able to define your base AngelScript class using empty interface ScriptObject then derived your Anglescript inherintance from this base class. I don’t think you need C++ abstract base class for that. You can then invoke the ScriptInstance’s method by using ScriptInstance::Execute() or ScriptInstance::DelayedExecute() methods from the C++ side.

thebluefish

I never noticed ScriptInstance, I only saw ScriptFile when I was looking through before.

This seems promising, especially as it appears the ScriptInstance can register for events. I am confused about this bit:

Say I have the file testCard.as with the following:

bool HandlePreDraw()
{
// Do something
}

void HandlePostDraw()
{
// Do something
}

Would it still receive events properly if I called SubscribeToEvent() from the Start() function? Or would I need to set it up a different way? Also, event data is read in AngelScript the same way it is read in C++, correct?

weitjong

As I understand it, you only use SubscribeToEvent() when you know there is other Object sending a particular event that your object is interested (and want to become the event receiver). For your case, I don’t think you can easily find the preexisting events that are suitable for your two handlers to listen to. I believe it is easier to just invoke the PreDraw() and PostDraw() as normal Script Object’s methods via the ScriptInstance::Execute() method as I pointed out earlier.

Regarding your confusion, You can observe the differences in the Samples by comparing SubscribeToEvent() call in Ninja.as and SubscribeToEvent() call in 01_HelloWorld.as. HTH.

friesencr

Some events like some of the ui events don’t have senders. So using the syntax without a the sender is used. It’s contextual.

thebluefish

Due to the way events work, it’s trivial to add new events as needed. My game already has a few dozen custom events in use, same as the InputManager class that I released.

Completely forgot about the .as demos as I had previously deleted them from my build. I looked through them and found what I needed.

For a test, I created a Card class inherited from ScriptInstance. Luckily, it’s easy to tell Angelscript that one class inherits another. Therefore instead of traversing nodes looking for an attached ScriptInstance components which might or might not be a Card (could be something else scripted), it’s now easier to traverse the nodes looking for an attached Card component.

One thing I haven’t yet figured out (remember I’m fairly new to scripting) is how to access the current node within the script. I know I can call my globally registered class from the script. Does ScriptInstance give global variables to the underlying script to do this?

friesencr

if you are in a ScriptInstance and call “node” not this.node, it will give you the node that is attached to the scriptinstance.

weitjong

ScriptInstance is a Component. So, it should have all the common methods and properties the base Component class gives, including the node property. See urho3d.github.io/documentation/H … ptInstance

Yes, adding new events to suit your need works as well. I think it is all comes to the design decision whether you want to have loosely coupled or tightly coupled design. The Urho3D library cannot anticipate how all the components in the end application interacts with each other, so having the event subscription mechanism solves that problem by allowing the components to depend on each other loosely. While in your own application, you do have a choice between the two as you should already know how your components would interact with each other before hand.

thebluefish

Awesome :smiley:

I was able to further work the ScriptInstance class to fit what I was trying to do. I find it’s easy to simply inherit from this class, and expose additional script functions explicitly.