Files
my-project/Assets/charecter-controller/scripts/PlayerBuilderTool.cs
2026-04-16 01:51:46 +05:00

365 lines
18 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine.UI;
using UnityEngine.EventSystems;
/// <summary>
/// Инструмент создания полного игрока с опциональным инвентарём.
/// Tools > Player Builder
/// </summary>
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<PlayerBuilderTool>("Конструктор Персонажа");
}
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<CharacterController>();
cc.height = height;
cc.radius = radius;
cc.center = new Vector3(0, centerY, 0);
// Скрипт персонажа
var character = player.AddComponent<charecter>();
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<Animator>();
if (modelAnimator != null && modelAnimator.avatar != null)
{
var animator = player.AddComponent<Animator>();
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<Camera>();
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<PlayerInput>();
if (inputActions != null) pi.actions = inputActions;
// HandEquipSystem
if (resolvedHand != null)
{
var handEquip = player.AddComponent<HandEquipSystem>();
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<Collider>());
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<EventSystem>() == null)
{
var esGO = new GameObject("EventSystem");
esGO.AddComponent<EventSystem>();
esGO.AddComponent<StandaloneInputModule>();
}
// InventoryEvents
new GameObject("InventoryEvents").AddComponent<InventoryEvents>();
// InventoryManager
var imGO = new GameObject("InventoryManager");
var im = imGO.AddComponent<InventoryManager>();
im.inventorySize = inventorySlots;
// InventoryCanvas — дочерний объект Player
var canvasGO = new GameObject("InventoryCanvas");
canvasGO.transform.SetParent(player.transform, false);
var canvas = canvasGO.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.sortingOrder = 100;
canvasGO.AddComponent<UnityEngine.UI.GraphicRaycaster>();
var uiBuilder = canvasGO.AddComponent<InventoryUIBuilder>();
uiBuilder.inventoryRows = inventorySlots / 9;
uiBuilder.hotbarSlots = hotbarSlots;
// Строим UI сразу до запуска игры
uiBuilder.BuildInventoryUI();
// Назначаем ссылки в InventoryManager
im.slots = uiBuilder.GetInventorySlots();
im.hotbarSlots = uiBuilder.GetHotbarSlots();
im.inventoryPanel = uiBuilder.inventoryPanelRect != null ? uiBuilder.inventoryPanelRect.gameObject : null;
im.inventoryCanvas = canvas;
if (im.inventoryPanel != null)
im.inventoryPanel.SetActive(false);
Debug.Log("[PlayerBuilder] Инвентарь создан и назначен.");
}
// ── Утилиты ───────────────────────────────────────────────────────────────
Transform FindChildBone(Transform parent, string boneName)
{
if (parent == null) return null;
if (parent.name.ToLower().Contains(boneName.ToLower())) return parent;
foreach (Transform child in parent)
{
var result = FindChildBone(child, boneName);
if (result != null) return result;
}
return null;
}
}