cak3_lover
cak3_lover

Reputation: 1938

Overriding AnimationPlayer functions

I'm trying to override my animation player functions like this:

tool
extends AnimationPlayer

func seek (seconds:float,update:bool=false ) -> void:
    print("seek =>",seconds)
    .seek(seconds,update)

func advance (delta:float) -> void:
    print("advanced=>",delta)
    .advance(delta)

func play (name:String="", custom_blend:float=-1, custom_speed:float=1.0,from_end:bool=false) -> void:
    print("play")
    .play(name,custom_blend,custom_speed,from_end)

but I have no idea why it isn't working, nothing is being printed when I test it

I'm particularly interested in seek(), when I move the time bar/when the animation is playing, it should be simultaneously print the seconds of where it is

enter image description here

Basically I'm trying to track the time bar

Upvotes: 0

Views: 721

Answers (1)

Theraot
Theraot

Reputation: 40170

These methods are not virtual.

Some people "override" non-virtual Godot methods (e.g. add_child) but the instances where people report that working, it is because of the type information (or lack thereof). And I should emphasize it is not overriding, but hiding the original method from late binding.

If the code that is calling seek knows it is an AnimationPlayer, it will call seek from the AnimationPlayer class. And since seek from the AnimationPlayer class is not virtual, your code does not have a chance to run. If it were doing late binding, then it might, but you should not rely on that working.

The case we are talking about, is the animation panel in the editor. Which is written in C++ and thus would know it is dealing with an AnimationPlayer.


Hacking the animation panel

First of all, make an EditorPlugin. You can refer to the documentation on Making Plugins. But, to cover the basics:

  • Go to Project -> Project Settings… -> Plugins and then click on Create.
  • Godot gives you a form to fill information about the Plugin.
  • Once you fill the form, it will create an EditorPlugin script on the sub folders of the "addons" folder you specified and with the name you specified.

You got your EditorPlugin script? Good.

The first steps are as I described in another answer.

You can get the current AnimationPlayer like this:

tool
extends EditorPlugin


var edited_animation_player:AnimationPlayer


func handles(object:Object) -> bool:
    return object is AnimationPlayer
    

func edit(object:Object) -> void:
    edited_animation_player = object
    print(edited_animation_player)

And you can get a reference to the AnimationToolsPanel like this:

var animation_tools_panel:AnimationToolsPanel


func _enter_tree() -> void:
    var control:= Control.new()
    var animation_player_editor:Control
    add_control_to_bottom_panel(control, "probe")
    for sibling in control.get_parent().get_children():
        if (sibling as Control).get_class() == "AnimationPlayerEditor":
            animation_player_editor = sibling
            break

    remove_control_from_bottom_panel(control)
    if animation_player_editor == null:
        push_error("Unable to find animation player editor")

Alright, now that we got that, we can extract some stuff from it. In particular we want:

  • The control that holds the name of the current animation.
  • The control that holds the current time.

So, let us have variables for those two:

var animation_name_input:OptionButton
var animation_time_input:SpinBox

Now, the first child of the AnimationToolsPanel is the top bar. And the top bar contains the controls we want. So we are going to iterate over the children of the top bar to find them:

    var found_option_button:OptionButton
    var found_spin_box:SpinBox
    for child in animation_player_editor.get_child(0).get_children():
        if child is OptionButton:
            found_option_button = child as OptionButton
        elif child is SpinBox:
            found_spin_box = child as SpinBox
        
        if (
            is_instance_valid(found_option_button)
            and is_instance_valid(found_spin_box)
        ):
            break

    animation_name_input = found_option_button
    animation_time_input = found_spin_box

Finally, in your _process if the references you got (the animation player, the editor panel, and the controls) are valid, you can get the animation name and time:

    var index := animation_name_input.selected
    var animation_name = "" if index == -1 else animation_name_input.get_item_text(index)
    var animation_time := animation_time_input.value

You want to do that in _process so it updates in real time when an animation is playing from the animation panel.


Of course, the issue is that you want to subclass AnimationPlayer. Thus, in your plugin you can check if the current AnimationPlayer is one of yours, and if it isn't disable this mechanism. But if it is, you can tell your custom AnimationPlayer what is the current animation and time is according to the panel from the plugin code by calling a custom method that takes those values as parameters, and does whatever you need to do with them.

Upvotes: 1

Related Questions