parent
32caf91234
commit
1a7aa03c36
9 changed files with 155 additions and 42 deletions
@ -0,0 +1,110 @@ |
|||||||
|
[Tool] |
||||||
|
public partial class Interactable : RigidBody3D |
||||||
|
{ |
||||||
|
PackedScene _modelScene; |
||||||
|
[Export] public PackedScene ModelScene { |
||||||
|
get => _modelScene; |
||||||
|
set => _modelScene = OnModelSceneChanged(value); |
||||||
|
} |
||||||
|
|
||||||
|
Vector3I _gridSize; |
||||||
|
/// <summary> Get the size of this item in grid units. </summary> |
||||||
|
[Export] public Vector3I GridSize { |
||||||
|
get => _gridSize; |
||||||
|
set => _gridSize = OnGridSizeChanged(value); |
||||||
|
} |
||||||
|
|
||||||
|
public override void _Ready() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public override void _Process(double delta) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
// FIXME: Only change this if ModelScene is actually changed, not when loaded. |
||||||
|
PackedScene OnModelSceneChanged(PackedScene value) |
||||||
|
{ |
||||||
|
if (GetNodeOrNull("_Model") is Node oldModel) { |
||||||
|
RemoveChild(oldModel); |
||||||
|
oldModel.QueueFree(); |
||||||
|
} |
||||||
|
|
||||||
|
// Remove any previously added `CollisionShape3D` nodes. |
||||||
|
foreach (var child in GetChildren(true).OfType<CollisionShape3D>()) { |
||||||
|
RemoveChild(child); |
||||||
|
child.QueueFree(); |
||||||
|
} |
||||||
|
|
||||||
|
if (value is PackedScene scene) { |
||||||
|
var model = scene.Instantiate<Node3D>(); |
||||||
|
model.Name = "_Model"; |
||||||
|
|
||||||
|
var numShapes = 0; |
||||||
|
var min = Vector3.Zero; |
||||||
|
var max = Vector3.Zero; |
||||||
|
|
||||||
|
// Find all the `StaticBody3D` nodes in the model and parent |
||||||
|
// their `CollisionShape3D` children to the this `RigidBody3D`. |
||||||
|
// Required because shapes must be immediate children of the body. |
||||||
|
// See: https://github.com/godotengine/godot-proposals/issues/535 |
||||||
|
// https://github.com/godotengine/godot/pull/77937 |
||||||
|
foreach (var body in model.FindChildren("*", "StaticBody3D").Cast<StaticBody3D>()) { |
||||||
|
body.GetParent().RemoveChild(body); |
||||||
|
body.QueueFree(); |
||||||
|
|
||||||
|
foreach (var shape in body.GetChildren().OfType<CollisionShape3D>()) { |
||||||
|
// Not unsetting the owner results in this warning: |
||||||
|
// "Adding 'CollisionShape3D' as child to 'Interactable' will make owner '...' inconsistent." |
||||||
|
shape.Owner = null; |
||||||
|
|
||||||
|
body.RemoveChild(shape); |
||||||
|
shape.Name = $"_{nameof(CollisionShape3D)}_{numShapes + 1}"; |
||||||
|
AddChild(shape, false, InternalMode.Front); |
||||||
|
// shape.Owner = this; |
||||||
|
numShapes++; |
||||||
|
|
||||||
|
// Finds the axis-aligned boundary of all collision shapes. |
||||||
|
// This assumes that the shape has an identity transformation. |
||||||
|
var vertices = (shape.Shape as ConvexPolygonShape3D)?.Points |
||||||
|
?? (shape.Shape as ConcavePolygonShape3D)?.Data |
||||||
|
?? throw new Exception("Shape must be either convex or concave"); |
||||||
|
foreach (var vert in vertices) { |
||||||
|
min = new(Min(min.X, vert.X), Min(min.Y, vert.Y), Min(min.Z, vert.Z)); |
||||||
|
max = new(Max(max.X, vert.X), Max(max.Y, vert.Y), Max(max.Z, vert.Z)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
AddChild(model, false, InternalMode.Front); |
||||||
|
|
||||||
|
// Set the grid size based on the boundary of all collision shapes. |
||||||
|
GridSize = (Vector3I)((max - min).Snapped(Epsilon) / Grid.StepSize).Ceil(); |
||||||
|
} else { |
||||||
|
GridSize = Vector3I.Zero; |
||||||
|
} |
||||||
|
|
||||||
|
return value; |
||||||
|
} |
||||||
|
|
||||||
|
Vector3I OnGridSizeChanged(Vector3I value) |
||||||
|
{ |
||||||
|
if (GetNodeOrNull("_GridArea") is Node oldGridArea) { |
||||||
|
RemoveChild(oldGridArea); |
||||||
|
oldGridArea.QueueFree(); |
||||||
|
} |
||||||
|
|
||||||
|
if (value.X > 0 && value.Y > 0 && value.Z > 0) { |
||||||
|
var gridArea = new Area3D(); |
||||||
|
gridArea.Name = "_GridArea"; |
||||||
|
|
||||||
|
var shape = new CollisionShape3D(); |
||||||
|
shape.Shape = new BoxShape3D { Size = (Vector3)value * Grid.StepSize }; |
||||||
|
gridArea.AddChild(shape); |
||||||
|
|
||||||
|
AddChild(gridArea, false, InternalMode.Front); |
||||||
|
} |
||||||
|
|
||||||
|
return value; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://d4l5bguk01qqr"] |
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://objects/Interactable.cs" id="1_vnawe"] |
||||||
|
[ext_resource type="PackedScene" uid="uid://dh18opmr8mjet" path="res://assets/models/plank.blend" id="2_cvuqp"] |
||||||
|
|
||||||
|
[node name="Interactable" type="RigidBody3D"] |
||||||
|
script = ExtResource("1_vnawe") |
||||||
|
ModelScene = ExtResource("2_cvuqp") |
||||||
|
GridSize = Vector3i(16, 1, 3) |
||||||
|
metadata/_edit_group_ = true |
@ -1,20 +1,19 @@ |
|||||||
[Flags] |
[Flags] |
||||||
public enum PhysicsLayer : uint |
public enum PhysicsLayer : uint |
||||||
{ |
{ |
||||||
/// <summary> Objects that are part of the scene, and never move. </summary> |
/// <summary> Physical objects that are part of the scene and won't move. </summary> |
||||||
Static = 1 << 0, |
Static = 0b0000_0000_0000_0001, |
||||||
/// <summary> Objects players collide with, but may move. </summary> |
/// <summary> Physical objects that may move. Player collide with these. </summary> |
||||||
Dynamic = 1 << 1, |
Dynamic = 0b0000_0000_0000_0010, |
||||||
/// <summary> Any players, both local and remote. </summary> |
/// <summary> Small physical objects that may move. Players don't collide with these. </summary> |
||||||
Player = 1 << 2, |
Small = 0b0000_0000_0000_0100, |
||||||
/// <summary> Small objects players don't collide with, but collide with players. </summary> |
/// <summary> Physical bodies such as those of players. </summary> |
||||||
Item = 1 << 3, |
Body = 0b0000_0000_0000_1000, |
||||||
|
|
||||||
/// <summary> Objects that may be picked up. </summary> |
/// <summary> Areas in the game that can be entered or left by players. </summary> |
||||||
Pickup = 1 << 8, |
Zone = 0b0000_0001_0000_0000, |
||||||
/// <summary> Objects that other objects may be placed against. </summary> |
/// <summary> Objects that may be ray-traced to interact with them. </summary> |
||||||
Place = 1 << 9, |
Interact = 0b0000_0010_0000_0000, |
||||||
|
/// <summary> Objects that others may be placed again, grid-aligned bounding box. </summary> |
||||||
/// <summary> Various areas, such as workshops. </summary> |
Placable = 0b0000_0100_0000_0000, |
||||||
Areas = 1 << 10, |
|
||||||
} |
} |
||||||
|
Loading…
Reference in new issue