Files
the-island/unity-client/Assets/Editor/AgentPrefabCreator.cs
empty 64ed46215f feat: add Unity 6 client with 2.5D visual system
Unity client features:
- WebSocket connection via NativeWebSocket
- 2.5D agent visuals with programmatic placeholder sprites
- Billboard system for sprites and UI elements
- Floating UI panels (name, HP, energy bars)
- Speech bubble system with pop-in animation
- RTS-style camera controller (WASD + scroll zoom)
- Editor tools for prefab creation and scene setup

Scripts:
- NetworkManager: WebSocket singleton
- GameManager: Agent spawning and event handling
- AgentVisual: 2.5D sprite and UI creation
- Billboard: Camera-facing behavior
- SpeechBubble: Animated dialogue display
- CameraController: RTS camera with UI input detection
- UIManager: HUD and command input

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 12:17:04 +08:00

276 lines
10 KiB
C#

#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
using TheIsland.Visual;
namespace TheIsland.Editor
{
/// <summary>
/// Editor utility for creating and updating Agent prefabs.
/// Access via menu: The Island > Create Agent Prefab
/// </summary>
public class AgentPrefabCreator : EditorWindow
{
private Sprite characterSprite;
private string prefabName = "AgentPrefab";
private Color agentColor = new Color(0.3f, 0.6f, 0.9f);
private bool createWithPlaceholder = true;
[MenuItem("The Island/Create Agent Prefab")]
public static void ShowWindow()
{
var window = GetWindow<AgentPrefabCreator>("Agent Prefab Creator");
window.minSize = new Vector2(350, 300);
}
[MenuItem("The Island/Quick Create Agent Prefab")]
public static void QuickCreatePrefab()
{
CreateAgentPrefab("AgentPrefab", null, true);
}
private void OnGUI()
{
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("Agent Prefab Creator", EditorStyles.boldLabel);
EditorGUILayout.Space(5);
EditorGUILayout.HelpBox(
"Creates a new Agent prefab with:\n" +
"- 2D Sprite with Billboard\n" +
"- Floating UI (Name, HP, Energy bars)\n" +
"- Speech Bubble system\n" +
"- Click interaction",
MessageType.Info);
EditorGUILayout.Space(10);
// Prefab name
prefabName = EditorGUILayout.TextField("Prefab Name", prefabName);
EditorGUILayout.Space(5);
// Sprite selection
createWithPlaceholder = EditorGUILayout.Toggle("Use Placeholder Sprite", createWithPlaceholder);
if (!createWithPlaceholder)
{
characterSprite = (Sprite)EditorGUILayout.ObjectField(
"Character Sprite",
characterSprite,
typeof(Sprite),
false
);
}
else
{
agentColor = EditorGUILayout.ColorField("Placeholder Color", agentColor);
}
EditorGUILayout.Space(20);
// Create button
if (GUILayout.Button("Create Prefab", GUILayout.Height(35)))
{
CreateAgentPrefab(prefabName, characterSprite, createWithPlaceholder);
}
EditorGUILayout.Space(10);
EditorGUILayout.HelpBox(
"Prefab will be saved to: Assets/Prefabs/\n\n" +
"After creation:\n" +
"1. Assign the prefab to GameManager.agentPrefab\n" +
"2. (Optional) Replace placeholder sprite later",
MessageType.None);
}
private static void CreateAgentPrefab(string name, Sprite sprite, bool usePlaceholder)
{
// Ensure Prefabs folder exists
string prefabFolder = "Assets/Prefabs";
if (!AssetDatabase.IsValidFolder(prefabFolder))
{
AssetDatabase.CreateFolder("Assets", "Prefabs");
}
// Create root GameObject
GameObject agentRoot = new GameObject(name);
// Add AgentVisual component - it will create all visuals automatically
AgentVisual visual = agentRoot.AddComponent<AgentVisual>();
// If sprite is provided, we need to set it via SerializedObject
if (!usePlaceholder && sprite != null)
{
SerializedObject so = new SerializedObject(visual);
SerializedProperty spriteProp = so.FindProperty("characterSprite");
if (spriteProp != null)
{
spriteProp.objectReferenceValue = sprite;
so.ApplyModifiedPropertiesWithoutUndo();
}
}
// Save as prefab
string prefabPath = $"{prefabFolder}/{name}.prefab";
// Check if prefab already exists
GameObject existingPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (existingPrefab != null)
{
if (!EditorUtility.DisplayDialog("Prefab Exists",
$"A prefab named '{name}' already exists. Overwrite?",
"Overwrite", "Cancel"))
{
DestroyImmediate(agentRoot);
return;
}
}
// Save prefab
GameObject prefab = PrefabUtility.SaveAsPrefabAsset(agentRoot, prefabPath);
// Cleanup scene object
DestroyImmediate(agentRoot);
// Select the new prefab
Selection.activeObject = prefab;
EditorGUIUtility.PingObject(prefab);
Debug.Log($"[AgentPrefabCreator] Created prefab at: {prefabPath}");
EditorUtility.DisplayDialog("Success",
$"Agent prefab created at:\n{prefabPath}\n\n" +
"Don't forget to assign it to GameManager!",
"OK");
}
}
/// <summary>
/// Additional menu items for quick actions.
/// </summary>
public static class IslandEditorMenus
{
[MenuItem("The Island/Setup Scene")]
public static void SetupScene()
{
// Check for Camera
Camera mainCam = Camera.main;
if (mainCam == null)
{
GameObject camObj = new GameObject("Main Camera");
mainCam = camObj.AddComponent<Camera>();
camObj.tag = "MainCamera";
}
// Add CameraController if not present
if (mainCam.GetComponent<TheIsland.Core.CameraController>() == null)
{
mainCam.gameObject.AddComponent<TheIsland.Core.CameraController>();
Debug.Log("[Setup] Added CameraController to Main Camera");
}
// Set camera position and rotation for isometric view
mainCam.transform.position = new Vector3(0, 15, -10);
mainCam.transform.rotation = Quaternion.Euler(50, 0, 0);
mainCam.backgroundColor = new Color(0.4f, 0.6f, 0.9f); // Sky blue
mainCam.clearFlags = CameraClearFlags.SolidColor;
// Create ground plane if not exists
if (GameObject.Find("Ground") == null)
{
GameObject ground = GameObject.CreatePrimitive(PrimitiveType.Plane);
ground.name = "Ground";
ground.transform.position = Vector3.zero;
ground.transform.localScale = new Vector3(5, 1, 5);
// Set ground material color
var renderer = ground.GetComponent<Renderer>();
if (renderer != null)
{
Material mat = new Material(Shader.Find("Standard"));
mat.color = new Color(0.3f, 0.5f, 0.3f); // Grass green
renderer.material = mat;
}
Debug.Log("[Setup] Created Ground plane");
}
// Create Agent Container if not exists
if (GameObject.Find("Agents") == null)
{
GameObject container = new GameObject("Agents");
container.transform.position = Vector3.zero;
Debug.Log("[Setup] Created Agents container");
}
Debug.Log("[Setup] Scene setup complete!");
EditorUtility.DisplayDialog("Scene Setup",
"Scene has been configured with:\n" +
"- Camera with CameraController\n" +
"- Sky blue background\n" +
"- Ground plane\n" +
"- Agents container",
"OK");
}
[MenuItem("The Island/Setup Managers")]
public static void SetupManagers()
{
// Create NetworkManager
if (Object.FindFirstObjectByType<TheIsland.Network.NetworkManager>() == null)
{
GameObject netMgr = new GameObject("NetworkManager");
netMgr.AddComponent<TheIsland.Network.NetworkManager>();
Debug.Log("[Setup] Created NetworkManager");
}
// Create GameManager
if (Object.FindFirstObjectByType<TheIsland.Core.GameManager>() == null)
{
GameObject gameMgr = new GameObject("GameManager");
gameMgr.AddComponent<TheIsland.Core.GameManager>();
Debug.Log("[Setup] Created GameManager");
}
// Create UIManager
if (Object.FindFirstObjectByType<TheIsland.UI.UIManager>() == null)
{
GameObject uiMgr = new GameObject("UIManager");
uiMgr.AddComponent<TheIsland.UI.UIManager>();
Debug.Log("[Setup] Created UIManager");
}
Debug.Log("[Setup] Managers setup complete!");
EditorUtility.DisplayDialog("Managers Setup",
"Created/verified:\n" +
"- NetworkManager\n" +
"- GameManager\n" +
"- UIManager\n\n" +
"Don't forget to assign AgentPrefab to GameManager!",
"OK");
}
[MenuItem("The Island/Documentation")]
public static void OpenDocs()
{
EditorUtility.DisplayDialog("The Island - Quick Guide",
"CONTROLS:\n" +
"- WASD: Move camera\n" +
"- Mouse Scroll: Zoom in/out\n" +
"- Click Agent: Feed agent\n\n" +
"COMMANDS (in input field):\n" +
"- feed Jack/Luna/Bob: Feed agent\n" +
"- check: Check status\n" +
"- reset: Reset all agents\n\n" +
"SETUP ORDER:\n" +
"1. The Island > Setup Scene\n" +
"2. The Island > Setup Managers\n" +
"3. The Island > Create Agent Prefab\n" +
"4. Assign prefab to GameManager",
"OK");
}
}
}
#endif