Which AnimationPlayer is currently shown in bottom panel Animation? - godot

I'm trying to create my own plugin and I need to figure out which AnimationPlayer node is currently shown in the bottom Animation panel
and please do not suggest I use get_selection() because even if I select multiple (or none ) AnimationPlayer node(s), only one is show on the Animation Panel
so far my approach has been to try finding the node to the Animation panel using get_editor_interface() and then maybe viewing it's properties?
Edit
Based on the answer given by user #Theraot I tried this:
tool
extends EditorPlugin
func handles(object):
print('handles=>',object);
return true;
func edit(object):
print('edit=>',object);
func _enter_tree():
pass;
but using this I only get the selected nodes within the tree but even if I haven't selected any AnimationPlayer Node it still shows up in the animation panel

On your EditorPlugin implement handles to return true on any AnimationPlayer given to it, and Godot should call edit with the AnimationPlayer that is to be edited currently.
Example code:
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)
I was hoping the information of which AnimationPlayer is currently being edited in the Animation panel was perhaps stored in some node for the bottom panel?
Even if get the correct node from the interface:
var control:= Control.new()
var animation_player_editor:Control
add_control_to_bottom_panel(control, "puff")
for sibling in control.get_parent().get_children():
if (sibling as Control).get_class() == "AnimationPlayerEditor":
animation_player_editor = sibling
remove_control_from_bottom_panel(control)
if animation_player_editor != null:
print(animation_player_editor)
Which AnimationPlayer it is editing is not exposed to scripting. We can find a get_player method in the source code and a get_state method also in the source code, but we could only access them from C++, as they are not bound for scripting (source code).
In theory we should be able to get the EditorPlugin and call get_state on it, however it is not working for me:
var animation_player_editor_plugin:EditorPlugin
for sibling in get_parent().get_children():
if (sibling as Node).get_class() == "AnimationPlayerEditorPlugin":
animation_player_editor_plugin = sibling
if animation_player_editor_plugin != null:
print(animation_player_editor_plugin.call("get_state"))
Despite get_state being exposed on EditorPlugin, it appears to not be possible to call it.
By the way, we can see in the source code for AnimationPlayerEditorPlugin that it follows the handles and edit logic I used above.

Related

How to invoke a function when changing position?

I'm trying to invoke a function everytime the position of a position2D node is changed
I tried to override the setter as given in the docs like this:
extends Position2D
tool
func set_position(value):
print("changed position ",value)
self.position=value;
what am I missing here? is there a way to achieve what I'm trying?
The method set_position is not virtual. You are not overriding it. At best you are hiding it.
If you want to follow that approach, intercept the property instead, using _set (Notice that the documentation mentions that _set is virtual). The idea is that we are going to take the attempt to set position, do whatever we want to do when it is set, and then let it continue normally.
func _set(property:String, value) -> bool:
if property == "position":
print("changed position ",value)
return false
And you should see that when you modify the position in the inspector panel, you get the message.
I suspect that is not enough for you. In particular the above solution does not detect when the position changes because you drag the Position2D in the editor.
This is becase when you move the Position2D in the editor, Godot is not setting the position property. Instead it is (after digging in the source code) calling the undocumented method _edit_set_position… Which you can also call from GDScript! Anyway, the point is we cannot intercept it (and we cannot override it. Yes, I tried).
Instead we are going to use _notification. We are looking for NOTIFICATION_TRANSFORM_CHANGED, but to get that notification we need to enable it with set_notify_transform. The code looks like this:
tool
extends Position2D
func _enter_tree() -> void:
set_notify_transform(true)
func _notification(what: int) -> void:
if what == NOTIFICATION_TRANSFORM_CHANGED:
print("changed position", position)
With this approach you will also be able to react to when the user moves the Position2D in the editor by dragging it around or with the keyboard directional keys. Which are the cases where Godot uses _edit_set_position. Or any other means that we could not intercept with the initial approach.
You can use this approach if you need to do something every time the Node2D moves, but it does not move very often.
By the way, this also works for 3D.

Godot: Call external method

Lots of googling and I'm still not grasping what is probably a simple solution.
Scene: "Main". Contains a TileMap "Grid" with a script attached to it "Grid.gd".
Scene: "Player". Contains a KinematicBody2D "Player" with a script attached to it "Player.gd"
In Player.gd, I need to call a method in Grid.gd "_Calculate", pass it two variables, and have it return one variable.
var vNewPosition = Grid._Calculate(vPlayer, vInputDirection)
Error: The identifier "Grid" isn't declared in the current scope.
Obiously I need to reference the Grid.gd script somewhere to access it, but none of the many examples I have tried work.
Thanks in advance,
Josh
Given that
The Main scene has an instance of the Player scene
And
In Player.gd, I need to call a method in Grid.gd "_Calculate", pass it two variables, and have it return one variable.
I'll give you a few options.
The bad way
If Player knows the node path to Grid, it could use get_node to get it.
onready var _grid = get_node("../../grid")
Or something like that.
And then use it:
var vNewPosition = _grid._Calculate(vPlayer, vInputDirection)
This approach is, of course, not recommended. It will break if the node path is wrong.
The common way
Assuming Player does not know the node path (which is more likely), you can export a NodePath variable in Player.gd and use it to get the Grid:
exprort var grid_path:NodePath
onready var _grid = get_node(grid_path)
Then you need to set the Grid Path property in the Main scene to Grid.
This approach is better than the prior one, in that it does not depend on the path to Grid. However it still depends on a Grid being there. If Player makes no sense when there is no Grid, this approach is OK.
The good way
If Grid may or may not be there, and we want Player to work regardless, we can tweak the above approach:
exprort var grid_path:NodePath
onready var _grid = get_node_or_null(grid_path)
And remember to check if grid is null before using it:
if grid == null:
return # or whatever
var vNewPosition = grid._Calculate(vPlayer, vInputDirection)
Or like this:
var vNewPosition = grid._Calculate(vPlayer, vInputDirection) if grid != null else null
Which also allows us to specify a default value that vNewPosition will take when there is no grid (the null at the end of the line). And thus you can have the Player work when there is no Grid, and when there is it will use, and regardless of where (because Main tells it where it is).
This solution is not completely decoupled, because Player still knows grid is a thing that has _Calculate. Also we presume there is zero or one, not multiple. I can think of a way to approach that, but decoupling for the sake of decoupling is unnecessary. I'm calling this good enough for your use case. Yet, let me know if you want the over-engineered way.

Detecting click/touchscreen input on a 3d object inside godot

I am trying my best to build a mobile game in the godot game engine. And i have run into a problem. In a 3D game, how can i detect a mouse click on a specific object or rather where that object is on the screen. I do not understand how to use the control nodes in order to to this.
The easiest way to make a 3D object clickable is to give it a CollisionObject (such as a StaticBody) and connect to the input_event signal. For example, to detect a left-click:
extends StaticBody
func _ready():
connect("input_event", self, "on_input_event")
func on_input_event(camera, event, click_position, click_normal, shape_idx):
var mouse_click = event as InputEventMouseButton
if mouse_click and mouse_click.button_index == 1 and mouse_click.pressed:
print("clicked")
The docs mention that touch events are similar to click events.
Note that input_ray_pickable must be true on the CollisionObject (this is the default).

How to get text on unity 2D to display

I can't display text and I get a NullReferenceException when the method is executed. On top of that the code doesn't stop running as it should.
// Use this for initialization
void Start()
{
// Default position not valid? Then it's game over
if (!isValidGridPos())
{
Text text;
text = GetComponent<Text>();
text.text = "Game Over";
Destroy(gameObject);
if (Input.GetKeyDown(KeyCode.R))
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
}
}
The code comes from this tutorial. I just wanted to add it a restart and a Game Over message.
EDIT: Trying to display text with text = GetComponent() is not working. What's another way to display text on unity that actually works? I tried GUIText too but I can't make it visible on display.
I have looked at the tutorial and they have the following code:
if (!isValidGridPos()) {
Debug.Log("GAME OVER");
Destroy(gameObject);
}
This works because it prints to the debug log console. You obviously want to capture that and print it on the screen. This requires a canvas and a text component being added to the gameobject the script is on. Declaring a text variable here won't add one to the game object, it has to be done in the unity inspector.
The following code:
if (Input.GetKeyDown(KeyCode.R))
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
Shouldn't be here, as this object will already be destroyed this code will be rendered useless unless you are holding the key down in the same frame that the object is created. It should instead be on in another script on a gameobject that won't be destroyed and it will await an r key to reload the level.
If text = GetComponent<Text>(); isn't working it could be because your Text component is not on the same gameobject that your script is attached to.
In the Unity Editor, check that the text component and your script are on the same gameobject
Did you inspect editor view while running?
Don't use destroy in Strar().
You can set delay like so Destroy(gameobject, 5.0f) and use it in some other callback function (ex. Action).
Now you just destroying whole gameobject all its scripts and components(including Text) right after it initialized. If it is in scene from beginning, then it destroyed as soon as you press Play.
Start() runs just once so what does isValidGridPos() doing?

Unity - How to write a "Behaviour" or script for entire scene (in C#)?

I am starting to learn Unity.
As I understand, We can write scripts(behaviors) in the form of C# files and apply them to each objects on the scene.
But how to write a script for the entire scene? I know this is a obvious question - there has to be a script for the entire scene so that all my objects "behave" in a synchronized way and it's gotta be pretty basic, but preliminary Google searches has not borne much fruit.
Can someone give me a quick guide?
Taking your "boxes" example comment I would do the following:
Create an empty gameobject, let's call it BoxesController...
Attach below BoxesController.cs script to it
In the editor inspector reference all boxes
BoxesController.cs
public class BoxesController: MonoBehaviour
{
public Transform box1, box2, box3;
void Update() {
// change boxes position
}
}
Now imagine you will need to have > 30 boxes in current scene... You will have a lot of work to reference each box. So you could change your script if you add a Tag to all boxes. Let's say you create a new tag inside Unity Tag Manager called "Box" and give it to all boxes.
You now can change BoxesController.cs script to the above and you will not have to reference all boxes in the Editor Inspector because they will be searched and referenced inside Start method.
BoxesController.cs
public class BoxesController: MonoBehaviour
{
public GameObject[] boxes;
void Start()
{
boxes = GameObject.FindGameObjectsWithTag("Box");
}
void Update() {
// change boxes position
foreach (GameObject go in boxes)
{
//get box name
string box_name = go.Name;
// get box transform property
Transform t = go.transform;
}
}
}
Please note that GameObject.FindGameObjectsWithTag is a heavy operation and that's why I did it in the Start method and saved the result to reuse it in Update method calls.
what you can do is create an empty GameObject and add a script to it and use one of the techniques described in the link to get access to the 3 boxes you want to move.
http://docs.unity3d.com/412/Documentation/ScriptReference/index.Accessing_Other_Game_Objects.html
In this case you probably want to use "1. Through inspector assignable references." which just means create a public Transform variable in the script, save, then in the Inspector drag the box in the slot that appeared in the script-component
edit: for further reading i'd suggest googling the term "Game Manager" in combination with "Singelton" and "Unity" :)

Resources