Compare commits

..

No commits in common. 'wip/interactable-rework' and 'main' have entirely different histories.

  1. 1
      assets/shaders/ReplacePalette.cs
  2. 110
      objects/Interactable.cs
  3. 9
      objects/Item.cs
  4. 10
      objects/interactable.tscn
  5. 18
      player/PickupController.cs
  6. 8
      player/player.tscn
  7. 10
      project.godot
  8. 2
      scenes/workshop.tscn
  9. 29
      scripts/globals/PhysicsLayer.cs

@ -1,4 +1,3 @@
// FIXME: Unable to cast object of type 'ReplacePalette' to type 'Godot.CollisionObject3D'.
public partial class ReplacePalette : Node3D public partial class ReplacePalette : Node3D
{ {
readonly struct TextureInfo(float min, float max) readonly struct TextureInfo(float min, float max)

@ -1,110 +0,0 @@
[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;
}
}

@ -1,3 +1,5 @@
using Godot.NativeInterop;
public partial class Item : RigidBody3D public partial class Item : RigidBody3D
{ {
public MultiplayerSynchronizer Sync { get; internal set; } public MultiplayerSynchronizer Sync { get; internal set; }
@ -16,11 +18,8 @@ public partial class Item : RigidBody3D
public override void _Ready() public override void _Ready()
{ {
// Set the collision properties here so we don't have to specify them in each item scene separately. // Set the collision properties here so we don't have to specify them in each item scene separately.
CollisionLayer = (uint)(PhysicsLayer.Small | PhysicsLayer.Interact | PhysicsLayer.Placable); CollisionLayer = (uint)(PhysicsLayer.Item | PhysicsLayer.Pickup);
CollisionMask = (uint)(PhysicsLayer.Static | PhysicsLayer.Dynamic | PhysicsLayer.Small | PhysicsLayer.Body); CollisionMask = (uint)(PhysicsLayer.Static | PhysicsLayer.Dynamic | PhysicsLayer.Player | PhysicsLayer.Item);
// TODO: Create a separate area for .Placable.
// Though gotta keep in mind that for some objects it needs to be same as physics collider, like small box.
// TODO: Find a better way to better import models with colliders. // TODO: Find a better way to better import models with colliders.
// TODO: Import items dynamically at runtime? // TODO: Import items dynamically at runtime?

@ -1,10 +0,0 @@
[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

@ -73,7 +73,7 @@ public partial class PickupController : Node3D
_targetedGrid = null; _targetedGrid = null;
} }
} else { } else {
var interactable = RayToMouseCursor(PhysicsLayer.Interact)?.Collider; var interactable = RayToMouseCursor(PhysicsLayer.Pickup)?.Collider;
// Remove the outline from the previously looked-at item. // Remove the outline from the previously looked-at item.
if (TargetedItem != null) SetMeshLayerOutline(TargetedItem.Model, OutlineMode.Disable); if (TargetedItem != null) SetMeshLayerOutline(TargetedItem.Model, OutlineMode.Disable);
@ -122,16 +122,22 @@ public partial class PickupController : Node3D
(Grid Grid, Transform3D Target, bool IsFree)? GetValidPlacement(Item itemToPlace) (Grid Grid, Transform3D Target, bool IsFree)? GetValidPlacement(Item itemToPlace)
{ {
// This ray will be blocked by static and dynamic objects.
const PhysicsLayer Mask = PhysicsLayer.Static | PhysicsLayer.Dynamic | PhysicsLayer.Item;
// FIXME: Remove .Place and .Pickup physics layers?
// TODO: We need a separate physics layers for:
// - The physical item collider used for physics calculations (simplified).
// - The placement collider which should match the item's appearance.
// - The general space / size an item takes up, as a cuboid.
// TODO: Probably just overhaul the physics layers altogether.
// It would be better to have a "collides with player" and "player collides with it" layer, etc.
var excludeSet = new HashSet<CollisionObject3D> { HeldItem }; var excludeSet = new HashSet<CollisionObject3D> { HeldItem };
var heldItemGrid = HeldItem.GetNodeOrNull<Grid>(nameof(Grid)); var heldItemGrid = HeldItem.GetNodeOrNull<Grid>(nameof(Grid));
heldItemGrid?.AddItemsRecursively(excludeSet); heldItemGrid?.AddItemsRecursively(excludeSet);
// Cast a ray and make sure it hit something. // Cast a ray and make sure it hit something.
// This ray will be blocked by static and dynamic objects. if (RayToMouseCursor(Mask, excludeSet) is not RayResult ray) return null;
const PhysicsLayer MASK = PhysicsLayer.Static
| PhysicsLayer.Dynamic
| PhysicsLayer.Placable;
if (RayToMouseCursor(MASK, excludeSet) is not RayResult ray) return null;
// Find a grid to place against, which will be either the grid belonging // Find a grid to place against, which will be either the grid belonging
// to the item the ray intersected, or the grid said item is placed upon. // to the item the ray intersected, or the grid said item is placed upon.

@ -103,7 +103,7 @@ node_connections = [&"is_holding", 0, &"walk_state", &"is_holding", 1, &"hold",
[node name="Player" type="CharacterBody3D"] [node name="Player" type="CharacterBody3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.75, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.75, 0)
collision_layer = 8 collision_layer = 4
collision_mask = 3 collision_mask = 3
script = ExtResource("1_a0mas") script = ExtResource("1_a0mas")
@ -112,15 +112,15 @@ shape = SubResource("CapsuleShape3D_h1mfd")
[node name="WorkshopTracker" type="Area3D" parent="."] [node name="WorkshopTracker" type="Area3D" parent="."]
collision_layer = 0 collision_layer = 0
collision_mask = 256 collision_mask = 1024
monitorable = false monitorable = false
[node name="CollisionShape3D" type="CollisionShape3D" parent="WorkshopTracker"] [node name="CollisionShape3D" type="CollisionShape3D" parent="WorkshopTracker"]
shape = SubResource("BoxShape3D_gtuvx") shape = SubResource("BoxShape3D_gtuvx")
[node name="PushbackArea" type="Area3D" parent="."] [node name="PushbackArea" type="Area3D" parent="."]
collision_layer = 8 collision_layer = 4
collision_mask = 8 collision_mask = 4
script = ExtResource("2_almik") script = ExtResource("2_almik")
[node name="CollisionShape3D" type="CollisionShape3D" parent="PushbackArea"] [node name="CollisionShape3D" type="CollisionShape3D" parent="PushbackArea"]

@ -85,11 +85,11 @@ interact_place={
3d_render/layer_2="Outline" 3d_render/layer_2="Outline"
3d_physics/layer_1="Static" 3d_physics/layer_1="Static"
3d_physics/layer_2="Dynamic" 3d_physics/layer_2="Dynamic"
3d_physics/layer_3="Small" 3d_physics/layer_3="Player"
3d_physics/layer_4="Body" 3d_physics/layer_4="Item"
3d_physics/layer_9="Zone" 3d_physics/layer_9="Pickup"
3d_physics/layer_10="Interact" 3d_physics/layer_10="Place"
3d_physics/layer_11="Placable" 3d_physics/layer_11="Areas"
[rendering] [rendering]

@ -81,6 +81,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4, 0.5, -4)
[node name="Table" type="StaticBody3D" parent="Objects"] [node name="Table" type="StaticBody3D" parent="Objects"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -4) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -4)
collision_layer = 2
collision_mask = 0 collision_mask = 0
[node name="Grid" parent="Objects/Table" instance=ExtResource("6_okibm")] [node name="Grid" parent="Objects/Table" instance=ExtResource("6_okibm")]
@ -150,6 +151,7 @@ skeleton = NodePath("../MeshInstance3D")
[node name="Table2" type="StaticBody3D" parent="Objects"] [node name="Table2" type="StaticBody3D" parent="Objects"]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 1.3, -4.6) transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 1.3, -4.6)
collision_layer = 2
collision_mask = 0 collision_mask = 0
[node name="Grid" parent="Objects/Table2" instance=ExtResource("6_okibm")] [node name="Grid" parent="Objects/Table2" instance=ExtResource("6_okibm")]

@ -1,19 +1,20 @@
[Flags] [Flags]
public enum PhysicsLayer : uint public enum PhysicsLayer : uint
{ {
/// <summary> Physical objects that are part of the scene and won't move. </summary> /// <summary> Objects that are part of the scene, and never move. </summary>
Static = 0b0000_0000_0000_0001, Static = 1 << 0,
/// <summary> Physical objects that may move. Player collide with these. </summary> /// <summary> Objects players collide with, but may move. </summary>
Dynamic = 0b0000_0000_0000_0010, Dynamic = 1 << 1,
/// <summary> Small physical objects that may move. Players don't collide with these. </summary> /// <summary> Any players, both local and remote. </summary>
Small = 0b0000_0000_0000_0100, Player = 1 << 2,
/// <summary> Physical bodies such as those of players. </summary> /// <summary> Small objects players don't collide with, but collide with players. </summary>
Body = 0b0000_0000_0000_1000, Item = 1 << 3,
/// <summary> Areas in the game that can be entered or left by players. </summary> /// <summary> Objects that may be picked up. </summary>
Zone = 0b0000_0001_0000_0000, Pickup = 1 << 8,
/// <summary> Objects that may be ray-traced to interact with them. </summary> /// <summary> Objects that other objects may be placed against. </summary>
Interact = 0b0000_0010_0000_0000, Place = 1 << 9,
/// <summary> Objects that others may be placed again, grid-aligned bounding box. </summary>
Placable = 0b0000_0100_0000_0000, /// <summary> Various areas, such as workshops. </summary>
Areas = 1 << 10,
} }

Loading…
Cancel
Save