VisualServer transform & shape with Physics2DServer body

Viewed 112

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)
1 Answers

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.

Related