Подземелье: лестницы, объекты и гуляющие персонажи
- Процедурные лестницы-шахты между этажами - Объекты в комнатах (фонари Lantern, свечение Light) - 5 нативных персонажей патрулируют этажи (walk_0/idle_0, разворот, простое блуждание) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a0e08cf146
commit
b048a83966
+156
-2
@@ -1,6 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
|
||||
// Демо-карта «убежища» в разрезе (à la Fallout Shelter): сетка комнат из игровых текстур.
|
||||
// Демо-карта «убежища» в разрезе (à la Fallout Shelter): комнаты, лестницы, объекты
|
||||
// и несколько персонажей, гуляющих по этажам.
|
||||
// ЛКМ-перетаскивание или WASD/стрелки — панорама
|
||||
// колесо мыши — зум, R — сброс вида
|
||||
public partial class RoomMap : Node2D
|
||||
@@ -8,6 +10,8 @@ public partial class RoomMap : Node2D
|
||||
private const float Cell = 256f;
|
||||
private const int Cols = 6;
|
||||
private const int Rows = 5;
|
||||
private const float CharScale = 0.34f;
|
||||
private static readonly Vector2 FootAnchor = new Vector2(640f, 604f); // ноги в кадре 1280x720
|
||||
|
||||
private Node2D _world;
|
||||
private Label _label;
|
||||
@@ -15,6 +19,18 @@ public partial class RoomMap : Node2D
|
||||
private float _viewZoom = 1f;
|
||||
private Vector2 _homePos;
|
||||
|
||||
private readonly List<Pawn> _pawns = new();
|
||||
private System.Random _rng;
|
||||
|
||||
private class Pawn
|
||||
{
|
||||
public Node2D Node;
|
||||
public AnimationPlayer Ap;
|
||||
public float X, TargetX, FloorY, Speed, MinX, MaxX, IdleTimer;
|
||||
public bool Walking;
|
||||
public string WalkAnim, IdleAnim;
|
||||
}
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
var bg = new ColorRect
|
||||
@@ -65,6 +81,142 @@ public partial class RoomMap : Node2D
|
||||
for (int r = 0; r < Rows; r++)
|
||||
for (int c = 0; c < Cols; c++)
|
||||
AddCell("res://assets/room_sliced/Frame.png", c, r, 1, 1, new Color(1, 1, 1, 0.5f));
|
||||
|
||||
BuildStairs();
|
||||
BuildObjects();
|
||||
BuildPawns();
|
||||
}
|
||||
|
||||
// ---------- лестницы (вертикальные шахты с перекладинами) ----------
|
||||
private void BuildStairs()
|
||||
{
|
||||
AddLadder(2 * Cell);
|
||||
AddLadder(4 * Cell);
|
||||
}
|
||||
|
||||
private void AddLadder(float centerX)
|
||||
{
|
||||
const float w = 48f;
|
||||
float x = centerX - w / 2f;
|
||||
float h = Rows * Cell;
|
||||
AddRect(new Rect2(x, 0, w, h), new Color(0.10f, 0.08f, 0.06f, 0.9f)); // шахта
|
||||
AddRect(new Rect2(x + 3, 0, 7, h), new Color(0.45f, 0.30f, 0.17f)); // рельс слева
|
||||
AddRect(new Rect2(x + w - 10, 0, 7, h), new Color(0.45f, 0.30f, 0.17f)); // рельс справа
|
||||
for (float ry = 22; ry < h; ry += 40)
|
||||
AddRect(new Rect2(x + 6, ry, w - 12, 8), new Color(0.55f, 0.38f, 0.20f)); // ступени
|
||||
}
|
||||
|
||||
private void AddRect(Rect2 r, Color c)
|
||||
{
|
||||
var p = new Polygon2D
|
||||
{
|
||||
Color = c,
|
||||
Polygon = new[]
|
||||
{
|
||||
r.Position, new Vector2(r.End.X, r.Position.Y), r.End, new Vector2(r.Position.X, r.End.Y)
|
||||
}
|
||||
};
|
||||
_world.AddChild(p);
|
||||
}
|
||||
|
||||
// ---------- объекты в комнатах ----------
|
||||
private void BuildObjects()
|
||||
{
|
||||
(int c, int r)[] lanterns = { (0, 0), (5, 0), (3, 1), (5, 2), (1, 3), (0, 4), (5, 4) };
|
||||
foreach (var (c, r) in lanterns)
|
||||
AddTex("res://assets/room_sliced/Lantern.png",
|
||||
new Rect2(c * Cell + Cell * 0.72f, r * Cell + Cell * 0.06f, Cell * 0.16f, Cell * 0.22f), _world, Colors.White);
|
||||
|
||||
(int c, int r)[] lights = { (1, 1), (3, 3), (4, 0) };
|
||||
foreach (var (c, r) in lights)
|
||||
AddTex("res://assets/room_sliced/Light.png",
|
||||
new Rect2(c * Cell + Cell * 0.28f, r * Cell + Cell * 0.02f, Cell * 0.44f, Cell * 0.44f), _world, new Color(1, 1, 1, 0.75f));
|
||||
}
|
||||
|
||||
// ---------- персонажи, гуляющие по этажам ----------
|
||||
private void BuildPawns()
|
||||
{
|
||||
_rng = new System.Random(20260803);
|
||||
(string path, int row)[] picks =
|
||||
{
|
||||
("res://characters/elf1/elf1_combat_native.tscn", 0),
|
||||
("res://characters/knight/knight_combat_native.tscn", 1),
|
||||
("res://characters/orc/orc_combat_native.tscn", 2),
|
||||
("res://characters/sister/sister_combat_native.tscn", 3),
|
||||
("res://characters/satyress/satyress_combat_native.tscn", 4),
|
||||
};
|
||||
float minX = Cell * 0.4f, maxX = Cols * Cell - Cell * 0.4f;
|
||||
|
||||
foreach (var (path, row) in picks)
|
||||
{
|
||||
if (!ResourceLoader.Exists(path)) continue;
|
||||
var scene = ResourceLoader.Load<PackedScene>(path).Instantiate<Node2D>();
|
||||
scene.Position = -FootAnchor * CharScale;
|
||||
scene.Scale = new Vector2(CharScale, CharScale);
|
||||
|
||||
var node = new Node2D();
|
||||
node.AddChild(scene);
|
||||
_world.AddChild(node);
|
||||
|
||||
float floorY = row * Cell + Cell * 0.9f;
|
||||
float x = (float)(_rng.NextDouble() * (maxX - minX) + minX);
|
||||
node.Position = new Vector2(x, floorY);
|
||||
|
||||
var ap = scene.GetNodeOrNull<AnimationPlayer>("AnimationPlayer");
|
||||
string[] anims = ap != null ? ap.GetAnimationList() : System.Array.Empty<string>();
|
||||
var p = new Pawn
|
||||
{
|
||||
Node = node, Ap = ap, X = x, FloorY = floorY, MinX = minX, MaxX = maxX,
|
||||
Speed = 70f + (float)_rng.NextDouble() * 55f,
|
||||
WalkAnim = Pick(anims, "walk_0"), IdleAnim = Pick(anims, "idle_0"),
|
||||
TargetX = (float)(_rng.NextDouble() * (maxX - minX) + minX)
|
||||
};
|
||||
StartWalk(p);
|
||||
_pawns.Add(p);
|
||||
}
|
||||
}
|
||||
|
||||
private static string Pick(string[] anims, string want)
|
||||
{
|
||||
foreach (var a in anims) if (a == want) return a;
|
||||
return anims.Length > 0 ? anims[0] : null;
|
||||
}
|
||||
|
||||
private static void StartWalk(Pawn p)
|
||||
{
|
||||
p.Walking = true;
|
||||
if (p.Ap != null && p.WalkAnim != null) p.Ap.Play(p.WalkAnim);
|
||||
}
|
||||
|
||||
private void StartIdle(Pawn p)
|
||||
{
|
||||
p.Walking = false;
|
||||
p.IdleTimer = 0.8f + (float)_rng.NextDouble() * 2.2f;
|
||||
if (p.Ap != null && p.IdleAnim != null) p.Ap.Play(p.IdleAnim);
|
||||
}
|
||||
|
||||
private void UpdatePawns(float dt)
|
||||
{
|
||||
foreach (var p in _pawns)
|
||||
{
|
||||
if (p.Walking)
|
||||
{
|
||||
float d = Mathf.Sign(p.TargetX - p.X);
|
||||
p.X += d * p.Speed * dt;
|
||||
p.Node.Scale = new Vector2(d >= 0 ? 1f : -1f, 1f);
|
||||
p.Node.Position = new Vector2(p.X, p.FloorY);
|
||||
if (Mathf.Abs(p.TargetX - p.X) < 4f) StartIdle(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
p.IdleTimer -= dt;
|
||||
if (p.IdleTimer <= 0f)
|
||||
{
|
||||
p.TargetX = (float)(_rng.NextDouble() * (p.MaxX - p.MinX) + p.MinX);
|
||||
StartWalk(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddCell(string path, int col, int row, int cw, int ch, Color? modulate = null)
|
||||
@@ -114,7 +266,7 @@ public partial class RoomMap : Node2D
|
||||
private void UpdateLabel()
|
||||
{
|
||||
_label.Text =
|
||||
$"Карта убежища — {Cols}×{Rows} комнат из игровых текстур\n" +
|
||||
$"Карта убежища — {Cols}×{Rows} комнат, лестницы, объекты, {_pawns.Count} персонажа гуляют\n" +
|
||||
$"ЛКМ/WASD — панорама · колесо — зум (x{_viewZoom:0.00}) · R — сброс · Esc — меню";
|
||||
}
|
||||
|
||||
@@ -129,6 +281,8 @@ public partial class RoomMap : Node2D
|
||||
if (Input.IsKeyPressed(Key.S) || Input.IsKeyPressed(Key.Down)) dir.Y -= 1;
|
||||
if (dir != Vector2.Zero)
|
||||
_world.Position += dir * (float)delta * 600f;
|
||||
|
||||
UpdatePawns((float)delta);
|
||||
}
|
||||
|
||||
public override void _UnhandledInput(InputEvent @event)
|
||||
|
||||
Reference in New Issue
Block a user