Files
TheDeclineOfWarriors/Assets/Editor/WorldGeneratorEditorWindow.cs
T
2026-03-29 02:26:31 +07:00

316 lines
12 KiB
C#

using UnityEditor;
using UnityEngine;
namespace InfiniteWorld.Editor
{
public class WorldGeneratorEditorWindow : EditorWindow
{
private enum BrushMode
{
Erase,
Wall,
Environment
}
private WorldAutotileProfile profile;
private ChunkTemplate template;
private SerializedObject serializedProfile;
private Vector2 profileScroll;
private Vector2 chunkScroll;
private BrushMode brushMode = BrushMode.Wall;
private bool isPainting;
private int pendingWidth = 16;
private int pendingHeight = 16;
[MenuItem("Tools/Infinite World/World Builder")]
public static void Open()
{
GetWindow<WorldGeneratorEditorWindow>("World Builder");
}
private void OnGUI()
{
DrawToolbar();
EditorGUILayout.Space(6f);
EditorGUILayout.BeginHorizontal();
DrawChunkEditor();
GUILayout.Space(8f);
DrawProfileEditor();
EditorGUILayout.EndHorizontal();
}
private void DrawToolbar()
{
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
template = (ChunkTemplate)EditorGUILayout.ObjectField(template, typeof(ChunkTemplate), false, GUILayout.Width(position.width * 0.38f));
profile = (WorldAutotileProfile)EditorGUILayout.ObjectField(profile, typeof(WorldAutotileProfile), false, GUILayout.Width(position.width * 0.38f));
if (GUILayout.Button("New Chunk", EditorStyles.toolbarButton, GUILayout.Width(80f)))
{
CreateChunkAsset();
}
if (GUILayout.Button("New Profile", EditorStyles.toolbarButton, GUILayout.Width(80f)))
{
CreateProfileAsset();
}
EditorGUILayout.EndHorizontal();
}
private void DrawChunkEditor()
{
EditorGUILayout.BeginVertical(GUILayout.Width(position.width * 0.52f));
EditorGUILayout.LabelField("Chunk Painter", EditorStyles.boldLabel);
if (template == null)
{
EditorGUILayout.HelpBox("Create or assign a ChunkTemplate asset. Paint walls and environment directly on the chunk grid and mark exits on each side.", MessageType.Info);
EditorGUILayout.EndVertical();
return;
}
template.EnsureCellData();
pendingWidth = EditorGUILayout.IntField("Width", pendingWidth == 0 ? template.width : pendingWidth);
pendingHeight = EditorGUILayout.IntField("Height", pendingHeight == 0 ? template.height : pendingHeight);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Resize"))
{
Undo.RecordObject(template, "Resize Chunk Template");
template.Resize(pendingWidth, pendingHeight);
EditorUtility.SetDirty(template);
}
if (GUILayout.Button("Clear"))
{
Undo.RecordObject(template, "Clear Chunk Template");
template.Clear();
EditorUtility.SetDirty(template);
}
if (GUILayout.Button("Border From Exits"))
{
Undo.RecordObject(template, "Apply Border Walls");
template.ApplyBorderWallsFromExits(3);
EditorUtility.SetDirty(template);
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(4f);
EditorGUILayout.LabelField("Brush", EditorStyles.miniBoldLabel);
brushMode = (BrushMode)GUILayout.Toolbar((int)brushMode, new[] { "Erase", "Wall", "Environment" });
EditorGUILayout.Space(4f);
EditorGUILayout.LabelField("Exits", EditorStyles.miniBoldLabel);
EditorGUILayout.BeginHorizontal();
DrawExitToggle("Top", ChunkExit.Top);
DrawExitToggle("Right", ChunkExit.Right);
DrawExitToggle("Bottom", ChunkExit.Bottom);
DrawExitToggle("Left", ChunkExit.Left);
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(8f);
chunkScroll = EditorGUILayout.BeginScrollView(chunkScroll, GUILayout.ExpandHeight(true));
Rect gridRect = GUILayoutUtility.GetRect(template.width * 24f, template.height * 24f, GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(false));
DrawGrid(gridRect);
EditorGUILayout.EndScrollView();
EditorGUILayout.HelpBox("Gray = floor, brown = wall, green = environment. Drag with the selected brush to paint the chunk visually.", MessageType.None);
EditorGUILayout.EndVertical();
}
private void DrawProfileEditor()
{
EditorGUILayout.BeginVertical(GUILayout.ExpandWidth(true));
EditorGUILayout.LabelField("Tile Profile", EditorStyles.boldLabel);
if (profile == null)
{
EditorGUILayout.HelpBox("Assign a WorldAutotileProfile to map real tiles onto ground, walls, and environment.", MessageType.Info);
EditorGUILayout.EndVertical();
return;
}
if (serializedProfile == null || serializedProfile.targetObject != profile)
{
serializedProfile = new SerializedObject(profile);
}
serializedProfile.Update();
profileScroll = EditorGUILayout.BeginScrollView(profileScroll);
EditorGUILayout.PropertyField(serializedProfile.FindProperty("baseGroundTile"));
EditorGUILayout.Space(6f);
EditorGUILayout.LabelField("Wall Autotile", EditorStyles.miniBoldLabel);
SerializedProperty walls = serializedProfile.FindProperty("wallTiles");
DrawWallGrid(walls);
EditorGUILayout.Space(6f);
EditorGUILayout.PropertyField(serializedProfile.FindProperty("environmentTiles"), true);
EditorGUILayout.EndScrollView();
serializedProfile.ApplyModifiedProperties();
if (GUI.changed)
{
EditorUtility.SetDirty(profile);
}
EditorGUILayout.EndVertical();
}
private void DrawWallGrid(SerializedProperty walls)
{
DrawTriple(walls, "outerTopLeft", "top", "outerTopRight");
DrawTriple(walls, "left", "center", "right");
DrawTriple(walls, "outerBottomLeft", "bottom", "outerBottomRight");
DrawDouble(walls, "innerTopLeft", "innerTopRight");
DrawDouble(walls, "innerBottomLeft", "innerBottomRight");
}
private void DrawTriple(SerializedProperty root, string a, string b, string c)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(root.FindPropertyRelative(a), GUIContent.none);
EditorGUILayout.PropertyField(root.FindPropertyRelative(b), GUIContent.none);
EditorGUILayout.PropertyField(root.FindPropertyRelative(c), GUIContent.none);
EditorGUILayout.EndHorizontal();
}
private void DrawDouble(SerializedProperty root, string a, string b)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(root.FindPropertyRelative(a), GUIContent.none);
EditorGUILayout.PropertyField(root.FindPropertyRelative(b), GUIContent.none);
EditorGUILayout.EndHorizontal();
}
private void DrawExitToggle(string label, ChunkExit exit)
{
bool current = template.GetExit(exit);
bool next = GUILayout.Toggle(current, label, "Button");
if (next == current)
{
return;
}
Undo.RecordObject(template, "Toggle Chunk Exit");
switch (exit)
{
case ChunkExit.Top:
template.exitTop = next;
break;
case ChunkExit.Right:
template.exitRight = next;
break;
case ChunkExit.Bottom:
template.exitBottom = next;
break;
case ChunkExit.Left:
template.exitLeft = next;
break;
}
EditorUtility.SetDirty(template);
}
private void DrawGrid(Rect rect)
{
const float cellSize = 24f;
Event evt = Event.current;
for (int y = 0; y < template.height; y++)
{
for (int x = 0; x < template.width; x++)
{
Rect cellRect = new Rect(rect.x + x * cellSize, rect.y + (template.height - 1 - y) * cellSize, cellSize - 1f, cellSize - 1f);
EditorGUI.DrawRect(cellRect, GetCellColor(x, y));
if ((evt.type == EventType.MouseDown || evt.type == EventType.MouseDrag) && cellRect.Contains(evt.mousePosition) && evt.button == 0)
{
isPainting = true;
PaintCell(x, y);
evt.Use();
}
}
}
if (evt.type == EventType.MouseUp)
{
isPainting = false;
}
if (isPainting)
{
Repaint();
}
}
private Color GetCellColor(int x, int y)
{
if (template.GetWall(x, y))
{
return new Color(0.52f, 0.36f, 0.22f, 1f);
}
if (template.GetEnvironment(x, y))
{
return new Color(0.2f, 0.54f, 0.25f, 1f);
}
return new Color(0.4f, 0.4f, 0.4f, 1f);
}
private void PaintCell(int x, int y)
{
Undo.RecordObject(template, "Paint Chunk Template");
switch (brushMode)
{
case BrushMode.Erase:
template.SetWall(x, y, false);
template.SetEnvironment(x, y, false);
break;
case BrushMode.Wall:
template.SetWall(x, y, true);
break;
case BrushMode.Environment:
template.SetEnvironment(x, y, true);
break;
}
EditorUtility.SetDirty(template);
}
private void CreateChunkAsset()
{
string path = EditorUtility.SaveFilePanelInProject("Create Chunk Template", "ChunkTemplate", "asset", "Choose where to save the chunk template.");
if (string.IsNullOrEmpty(path))
{
return;
}
ChunkTemplate asset = CreateInstance<ChunkTemplate>();
asset.Resize(16, 16);
asset.ApplyBorderWallsFromExits(3);
AssetDatabase.CreateAsset(asset, path);
AssetDatabase.SaveAssets();
template = asset;
pendingWidth = asset.width;
pendingHeight = asset.height;
Selection.activeObject = asset;
}
private void CreateProfileAsset()
{
string path = EditorUtility.SaveFilePanelInProject("Create World Profile", "WorldAutotileProfile", "asset", "Choose where to save the tile profile.");
if (string.IsNullOrEmpty(path))
{
return;
}
WorldAutotileProfile asset = CreateInstance<WorldAutotileProfile>();
AssetDatabase.CreateAsset(asset, path);
AssetDatabase.SaveAssets();
profile = asset;
serializedProfile = new SerializedObject(profile);
Selection.activeObject = asset;
}
}
}