VisualServer 使用 Physics2DServer 主体进行变换和塑形

VisualServer transform & shape with Physics2DServer body

我正在尝试仅使用 Physics2DServerVisualServer 创建像 Item 这样的刚体 像这样:

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)

但由于某些原因,碰撞不起作用

编辑:

我认为主要问题是 var shape= RectangleShape2D.new();

因为当我添加一个 export(Shape2D) var shape; 并手动添加一个 RectangleShape2D 时,碰撞工作正常

编辑基于变换的问题:

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)

缺少形状

形状的问题在于它正在释放。

如果您使用脚本顶部的形状声明一个字段,然后对其进行初始化 _enter_tree,它会正常工作。但是在 _enter_tree 内部声明它会一直存在到方法结束时超出范围。


教训是 RID 不是引用。

另一种方法是使用 Physics2DServer:

创建形状和纹理
var shape_rid := Physics2DServer.rectangle_shape_create()
Physics2DServer.shape_set_data(shape_rid, Vector2(30,30))

并且由于这是使用 Physics2DServer 创建的 RID,您可以通过调用 Physics2DServerfree_rid 方法来释放它。


顺便说一句,我收到了有关纹理的警告。您可以将其加载为 Texture 而不是:

var texture:Texture = load("res://icon.png")
var image:Image = texture.get_data()

哪个警告会消失。

然后我们很想这样做:

var texture_rid := texture.get_rid()

但是我们得到了错误……问题是一样的,纹理在方法结束时被释放,因为变量是唯一的引用并且它超出了范围。在文件顶部声明纹理可修复垃圾邮件错误。

而且,是的,我们可以使用 VisualServer 创建纹理。但是因为你是从资源中加载它,所以不实用。


转换

首先,canvas项相对于代码所在的节点定位运行:

VisualServer.canvas_item_set_parent(ci_rid, get_canvas_item())
VisualServer.canvas_item_set_transform(ci_rid, Transform2D.IDENTITY)

其次,body放在全局坐标中。所以我们应该这样做:

Physics2DServer.body_set_state(_body, Physics2DServer.BODY_STATE_TRANSFORM, global_transform)

注意 global_transform 而不是 transform


这让我们想到了这一点:

  • state.transform为全局坐标。
  • canvas_item_set_transform 想要相对于 parent(代码为 运行 的节点)的坐标。

这是一种处理方式:

VisualServer.canvas_item_set_transform(ci_rid, global_transform.inverse() * state.transform)

或者,您可以移动节点以跟随 body:

func _body_moved(state:Physics2DDirectBodyState, _user_data) -> void:
    global_transform = state.transform

这也会移动 canvas 项,因为请记住它是 parented(canvas_item_set_parent)。

由于您在节点移动时移动 body(在 _notification 中),我相信移动节点以跟随 body 是正确的解决方案。这样,他们就一直保持同步。


关于形状:它是相对于body放置的。形状和 body 都可以移动。物理引擎将移动 body(并且形状跟随,因为它是相对于 body 定位的),但物理引擎不会直接移动形状。但是,您可以根据需要直接移动形状。这与 RigidBody2D 及其 CollisionShape2D 的关系相同。