Подземелье: лестницы, объекты и гуляющие персонажи

- Процедурные лестницы-шахты между этажами
- Объекты в комнатах (фонари Lantern, свечение Light)
- 5 нативных персонажей патрулируют этажи (walk_0/idle_0, разворот, простое блуждание)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Leonid Pershin
2026-08-03 17:05:37 +03:00
co-authored by Claude Opus 4.8
parent a0e08cf146
commit b048a83966
3 changed files with 164 additions and 3 deletions
+3 -1
View File
@@ -26,7 +26,9 @@ D:\Godot_v4.7.1\Godot_v4.7.1-stable_mono_win64.exe
Главное меню (`scenes/MainMenu.tscn`) ведёт на три сцены (назад в меню — `Esc`): Главное меню (`scenes/MainMenu.tscn`) ведёт на три сцены (назад в меню — `Esc`):
1. **Подземелье** (`scenes/Dungeon.tscn`) — разрез убежища à la Fallout Shelter: сетка 6×5 1. **Подземелье** (`scenes/Dungeon.tscn`) — разрез убежища à la Fallout Shelter: сетка 6×5
комнат. ЛКМ/WASD — панорама, колесо — зум, `R` — сброс. комнат, **лестницы** между этажами, **объекты** (фонари/свет) и **несколько персонажей**,
которые ходят по этажам (нативные сцены с анимацией `walk_0`/`idle_0`).
ЛКМ/WASD — панорама, колесо — зум, `R` — сброс.
2. **Просмотр спрайтов** (`scenes/SpriteViewer.tscn`) — галерея вытащенных изображений 2. **Просмотр спрайтов** (`scenes/SpriteViewer.tscn`) — галерея вытащенных изображений
(UI, фоны, эффекты, нарезанные спрайты). `A/D` — картинка, `W/S` — категория, колесо — зум. (UI, фоны, эффекты, нарезанные спрайты). `A/D` — картинка, `W/S` — категория, колесо — зум.
3. **Нативные анимации** (`scenes/NativeViewer.tscn`) — браузер всех 38 персонажей нативно: 3. **Нативные анимации** (`scenes/NativeViewer.tscn`) — браузер всех 38 персонажей нативно:
+5
View File
@@ -19,6 +19,11 @@ config/icon="res://icon.svg"
project/assembly_name="ProjectRR" project/assembly_name="ProjectRR"
[editor]
version_control/plugin_name="GitPlugin"
version_control/autoload_on_startup=true
[physics] [physics]
3d/physics_engine="Jolt Physics" 3d/physics_engine="Jolt Physics"
+156 -2
View File
@@ -1,6 +1,8 @@
using System.Collections.Generic;
using Godot; using Godot;
// Демо-карта «убежища» в разрезе (à la Fallout Shelter): сетка комнат из игровых текстур. // Демо-карта «убежища» в разрезе (à la Fallout Shelter): комнаты, лестницы, объекты
// и несколько персонажей, гуляющих по этажам.
// ЛКМ-перетаскивание или WASD/стрелки — панорама // ЛКМ-перетаскивание или WASD/стрелки — панорама
// колесо мыши — зум, R — сброс вида // колесо мыши — зум, R — сброс вида
public partial class RoomMap : Node2D public partial class RoomMap : Node2D
@@ -8,6 +10,8 @@ public partial class RoomMap : Node2D
private const float Cell = 256f; private const float Cell = 256f;
private const int Cols = 6; private const int Cols = 6;
private const int Rows = 5; 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 Node2D _world;
private Label _label; private Label _label;
@@ -15,6 +19,18 @@ public partial class RoomMap : Node2D
private float _viewZoom = 1f; private float _viewZoom = 1f;
private Vector2 _homePos; 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() public override void _Ready()
{ {
var bg = new ColorRect var bg = new ColorRect
@@ -65,6 +81,142 @@ public partial class RoomMap : Node2D
for (int r = 0; r < Rows; r++) for (int r = 0; r < Rows; r++)
for (int c = 0; c < Cols; c++) 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)); 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) 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() private void UpdateLabel()
{ {
_label.Text = _label.Text =
$"Карта убежища — {Cols}×{Rows} комнат из игровых текстур\n" + $"Карта убежища — {Cols}×{Rows} комнат, лестницы, объекты, {_pawns.Count} персонажа гуляют\n" +
$"ЛКМ/WASD — панорама · колесо — зум (x{_viewZoom:0.00}) · R — сброс · Esc — меню"; $"ЛКМ/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 (Input.IsKeyPressed(Key.S) || Input.IsKeyPressed(Key.Down)) dir.Y -= 1;
if (dir != Vector2.Zero) if (dir != Vector2.Zero)
_world.Position += dir * (float)delta * 600f; _world.Position += dir * (float)delta * 600f;
UpdatePawns((float)delta);
} }
public override void _UnhandledInput(InputEvent @event) public override void _UnhandledInput(InputEvent @event)