using UnityEngine;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine.UI;
using UnityEngine.EventSystems;
///
/// Инструмент создания полного игрока с опциональным инвентарём.
/// Tools > Player Builder
///
public class PlayerBuilderTool : EditorWindow
{
// ── Модель ────────────────────────────────────────────────────────────────
private GameObject characterModel;
private Transform headBone;
private Transform handBone;
// ── Параметры движения ────────────────────────────────────────────────────
private float walkSpeed = 5f;
private float runSpeed = 9f;
private float crouchSpeed = 2.5f;
private float jumpHeight = 1.5f;
private float gravity = -9.81f;
// ── Коллайдер ─────────────────────────────────────────────────────────────
private float height = 1.8f;
private float radius = 0.5f;
private float centerY = 0.9f;
// ── Камера ────────────────────────────────────────────────────────────────
private bool addCamera = true;
private float cameraHeight = 1.7f;
// ── Инвентарь ─────────────────────────────────────────────────────────────
private bool includeInventory = true;
private int inventorySlots = 36;
private int hotbarSlots = 9;
// ── Input Actions ─────────────────────────────────────────────────────────
private UnityEngine.InputSystem.InputActionAsset inputActions;
// ── Анимации ──────────────────────────────────────────────────────────────
private AnimationClip idleClip;
private AnimationClip walkClip;
private AnimationClip runClip;
private AnimationClip jumpClip;
private AnimationClip crouchIdleClip;
private AnimationClip crouchWalkClip;
// ── Scroll ────────────────────────────────────────────────────────────────
private Vector2 _scroll;
[MenuItem("Tools/Player Builder")]
public static void ShowWindow()
{
GetWindow("Конструктор Персонажа");
}
void OnGUI()
{
_scroll = EditorGUILayout.BeginScrollView(_scroll);
GUILayout.Label("Конструктор Персонажа", EditorStyles.boldLabel);
// ── Секция модели ─────────────────────────────────────────────────────
GUILayout.Space(6);
GUILayout.Label("Модель", EditorStyles.miniBoldLabel);
characterModel = (GameObject)EditorGUILayout.ObjectField("Модель персонажа", characterModel, typeof(GameObject), false);
headBone = (Transform)EditorGUILayout.ObjectField("Кость головы (Head)", headBone, typeof(Transform), true);
handBone = (Transform)EditorGUILayout.ObjectField("Кость руки (RightHand)", handBone, typeof(Transform), true);
if (characterModel != null && GUILayout.Button("Автопоиск костей Head/Hand"))
AutoFindBones();
// ── Движение ──────────────────────────────────────────────────────────
GUILayout.Space(6);
GUILayout.Label("Движение", EditorStyles.miniBoldLabel);
walkSpeed = EditorGUILayout.FloatField("Скорость ходьбы", walkSpeed);
runSpeed = EditorGUILayout.FloatField("Скорость бега", runSpeed);
crouchSpeed = EditorGUILayout.FloatField("Скорость присядки", crouchSpeed);
jumpHeight = EditorGUILayout.FloatField("Высота прыжка", jumpHeight);
gravity = EditorGUILayout.FloatField("Гравитация", gravity);
// ── Коллайдер ─────────────────────────────────────────────────────────
GUILayout.Space(6);
GUILayout.Label("Коллайдер CharacterController", EditorStyles.miniBoldLabel);
height = EditorGUILayout.FloatField("Высота", height);
radius = EditorGUILayout.FloatField("Радиус", radius);
centerY = EditorGUILayout.FloatField("Центр Y", centerY);
// ── Камера ────────────────────────────────────────────────────────────
GUILayout.Space(6);
GUILayout.Label("Камера", EditorStyles.miniBoldLabel);
addCamera = EditorGUILayout.Toggle("Добавить камеру", addCamera);
if (addCamera)
cameraHeight = EditorGUILayout.Slider("Высота камеры", cameraHeight, 1f, 2f);
// ── Input Actions ─────────────────────────────────────────────────────
GUILayout.Space(6);
GUILayout.Label("Input System", EditorStyles.miniBoldLabel);
inputActions = (UnityEngine.InputSystem.InputActionAsset)EditorGUILayout.ObjectField(
"PlayerControls.inputactions",
inputActions,
typeof(UnityEngine.InputSystem.InputActionAsset),
false);
if (inputActions == null)
EditorGUILayout.HelpBox("Назначьте Assets/Input/PlayerControls.inputactions", MessageType.Warning);
// ── Инвентарь ─────────────────────────────────────────────────────────
GUILayout.Space(6);
GUILayout.Label("Инвентарь", EditorStyles.miniBoldLabel);
includeInventory = EditorGUILayout.Toggle("Включить инвентарь", includeInventory);
if (includeInventory)
{
inventorySlots = EditorGUILayout.IntField("Слотов инвентаря", inventorySlots);
hotbarSlots = EditorGUILayout.IntField("Слотов хотбара", hotbarSlots);
}
// ── Анимации ──────────────────────────────────────────────────────────
GUILayout.Space(6);
GUILayout.Label("Анимации (опционально)", EditorStyles.miniBoldLabel);
idleClip = (AnimationClip)EditorGUILayout.ObjectField("Idle", idleClip, typeof(AnimationClip), false);
walkClip = (AnimationClip)EditorGUILayout.ObjectField("Walk", walkClip, typeof(AnimationClip), false);
runClip = (AnimationClip)EditorGUILayout.ObjectField("Run", runClip, typeof(AnimationClip), false);
jumpClip = (AnimationClip)EditorGUILayout.ObjectField("Jump", jumpClip, typeof(AnimationClip), false);
crouchIdleClip = (AnimationClip)EditorGUILayout.ObjectField("Crouch Idle", crouchIdleClip, typeof(AnimationClip), false);
crouchWalkClip = (AnimationClip)EditorGUILayout.ObjectField("Crouch Walk", crouchWalkClip, typeof(AnimationClip), false);
GUILayout.Space(20);
// ── Кнопки ────────────────────────────────────────────────────────────
GUI.color = Color.green;
if (GUILayout.Button("Создать игрока" + (includeInventory ? " + Инвентарь" : "")))
BuildPlayer("Player");
GUI.color = Color.yellow;
if (GUILayout.Button("Создать тестового игрока (капсула)"))
BuildTestPlayer();
GUI.color = Color.cyan;
if (GUILayout.Button("Сохранить как префаб"))
SaveAsPrefab();
GUI.color = Color.white;
GUILayout.Space(10);
EditorGUILayout.HelpBox(
"1. Назначьте модель персонажа (опционально)\n" +
"2. Укажите кость головы и кость руки\n" +
"3. Назначьте PlayerControls.inputactions\n" +
"4. Нажмите 'Создать игрока'",
MessageType.Info);
EditorGUILayout.EndScrollView();
}
// ── Автопоиск костей ─────────────────────────────────────────────────────
void AutoFindBones()
{
if (characterModel == null) return;
headBone = FindChildBone(characterModel.transform, "head") ??
FindChildBone(characterModel.transform, "Head");
handBone = FindChildBone(characterModel.transform, "hand_r") ??
FindChildBone(characterModel.transform, "RightHand") ??
FindChildBone(characterModel.transform, "Hand_R") ??
FindChildBone(characterModel.transform, "hand");
Debug.Log($"[PlayerBuilder] Head: {headBone?.name ?? "не найдена"}, Hand: {handBone?.name ?? "не найдена"}");
}
// ── Создание игрока ───────────────────────────────────────────────────────
GameObject BuildPlayerGO(string playerName)
{
var player = new GameObject(playerName);
// CharacterController
var cc = player.AddComponent();
cc.height = height;
cc.radius = radius;
cc.center = new Vector3(0, centerY, 0);
// Скрипт персонажа
var character = player.AddComponent();
character.walkSpeed = walkSpeed;
character.runSpeed = runSpeed;
character.crouchSpeed = crouchSpeed;
character.jumpHeight = jumpHeight;
character.gravity = gravity;
// Модель
Transform resolvedHead = headBone;
Transform resolvedHand = handBone;
if (characterModel != null)
{
var model = (GameObject)PrefabUtility.InstantiatePrefab(characterModel, player.transform);
model.transform.localPosition = Vector3.zero;
model.transform.localRotation = Quaternion.identity;
if (resolvedHead == null)
resolvedHead = FindChildBone(model.transform, "head") ?? FindChildBone(model.transform, "Head");
if (resolvedHand == null)
resolvedHand = FindChildBone(model.transform, "hand_r") ?? FindChildBone(model.transform, "RightHand");
var modelAnimator = model.GetComponent();
if (modelAnimator != null && modelAnimator.avatar != null)
{
var animator = player.AddComponent();
animator.avatar = modelAnimator.avatar;
}
}
// Кость головы (или создаём точку)
if (resolvedHead == null)
{
var headObj = new GameObject("HeadPoint");
headObj.transform.SetParent(player.transform);
headObj.transform.localPosition = new Vector3(0, height - 0.2f, 0);
resolvedHead = headObj.transform;
}
character.head = resolvedHead;
// Камера
if (addCamera)
{
var camObj = new GameObject("PlayerCamera");
camObj.transform.SetParent(resolvedHead);
camObj.transform.localPosition = new Vector3(0, 0.1f, 0);
var cam = camObj.AddComponent();
cam.tag = "MainCamera";
cam.nearClipPlane = 0.05f;
var oldCam = GameObject.FindWithTag("MainCamera");
if (oldCam != null && oldCam != camObj)
DestroyImmediate(oldCam);
}
// PlayerInput
var piGO = new GameObject("PlayerInput");
var pi = piGO.AddComponent();
if (inputActions != null) pi.actions = inputActions;
// HandEquipSystem
if (resolvedHand != null)
{
var handEquip = player.AddComponent();
handEquip.handBone = resolvedHand;
}
else
{
Debug.LogWarning("[PlayerBuilder] Кость руки не найдена — HandEquipSystem не добавлен. Назначьте вручную.");
}
player.tag = "Player";
player.transform.position = new Vector3(0, 2, -5);
// Инвентарь
if (includeInventory)
SetupInventoryForPlayer(player);
return player;
}
void BuildPlayer(string name)
{
var player = BuildPlayerGO(name);
Selection.activeGameObject = player;
Debug.Log($"[PlayerBuilder] Создан: {name}");
}
void BuildTestPlayer()
{
var savedModel = characterModel;
characterModel = null;
var player = BuildPlayerGO("TestPlayer");
characterModel = savedModel;
// Визуальная капсула
var body = GameObject.CreatePrimitive(PrimitiveType.Capsule);
body.name = "Body";
body.transform.SetParent(player.transform, false);
body.transform.localPosition = new Vector3(0, centerY, 0);
body.transform.localScale = new Vector3(radius * 2, height / 2f, radius * 2);
DestroyImmediate(body.GetComponent());
Selection.activeGameObject = player;
Debug.Log("[PlayerBuilder] Создан тестовый игрок (капсула)");
}
void SaveAsPrefab()
{
string path = EditorUtility.SaveFilePanelInProject("Сохранить префаб", "Player", "prefab", "Выберите место");
if (string.IsNullOrEmpty(path)) return;
var temp = BuildPlayerGO("PlayerTemp");
var prefab = PrefabUtility.SaveAsPrefabAsset(temp, path);
DestroyImmediate(temp);
Selection.activeObject = prefab;
Debug.Log($"[PlayerBuilder] Сохранено: {path}");
}
// ── Создание инвентаря ────────────────────────────────────────────────────
void SetupInventoryForPlayer(GameObject player)
{
// EventSystem
if (FindObjectOfType() == null)
{
var esGO = new GameObject("EventSystem");
esGO.AddComponent();
esGO.AddComponent();
}
// InventoryEvents
new GameObject("InventoryEvents").AddComponent();
// InventoryManager
var imGO = new GameObject("InventoryManager");
var im = imGO.AddComponent();
im.inventorySize = inventorySlots;
// InventoryCanvas — дочерний объект Player
var canvasGO = new GameObject("InventoryCanvas");
canvasGO.transform.SetParent(player.transform, false);
var canvas = canvasGO.AddComponent