ProjectRR — нативный прототип на ассетах ProjectR

- Все 189 скелетов Spine сконвертированы в нативные Godot Skeleton2D (.tscn), без Spine в рантайме
- Меню + сцены: подземелье (карта комнат), просмотр спрайтов, нативные анимации
- Кастомизация цвета (кожа/волосы/глаза) через шейдер
- Вытащенные текстуры/UI/шрифты + нарезка спрайтов из атласа
- Датасет комнат для LoRA, офлайн-инструменты конвертации в tools/

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Leonid Pershin
2026-08-03 16:29:21 +03:00
co-authored by Claude Opus 4.8
commit 04cf5f90b0
2193 changed files with 360105 additions and 0 deletions
+159
View File
@@ -0,0 +1,159 @@
using Godot;
// Демо-карта «убежища» в разрезе (à la Fallout Shelter): сетка комнат из игровых текстур.
// ЛКМ-перетаскивание или WASD/стрелки — панорама
// колесо мыши — зум, R — сброс вида
public partial class RoomMap : Node2D
{
private const float Cell = 256f;
private const int Cols = 6;
private const int Rows = 5;
private Node2D _world;
private Label _label;
private bool _dragging;
private float _viewZoom = 1f;
private Vector2 _homePos;
public override void _Ready()
{
var bg = new ColorRect
{
Color = new Color(0.06f, 0.05f, 0.04f), // земля вокруг убежища
AnchorRight = 1, AnchorBottom = 1,
MouseFilter = Control.MouseFilterEnum.Ignore
};
AddChild(bg);
_world = new Node2D();
AddChild(_world);
BuildVault();
_label = new Label { Position = new Vector2(16, 12) };
_label.AddThemeFontSizeOverride("font_size", 18);
_label.AddThemeConstantOverride("outline_size", 4);
_label.AddThemeColorOverride("font_outline_color", Colors.Black);
AddChild(_label);
ResetView();
}
private void BuildVault()
{
// «поверхность» над убежищем
AddTex("res://assets/backgrounds/background_sky.png",
new Rect2(-Cell, -Cell * 1.2f, Cols * Cell + Cell * 2, Cell * 1.2f), _world, new Color(1, 1, 1, 0.9f));
// пол каждой ячейки — пустая комната (шахматкой две вариации)
for (int r = 0; r < Rows; r++)
for (int c = 0; c < Cols; c++)
{
string tex = (r + c) % 2 == 0
? "res://assets/room_sliced/Room_normal.png"
: "res://assets/room_sliced/Room_normal2.png";
AddCell(tex, c, r, 1, 1);
}
// несколько «обставленных» комнат-акцентов поверх пустых
AddCell("res://assets/room_sliced/PlayerRoom.png", 1, 0, 3, 1); // широкая на 3 клетки
AddCell("res://assets/room_sliced/BreedingRoom.png", 0, 2, 1, 1);
AddCell("res://assets/room_sliced/LactationRoom.png", 4, 3, 1, 1);
AddCell("res://assets/room_sliced/Light.png", 2, 2, 1, 1);
// рамки поверх всех ячеек — читаемость сетки
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));
}
private void AddCell(string path, int col, int row, int cw, int ch, Color? modulate = null)
{
AddTex(path, new Rect2(col * Cell, row * Cell, cw * Cell, ch * Cell), _world, modulate ?? Colors.White);
}
private static void AddTex(string path, Rect2 dest, Node2D parent, Color modulate)
{
var tex = ResourceLoader.Exists(path) ? ResourceLoader.Load<Texture2D>(path) : null;
if (tex == null)
return;
var s = new Sprite2D
{
Texture = tex,
Centered = false,
Position = dest.Position,
Scale = new Vector2(dest.Size.X / tex.GetWidth(), dest.Size.Y / tex.GetHeight()),
Modulate = modulate
};
parent.AddChild(s);
}
private void ResetView()
{
Vector2 vp = GetViewportRect().Size;
Vector2 gridSize = new Vector2(Cols * Cell, Rows * Cell);
float fit = Mathf.Min(vp.X * 0.9f / gridSize.X, vp.Y * 0.8f / gridSize.Y);
_viewZoom = fit;
_world.Scale = new Vector2(fit, fit);
_homePos = (vp - gridSize * fit) * 0.5f;
_world.Position = _homePos;
UpdateLabel();
}
private void ZoomAt(Vector2 screenPoint, float factor)
{
float newZoom = Mathf.Clamp(_viewZoom * factor, 0.1f, 3f);
factor = newZoom / _viewZoom;
_viewZoom = newZoom;
// удерживаем точку под курсором на месте
_world.Position = screenPoint - (screenPoint - _world.Position) * factor;
_world.Scale = new Vector2(_viewZoom, _viewZoom);
UpdateLabel();
}
private void UpdateLabel()
{
_label.Text =
$"Карта убежища — {Cols}×{Rows} комнат из игровых текстур\n" +
$"ЛКМ/WASD — панорама · колесо — зум (x{_viewZoom:0.00}) · R — сброс · Esc — меню";
}
public override void _Process(double delta)
{
if (!Visible)
return;
Vector2 dir = Vector2.Zero;
if (Input.IsKeyPressed(Key.A) || Input.IsKeyPressed(Key.Left)) dir.X += 1;
if (Input.IsKeyPressed(Key.D) || Input.IsKeyPressed(Key.Right)) dir.X -= 1;
if (Input.IsKeyPressed(Key.W) || Input.IsKeyPressed(Key.Up)) dir.Y += 1;
if (Input.IsKeyPressed(Key.S) || Input.IsKeyPressed(Key.Down)) dir.Y -= 1;
if (dir != Vector2.Zero)
_world.Position += dir * (float)delta * 600f;
}
public override void _UnhandledInput(InputEvent @event)
{
if (@event is InputEventKey esc && esc.Pressed && esc.Keycode == Key.Escape)
{
GetTree().ChangeSceneToFile("res://scenes/MainMenu.tscn");
return;
}
if (@event is InputEventMouseButton mb)
{
if (mb.ButtonIndex == MouseButton.Left)
_dragging = mb.Pressed;
else if (mb.Pressed && mb.ButtonIndex == MouseButton.WheelUp)
ZoomAt(mb.Position, 1.1f);
else if (mb.Pressed && mb.ButtonIndex == MouseButton.WheelDown)
ZoomAt(mb.Position, 1f / 1.1f);
}
else if (@event is InputEventMouseMotion mm && _dragging)
{
_world.Position += mm.Relative;
}
else if (@event is InputEventKey k && k.Pressed && !k.Echo && k.Keycode == Key.R)
{
ResetView();
}
}
}