I'd like to implement a node for 2D and possibly 3D physics (if 3D is wanted). Children on this node would use the Space it creates, much like a Viewport but this wouldn't impact how the nodes are rendered.
I'm mainly going to outline the PhysicsSpace2D node I'm imagining because I think it has more obvious value. I want to discuss if this type of node would be useful, if it should be implemented differently, and if it's even wanted.
get_world_2d (or get_world for 3d) to get the space, would look up the tree for this kind of node also.This technically could be done using layers/masks currently, but that limits the usefulness of layers/masks. Adding this node would allow you to keep your masks for enemies or specific collision instructions, but allow them to simulate in a separate space.
This can also be done in a script currently but I imagine this would be useful to a lot of people who may not know how to interact with the physics server directly.
Simple POC:
PhysicsSpace2D.zip
Maybe a PhysicsSpace node with a Space State Resource? so you can "merge" spaces from different scenes, also can work as a shortcut for some PhysicsDirectSpaceState queries.
I think this can already be done as a custom node, a PoC may show if is too complex and makes sense to make a node instead of just a custom GDScript one.
I've created a POC.
PhysicsSpace2D.zip
The POC was easy enough and a simple GDScript node works. I'd still be interested in the desire for something like this as a default node, but I'm satisfied haha.
Seems like a reasonable idea at first glance.
Would be awesome if it could make it possible to activate/deactivate physics spaces selectively, for example:
in the editor there could be a physics space which is deactivated by default (and should remain deactivated in general), but a plugin could create anohter physics space and let it run at editor time, making running almost 100% of an actual game possible in the editor for design/testing/whatever reason!
I think this was easy enough to implement in a usable way with GDScript that I may just put the node on the asset store. I'm going to close this issue for now.
Turns out you can't set the space on TileMap quadrants.
I think adding a method maybe called get_physics_space() and using that in places instead of get_world_2d()->get_space()
It can nearly the same thing as get_world_2d but stop when it hits a PhysicsSpace2D, call physics_space->get_space() or if it doesn't find that before reaching a viewport it'll call viewport->find_world_2d()->get_space()
The same could be done in 3D, although I still don't know if the use case exists there.
For reference, here is where the tilemap assigns the space when creating a quadrant
https://github.com/godotengine/godot/blob/master/scene/2d/tile_map.cpp#L689
Here is the script I'm currently using, and obviously a tilemap doesn't inherit from a CollisionObject2D, but you couldn't set the space if you wanted to
extends Node2D
class_name PhysicsSpace2D
export(Resource) var space_2d setget, get_space_2d
func get_space_2d():
return space_2d as Space2D
func _update_space(node:Node, space_2d:Space2D):
if node is CollisionObject2D:
Physics2DServer.body_set_space(node.get_rid(),space_2d.space)
for child in node.get_children():
_update_space(child,space_2d)
func _ready():
_update_space(self,space_2d)
func _enter_tree():
get_tree().connect("node_added",self,"_node_added")
func _exit_tree():
get_tree().disconnect("node_added",self,"_node_added")
func _node_added(node:Node):
if is_a_parent_of(node) and node is CollisionObject2D:
Physics2DServer.body_set_space(node.get_rid(),space_2d.space)
extends Resource
class_name Space2D
export(bool) var active = true setget set_active
export(float) var gravity = 98.0 setget set_gravity
export(Vector2) var gravity_vector = Vector2(0,1) setget set_gravity_vector
export(float) var linear_damp = 0.1 setget set_linear_damp
export(float) var angular_damp = 1.0 setget set_angular_damp
var space : RID = RID()
var direct_space_state : Physics2DDirectSpaceState setget ,get_direct_space_state
func set_active(_active:bool):
active = _active
Physics2DServer.space_set_active(space,_active)
func set_gravity(_gravity:float):
gravity = _gravity
Physics2DServer.area_set_param(space, Physics2DServer.AREA_PARAM_GRAVITY, _gravity)
func set_gravity_vector(_gravity_vector:Vector2):
gravity_vector = _gravity_vector
Physics2DServer.area_set_param(space, Physics2DServer.AREA_PARAM_GRAVITY_VECTOR, _gravity_vector)
func set_linear_damp(_linear_damp:float):
linear_damp = _linear_damp
Physics2DServer.area_set_param(space, Physics2DServer.AREA_PARAM_LINEAR_DAMP, _linear_damp)
func set_angular_damp(_angular_damp:float):
angular_damp = _angular_damp
Physics2DServer.area_set_param(space, Physics2DServer.AREA_PARAM_ANGULAR_DAMP, _angular_damp)
func get_direct_space_state():
return Physics2DServer.space_get_direct_state(space)
func _init():
space = Physics2DServer.space_create()
set_active(active)
set_gravity(gravity)
set_gravity_vector(gravity_vector)
set_linear_damp(linear_damp)
set_angular_damp(angular_damp)
This could also be called a Room2D? I don't love the Space2D.space thing, I could change that to .get_rid()
This technically could be done using layers/masks currently, but that limits the usefulness of layers/masks. Adding this node would allow you to keep your masks for enemies or specific collision instructions, but allow them to simulate in a separate space.
How, exactly, does using layers for this limit other uses of layers? Are you worried about running out of layers? We could always add more. You can already use multiple layers at once.
Room name clashes with the visual rooms, call it subspace?
@aaronfranke how I imagine it is layers and masks are very useful for describing how things collide on an individual level, if you wanted to simulate separate worlds you'd need layers like:
layer-b-world0
layer-c-world0
layer-a-world1
layer-b-world1
layer-c-world1
layer-a-world2
layer-b-world2
layer-c-world2
if you wanted to preserve the interactions between objects, while also separating groups from each other. My node makes that easier by using separate spaces instead and using parent relationships, so the type of interactions between objects can remain the same with no code changes, while the possible objects that can interact can be grouped separately.
If you didn't do that, your number of layers that are useful are divided among the different groups, limiting the uses.
Moving an object to a different group is much easier than flipping a bunch of layers on and off.
When I say group, I'm not talking about actual groups in godot.
Consider formatting this as a proposal so that it can be moved to the proposals repo.