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

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.

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.

Question about the position vector of an object(godot)

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.

When compiling Rust to wasm (web assembly), how can I sleep for 10 milliseconds?

My rust program is managing memory for a 2d html canvas context, and I'm trying to hit ~60fps. I can calculate the delta between each frame easily, and it turns out to be roughly ~5ms.
I'm unclear on how to put my Rust webassembly program to sleep for the remaining 11ms. One option would be to have JavaScript call into Rust on every requestAnimationFrame and use that as the driver, but I'm curious to keep it all in Rust if possible.
I'm effectively looking for the Rust equivalent of JavaScript's setTimeout(renderNext, 11) when compiling out to the wasm target.
In your requestAnimationFrame callback, call setTimeout, and have that in turn make the next call to requestAnimationFrame. You can see the JS version of this here.
Based on the example in the wasm-bindgen book, here's how I do this in Rust:
fn animate_limited(mut draw_frame: impl FnMut() + 'static, max_fps: i32) {
// Based on:
// https://rustwasm.github.io/docs/wasm-bindgen/examples/request-animation-frame.html#srclibrs
// https://doc.rust-lang.org/book/ch15-05-interior-mutability.html
let animate_cb = Rc::new(RefCell::new(None));
let animate_cb2 = animate_cb.clone();
let timeout_cb = Rc::new(RefCell::new(None));
let timeout_cb2 = timeout_cb.clone();
let w = window();
*timeout_cb2.borrow_mut() = Some(Closure::wrap(Box::new(move || {
request_animation_frame(&w, animate_cb.borrow().as_ref().unwrap());
}) as Box<dyn FnMut()>));
let w2 = window();
*animate_cb2.borrow_mut() = Some(Closure::wrap(Box::new(move || {
draw_frame();
set_timeout(&w2, timeout_cb.borrow().as_ref().unwrap(), 1000 / max_fps);
}) as Box<dyn FnMut()>));
request_animation_frame(&window(), animate_cb2.borrow().as_ref().unwrap());
}
fn window() -> web_sys::Window {
web_sys::window().expect("no global `window` exists")
}
fn request_animation_frame(window: &web_sys::Window, f: &Closure<dyn FnMut()>) -> i32 {
window
.request_animation_frame(f.as_ref().unchecked_ref())
.expect("should register `requestAnimationFrame` OK")
}
fn set_timeout(window: &web_sys::Window, f: &Closure<dyn FnMut()>, timeout_ms: i32) -> i32 {
window
.set_timeout_with_callback_and_timeout_and_arguments_0(
f.as_ref().unchecked_ref(),
timeout_ms,
)
.expect("should register `setTimeout` OK")
}
Then you simply pass animate_limited a function to do your drawing (a closure like move || { /* drawing logic here */ } will do the trick), and the maximum framerate you want.
There are almost certainly improvements to be made, there. I'm very new to Rust and just spent far too long figuring out how to make this work. Hopefully this makes it quicker for someone else in the future.
I'm effectively looking for the Rust equivalent of JavaScript's setTimeout(renderNext, 11) when compiling out to the wasm target.
There are several Rust crates that have bindings to the JavaScript web API, most notably web-sys. Take a look at the documentation for one of the setTimeout overloads.
This is not really a Rust equivalent though, as it pretty directly calls the JS function. But you won't be able to get around that: sleeping or getting the current time are both functions that the host environment has to offer. They cannot be implemented in the raw language alone.
One option would be to have JavaScript call into Rust on every requestAnimationFrame and use that as the driver, but I'm curious to keep it all in Rust if possible.
Yes, you should use requestAnimationFrame (link to web-sys docs). This is much preferred over timing it yourself. In particular, this method will also pause calling your code when the tab is not active and stuff like that. In a desktop environment you would do the same: ask the host environment (i.e. the operating system, often via OpenGL or so) to synchronize your program to screen refreshes.

How can we view a multiplayer game replay in reverse?

We are building a multiplayer game that supports replays. The replays work like this:
All state updates from the server get saved into a file. When we are replaying the file we just take the initial state recieved from the server and apply the state updates. This works fine. We can play, fast-forward, pause and go ba... oh wait.
What would be the best way to actually allow players to view the replay backwards?
Some implementation details, we use a huge state struct (well, not really, but you could see it as such). This struct gets updated over the network (UDP) by calculating differences between the last sent struct and the current one. The client interpolates between the last two recieved structs and extrapolates when needed (when ping > throttle of updates). In addition we use an acked event system for stuff that hasn't to be drawn smoothly on the screen but needs to be, well, acked.
A couple of suggestions (assuming you can make a snapshot at any point, and you're storing forward deltas).
1.. If the replay is short and the state isn't too big, actually generate snapshots for each frame in the sequence and "render" them in reverse.
2.. If the whole state is very big, then only generate a snapshot for the last frame of the reversed replay. Then apply X forward deltas to the snap shot to generate the first frame of the reversed replay. Render that. Then apply X-1 deltas, and render that, repeat until X = 0, when you're at the end of replay (just the first snap shot).
// pseudo code for reverse play
first, last = GetReplayRange();
if (playReversed) {
GameState snapShot = GenerateSnapshot(first);
GameState currentState;
currentFrame = last;
while (currentFrame != first) {
// loop from snapshot up to current frame
for (i = first; i < currentFrame; i++) {
currentState = snapShot.AddDetlasTo(currentFrame);
}
Render(currentState);
currentFrame--;
}
}
3.. If its too slow to apply X updates to a snapshots to "rewind" then make a series of snap shots N frames apart, and do the same reverse trick in #2, over a narrower range.
Remember that even if you have a reversed game state ready to render - you'll still have to render all your effects in reverse, which most effect systems probably aren't geared for.

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