Jump to content

member functions and global functions in Character callbacks


photo

Recommended Posts

Hello,

 

//agent.character
<inputs>
   <input name="case" type="function">Character::getGroundState</input>
</inputs>

This code here defines a function input, which uses the specified global function to provide a basis for an input value.

 

However, here is the definition of the getGroundState function:

//character.h
class Character {
//...
int getGroundState() {
   if(!ground) return GROUND_STATE_JUMP;
   if(actor.getState(PLAYER_ACTOR_STATE_CROUCH)) return GROUND_STATE_CROUCH;
   return GROUND_STATE_STAND;
}
//...
}

Here, it is defined as a member function of class type Character. Without specifying a particular instance of Character, how does the input function call this member function? I've searched through all the files I can find for a global getGroundState function, but cannot find it.

 

Here, we see the code for the InputFunction's usage of the getter (callback) function:

//animation_inputs.h
void getValue() {
   if(getter == "") return 0.0;
   [b]float new_value = clamp(call(getter) * scale + shift,min_value,max_value);[/b]
   if(smooth > EPSILON) {
float delta = new_value - value;
float dt = engine.game.getIFps();
value += sign(delta) * min(abs(delta),dt * smooth);
   } else {
       value = new_value;
   }
   return value;
}

Using the call(), they call a function with specified string name.

I attempted to make this call in the update() function of my world script, by typing the following, which results in a interpreter error saying the function does not exist. This is being tested within the same world script within which I have already successfully loaded and run the character through the world, and animation works properly as far as I can tell.

// MyWorld.h
int value = call("Character::getGroundState");

 

So, somehow the InputFunction is able to run this non-existant global function without crashing, and function properly. But I cannot find out where or what it is running, or how it knows which instance of Character to ask for the ground state from.

 

 

Am I misunderstanding something crucial about unigine scripting?

Link to comment
  • 2 weeks later...

Loading character xml (agent.character) call comes from the constructor of Character class so every callback is applyed to currently constructed object.

When you create an instance of Character all callbacks will work on this instance.

Link to comment

Loading character xml (agent.character) call comes from the constructor of Character class so every callback is applyed to currently constructed object.

When you create an instance of Character all callbacks will work on this instance.

 

Interesting. So, I understand the behavior which you have described. However, I am having trouble understanding how the code achieves this behavior. Since, all I see is a

call(getter)
// or
case 0: call(callback); break;

Based upon documentation, I would expect that it would need a reference to the character.

Unigine::Character character; // initialize somewhere

character.call("someMethod");

And, in this case the callback name would not be prefixed with "Character::"

Link to comment

Here's another way of looking at my question:

 

How do you check if a given callback exists?

 

is_function(callback, args.size())

 

This code, which should check if some function with given argument count exists... does not work for event callbacks.

Link to comment

Here's another way of looking at my question:

 

How do you check if a given callback exists?

 

is_function(callback, args.size())

 

This code, which should check if some function with given argument count exists... does not work for event callbacks.

 

To check if callback exists you need to specify the full name for it (with all namespaces).

 

So, if you call this:

 

is_function("Unigine::Character::getGroundState")

 

this will return 1.

Link to comment

To check if callback exists you need to specify the full name for it (with all namespaces).

 

So, if you call this:

 

is_function("Unigine::Character::getGroundState")

 

this will return 1.

 

Thank you Anna! That did the trick!

 

I would greatly appreciate it if you could explain to me why checking for the existence of the function requires the full name; however, calling it using the call() does not. Furthermore, I am still completely baffled as to how it is calling a member function for an specific instance of Character. Typically, in C++, one would expect that if a Character is composed of Event objects, which use the call() method, at best it would call member variables of Event. But since Event does not inherit from Character, it would not know about the member functions of Character. Furthermore, this strategy would fail if more than one Character contained the same instance of Event.

 

Extaliones explained it as the callbacks are tied to the Character within which they were loaded from file. This would make it seem that if I construct an Animation Tree from a character file, I would be unable to create a new Character, and assign its AnimationTree reference to point to the existing character's Animation tree. (Copying the animation tree to a new character). Furthermore, having dug through the Character loading code, I see no references to tying the callbacks to a Character. I would expect to need a reference to the character..., and calls to the callback looking like:

 

character.call("Character::callback");

 

I would really like to understand what is going on here, rather than treat it like a magic box.

1) So, if I need to change something, I can.

2) So, I can use this rather fascinating feature if I need it.

 

 

 

Thanks again for explaining the callback check, been waiting 2 weeks to finish this piece of my project .

Michael Zhang

Link to comment

I would greatly appreciate it if you could explain to me why checking for the existence of the function requires the full name; however, calling it using the call() does not.

call() is an internal UnigineScript function, so it receives current namespace during compilation time (the same as it works for thread, for, etc.)

 

is_function() is a part of Core Library but it is not internal. So it requires full namespace specification.

 

Furthermore, I am still completely baffled as to how it is calling a member function for an specific instance of Character. Typically, in C++, one would expect that if a Character is composed of Event objects, which use the call() method, at best it would call member variables of Event. But since Event does not inherit from Character, it would not know about the member functions of Character. Furthermore, this strategy would fail if more than one Character contained the same instance of Event.

The trick is that Input::getValue() method is called from Character context. And when you do something like:

 

call("Character::getGroundState");

original character instance is taken because it was previously set.

 

So, here is the simplified scheme of this process:

 

class Character {

   AnimationTree anim_tree;

   Character() {
        anim_tree = new AnimationTree();
   }

   void update() {
        // here Character instance is set
        anim_tree.update()
   }

   // ...

   int getGroundState() {
       return ground_state;
   }
};

class AnimationTree {

   // ....

   void update() {
       // when we call Character::getGroundState previously set Character instance will be available
       // because AnimationTree::update() was called from its context
       int ground_state = call("Character::getGroundState");
   }

   // ....
}

Link to comment

Thanks for the detailed explanation :blink:

 

That is an interesting feature of UnigineScript... context of calling class is available to callee when using the call().

 

 

Thanks! My animation editor is finally complete thanks to this!

Link to comment
×
×
  • Create New...