Maps are editor-built scenes: [Tool] Playground/Crag, headless rebuild for agents
scenes/maps/playground.tscn and crag.tscn are generated by the map scripts' Rebuild (inspector button, or godot-mono --headless --path . tools/rebuild_maps.tscn) and loaded as saved by the game, so hand edits stick. The crag route is saved as Marker3D nodes and crag_spike checks the saved scene. Layout verified identical to the code-built one (tools/map_dump.tscn). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3e300c7f04
commit
df7a2c5a89
17 files changed
+6863
-65
No files matched your search
+42
-26
@@ -1,7 +1,9 @@
|
||||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
|
||||
// The body-physics sandbox (Nimso-sandbox style), built in code around the spawn/table area:
|
||||
// The body-physics sandbox (Nimso-sandbox style) around the spawn/table area. EDITOR-BUILT: this is the
|
||||
// root of scenes/maps/playground.tscn and Rebuild() generates the pieces below into that scene
|
||||
// (MapScene: inspector button, or tools/rebuild_maps.tscn headless); the game loads the saved scene.
|
||||
// EAST — mantling ledges (0.3 … 1.5 m), a 1 m platform with stairs, a walkable 18° ramp and a
|
||||
// too-steep 40° one, a climbing wall with holds onto a 2.4 m top, a pull-up bar, monkey bars.
|
||||
// WEST — physics toys: a plank bridge on two blocks (walk on it / knock it off), loose planks,
|
||||
@@ -10,8 +12,12 @@ using System.Collections.Generic;
|
||||
// SOUTH — a crawl tunnel (0.6 m clearance), a duck-under bar (1.2 m) and a doorway.
|
||||
// Static pieces are world (layer 1) and grabbable for climbing (PropGrabber grabs any static body
|
||||
// not in "no_grab"); dynamic pieces are "prop"s (grab/push) and come back with Reset().
|
||||
[Tool]
|
||||
public partial class Playground : Node3D
|
||||
{
|
||||
[ExportToolButton("Rebuild (replaces all children)")]
|
||||
public Callable RebuildButton => Callable.From(RebuildInEditor);
|
||||
|
||||
private readonly List<(RigidBody3D Body, Transform3D Start)> _dynamic = new();
|
||||
private const uint WorldLayer = 1, WorldMask = 0b11101, PropMask = 0b11101;
|
||||
|
||||
@@ -21,22 +27,28 @@ public partial class Playground : Node3D
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
if (!Engine.IsEditorHint())
|
||||
MapScene.CollectProps(this, _dynamic);
|
||||
}
|
||||
|
||||
public void Reset() => MapScene.Reset(_dynamic);
|
||||
|
||||
private void RebuildInEditor()
|
||||
{
|
||||
if (!MapScene.CanRebuildInEditor(this))
|
||||
return;
|
||||
Rebuild();
|
||||
MapScene.MarkUnsaved();
|
||||
}
|
||||
|
||||
// Replaces every child with the generated layout, owned by this map (saved with its scene).
|
||||
public void Rebuild()
|
||||
{
|
||||
MapScene.Clear(this);
|
||||
BuildEast();
|
||||
BuildWest();
|
||||
BuildSouth();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
foreach (var (body, start) in _dynamic)
|
||||
{
|
||||
if (!IsInstanceValid(body))
|
||||
continue;
|
||||
body.GlobalTransform = start;
|
||||
body.LinearVelocity = Vector3.Zero;
|
||||
body.AngularVelocity = Vector3.Zero;
|
||||
body.ResetPhysicsInterpolation();
|
||||
}
|
||||
MapScene.Adopt(this);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- builders
|
||||
@@ -46,7 +58,7 @@ public partial class Playground : Node3D
|
||||
StaticBody3D body = new() { Name = name, CollisionLayer = WorldLayer, CollisionMask = WorldMask, Transform = new Transform3D(basis ?? Basis.Identity, center) };
|
||||
body.AddChild(new CollisionShape3D { Shape = new BoxShape3D { Size = size } });
|
||||
body.AddChild(Mesh(new BoxMesh { Size = size }, color));
|
||||
AddChild(body);
|
||||
AddChild(body, true);
|
||||
return body;
|
||||
}
|
||||
|
||||
@@ -59,7 +71,7 @@ public partial class Playground : Node3D
|
||||
StaticBody3D body = new() { Name = name, CollisionLayer = WorldLayer, CollisionMask = WorldMask, Transform = new Transform3D(new Basis(x, y, x.Cross(y)), (from + to) * 0.5f) };
|
||||
body.AddChild(new CollisionShape3D { Shape = new CylinderShape3D { Radius = radius, Height = len } });
|
||||
body.AddChild(Mesh(new CylinderMesh { TopRadius = radius, BottomRadius = radius, Height = len, RadialSegments = 12 }, color));
|
||||
AddChild(body);
|
||||
AddChild(body, true);
|
||||
return body;
|
||||
}
|
||||
|
||||
@@ -75,11 +87,10 @@ public partial class Playground : Node3D
|
||||
MaxContactsReported = 4,
|
||||
PhysicsMaterialOverride = new PhysicsMaterial { Friction = friction, Bounce = 0.05f },
|
||||
};
|
||||
body.AddToGroup("prop");
|
||||
body.AddToGroup(MapScene.PropGroup, true);
|
||||
body.AddChild(new CollisionShape3D { Shape = shape });
|
||||
body.AddChild(Mesh(mesh, color));
|
||||
AddChild(body);
|
||||
_dynamic.Add((body, body.GlobalTransform));
|
||||
AddChild(body, true);
|
||||
return body;
|
||||
}
|
||||
|
||||
@@ -97,13 +108,14 @@ public partial class Playground : Node3D
|
||||
{
|
||||
AddChild(new Label3D
|
||||
{
|
||||
Name = "Sign",
|
||||
Text = text,
|
||||
FontSize = 96,
|
||||
PixelSize = 0.002f,
|
||||
OutlineSize = 12,
|
||||
Billboard = BaseMaterial3D.BillboardModeEnum.FixedY,
|
||||
Position = at,
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- zones
|
||||
@@ -183,13 +195,16 @@ public partial class Playground : Node3D
|
||||
// Seesaw: a plank hinged on a pivot block.
|
||||
Block("SeesawPivot", new Vector3(0.3f, 0.45f, 0.5f), new Vector3(-8.5f, 0.225f, -1.5f), Metal);
|
||||
RigidBody3D seesaw = BoxProp("Seesaw", new Vector3(3.2f, 0.06f, 0.45f), 12f, new Vector3(-8.5f, 0.48f, -1.5f), DarkWood);
|
||||
// Joint paths resolve relative to the joint (a sibling here) and are saved that way; set
|
||||
// before AddChild so the joint never exists without its bodies.
|
||||
HingeJoint3D pivot = new()
|
||||
{
|
||||
Name = "SeesawHinge",
|
||||
Transform = new Transform3D(new Basis(Vector3.Right, Vector3.Up, Vector3.Back), new Vector3(-8.5f, 0.48f, -1.5f)),
|
||||
NodeA = GetNode<Node>("SeesawPivot").GetPath(), // absolute: joint paths resolve relative to the joint
|
||||
NodeB = seesaw.GetPath(),
|
||||
NodeA = $"../{GetNode<Node>("SeesawPivot").Name}",
|
||||
NodeB = $"../{seesaw.Name}",
|
||||
};
|
||||
AddChild(pivot);
|
||||
AddChild(pivot, true);
|
||||
Sign("Seesaw", new Vector3(-8.5f, 1.3f, -1.5f));
|
||||
|
||||
// Swing board: a 10 kg board hinged at its top edge from a 2.4 m beam — grab it and hang.
|
||||
@@ -199,11 +214,12 @@ public partial class Playground : Node3D
|
||||
RigidBody3D board = BoxProp("SwingBoard", new Vector3(1.2f, 1.2f, 0.05f), 10f, new Vector3(-6f, 1.75f, 1.5f), Wood);
|
||||
HingeJoint3D swing = new()
|
||||
{
|
||||
Name = "SwingHinge",
|
||||
Transform = new Transform3D(new Basis(Vector3.Back, Vector3.Up, Vector3.Left), new Vector3(-6f, 2.35f, 1.5f)),
|
||||
NodeA = beam.GetPath(),
|
||||
NodeB = board.GetPath(),
|
||||
NodeA = $"../{beam.Name}",
|
||||
NodeB = $"../{board.Name}",
|
||||
};
|
||||
AddChild(swing);
|
||||
AddChild(swing, true);
|
||||
Sign("Hanging board", new Vector3(-6f, 2.9f, 1.5f));
|
||||
|
||||
// Crate pyramid (4 kg each), a heavy 25 kg box, barrels.
|
||||
|
||||
Reference in new issue
Block a user