I'm currently trying to make an explosion when a node hits the ground, but there's no explosion. I played the explosion scene and it works just fine. I tried spawning a different object and it works just fine. I checked the position of the explosion and it's the same as the body position. The explosion shows up in the inspector but not in game.
func _on_Ground_body_shape_entered(body_id, body, body_shape, local_shape):
print("collision")
var scene = load("res://Rock/Explosion.tscn")
var explosion = scene.instance()
add_child(explosion)
explosion.global_position = body.global_position
explosion.emitting = true
print(explosion.position)
print(body.position)
body.queue_free()
"load" isn't a thing, do var scene = preload("res://Rock/Explosion.tscn")
Related
I'm working on a game where a level is separated into rooms where the camera is confined into the current room, meaning the camera wont "bleed" into adjacent ones. A room is a scene RoomBase consisting of Positions RoomLimitTopLeft and RoomLimitBottomRight (which define the camera's limits) and an Area2D which detects when the player enters the room. As each room's size and exits will wary, I created a script which streches the Area2D's CollisionShape to the rooms's size.
RoomBase.gd
const ROOM_TRANS_LIMIT = 16
onready var limit_tl = $RoomLimitTopLeft # Position
onready var limit_br = $RoomLimitBottomRight # Position
onready var room_transition = $RoomTransition # Area2D
onready var room_transition_coll = $RoomTransition/CollisionShape2D
func setRoomTransition():
var width = limit_br.position.x - limit_tl.position.x - 2*ROOM_TRANS_LIMIT
var height = limit_br.position.y - limit_tl.position.y - 2*ROOM_TRANS_LIMIT
self.room_transition.position = Vector2(limit_br.position.x/2, limit_br.position.y/2)
self.room_transition_coll.shape.set_extents(Vector2(width/2, height/2))
This code centers the Area2D's position to its room and stretches its collision to cover almost the whole room, which works fine with only one room. However, if a level consists of multiple rooms, the most recent room instance overwrites the collision shape of the previous ones. For example, if Room 1 is horizontal and Room 2 is vertical, then Room 1 will share 2's vertical collision shape.
Is it possible to change an individual scene instances collisions without affecting any others?
you could also change the setting of the node, if possible, in the Inspector
I'm not sure if this is the issue, but for what I understand you are using the same Shape2D for every room. If all they are all using the same Shape2D, and you modify that Shape2D, the changes are reflected in all of them.
You can duplicate the Shape2D from code (or make it unique from the inspector). That way you have a different Shape2D.
I'm trying to create a rigid body like Item using only Physics2DServer and VisualServer
like this:
extends Node2D
var _body:RID
var canvasItem:RID
func _enter_tree():
_body=Physics2DServer.body_create();
Physics2DServer.body_set_space(_body, get_world_2d().space);
var shape= RectangleShape2D.new();
shape.extents=Vector2(30,30);
var ci_rid = VisualServer.canvas_item_create() # ci= Canvas Item
VisualServer.canvas_item_set_parent(ci_rid, get_canvas_item())
Physics2DServer.body_add_shape(_body, shape.get_rid(), self.global_transform, false);
Physics2DServer.body_set_force_integration_callback(_body, self, "_body_moved",ci_rid);
var image=Image.new();
image.load("res://icon.png")
var texture_rid := VisualServer.texture_create_from_image(image)
VisualServer.texture_set_flags (texture_rid,VisualServer.TEXTURE_FLAG_ANISOTROPIC_FILTER)
VisualServer.canvas_item_set_parent(ci_rid, get_canvas_item())
VisualServer.canvas_item_add_texture_rect(ci_rid, Rect2(image.get_size() * -0.5, image.get_size()), texture_rid);
VisualServer.canvas_item_set_transform(ci_rid, self.transform)
func _body_moved(state:Physics2DDirectBodyState,ci_rid):
VisualServer.canvas_item_set_transform(ci_rid,state.transform)
but for some reason the collision is not working
Edit:
I think the main problem is the var shape= RectangleShape2D.new();
because when I added a export(Shape2D) var shape; instead and added a RectangleShape2D manually, then the collision worked properly
Edit for transform based problem:
extends Node2D
var _body:RID
var _shape:RID
var canvasItem:RID
func _enter_tree():
_body=Physics2DServer.body_create();
Physics2DServer.body_set_space(_body, get_world_2d().space);
var ci_rid = VisualServer.canvas_item_create() # ci= Canvas Item
VisualServer.canvas_item_set_parent(ci_rid, get_canvas_item())
_shape = Physics2DServer.rectangle_shape_create()
Physics2DServer.shape_set_data(_shape, Vector2(30,30))
Physics2DServer.body_add_shape(_body,_shape);
Physics2DServer.body_set_force_integration_callback(_body, self, "_body_moved",ci_rid);
var texture:Texture = load("res://icon.png")
var image:Image = texture.get_data()
var texture_rid := VisualServer.texture_create_from_image(image)
VisualServer.texture_set_flags(texture_rid,VisualServer.TEXTURE_FLAG_ANISOTROPIC_FILTER)
VisualServer.canvas_item_set_parent(ci_rid, get_canvas_item())
VisualServer.canvas_item_add_texture_rect(ci_rid, Rect2(image.get_size() * -0.5, image.get_size()), texture_rid);
VisualServer.canvas_item_set_transform(ci_rid, Transform2D.IDENTITY)
set_notify_transform(true)
func _exit_tree():
if(_body.get_id()!=0):
Physics2DServer.free_rid(_body)
if(_shape.get_id()!=0):
Physics2DServer.free_rid(_shape)
func _body_moved(state:Physics2DDirectBodyState,ci_rid):
VisualServer.canvas_item_set_transform(ci_rid,state.transform)
func _notification(what: int) -> void:
if what == NOTIFICATION_TRANSFORM_CHANGED:
if _body.get_id() != 0:
Physics2DServer.body_set_state(_body, Physics2DServer.BODY_STATE_TRANSFORM, transform)
Missing Shape
The problem with the shape is that it is getting freed.
If you declare a field with the shape at the top of the script, and then initialize it _enter_tree, it works correctly. But declared inside _enter_tree it lives until the end of the method when it goes out of scope.
The lesson is that a RID is not a reference.
The alternative is to create the shape and the texture with the Physics2DServer:
var shape_rid := Physics2DServer.rectangle_shape_create()
Physics2DServer.shape_set_data(shape_rid, Vector2(30,30))
And since that is a RID created with the Physics2DServer you would free it by calling the free_rid method of the Physics2DServer.
By the way, I'm getting a warning about the texture. You can load it as a Texture instead:
var texture:Texture = load("res://icon.png")
var image:Image = texture.get_data()
Which will the warning go away.
And then we are tempted to do this:
var texture_rid := texture.get_rid()
But we get errors… The issue is the same, the texture is getting freed when the method ends because the variable is the only reference and it is going out of scope. Declaring the texture at the top of the file fixes the error spam.
And, yes, we could create the texture with the VisualServer. But since you are loading it from a resource, it is not practical.
Transform
First of all, the canvas item is positioned relative to the node where the code is running:
VisualServer.canvas_item_set_parent(ci_rid, get_canvas_item())
VisualServer.canvas_item_set_transform(ci_rid, Transform2D.IDENTITY)
Second, the body is placed in global coordinates. So we should do this:
Physics2DServer.body_set_state(_body, Physics2DServer.BODY_STATE_TRANSFORM, global_transform)
Notice global_transform instead of transform.
And that brings us to this:
state.transform is global coordinates.
canvas_item_set_transform wants coordinates relative to the parent (the node where the code is running).
This a way to deal with it:
VisualServer.canvas_item_set_transform(ci_rid, global_transform.inverse() * state.transform)
Alternatively, you could move the node to follow the body:
func _body_moved(state:Physics2DDirectBodyState, _user_data) -> void:
global_transform = state.transform
And that would move the canvas item too, because, remember that it is parented (with canvas_item_set_parent).
Since you are moving the body when the node moves (in _notification), I believe moving the node to follow the body is the correct solution. This way, they stay in sync all the time.
About the shape: it is placed relative to the body. Both the shape and the body can move. The physics engine will move the body (and the shape follows because it is positioned relative to the body), but the physics engine will not move the shape directly. You, however, could move the shape directly if you wanted. This is the same relationship that a RigidBody2D and its CollisionShape2D would have.
This is difficult to explain with typing...
I have a GameController scene (Node2D) that holds 3 instanced scenes within:
Mouse (scenes/Mouse.tscn) - this just swaps the mouse cursor for a custom graphic
HeaderBar (scenes/HeaderBar.tscn) - this is the score/label that just sits up top
Messages (scenes/Messages.tscn) - this is the "popup" message box that displays text to the user
In the main scene (Level1.tscn) I instance the GameController scene and it "works" fine. The header bar is there with the score/label and the custom mouse cursor is there and the message box is there (but I have it hidden by default but if I toggle its visibility in the remote it will show up).
Here's where my confusion comes in...
If I attempt, in the GameController script, to manipulate any of those nodes in the GameController scene (the mouse, header, messages) they return as null and will throw an error. For example; if I try to update the score in the $HeaderBar/Control/score I get the following:
Invalid set index 'text' (on base: 'null instance') with value of type 'String'.
The code completion will autofill the node names as I type them (so it recognizes them in the group) but any attempt to reference/use them in script throws similar errors to above.
I'm very new to Godot so I'm sure it's just some misunderstanding I have on how this type of thing works but I'm stumped! Any insight is much appreciated!
UPDATE
I will try to simplify my explanation a bit (I have made some changes). Okay here is the object script:
extends StaticBody2D
onready var main = load("res://scenes/MainGame.gd").new()
func _ready():
pass
# mouse [left] clicked on object
func _on_trigger_input_event(viewport, event, shape_idx):
if Input.is_action_just_pressed("left-click"):
main.display_message("you [left] clicked on the object!")
the call to main.display_message() works. It does call the function but here is that function in the MainGame.gd
extends Node2D
onready var box = $Message/Control
onready var label = $Message/Control/Label
func _ready():
# hide the mouse cursor (will use custom cursor)
Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
func display_message(msg):
label.text = msg
box.visible = true
It errors out because label (and box) are null. If I call display_message from the _ready function (in the MainGame.gd) it works as it should. Call it from outside (in the Object.gd) and the nodes are null for some reason.
This instances the scene as expected:
onready var main = load("res://scenes/MainGame.gd").new()
However, this code will run when the instanced scene is added to the scene tree:
onready var box = $Message/Control
onready var label = $Message/Control/Label
func _ready():
# hide the mouse cursor (will use custom cursor)
Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
Yes, both onready variables and _ready will run when this is added to the scene tree.
And when do you add main to the scene tree? I don't see where.
Then when you do this:
main.display_message("you [left] clicked on the object!")
Both box and label will still be null (which is the default value).
Thus, add it to the scene tree, for example:
func _ready():
add_child(main)
Except that does not work either, does it? Look at the code again:
onready var main = load("res://scenes/MainGame.gd").new()
You are instancing a script. A script. Not a scene. It will not have its children nodes and so on. You want to instance a scene, for example:
onready var main = load("res://scenes/MainGame.tscn").instance()
However, I find it odd that you are doing this in a StaticBody2D. I don't think it makes sense that MainGame belongs to a StaticBody2D.
I suspect, you are instancing MainGame in multiple places expecting it be the same instance. But it is not the same instance, it is a new instance.
Here is where I suggest you make MainGame into a autoload, so there is a single instance that you can access from anywhere. However, perhaps that is not right either. Perhaps you should use a Signal Bus. See Why does this variable keep returning to its original value? which had a similar problem (they where trying to open a door, you are trying to show a message, but still).
I'm making a prototype game that involves a menu and a main scene. And while the main scene isn't big enough that the get_tree().change_scene("res://Main.tscn") thread block is a problem, I wanted to develop a loading so that I can show this functionality working.
The issue is that the loader.poll() isn't loading all "polls" (it loads 8 out of 9).
I've looked at the docs from the Godot, and I still havet found the issue neither how to solve it.
Thanks a bunch for the help! :)
Here is the function that loads the scene
func load_scene(scene_path): #receives the path to the next scene
var loader = ResourceLoader.load_interactive(scene_path)
#Disable some buttons and displays the progress bar animation
$Animation.visible = true
$X.disabled = true
$Creditos.disabled = true
$About.disabled = true
$PlayButton.disabled = true
$Sair.disabled = true
while not loader.poll():
var percentage_complete = (loader.get_stage()*100.0)/loader.get_stage_count()
$Animation/Label/ProgressBar.value = percentage_complete #updates the progress bar
Well, dumb me was counting the times that the code entered the while, and so, it would be always one less... Problem solved... :D
I am beginning to use snap.svg, and stuck in a probably simple question. I draw a circle, and want to let the user drag it around, and when the user stops dragging, I want to know the alert the final position of the circle.
I started with this:
var s = Snap("#svg");
var d = s.circle(20,20,5);
Then I created the following functions as drag event-handlers; I only need the drag-end event so I made the two other event-handlers null:
var endFnc = function(e) {
alert("end: "+e.layerX+","+e.layerY);
}
var startFnc = null;
var moveFnc = null;
Then I installed the drag event handlers:
d.drag(moveFnc,startFnc,endFnc);
Whenever I tried to drag a circle, the end event fired, but, the circle did not move.
So I figured maybe I have to also create the drag move event (although I don't need it):
var moveFnc = function(dx, dy, x, y) {
this.transform('t' + x + ',' + y);
}
Now the circle did move, but, not exactly at the mouse, but approximately 20 pixels at its bottom right.
I read the documentation here: http://snapsvg.io/docs and there seems to be no explanation about how to create working drag events.
How does anyone get this knowledge?!
After struggling for some hours to do this with snap.js, I finally discovered svg.js and its draggable plugin, with which it is so much easier:
var draw = SVG('svg');
var circle = draw.circle(10).attr({cx:30,cy:30,fill:'#f06'});
circle.dragend = function(delta, event) {
alert(this.attr('cx'))
}
circle.draggable();
So, I switched to svg.js ...