some adjustments on stage editor
This commit is contained in:
parent
bfab987ae7
commit
1a4b2443ff
@ -1,22 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!114 &11400000
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 778f9d79eb2c410482efdc25b248dd46, type: 3}
|
|
||||||
m_Name: StageDefinition
|
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::Intrepid.Gameplay.Prototyping.StageDefinition
|
|
||||||
stageId: stagedefinition
|
|
||||||
cellSize: 1
|
|
||||||
floorHeight: 6
|
|
||||||
activeFloorIndex: 0
|
|
||||||
floors:
|
|
||||||
- floorId: F0
|
|
||||||
cells: []
|
|
||||||
objects: []
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: bb896366809bc439b8be4d0f1158dc6f
|
|
||||||
NativeFormatImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
mainObjectFileID: 11400000
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
File diff suppressed because one or more lines are too long
@ -1,109 +0,0 @@
|
|||||||
# Gardens Stage Editor Starter Kit
|
|
||||||
|
|
||||||
Starter kit para um editor lógico de stages do **Gardens of Nil** dentro da Unity.
|
|
||||||
|
|
||||||
Este pacote contém somente:
|
|
||||||
|
|
||||||
- modelo de dados (`StageDefinition`, floors, células estruturais e objetos lógicos);
|
|
||||||
- janela de editor com UI Toolkit;
|
|
||||||
- grid 1x1 com pan/zoom;
|
|
||||||
- paleta por layer;
|
|
||||||
- criação/seleção/remoção de elementos lógicos;
|
|
||||||
- inspector simples;
|
|
||||||
- validação básica.
|
|
||||||
|
|
||||||
Não contém bake/rebake de scene, geração de colliders ou integração com Tiled.
|
|
||||||
|
|
||||||
## Instalação
|
|
||||||
|
|
||||||
Descompacte a pasta `GardensStageEditor` dentro de:
|
|
||||||
|
|
||||||
```txt
|
|
||||||
Assets/Editor/
|
|
||||||
```
|
|
||||||
|
|
||||||
O resultado deve ficar parecido com:
|
|
||||||
|
|
||||||
```txt
|
|
||||||
Assets/Editor/GardensStageEditor/
|
|
||||||
Data/
|
|
||||||
Editor/
|
|
||||||
README.md
|
|
||||||
```
|
|
||||||
|
|
||||||
Depois espere a Unity recompilar.
|
|
||||||
|
|
||||||
## Abrir o editor
|
|
||||||
|
|
||||||
Na Unity:
|
|
||||||
|
|
||||||
```txt
|
|
||||||
Gardens > Stage Editor
|
|
||||||
```
|
|
||||||
|
|
||||||
Na janela:
|
|
||||||
|
|
||||||
1. Clique em `New` para criar um `StageDefinition.asset`.
|
|
||||||
2. Escolha a layer na paleta inferior.
|
|
||||||
3. Use `Paint`, `Select`, `Erase` ou `Pan`.
|
|
||||||
4. Pinte o mapa lógico no canvas central.
|
|
||||||
5. Edite o elemento selecionado no inspector à direita.
|
|
||||||
6. Use `Validate` para checagens básicas.
|
|
||||||
|
|
||||||
## Controles
|
|
||||||
|
|
||||||
```txt
|
|
||||||
LMB: pintar / selecionar
|
|
||||||
RMB ou MMB: pan
|
|
||||||
Mouse wheel: zoom
|
|
||||||
```
|
|
||||||
|
|
||||||
## Modelo mental
|
|
||||||
|
|
||||||
O editor trabalha em um plano lógico 2D:
|
|
||||||
|
|
||||||
```txt
|
|
||||||
Editor X/Y
|
|
||||||
```
|
|
||||||
|
|
||||||
Mais tarde, o baker pode mapear isso para o mundo Unity:
|
|
||||||
|
|
||||||
```txt
|
|
||||||
Editor X -> Unity X
|
|
||||||
Editor Y -> Unity Z
|
|
||||||
Floor index -> Unity Y offset
|
|
||||||
```
|
|
||||||
|
|
||||||
## Layers iniciais
|
|
||||||
|
|
||||||
```txt
|
|
||||||
Structural: Ground, Wall, Hole, Erase Structural
|
|
||||||
Design: Falling Floor, Door, Interactable, Portal
|
|
||||||
Camera: Camera Zone
|
|
||||||
Markers: Player Spawn, Elevator, Marker
|
|
||||||
```
|
|
||||||
|
|
||||||
## Filosofia
|
|
||||||
|
|
||||||
A scene não é fonte da verdade.
|
|
||||||
|
|
||||||
```txt
|
|
||||||
StageDefinition.asset = source of truth
|
|
||||||
Generated scene/prefab = output futuro do baker
|
|
||||||
```
|
|
||||||
|
|
||||||
Este pacote só cuida do primeiro lado: autoria visual e dados lógicos.
|
|
||||||
|
|
||||||
## Próximos passos sugeridos
|
|
||||||
|
|
||||||
1. Transformar `StagePaletteEntry` em assets configuráveis, se necessário.
|
|
||||||
2. Adicionar hotkeys e melhor seleção/move/resize visual.
|
|
||||||
3. Adicionar overlays de erro no canvas.
|
|
||||||
4. Implementar o baker separado consumindo `StageDefinition`.
|
|
||||||
5. Depois, se fizer sentido, criar import/export Tiled apenas para o mapa lógico.
|
|
||||||
|
|
||||||
## Requisitos recomendados
|
|
||||||
|
|
||||||
Unity 2022.3 LTS ou superior. Unity 6 também deve funcionar.
|
|
||||||
|
|
||||||
O canvas usa UI Toolkit custom drawing (`Painter2D`). Em versões muito antigas da Unity, pode ser necessário trocar o canvas por IMGUI ou SceneView Handles.
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 494fb84451f94e218111409283a7f795
|
|
||||||
timeCreated: 1781500522
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
# Stage Model Notes
|
|
||||||
|
|
||||||
## StageDefinition
|
|
||||||
|
|
||||||
Fonte da verdade do stage lógico.
|
|
||||||
|
|
||||||
Contém:
|
|
||||||
|
|
||||||
- `stageId`
|
|
||||||
- `cellSize`
|
|
||||||
- `floorHeight`
|
|
||||||
- `floors`
|
|
||||||
- `objects`
|
|
||||||
|
|
||||||
## StageFloorDefinition
|
|
||||||
|
|
||||||
Um andar lógico do stage.
|
|
||||||
|
|
||||||
Contém:
|
|
||||||
|
|
||||||
- `floorId`
|
|
||||||
- `width`
|
|
||||||
- `height`
|
|
||||||
- células estruturais preenchidas
|
|
||||||
|
|
||||||
Células vazias não são serializadas explicitamente. Apenas células com `Ground`, `Wall` ou `Hole` são guardadas.
|
|
||||||
|
|
||||||
## Structural cells
|
|
||||||
|
|
||||||
A layer estrutural é cell-based.
|
|
||||||
|
|
||||||
```txt
|
|
||||||
Empty
|
|
||||||
Ground
|
|
||||||
Wall
|
|
||||||
Hole
|
|
||||||
```
|
|
||||||
|
|
||||||
## StageObjectDefinition
|
|
||||||
|
|
||||||
Objetos lógicos são object-based.
|
|
||||||
|
|
||||||
Exemplos:
|
|
||||||
|
|
||||||
- traps
|
|
||||||
- doors
|
|
||||||
- interactables
|
|
||||||
- portals
|
|
||||||
- camera zones
|
|
||||||
- spawn points
|
|
||||||
- elevators
|
|
||||||
- markers
|
|
||||||
|
|
||||||
Cada objeto tem:
|
|
||||||
|
|
||||||
- `id`
|
|
||||||
- `layer`
|
|
||||||
- `kind`
|
|
||||||
- `typeId`
|
|
||||||
- `prefabId`
|
|
||||||
- `floorId`
|
|
||||||
- `x`, `y`, `width`, `height`
|
|
||||||
- flags como `cutsGround` e `blocksMovement`
|
|
||||||
|
|
||||||
## Regra sugerida para bake futuro
|
|
||||||
|
|
||||||
```txt
|
|
||||||
GroundCollision = Structural.Ground - Structural.Hole - Objects(cutsGround)
|
|
||||||
```
|
|
||||||
|
|
||||||
Depois o baker pode compactar células em BoxColliders maiores.
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: ffb9c2df7a0f4b3ca08544979287cf20
|
|
||||||
timeCreated: 1781500522
|
|
||||||
@ -16,21 +16,25 @@ namespace Editor.GardensStageEditor
|
|||||||
{
|
{
|
||||||
Empty = 0,
|
Empty = 0,
|
||||||
Ground = 1,
|
Ground = 1,
|
||||||
Wall = 2,
|
Wall = 2
|
||||||
Hole = 3
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum StageObjectKind
|
public enum StageObjectKind
|
||||||
{
|
{
|
||||||
Trap = 0,
|
Trap_FallingFloor = 0,
|
||||||
Door = 1,
|
|
||||||
Interactable = 2,
|
DoorHorizontal = 10,
|
||||||
Portal = 3,
|
DoorVertical = 11,
|
||||||
CameraZone = 4,
|
|
||||||
Spawn = 5,
|
// Interactable = 2,
|
||||||
Elevator = 6,
|
// Portal = 3,
|
||||||
Marker = 7,
|
|
||||||
Custom = 100
|
CameraBoundary = 40,
|
||||||
|
|
||||||
|
Spawn = 50,
|
||||||
|
// Elevator = 6,
|
||||||
|
// Marker = 7,
|
||||||
|
// Custom = 100
|
||||||
}
|
}
|
||||||
|
|
||||||
[CreateAssetMenu(menuName = "GON/Editor/Stage Definition", fileName = "StageDefinition")]
|
[CreateAssetMenu(menuName = "GON/Editor/Stage Definition", fileName = "StageDefinition")]
|
||||||
@ -95,7 +99,7 @@ namespace Editor.GardensStageEditor
|
|||||||
|
|
||||||
public bool ContainsFloor(string floorId)
|
public bool ContainsFloor(string floorId)
|
||||||
{
|
{
|
||||||
return GetFloor(floorId) != null;
|
return GetFloor(floorId) is not null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string CreateUniqueObjectId(StageObjectKind kind)
|
public string CreateUniqueObjectId(StageObjectKind kind)
|
||||||
@ -152,7 +156,7 @@ namespace Editor.GardensStageEditor
|
|||||||
|
|
||||||
public StructuralCellKind GetCell(int x, int y)
|
public StructuralCellKind GetCell(int x, int y)
|
||||||
{
|
{
|
||||||
int index = FindCellIndex(x, y);
|
var index = FindCellIndex(x, y);
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
{
|
{
|
||||||
return StructuralCellKind.Empty;
|
return StructuralCellKind.Empty;
|
||||||
@ -174,7 +178,7 @@ namespace Editor.GardensStageEditor
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogicalCell cell = new LogicalCell
|
var cell = new LogicalCell
|
||||||
{
|
{
|
||||||
x = x,
|
x = x,
|
||||||
y = y,
|
y = y,
|
||||||
@ -270,6 +274,11 @@ namespace Editor.GardensStageEditor
|
|||||||
public bool blocksMovement;
|
public bool blocksMovement;
|
||||||
public string notes;
|
public string notes;
|
||||||
|
|
||||||
|
public bool IsResizable => IsKindResizable(kind);
|
||||||
|
public Vector2Int DefaultSize => GetDefaultSize(kind);
|
||||||
|
public Vector2Int MinSize => GetMinSize(kind);
|
||||||
|
public Vector2Int MaxSize => GetMaxSize(kind);
|
||||||
|
|
||||||
public RectInt Bounds => new(x, y, Mathf.Max(1, width), Mathf.Max(1, height));
|
public RectInt Bounds => new(x, y, Mathf.Max(1, width), Mathf.Max(1, height));
|
||||||
|
|
||||||
public bool ContainsCell(int cellX, int cellY)
|
public bool ContainsCell(int cellX, int cellY)
|
||||||
@ -277,5 +286,62 @@ namespace Editor.GardensStageEditor
|
|||||||
var bounds = Bounds;
|
var bounds = Bounds;
|
||||||
return cellX >= bounds.xMin && cellX < bounds.xMax && cellY >= bounds.yMin && cellY < bounds.yMax;
|
return cellX >= bounds.xMin && cellX < bounds.xMax && cellY >= bounds.yMin && cellY < bounds.yMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void NormalizeSize()
|
||||||
|
{
|
||||||
|
if (!IsResizable)
|
||||||
|
{
|
||||||
|
var fixedSize = DefaultSize;
|
||||||
|
width = Mathf.Max(1, fixedSize.x);
|
||||||
|
height = Mathf.Max(1, fixedSize.y);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var min = MinSize;
|
||||||
|
var max = MaxSize;
|
||||||
|
width = Mathf.Clamp(Mathf.Max(1, width), Mathf.Max(1, min.x), Mathf.Max(1, max.x));
|
||||||
|
height = Mathf.Clamp(Mathf.Max(1, height), Mathf.Max(1, min.y), Mathf.Max(1, max.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsKindResizable(StageObjectKind kind)
|
||||||
|
{
|
||||||
|
return kind switch
|
||||||
|
{
|
||||||
|
StageObjectKind.Trap_FallingFloor => true,
|
||||||
|
StageObjectKind.CameraBoundary => true,
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector2Int GetDefaultSize(StageObjectKind kind)
|
||||||
|
{
|
||||||
|
return kind switch
|
||||||
|
{
|
||||||
|
StageObjectKind.Trap_FallingFloor => 2 * Vector2Int.one,
|
||||||
|
StageObjectKind.DoorVertical => new Vector2Int(1, 2),
|
||||||
|
StageObjectKind.DoorHorizontal => new Vector2Int(2, 1),
|
||||||
|
StageObjectKind.CameraBoundary => new Vector2Int(8, 5),
|
||||||
|
_ => Vector2Int.one
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector2Int GetMinSize(StageObjectKind kind)
|
||||||
|
{
|
||||||
|
return kind switch
|
||||||
|
{
|
||||||
|
StageObjectKind.CameraBoundary => new Vector2Int(1, 1),
|
||||||
|
_ => GetDefaultSize(kind)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector2Int GetMaxSize(StageObjectKind kind)
|
||||||
|
{
|
||||||
|
return kind switch
|
||||||
|
{
|
||||||
|
StageObjectKind.Trap_FallingFloor => new Vector2Int(64, 64),
|
||||||
|
StageObjectKind.CameraBoundary => new Vector2Int(64, 64),
|
||||||
|
_ => GetDefaultSize(kind)
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 778f9d79eb2c410482efdc25b248dd46
|
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
using Intrepid.Diagnostics;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Editor.GardensStageEditor
|
namespace Editor.GardensStageEditor
|
||||||
@ -12,19 +13,25 @@ namespace Editor.GardensStageEditor
|
|||||||
public static readonly Color PreviewStroke = new(1f, 1f, 1f, 0.9f);
|
public static readonly Color PreviewStroke = new(1f, 1f, 1f, 0.9f);
|
||||||
public static readonly Color SelectionStroke = new(1f, 0.85f, 0.25f, 1f);
|
public static readonly Color SelectionStroke = new(1f, 0.85f, 0.25f, 1f);
|
||||||
|
|
||||||
|
public static Color FromHtml(string html)
|
||||||
|
{
|
||||||
|
if (!html.StartsWith("#")) html = $"#{html}";
|
||||||
|
if (ColorUtility.TryParseHtmlString(html, out var color))
|
||||||
|
{
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
DLogger.LogDebug("color not supported");
|
||||||
|
return Color.magenta;
|
||||||
|
}
|
||||||
|
|
||||||
public static Color Structural(StructuralCellKind kind)
|
public static Color Structural(StructuralCellKind kind)
|
||||||
{
|
{
|
||||||
switch (kind)
|
return kind switch
|
||||||
{
|
{
|
||||||
case StructuralCellKind.Ground:
|
StructuralCellKind.Ground => FromHtml("55E0FF"),
|
||||||
return new Color(0.28f, 0.52f, 0.35f, 0.8f);
|
StructuralCellKind.Wall => FromHtml("668B93"),
|
||||||
case StructuralCellKind.Wall:
|
_ => new Color(0f, 0f, 0f, 0f)
|
||||||
return new Color(0.38f, 0.38f, 0.42f, 0.9f);
|
};
|
||||||
case StructuralCellKind.Hole:
|
|
||||||
return new Color(0.05f, 0.05f, 0.05f, 0.9f);
|
|
||||||
default:
|
|
||||||
return new Color(0f, 0f, 0f, 0f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Color Layer(StageLogicalLayer layer)
|
public static Color Layer(StageLogicalLayer layer)
|
||||||
|
|||||||
@ -17,6 +17,7 @@ namespace Editor.GardensStageEditor
|
|||||||
private StageMapCanvas canvas;
|
private StageMapCanvas canvas;
|
||||||
private StageInspectorPanel inspector;
|
private StageInspectorPanel inspector;
|
||||||
private StagePalettePanel palettePanel;
|
private StagePalettePanel palettePanel;
|
||||||
|
private Label mouseCellLabel;
|
||||||
|
|
||||||
[MenuItem("Tools/Gardens Of Nil/Stage Editor")]
|
[MenuItem("Tools/Gardens Of Nil/Stage Editor")]
|
||||||
public static void Open()
|
public static void Open()
|
||||||
@ -217,7 +218,17 @@ namespace Editor.GardensStageEditor
|
|||||||
};
|
};
|
||||||
topBar.Add(spacer);
|
topBar.Add(spacer);
|
||||||
|
|
||||||
var hint = new Label("Infinite grid · LMB select/place/paint · RMB/MMB pan · Wheel zoom")
|
mouseCellLabel = new Label("Mouse: —")
|
||||||
|
{
|
||||||
|
style =
|
||||||
|
{
|
||||||
|
opacity = 0.85f,
|
||||||
|
marginRight = 16f
|
||||||
|
}
|
||||||
|
};
|
||||||
|
topBar.Add(mouseCellLabel);
|
||||||
|
|
||||||
|
var hint = new Label("Infinite grid · LMB select/place/paint/move · selected resizable objects have a resize handle · RMB/MMB pan · Wheel zoom")
|
||||||
{
|
{
|
||||||
style =
|
style =
|
||||||
{
|
{
|
||||||
@ -227,6 +238,19 @@ namespace Editor.GardensStageEditor
|
|||||||
topBar.Add(hint);
|
topBar.Add(hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void SetMouseCell(Vector2Int? cell)
|
||||||
|
{
|
||||||
|
if (mouseCellLabel is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseCellLabel.text = cell.HasValue
|
||||||
|
? "Mouse: X " + cell.Value.x + " Y " + cell.Value.y
|
||||||
|
: "Mouse: —";
|
||||||
|
}
|
||||||
|
|
||||||
private void SetStage(StageDefinition newStage)
|
private void SetStage(StageDefinition newStage)
|
||||||
{
|
{
|
||||||
stage = newStage;
|
stage = newStage;
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: f7065bab1fc14f53909281036dbb6368
|
|
||||||
@ -165,13 +165,8 @@ namespace Editor.GardensStageEditor
|
|||||||
id.RegisterValueChangedCallback(evt => owner.MutateStage("Change object id", delegate { obj.id = evt.newValue; }));
|
id.RegisterValueChangedCallback(evt => owner.MutateStage("Change object id", delegate { obj.id = evt.newValue; }));
|
||||||
Add(id);
|
Add(id);
|
||||||
|
|
||||||
var layer = new EnumField("Layer", obj.layer);
|
AddReadOnlyLine("Layer", obj.layer.ToString());
|
||||||
layer.RegisterValueChangedCallback(evt => owner.MutateStage("Change object layer", delegate { obj.layer = (StageLogicalLayer)evt.newValue; }));
|
AddReadOnlyLine("Kind", obj.kind.ToString());
|
||||||
Add(layer);
|
|
||||||
|
|
||||||
var kind = new EnumField("Kind", obj.kind);
|
|
||||||
kind.RegisterValueChangedCallback(evt => owner.MutateStage("Change object kind", delegate { obj.kind = (StageObjectKind)evt.newValue; }));
|
|
||||||
Add(kind);
|
|
||||||
|
|
||||||
var typeId = new TextField("Type Id")
|
var typeId = new TextField("Type Id")
|
||||||
{
|
{
|
||||||
@ -201,19 +196,56 @@ namespace Editor.GardensStageEditor
|
|||||||
y.RegisterValueChangedCallback(evt => owner.MutateStage("Move object y", delegate { obj.y = evt.newValue; }));
|
y.RegisterValueChangedCallback(evt => owner.MutateStage("Move object y", delegate { obj.y = evt.newValue; }));
|
||||||
Add(y);
|
Add(y);
|
||||||
|
|
||||||
var width = new IntegerField("Width")
|
if (obj.IsResizable)
|
||||||
{
|
{
|
||||||
value = obj.width
|
var min = obj.MinSize;
|
||||||
};
|
var max = obj.MaxSize;
|
||||||
width.RegisterValueChangedCallback(evt => owner.MutateStage("Resize object width", delegate { obj.width = Mathf.Max(1, evt.newValue); }));
|
Add(new Label("Resizable: yes · Min " + min.x + "x" + min.y + " · Max " + max.x + "x" + max.y)
|
||||||
Add(width);
|
{
|
||||||
|
style =
|
||||||
|
{
|
||||||
|
opacity = 0.75f,
|
||||||
|
marginTop = 4,
|
||||||
|
marginBottom = 2
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var height = new IntegerField("Height")
|
var width = new IntegerField("Width")
|
||||||
|
{
|
||||||
|
value = obj.width
|
||||||
|
};
|
||||||
|
width.RegisterValueChangedCallback(evt => owner.MutateStage("Resize object width", delegate
|
||||||
|
{
|
||||||
|
obj.width = evt.newValue;
|
||||||
|
obj.NormalizeSize();
|
||||||
|
}));
|
||||||
|
Add(width);
|
||||||
|
|
||||||
|
var height = new IntegerField("Height")
|
||||||
|
{
|
||||||
|
value = obj.height
|
||||||
|
};
|
||||||
|
height.RegisterValueChangedCallback(evt => owner.MutateStage("Resize object height", delegate
|
||||||
|
{
|
||||||
|
obj.height = evt.newValue;
|
||||||
|
obj.NormalizeSize();
|
||||||
|
}));
|
||||||
|
Add(height);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
value = obj.height
|
var fixedSize = obj.DefaultSize;
|
||||||
};
|
Add(new Label("Size: " + fixedSize.x + "x" + fixedSize.y + " (fixed)")
|
||||||
height.RegisterValueChangedCallback(evt => owner.MutateStage("Resize object height", delegate { obj.height = Mathf.Max(1, evt.newValue); }));
|
{
|
||||||
Add(height);
|
style =
|
||||||
|
{
|
||||||
|
opacity = 0.75f,
|
||||||
|
marginTop = 4,
|
||||||
|
marginBottom = 2
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Add(new HelpBox("This object kind has a fixed size. It can be moved, but it cannot be resized from the inspector or the grid.", HelpBoxMessageType.Info));
|
||||||
|
}
|
||||||
|
|
||||||
var cutsGround = new Toggle("Cuts Ground")
|
var cutsGround = new Toggle("Cuts Ground")
|
||||||
{
|
{
|
||||||
@ -254,5 +286,39 @@ namespace Editor.GardensStageEditor
|
|||||||
};
|
};
|
||||||
Add(delete);
|
Add(delete);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddReadOnlyLine(string label, string value)
|
||||||
|
{
|
||||||
|
var row = new VisualElement
|
||||||
|
{
|
||||||
|
style =
|
||||||
|
{
|
||||||
|
flexDirection = FlexDirection.Row,
|
||||||
|
marginTop = 2,
|
||||||
|
marginBottom = 2
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var left = new Label(label)
|
||||||
|
{
|
||||||
|
style =
|
||||||
|
{
|
||||||
|
width = 90,
|
||||||
|
opacity = 0.75f
|
||||||
|
}
|
||||||
|
};
|
||||||
|
row.Add(left);
|
||||||
|
|
||||||
|
var right = new Label(value)
|
||||||
|
{
|
||||||
|
style =
|
||||||
|
{
|
||||||
|
unityFontStyleAndWeight = FontStyle.Bold
|
||||||
|
}
|
||||||
|
};
|
||||||
|
row.Add(right);
|
||||||
|
|
||||||
|
Add(row);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: a1b3226b742b4e5e94cab859de25494a
|
|
||||||
@ -7,6 +7,16 @@ namespace Editor.GardensStageEditor
|
|||||||
{
|
{
|
||||||
private const float MinCellPixels = 12f;
|
private const float MinCellPixels = 12f;
|
||||||
private const float MaxCellPixels = 96f;
|
private const float MaxCellPixels = 96f;
|
||||||
|
private const float ResizeHandlePixels = 12f;
|
||||||
|
|
||||||
|
private enum DragMode
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Pan,
|
||||||
|
Paint,
|
||||||
|
MoveObject,
|
||||||
|
ResizeObject
|
||||||
|
}
|
||||||
|
|
||||||
private readonly StageEditorWindow owner;
|
private readonly StageEditorWindow owner;
|
||||||
private readonly StageEditorState state;
|
private readonly StageEditorState state;
|
||||||
@ -15,12 +25,18 @@ namespace Editor.GardensStageEditor
|
|||||||
private Vector2 pan = new(40f, 40f);
|
private Vector2 pan = new(40f, 40f);
|
||||||
private float cellPixels = 32f;
|
private float cellPixels = 32f;
|
||||||
|
|
||||||
private bool isPanning;
|
private DragMode dragMode = DragMode.None;
|
||||||
private bool isPainting;
|
|
||||||
private Vector2 panStartMouse;
|
private Vector2 panStartMouse;
|
||||||
private Vector2 panStartValue;
|
private Vector2 panStartValue;
|
||||||
private Vector2Int dragStartCell;
|
private Vector2Int dragStartCell;
|
||||||
private Vector2Int dragCurrentCell;
|
private Vector2Int dragCurrentCell;
|
||||||
|
private Vector2Int? hoverCell;
|
||||||
|
|
||||||
|
private StageObjectDefinition draggedObject;
|
||||||
|
private int objectStartX;
|
||||||
|
private int objectStartY;
|
||||||
|
private int objectStartWidth;
|
||||||
|
private int objectStartHeight;
|
||||||
|
|
||||||
public StageMapCanvas(StageEditorWindow owner, StageEditorState state)
|
public StageMapCanvas(StageEditorWindow owner, StageEditorState state)
|
||||||
{
|
{
|
||||||
@ -36,6 +52,7 @@ namespace Editor.GardensStageEditor
|
|||||||
RegisterCallback<MouseDownEvent>(OnMouseDown);
|
RegisterCallback<MouseDownEvent>(OnMouseDown);
|
||||||
RegisterCallback<MouseMoveEvent>(OnMouseMove);
|
RegisterCallback<MouseMoveEvent>(OnMouseMove);
|
||||||
RegisterCallback<MouseUpEvent>(OnMouseUp);
|
RegisterCallback<MouseUpEvent>(OnMouseUp);
|
||||||
|
RegisterCallback<MouseLeaveEvent>(OnMouseLeave);
|
||||||
RegisterCallback<WheelEvent>(OnWheel);
|
RegisterCallback<WheelEvent>(OnWheel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,6 +75,8 @@ namespace Editor.GardensStageEditor
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateHoverCell(evt.localMousePosition);
|
||||||
|
|
||||||
var mouse = evt.localMousePosition;
|
var mouse = evt.localMousePosition;
|
||||||
var before = ScreenToGridFloat(mouse);
|
var before = ScreenToGridFloat(mouse);
|
||||||
|
|
||||||
@ -74,6 +93,7 @@ namespace Editor.GardensStageEditor
|
|||||||
private void OnMouseDown(MouseDownEvent evt)
|
private void OnMouseDown(MouseDownEvent evt)
|
||||||
{
|
{
|
||||||
Focus();
|
Focus();
|
||||||
|
UpdateHoverCell(evt.localMousePosition);
|
||||||
|
|
||||||
if (stage is null)
|
if (stage is null)
|
||||||
{
|
{
|
||||||
@ -83,10 +103,7 @@ namespace Editor.GardensStageEditor
|
|||||||
// Pan is no longer a palette tool. It is always available through RMB/MMB.
|
// Pan is no longer a palette tool. It is always available through RMB/MMB.
|
||||||
if (evt.button == 2 || evt.button == 1)
|
if (evt.button == 2 || evt.button == 1)
|
||||||
{
|
{
|
||||||
isPanning = true;
|
BeginPan(evt.localMousePosition);
|
||||||
panStartMouse = evt.localMousePosition;
|
|
||||||
panStartValue = pan;
|
|
||||||
this.CaptureMouse();
|
|
||||||
evt.StopPropagation();
|
evt.StopPropagation();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -96,91 +113,229 @@ namespace Editor.GardensStageEditor
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryScreenToCell(evt.localMousePosition, out Vector2Int cell))
|
if (!TryScreenToCell(evt.localMousePosition, out var cell))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a resizable selected object has its handle clicked, resize wins over move/select.
|
||||||
|
if (TryBeginResizeSelectedObject(evt.localMousePosition, cell))
|
||||||
|
{
|
||||||
|
evt.StopPropagation();
|
||||||
|
MarkDirtyRepaint();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// LMB always tries to select an existing logical object first.
|
// LMB always tries to select an existing logical object first.
|
||||||
// This removes the need for a Select tool.
|
// If the user drags after selecting/hitting it, the same interaction moves it.
|
||||||
var hit = FindObjectAt(cell, false);
|
var hit = FindObjectAt(cell, false);
|
||||||
if (hit != null)
|
if (hit != null)
|
||||||
{
|
{
|
||||||
state.SelectObject(hit);
|
state.SelectObject(hit);
|
||||||
MarkDirtyRepaint();
|
BeginMoveObject(hit, cell);
|
||||||
evt.StopPropagation();
|
evt.StopPropagation();
|
||||||
|
MarkDirtyRepaint();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.SelectObject(null);
|
state.SelectObject(null);
|
||||||
|
|
||||||
// Empty space uses the active palette item to paint/place.
|
// Empty space uses the active palette item to paint/place.
|
||||||
dragStartCell = cell;
|
BeginPaint(cell);
|
||||||
dragCurrentCell = cell;
|
|
||||||
isPainting = true;
|
|
||||||
this.CaptureMouse();
|
|
||||||
|
|
||||||
evt.StopPropagation();
|
evt.StopPropagation();
|
||||||
MarkDirtyRepaint();
|
MarkDirtyRepaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMouseMove(MouseMoveEvent evt)
|
private void OnMouseMove(MouseMoveEvent evt)
|
||||||
{
|
{
|
||||||
if (isPanning)
|
UpdateHoverCell(evt.localMousePosition);
|
||||||
{
|
|
||||||
pan = panStartValue + evt.localMousePosition - panStartMouse;
|
|
||||||
evt.StopPropagation();
|
|
||||||
MarkDirtyRepaint();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPainting)
|
switch (dragMode)
|
||||||
{
|
{
|
||||||
if (TryScreenToCell(evt.localMousePosition, out Vector2Int cell))
|
case DragMode.Pan:
|
||||||
{
|
pan = panStartValue + evt.localMousePosition - panStartMouse;
|
||||||
dragCurrentCell = cell;
|
evt.StopPropagation();
|
||||||
MarkDirtyRepaint();
|
MarkDirtyRepaint();
|
||||||
}
|
return;
|
||||||
|
|
||||||
evt.StopPropagation();
|
case DragMode.Paint:
|
||||||
|
if (TryScreenToCell(evt.localMousePosition, out var paintCell))
|
||||||
|
{
|
||||||
|
dragCurrentCell = paintCell;
|
||||||
|
MarkDirtyRepaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
evt.StopPropagation();
|
||||||
|
return;
|
||||||
|
|
||||||
|
case DragMode.MoveObject:
|
||||||
|
if (TryScreenToCell(evt.localMousePosition, out var moveCell))
|
||||||
|
{
|
||||||
|
ApplyMoveObject(moveCell);
|
||||||
|
}
|
||||||
|
|
||||||
|
evt.StopPropagation();
|
||||||
|
return;
|
||||||
|
|
||||||
|
case DragMode.ResizeObject:
|
||||||
|
if (TryScreenToCell(evt.localMousePosition, out var resizeCell))
|
||||||
|
{
|
||||||
|
ApplyResizeObject(resizeCell);
|
||||||
|
}
|
||||||
|
|
||||||
|
evt.StopPropagation();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMouseUp(MouseUpEvent evt)
|
private void OnMouseUp(MouseUpEvent evt)
|
||||||
{
|
{
|
||||||
if (isPanning)
|
if (dragMode == DragMode.None)
|
||||||
{
|
|
||||||
isPanning = false;
|
|
||||||
if (this.HasMouseCapture())
|
|
||||||
{
|
|
||||||
this.ReleaseMouse();
|
|
||||||
}
|
|
||||||
|
|
||||||
evt.StopPropagation();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isPainting || evt.button != 0)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
isPainting = false;
|
if (dragMode == DragMode.Paint && evt.button == 0)
|
||||||
if (this.HasMouseCapture())
|
|
||||||
{
|
{
|
||||||
this.ReleaseMouse();
|
var rect = GetDragRect();
|
||||||
|
ApplyPalette(rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
var rect = GetDragRect();
|
EndDrag();
|
||||||
ApplyPalette(rect);
|
|
||||||
|
|
||||||
evt.StopPropagation();
|
evt.StopPropagation();
|
||||||
MarkDirtyRepaint();
|
MarkDirtyRepaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnMouseLeave(MouseLeaveEvent evt)
|
||||||
|
{
|
||||||
|
if (!this.HasMouseCapture())
|
||||||
|
{
|
||||||
|
hoverCell = null;
|
||||||
|
owner.SetMouseCell(null);
|
||||||
|
MarkDirtyRepaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BeginPan(Vector2 mouse)
|
||||||
|
{
|
||||||
|
dragMode = DragMode.Pan;
|
||||||
|
panStartMouse = mouse;
|
||||||
|
panStartValue = pan;
|
||||||
|
this.CaptureMouse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BeginPaint(Vector2Int cell)
|
||||||
|
{
|
||||||
|
dragMode = DragMode.Paint;
|
||||||
|
dragStartCell = cell;
|
||||||
|
dragCurrentCell = cell;
|
||||||
|
this.CaptureMouse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BeginMoveObject(StageObjectDefinition obj, Vector2Int cell)
|
||||||
|
{
|
||||||
|
dragMode = DragMode.MoveObject;
|
||||||
|
draggedObject = obj;
|
||||||
|
dragStartCell = cell;
|
||||||
|
dragCurrentCell = cell;
|
||||||
|
objectStartX = obj.x;
|
||||||
|
objectStartY = obj.y;
|
||||||
|
objectStartWidth = obj.width;
|
||||||
|
objectStartHeight = obj.height;
|
||||||
|
this.CaptureMouse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryBeginResizeSelectedObject(Vector2 mouse, Vector2Int cell)
|
||||||
|
{
|
||||||
|
var obj = state.selectedObject;
|
||||||
|
if (obj is null || !CanEditObjectOnActiveFloor(obj) || !obj.IsResizable)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GetResizeHandleRect(obj).Contains(mouse))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dragMode = DragMode.ResizeObject;
|
||||||
|
draggedObject = obj;
|
||||||
|
dragStartCell = cell;
|
||||||
|
dragCurrentCell = cell;
|
||||||
|
objectStartX = obj.x;
|
||||||
|
objectStartY = obj.y;
|
||||||
|
objectStartWidth = obj.width;
|
||||||
|
objectStartHeight = obj.height;
|
||||||
|
this.CaptureMouse();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyMoveObject(Vector2Int cell)
|
||||||
|
{
|
||||||
|
if (draggedObject is null || !CanEditObjectOnActiveFloor(draggedObject))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dragCurrentCell = cell;
|
||||||
|
|
||||||
|
var dx = cell.x - dragStartCell.x;
|
||||||
|
var dy = cell.y - dragStartCell.y;
|
||||||
|
var newX = objectStartX + dx;
|
||||||
|
var newY = objectStartY + dy;
|
||||||
|
|
||||||
|
if (draggedObject.x == newX && draggedObject.y == newY)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
owner.MutateStage("Move object", delegate
|
||||||
|
{
|
||||||
|
draggedObject.x = newX;
|
||||||
|
draggedObject.y = newY;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyResizeObject(Vector2Int cell)
|
||||||
|
{
|
||||||
|
if (draggedObject is null || !CanEditObjectOnActiveFloor(draggedObject) || !draggedObject.IsResizable)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dragCurrentCell = cell;
|
||||||
|
|
||||||
|
var min = draggedObject.MinSize;
|
||||||
|
var max = draggedObject.MaxSize;
|
||||||
|
var newWidth = Mathf.Clamp(cell.x - objectStartX + 1, Mathf.Max(1, min.x), Mathf.Max(1, max.x));
|
||||||
|
var newHeight = Mathf.Clamp(cell.y - objectStartY + 1, Mathf.Max(1, min.y), Mathf.Max(1, max.y));
|
||||||
|
|
||||||
|
if (draggedObject.width == newWidth && draggedObject.height == newHeight)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
owner.MutateStage("Resize object", delegate
|
||||||
|
{
|
||||||
|
draggedObject.width = newWidth;
|
||||||
|
draggedObject.height = newHeight;
|
||||||
|
draggedObject.NormalizeSize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EndDrag()
|
||||||
|
{
|
||||||
|
dragMode = DragMode.None;
|
||||||
|
draggedObject = null;
|
||||||
|
if (this.HasMouseCapture())
|
||||||
|
{
|
||||||
|
this.ReleaseMouse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ApplyPalette(RectInt rect)
|
private void ApplyPalette(RectInt rect)
|
||||||
{
|
{
|
||||||
StageFloorDefinition floor = stage?.ActiveFloor;
|
var floor = stage?.ActiveFloor;
|
||||||
if (floor is null)
|
if (floor is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -201,14 +356,7 @@ namespace Editor.GardensStageEditor
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var objectRect = rect;
|
var objectRect = GetPaletteObjectRect(entry, rect);
|
||||||
if (rect is { width: 1, height: 1 })
|
|
||||||
{
|
|
||||||
objectRect = new RectInt(rect.x, rect.y, Mathf.Max(1, entry.defaultSize.x), Mathf.Max(1, entry.defaultSize.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
objectRect.width = Mathf.Clamp(objectRect.width, Mathf.Max(1, entry.minSize.x), Mathf.Max(1, entry.maxSize.x));
|
|
||||||
objectRect.height = Mathf.Clamp(objectRect.height, Mathf.Max(1, entry.minSize.y), Mathf.Max(1, entry.maxSize.y));
|
|
||||||
|
|
||||||
owner.MutateStage("Place " + entry.displayName, delegate
|
owner.MutateStage("Place " + entry.displayName, delegate
|
||||||
{
|
{
|
||||||
@ -228,11 +376,38 @@ namespace Editor.GardensStageEditor
|
|||||||
blocksMovement = entry.blocksMovement
|
blocksMovement = entry.blocksMovement
|
||||||
};
|
};
|
||||||
|
|
||||||
|
obj.NormalizeSize();
|
||||||
stage.objects.Add(obj);
|
stage.objects.Add(obj);
|
||||||
state.SelectObject(obj);
|
state.SelectObject(obj);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RectInt GetPaletteObjectRect(StagePaletteEntry entry, RectInt paintedRect)
|
||||||
|
{
|
||||||
|
var defaultSize = entry.defaultSize;
|
||||||
|
if (defaultSize.x <= 0 || defaultSize.y <= 0)
|
||||||
|
{
|
||||||
|
defaultSize = StageObjectDefinition.GetDefaultSize(entry.objectKind);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StageObjectDefinition.IsKindResizable(entry.objectKind))
|
||||||
|
{
|
||||||
|
return new RectInt(paintedRect.x, paintedRect.y, Mathf.Max(1, defaultSize.x), Mathf.Max(1, defaultSize.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
var objectRect = paintedRect;
|
||||||
|
if (paintedRect is { width: 1, height: 1 })
|
||||||
|
{
|
||||||
|
objectRect = new RectInt(paintedRect.x, paintedRect.y, Mathf.Max(1, defaultSize.x), Mathf.Max(1, defaultSize.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
var min = StageObjectDefinition.GetMinSize(entry.objectKind);
|
||||||
|
var max = StageObjectDefinition.GetMaxSize(entry.objectKind);
|
||||||
|
objectRect.width = Mathf.Clamp(objectRect.width, Mathf.Max(1, min.x), Mathf.Max(1, max.x));
|
||||||
|
objectRect.height = Mathf.Clamp(objectRect.height, Mathf.Max(1, min.y), Mathf.Max(1, max.y));
|
||||||
|
return objectRect;
|
||||||
|
}
|
||||||
|
|
||||||
private StageObjectDefinition FindObjectAt(Vector2Int cell, bool activeLayerOnly)
|
private StageObjectDefinition FindObjectAt(Vector2Int cell, bool activeLayerOnly)
|
||||||
{
|
{
|
||||||
if (stage is null)
|
if (stage is null)
|
||||||
@ -263,6 +438,30 @@ namespace Editor.GardensStageEditor
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool CanEditObjectOnActiveFloor(StageObjectDefinition obj)
|
||||||
|
{
|
||||||
|
return stage is not null && obj is not null && stage.ActiveFloor is not null && obj.floorId == stage.ActiveFloor.floorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateHoverCell(Vector2 localMousePosition)
|
||||||
|
{
|
||||||
|
if (stage is null || !TryScreenToCell(localMousePosition, out var cell))
|
||||||
|
{
|
||||||
|
hoverCell = null;
|
||||||
|
owner.SetMouseCell(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hoverCell.HasValue && hoverCell.Value == cell)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hoverCell = cell;
|
||||||
|
owner.SetMouseCell(cell);
|
||||||
|
MarkDirtyRepaint();
|
||||||
|
}
|
||||||
|
|
||||||
private void OnGenerateVisualContent(MeshGenerationContext mgc)
|
private void OnGenerateVisualContent(MeshGenerationContext mgc)
|
||||||
{
|
{
|
||||||
var p = mgc.painter2D;
|
var p = mgc.painter2D;
|
||||||
@ -284,6 +483,7 @@ namespace Editor.GardensStageEditor
|
|||||||
DrawCells(p, floor);
|
DrawCells(p, floor);
|
||||||
DrawObjects(p, floor.floorId);
|
DrawObjects(p, floor.floorId);
|
||||||
DrawOriginAxes(p);
|
DrawOriginAxes(p);
|
||||||
|
DrawHoverCell(p);
|
||||||
DrawPreview(p);
|
DrawPreview(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,10 +519,22 @@ namespace Editor.GardensStageEditor
|
|||||||
if (state.selectedObject == obj)
|
if (state.selectedObject == obj)
|
||||||
{
|
{
|
||||||
StrokeRect(p, rect.Expanded(2f), StageEditorColors.SelectionStroke, 3f);
|
StrokeRect(p, rect.Expanded(2f), StageEditorColors.SelectionStroke, 3f);
|
||||||
|
|
||||||
|
if (obj.IsResizable)
|
||||||
|
{
|
||||||
|
DrawResizeHandle(p, obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawResizeHandle(Painter2D p, StageObjectDefinition obj)
|
||||||
|
{
|
||||||
|
var handle = GetResizeHandleRect(obj);
|
||||||
|
FillRect(p, handle, StageEditorColors.SelectionStroke);
|
||||||
|
StrokeRect(p, handle, Color.black.WithAlpha(0.75f), 1f);
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawInfiniteGrid(Painter2D p)
|
private void DrawInfiniteGrid(Painter2D p)
|
||||||
{
|
{
|
||||||
var r = contentRect;
|
var r = contentRect;
|
||||||
@ -363,19 +575,30 @@ namespace Editor.GardensStageEditor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawHoverCell(Painter2D p)
|
||||||
|
{
|
||||||
|
if (!hoverCell.HasValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cell = hoverCell.Value;
|
||||||
|
StrokeRect(p, CellRect(cell.x, cell.y).Expanded(-2f), StageEditorColors.PreviewStroke.WithAlpha(0.45f), 1f);
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawPreview(Painter2D p)
|
private void DrawPreview(Painter2D p)
|
||||||
{
|
{
|
||||||
if (!isPainting || state.activePaletteEntry is null)
|
if (dragMode != DragMode.Paint || state.activePaletteEntry is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var rect = GetDragRect();
|
var rect = GetDragRect();
|
||||||
var entry = state.activePaletteEntry;
|
var entry = state.activePaletteEntry;
|
||||||
|
|
||||||
if (!entry.paintsStructural && rect is { width: 1, height: 1 })
|
if (!entry.paintsStructural)
|
||||||
{
|
{
|
||||||
rect = new RectInt(rect.x, rect.y, Mathf.Max(1, entry.defaultSize.x), Mathf.Max(1, entry.defaultSize.y));
|
rect = GetPaletteObjectRect(entry, rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
var screenRect = GridRect(rect);
|
var screenRect = GridRect(rect);
|
||||||
@ -414,6 +637,13 @@ namespace Editor.GardensStageEditor
|
|||||||
return new Rect(pan.x + rect.x * cellPixels, pan.y + rect.y * cellPixels, rect.width * cellPixels, rect.height * cellPixels);
|
return new Rect(pan.x + rect.x * cellPixels, pan.y + rect.y * cellPixels, rect.width * cellPixels, rect.height * cellPixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Rect GetResizeHandleRect(StageObjectDefinition obj)
|
||||||
|
{
|
||||||
|
var rect = GridRect(obj.Bounds);
|
||||||
|
var size = Mathf.Min(ResizeHandlePixels, Mathf.Max(8f, cellPixels * 0.35f));
|
||||||
|
return new Rect(rect.xMax - size, rect.yMax - size, size, size);
|
||||||
|
}
|
||||||
|
|
||||||
private void FillRect(Painter2D p, Rect rect, Color color)
|
private void FillRect(Painter2D p, Rect rect, Color color)
|
||||||
{
|
{
|
||||||
p.fillColor = color;
|
p.fillColor = color;
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 8c584058841343debb04c00199a79393
|
|
||||||
@ -19,7 +19,7 @@ namespace Editor.GardensStageEditor
|
|||||||
public Vector2Int defaultSize = Vector2Int.one;
|
public Vector2Int defaultSize = Vector2Int.one;
|
||||||
public Vector2Int minSize = Vector2Int.one;
|
public Vector2Int minSize = Vector2Int.one;
|
||||||
public Vector2Int maxSize = new(32, 32);
|
public Vector2Int maxSize = new(32, 32);
|
||||||
public bool resizable = true;
|
public bool resizable;
|
||||||
public bool cutsGround;
|
public bool cutsGround;
|
||||||
public bool blocksMovement;
|
public bool blocksMovement;
|
||||||
|
|
||||||
@ -38,9 +38,21 @@ namespace Editor.GardensStageEditor
|
|||||||
resizable = true
|
resizable = true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StagePaletteEntry Object(string id, string name, StageLogicalLayer layer, StageObjectKind kind, string typeId, Vector2Int defaultSize)
|
public static StagePaletteEntry Object(
|
||||||
|
string id,
|
||||||
|
string name,
|
||||||
|
StageLogicalLayer layer,
|
||||||
|
StageObjectKind kind,
|
||||||
|
string typeId,
|
||||||
|
Vector2Int defaultSize = default,
|
||||||
|
Vector2Int min = default,
|
||||||
|
Vector2Int max = default)
|
||||||
{
|
{
|
||||||
|
var kindDefault = StageObjectDefinition.GetDefaultSize(kind);
|
||||||
|
var kindMin = StageObjectDefinition.GetMinSize(kind);
|
||||||
|
var kindMax = StageObjectDefinition.GetMaxSize(kind);
|
||||||
|
|
||||||
return new StagePaletteEntry
|
return new StagePaletteEntry
|
||||||
{
|
{
|
||||||
id = id,
|
id = id,
|
||||||
@ -50,10 +62,10 @@ namespace Editor.GardensStageEditor
|
|||||||
objectKind = kind,
|
objectKind = kind,
|
||||||
typeId = typeId,
|
typeId = typeId,
|
||||||
prefabId = string.Empty,
|
prefabId = string.Empty,
|
||||||
defaultSize = defaultSize,
|
defaultSize = defaultSize == default ? kindDefault : defaultSize,
|
||||||
minSize = Vector2Int.one,
|
minSize = min == default ? kindMin : min,
|
||||||
maxSize = new Vector2Int(64, 64),
|
maxSize = max == default ? kindMax : max,
|
||||||
resizable = true
|
resizable = StageObjectDefinition.IsKindResizable(kind)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,25 +75,49 @@ namespace Editor.GardensStageEditor
|
|||||||
{
|
{
|
||||||
Structural("struct_ground", "Ground", StructuralCellKind.Ground),
|
Structural("struct_ground", "Ground", StructuralCellKind.Ground),
|
||||||
Structural("struct_wall", "Wall", StructuralCellKind.Wall),
|
Structural("struct_wall", "Wall", StructuralCellKind.Wall),
|
||||||
Structural("struct_hole", "Hole", StructuralCellKind.Hole),
|
|
||||||
Structural("struct_empty", "Erase Structural", StructuralCellKind.Empty)
|
Structural("struct_empty", "Erase Structural", StructuralCellKind.Empty)
|
||||||
};
|
};
|
||||||
|
|
||||||
var trap = Object("trap_falling_floor", "Falling Floor", StageLogicalLayer.Design, StageObjectKind.Trap, "falling_floor", Vector2Int.one);
|
var trapFallingFloor = Object("trap_falling_floor",
|
||||||
trap.cutsGround = true;
|
"Trap (Falling Floor)",
|
||||||
trap.blocksMovement = true;
|
StageLogicalLayer.Design,
|
||||||
entries.Add(trap);
|
StageObjectKind.Trap_FallingFloor,
|
||||||
|
"trap_falling_floor",
|
||||||
|
defaultSize: Vector2Int.one);
|
||||||
|
trapFallingFloor.cutsGround = true;
|
||||||
|
trapFallingFloor.blocksMovement = true;
|
||||||
|
entries.Add(trapFallingFloor);
|
||||||
|
|
||||||
var door = Object("door_standard", "Door", StageLogicalLayer.Design, StageObjectKind.Door, "door_standard", new Vector2Int(1, 2));
|
var doorHorizontal = Object("door_horizontal_standard",
|
||||||
door.blocksMovement = true;
|
"Door Horizontal",
|
||||||
entries.Add(door);
|
StageLogicalLayer.Design,
|
||||||
|
StageObjectKind.DoorHorizontal,
|
||||||
|
"door_horizontal_standard",
|
||||||
|
new Vector2Int(2, 1));
|
||||||
|
doorHorizontal.blocksMovement = true;
|
||||||
|
entries.Add(doorHorizontal);
|
||||||
|
|
||||||
|
var doorVertical = Object("door_vertical_standard",
|
||||||
|
"Door Vertical",
|
||||||
|
StageLogicalLayer.Design,
|
||||||
|
StageObjectKind.DoorVertical,
|
||||||
|
"door_vertical_standard",
|
||||||
|
new Vector2Int(1, 2));
|
||||||
|
doorVertical.blocksMovement = true;
|
||||||
|
entries.Add(doorVertical);
|
||||||
|
|
||||||
entries.Add(Object("interactable", "Interactable", StageLogicalLayer.Design, StageObjectKind.Interactable, "interactable", Vector2Int.one));
|
// entries.Add(Object("interactable", "Interactable", StageLogicalLayer.Design, StageObjectKind.Interactable, "interactable", Vector2Int.one));
|
||||||
entries.Add(Object("portal", "Portal", StageLogicalLayer.Design, StageObjectKind.Portal, "portal", new Vector2Int(2, 2)));
|
// entries.Add(Object("portal", "Portal", StageLogicalLayer.Design, StageObjectKind.Portal, "portal", new Vector2Int(2, 2)));
|
||||||
entries.Add(Object("camera_zone", "Camera Zone", StageLogicalLayer.Camera, StageObjectKind.CameraZone, "camera_zone", new Vector2Int(8, 5)));
|
entries.Add(Object("camera_boundary",
|
||||||
entries.Add(Object("spawn_player", "Player Spawn", StageLogicalLayer.Markers, StageObjectKind.Spawn, "player_spawn", Vector2Int.one));
|
"Camera Boundary",
|
||||||
entries.Add(Object("elevator", "Elevator", StageLogicalLayer.Markers, StageObjectKind.Elevator, "elevator", new Vector2Int(2, 2)));
|
StageLogicalLayer.Camera,
|
||||||
entries.Add(Object("marker", "Marker", StageLogicalLayer.Markers, StageObjectKind.Marker, "marker", Vector2Int.one));
|
StageObjectKind.CameraBoundary,
|
||||||
|
"camera_boundary",
|
||||||
|
defaultSize: new Vector2Int(8, 5),
|
||||||
|
min: new Vector2Int(8, 5)));
|
||||||
|
// entries.Add(Object("spawn_player", "Player Spawn", StageLogicalLayer.Markers, StageObjectKind.Spawn, "player_spawn", Vector2Int.one));
|
||||||
|
// entries.Add(Object("elevator", "Elevator", StageLogicalLayer.Markers, StageObjectKind.Elevator, "elevator", new Vector2Int(2, 2)));
|
||||||
|
// entries.Add(Object("marker", "Marker", StageLogicalLayer.Markers, StageObjectKind.Marker, "marker", Vector2Int.one));
|
||||||
|
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 0e7ed87816264200a12fcc4bf3584153
|
|
||||||
timeCreated: 1781500522
|
|
||||||
Loading…
x
Reference in New Issue
Block a user