Question about the position vector of an object(godot) - vector

I have a scene in godot where there is a missile at the origin of the plane and I have another scene where I have a static platform "hanging in the air", the platform has a Position2d as child object and my goal is to create the missile on the Position2d object and direct it to wherever the player is. I was able to instantiate the missile scene at runtime, but I don't understand how to direct it towards the player. At this point I have a doubt (as per the question) about the vectors: considering the point where the missile is instantiated on the platform (which is far from the origin of the plane), does the position vector of the missile start from the origin of the plane and end on the point where the missile is? Or does it start directly from the point of the missile and end up I don't know where? Can anyone give me some clarification on this?

Side step the issue by working with global coordinates (global_position and global_transform).
Your vectors exist in a vector space. You should do your operation in the same vector space. The question is, in which vector space are your vectors?
While we are talking about the scene tree, the vector spaces are defined by the transform of the nodes. So the position of a node is respect to the origin of the parent node. In fact, the node exists in the vector space of the parent node, which may include rotation or scaling, for example.
Consequence of that, if you move a node, you also move all its children. If you rotate a node, you also rotate all its children, and so on.
You can also get transforms, and thus positions, relative to the origin of the scene tree. For that you use the global_transform (and globa_position). There are also helper methods to_local and to_global to convert between the two.
I have another scene where I have a static platform "hanging in the air", the platform has a Position2d as child object and my goal is to create the missile on the Position2d object and direct it to wherever the player is.
Since - I suppose - moving the platform should not move the missile. That is, the missile should move on its own, not attached to the platform. I'm going to suggest not making the missile a child of the platform. You can instance the missile and attach it to the scene tree root, for example. Or you may have a node dedicated for the purpose of holding the missiles.
Now, since you would be placing the missile as a child of a different node, let us work with global positions (and by extension with global transforms). Thus, ask the Position2D for its global_position (a.k.a global_transform.origin), then tell the missile to set is global_position to that:
missile.global_position = $Position2D.global_position
get_tree().get_root().add_child(missile)
By the way, if you are adding the missiles as children of the platform - which is ok as long as the platform does not move - you can still use global_position for this purpose. However in that particular case using position instead of global_position has the same result. That is because the Position2D and the missile would be siblings (have the platform as parent) and thus would be in the same vector space.
I was able to instantiate the missile scene at runtime, but I don't understand how to direct it towards the player
I strongly encourage having a script on the missile, where you can have a func you call to aim it, and then you can work it out there. Note: I'm assuming this missile will not track the player. The signature is something like this:
func aim(target_position:Vector2) -> void:
pass
Then when you instance the missile, you can call aim on it, passing the global_position of the player:
missile.aim(player.global_position)
The position of the player is relative to whatever the parent of the player is. And the missile, nor the platform are aware (and should not be aware) of what that is. So use global_position instead. That is, since you as developer know what the parent node of the player is, you can hardcode that logic there. But then you would need to remember to change that if you decide to change where the player is in the scene tree. So just don't! Use global_position.
In fact, let us rename the parameter:
func aim(target_global_position:Vector2) -> void:
pass
What do we need to do in that func? We need to compute a direction. We can do that:
var _direction:Vector2
func aim(target_global_position:Vector2) -> void:
_direction = global_position.direction_to(target_global_position)
Here we are figuring out the direction in global space (both vectors are in the same vector space, so we know this operation makes sense). Assuming there is no rotation or anything like that involved, this is ok. However, just to be sure, let us bring that to local space:
var _direction:Vector2
func aim(target_global_position:Vector2) -> void:
_direction = to_local(global_position.direction_to(target_global_position))
By the way, just so you know, that is equivalent to this:
var _direction:Vector2
func aim(target_global_position:Vector2) -> void:
_direction = global_transform.affice_inverse().xform(
global_position.direction_to(target_global_position)
)
And we might want to rotate the missile:
var _direction:Vector2
func aim(target_global_position:Vector2) -> void:
_direction = to_local(global_position.direction_to(target_global_position))
rotation = _direction.angle()
Or we can do it like this:
var _direction:Vector2
func aim(target_global_position:Vector2) -> void:
# …
rotation = get_angle_to(target_global_position)
Or like this:
var _direction:Vector2
func aim(target_global_position:Vector2) -> void:
# …
look_at(target_global_position)
Yes, both get_angle_to and look_at expect a global position.
Finally, you would want to move the missile. You can, for example, can use the direction to compute a velocity (multiplying the direction by the speed you want), and use that to move the missile.

Related

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
Basically I'm trying to track the time bar
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.

How can i make a rigid body jump in godot without givign it the ability to fly

I would have used a kinematic body but i want to add real life physics to my 2d object but it seems that i can literally fly by pressing the up key several times
extends RigidBody2D
var velocity = Vector2.ZERO
const GRAVITY = 35
const SPEED = 1000
const JUMPFORCE = -900
func _process(delta):
if Input.is_action_just_pressed("ui_right"):
apply_central_impulse(Vector2(1000,0))
if Input.is_action_just_pressed("ui_left"):
apply_central_impulse(Vector2(-1000,0))
if Input.is_action_just_released("ui_up"):
apply_central_impulse(Vector2(0,-1000))
With test_motion
I do not recommend handling this with the RigidBody2D collisions because not every collision means it is on the ground (e.g. could be hitting a wall or ceiling).
However, there is another way: you can use test_motion. It returns true if a movement would result in a collision (without actuallyu moving, of course).
So use test_motion downwards, if it detects a collision, it means the RigidBody2D is on the ground:
if Input.is_action_just_released("ui_up") and self.test_motion(Vector2(0, 1), false):
apply_central_impulse(Vector2(0,-1000))
With RayCast2D
Another simple setup is using RayCast2D instead. Position it to extend downwards from the the bottom of the CollisionShape2D or CollisionPolygon2D of the RigidBody2D. Make sure the RayCast2D is enabled. Then you can check if the RayCast2D is colliding to decide if the RigidBody2D can jump:
if Input.is_action_just_released("ui_up") and $RayCast2D.is_colliding():
apply_central_impulse(Vector2(0,-1000))
Consider that with the above method, if the RayCast2D fail to detect something, the RigidBody2D won't be able to jump. For example, if the RayCast2D is centered, but the RigidBody2D is only partially on the ground, such that the RayCast2D hits nothing.
With Area2D
Another alternative is to use an Area2D instead of a RayCast2D. Which has the advantage of having its own CollisionShape2D or CollisionPolygon2D. You will need to keep count of bodies that enter or exist the Area2D:
var on_ground := 0
func _on_Area2D_body_entered(body: Node) -> void:
on_ground += 1
func _on_Area2D_body_exited(body: Node) -> void:
on_ground -= 1
And of course, use that in your check to jump:
if Input.is_action_just_released("ui_up") and on_ground > 0:
apply_central_impulse(Vector2(0,-1000))
Finally I remind you that you have collision layers and collision masks. Using a RayCast2D or an Area2D would allow you to set a different mask than the one the RigidBody2D uses. Which is a way to specify that not all things that the RigidBody2D collides with, are things from which it can jump.
I find Area2D to be more reliable and versatile. However, it is more work to setup.
Just wanted to add something in the answer by Theraot.
Here one signal by area2d can be avoided.
var on_ground := 0
func _on_Area2D_body_entered(body: Node) -> void:
on_ground = 0
if Input.is_action_just_released("ui_up") and on_ground == 0:
apply_central_impulse(Vector2(0,-1000))
on_ground = 1
This works same as using both signals of body entered and exited.

how to make sprites stick together in game maker?

I don't know how to get sprites to stick to each other so they become one big object instead of tiny little pieces, for example:
attaching a thruster to a box, then the thruster stays in that spot while pushing the box, and also is there a certain term for what I'm talking about?
You could also attach all parts to one of the objects, it would sort of look like:
//Main object
x = 5;
y = 20;
//other object step event
x = obj_main.x + <any value to put it where you want>;
y = obj_main.y + <any value to put it where you want>;
//This will force the parts to follow the main object.
`
`
You can use an array, defined in the 'main' object to use a sort of grid to define where each piece is and then either draw each individual sprite, based on its position in the array, originating from the 'main' object's coordinates. Or just create an individual instance of an object if you would like to have additional functionality by trading off some performance.
For more information on arrays and how to position sprites and objects based on set coordinates, check out the GML documentation provided below:
Arrays:
https://docs.yoyogames.com/source/dadiospice/002_reference/001_gml%20language%20overview/401_06_arrays.html
lengthdir:
https://docs.yoyogames.com/source/dadiospice/002_reference/maths/real%20valued%20functions/lengthdir_x.html
what I did was make the object disabled, so when I press left and right it doesn't go anywhere, only the other piece would move, but when it came into contact it allowed the other piece to move along with it, and set its speed to the corresponding objects speed, in simpler term, when I collide with it, it turns the movement on and goes in the same direction as the current object in the same speed, making it look like its sticking

Qt: change QGraphicsItem receiver during mouse move

I am currently trying to implement a Bezier pen tool. The course of events looks like this:
click on point (QGraphicsItem), start moving while clicked
in QGraphicsScene mouseMoveEvent, prevent moves of point (with a boolean flag) until when distance from point.pos() to event.scenePos() reaches a threshold. When this happens unselect and mouseRelease point, add a node (QGraphicsItem) – select it and give it mousePress state (plus unset the boolean flag)
the user can move node after that, then release mouse.
(The node is a child item of the point.)
I tried to do this inside the scene’s mouseMoveEvent (I have a conditional branch to know when to do this):
point.setSelected(False)
point.ungrabMouse()
node.setPos(event.scenePos()-point.pos()) # positioning relative to point since it’s a childItem()
node.grabMouse()
event.accept()
But after doing this it occured that the node was only getting mouseMoveEvent’s after I release the mouse… (I print them in the console, the node itself did not move.)
So I figured, maybe the scene needs to eat a mouseReleaseEvent before sort of "releasing focus". I found an article that is tangent to the subject here.
So then instead of using ungrabMouse()/grabMouse(), I tried this:
mouseRelease = QEvent(QEvent.MouseButtonRelease)
self.sendEvent(point, mouseRelease)
node.setPos(event.scenePos()-point.pos()) # positioning relative to point since it’s a childItem()
mousePress = QEvent(QEvent.MouseButtonPress)
self.sendEvent(node, mousePress)
Now when I reach the distance threshold, I can see that only point gets selected (good) however as I move further both point and node are selected and moving… I would expect that since I have unselected and released (parent) point, it would not keep moving.
The article I linked to does do something different but it says "It turns out, we have to simulate a mouse release event to clear Qt’s internal state." which might be relevant to the current situation however I do not know what extra steps might need to be taken in order to “clear Qt’s internal state”… so I’m hoping a QGraphics aficionado can weigh in and help me out figuring this.
Thanks for having a look here.
A combination of sending mouse events and grabbing mouse manually works… has to be ungrabbed manually on mouseRelease though.

Conceptual question: Loose Coupling

I am building a graphic board like project where i am facing a design issue.
Main Class is Board which is a canvas responsible for handling mouse events when drawing shapes. It also has context variables such as currentShape or snapFlag to activate grid magnetism.
To handle the moving / resizing / rotating of the shapes, they inherit from a third party open source tool called ObjectHandles (flex).
I have a baseShape extending ObjectHandles main class to override some of its internal functions, like the onMove function.
When creating a shape (mouse down, move, mouse up) this is handle by the Board and it knows about his own snap flag.
var mouseUpPoint:Point = boardCanvas.globalToLocal(new Point(event.stageX, event.stageY));
var snapMouseUpPoint = snapPoint(mouseUpPoint.x, mouseUpPoint.y);
In my overidden onMove method i would like the shape to be aware of the Board snap flag and when its changing. How do i do this ?
Do i pass the Board as a parameter in my basicShape constructor so that i can check snap ?
Do i pass the flag as a parameter and somehow make all shapes listen for change ?
What is the cleanest solution ?
Thanks a lot.
I would approach this from a slightly different angle. I assume that the Board object traps mouse events first, so that it can decide which shape has been clicked on. I would have the board trap mouse movements as well, passing the correct (snapped or unsnapped) coordinates "down" to the selected Shape object, rather than letting the shape object figure it out.
This leaves the grid snap handling to the Board, and keeps your Shape object onMove method free of clutter.
Not knowing your app:
Is it ever possible for a Shape to have it's own 'snap' behavior? That is, could a Shape be excluded from snapping while others aren't? If so, make snapFlag a member of Shape. When snapFlag is set on the Board, iterate through your Shapes and set or don't set according to your rules.
If snapping behavior applies to all Shapes on the Board, consider an event-driven model (if it's available - I'm a Flex noob). When a Shape moves, have it raise an OnMove event. The Board can then respond and decide to 'snap' the Shape into place if it's appropriate.
If snap behavior applies to all Shapes and events aren't available, I'd just say the hell with loose coupling in this case - make the Shapes Board-aware. It sounds like you're saving a bunch of code by using the ObjectHandle. That benefit may out-weigh the cost of coupling your UI elements.
Just trying to think together with you..
I see no big deal in Shapes having IBoard interface.
Though, I don't like the idea that they have to check the flag on the board...
How would you pass the flag as parameter? In OnMove() method? didn't understood this quite well...could you expand?
Though..
If you try to think a bit about SRP - single responsibility principle...what is the responsibility of Shape classes?
Yea, this is what eJames wrote already.
It feels to me that their main responsibility is probably NOT handling mouse events...here need to know more about your application, but my general feeling is why not someone else get this mouse down and then figure out what the shape should do with it and for instance call Draw() on the Shape with new coordinates?
Let's say you want to apply something like Composite pattern (Shapes inside shapes...) and you want them to be able to handle those mouse events themselves...but then
Then it would be logical if they perceived this mouse event in their local coordinates, but then I think you should provide all the information through this event (local coordinates, mouse status...) so that they don't have to ask for "global" variables on the board...
Passing the flag as a parameter for the shape constructor. But it wont be good since flag is going to change and i have to make each shape to update their flag copy on change.
Its true that shape responsibility is not to know how to handle mouse events. But thats what ObjectHandles do: react to events, update height width rotation parameter of the shape.
Maybe i should transfer some of the library code in my board class to handle shape selection and movement / resizing / rotation.
OnMouseMove ObjectHandles
protected function onMouseMove(event:MouseEvent) : void
{
if( ! visible ) { return; }
if( ! event.buttonDown )
{
setMouseCursor( event.stageX, event.stageY );
return;
}
if(parent == null )
{
return;
}
var dest:Point = parent.globalToLocal( new Point(event.stageX, event.stageY) );
var desiredPos:Point = new Point();
var desiredSize:Point = new Point();
var desiredRotation:Number = 0;
... plenty more
then
if( wasMoved ) { dispatchMoving() ; }
if( wasResized ) { dispatchResizing() ; }
if( wasRotated ) { dispatchRotating(); }
So i can not listen for move event and tell the board to snap it since the shape is already moving freely. I should add snap here:
var dest:Point = parent.globalToLocal( new Point(event.stageX, event.stageY) );
All shapes follow the snap rule there can not be one snapping and the other free.
Solved it this way:
Since i overridde onMouseMove in my baseShape class and i am using PureMVC framework, i just made baseShape aware of my boardMediator.
override protected function onMouseMove(event:MouseEvent) : void
{
[...]
// added on override
var board:BoardMediator = ApplicationFacade.getInstance().retrieveMediator(BoardMediator.NAME) as BoardMediator;
Then
desiredPos = board.snapPoint(desiredPos.x, desiredPos.y);
Maybe not super pretty but it works, o
Overridding the globalToLocal method in my board view did work too but some more calculations were done inside onMouseMove resulting in an out of alignment snap move.
Use ObjectHandles Version 2, and then create a constraint to do what you want.

Resources