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.
I'm trying to make my game more dynamic by making attack hitboxes scaleable. However when adding a new shape to the current CollisionShape it shows the correct radius when printing but the radius ingame is not corresponding to the new value.
The code I currently use is this:
var shape = CylinderShape.new()
shape.set_radius(new_radius)
shape.set_height(new_height)
$Spatial/CollisionShape.shape = shape
When running this I tried to print the current CollisionShape.shape.radius and it showed the new number however ingame the collisionshape is still the old shape and nothing changed (both visually with the debug option on and when testing the collision size)
So my question is: how do I change the shape or size of the shape in runtime.
Things I've already tried:
$Spatial/CollisionShape.shape.radius = new_radius
$Spatial/CollisionShape.shape.height = new_height
This gave the same result unfortunately. I've also tried to make the shape null before putting the new shape in the CollisionShape. This changed nothing aswell.
Am I missing something?
It looks like it's not possible to update a collisioon shape; you have to create a new one. I solved it like this, where I have a CSGBox which owns a StaticBody which owns a CollisionShape, and I want to make the static body's collision shape match the CSGBox:
var box : BoxShape = BoxShape.new()
box.extents.x = self.width/2
box.extents.y = self.height/2
box.extents.z = self.depth/2
$StaticBody/CollisionShape.shape = box
(This script is attached to the CSGBox).
I am altering the elements contents using
svgTextLines[name].node.innerHTML = line.val();
svgTextLines[name].node.textContent = line.val();
and trying to get the height and width of the element after the content has changed but I cant seem to find any property in the elemement.node object that gets updated. As the element has not transformed I can understand why but is there a way I can get this information?
Regards
I'm using SnapSVG and I had the same problem.
You can use getBoundingClientRect to get an object with this parameter.
var s = Snap("#svg");
Snap.load("mascot.svg", function (f) {
s.append(f);
var rect = s.searchAll("g")[0];
var dimens = rect.node.getBoundingClientRect();
console.log(dimens.width)
});
Does anyone know if it's possible to change, say, the last 10 pixels of a path to be a different color? I tried doing it with gradients, and that didn't work. There doesn't seem to be any other way that I can find to do it either. Any help would be greatly appreciated.
You should be able to do this with Raphael's element.getSubpath() interface.
Subpath allows you to get a 'path of a path' between certain points. So, if you have a path already, like
var mainPath = paper.path("M10,10R20,70 30,40 40,80 50,10 60,50 70,20 80,30 90,90");
You can get a subpath which gives you a 'slice' of that path with arbitrary start and end points:
var subpathString = mainpath.getSubpath(20, 50);
And then you can create a new element using that path with, say, a different stroke width:
var subpath = paper.path(subpathString);
subpath.attr({'stroke-width' : 4});
This will then look like this jsFiddle: http://jsfiddle.net/1ndmz3d6/3/
To make the last 10 pixels of your path red, for instance, you just need to know the length of your path. With element.getTotalLength(), that's easy:
var pathLength = mainPath.getTotalLength();
var subpathStart = pathLength - 10;
var subpathString = mainPath.getSubpath(subpathStart, pathLength);
var highlightedPathSegment = paper.path(subpathString);
highlightedPathSegment.attr({'stroke-width' : 2, stroke : '#FF0000'});
And there you should have it: http://jsfiddle.net/1ndmz3d6/5/
I am trying to move (translate) an object that has been rotated, when I move (translate) the rotated object it loses it's rotation and does move correctly. If the use the same code on an object that is not rotated then the move correctly. What I am doing wrong here?
Here is a fiddle
This code loses the rotation.
var part = s.select("#part_2");
var t = new Snap.Matrix();
t.translate(part.getBBox().x,part.getBBox().y+18);
part.transform(t);
I'm not sure what order you want the transforms to apply. If you want the squares to move such that down is applied after transforming i.e. down for the rotated matrix is at an angle you'd do this...
var t = part.transform().localMatrix;
t.translate(0, 18);
part.transform(t);
If, however you want down to always be down then you'd do something like this...
var t = new Snap.Matrix();
t.translate(0, 18);
t.add(part.transform().localMatrix);
part.transform(t);
The trick is to get the existing matrix for the shape and append/prepend the transform you want to it.