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:
+159
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user