using System.Collections.Generic; using UnityEngine; using UnityEditor; using UnityEditor.SceneManagement; public static class CharacterSetup { private const string FbxPath = "Assets/mainhero/balthazar-rigged-animated/source/Balthazar walking/Balthazar walking.Fbx"; private const string ControllerPath = "Assets/mainhero/PlayerAnimator.controller"; private const string MaterialsFolder = "Assets/mainhero/Materials"; [MenuItem("Tools/Setup Main Hero On Player")] public static string ExecuteSetup() { var fbxPrefab = AssetDatabase.LoadAssetAtPath(FbxPath); if (fbxPrefab == null) { Debug.LogError($"[CharacterSetup] FBX model not found at {FbxPath}"); return "FBX model not found!"; } var controller = AssetDatabase.LoadAssetAtPath(ControllerPath); if (controller == null) { Debug.LogError($"[CharacterSetup] AnimatorController not found at {ControllerPath}"); return "AnimatorController not found!"; } // 1. Находим игрока на сцене (Capsule или Player) GameObject player = GameObject.Find("Capsule"); if (player == null) { player = GameObject.Find("Player"); } if (player == null) { // Попробуем найти через PlayerMovement var pmFound = Object.FindAnyObjectByType(); if (pmFound != null) player = pmFound.gameObject; } if (player == null) { return "Player / Capsule GameObject not found in scene!"; } Undo.RegisterFullObjectHierarchyUndo(player, "Setup Main Hero Character"); // 2. Переименовываем в Player для порядка player.name = "Player"; // 3. Отключаем визуальный меш капсулы var meshRenderer = player.GetComponent(); if (meshRenderer != null) { meshRenderer.enabled = false; } // Отключаем лишний CapsuleCollider, так как CharacterController сам является коллайдером var capCol = player.GetComponent(); if (capCol != null) { capCol.enabled = false; } // 4. Проверяем наличие дочерней модели, удаляем старую при повторном запуске Transform oldModel = player.transform.Find("Model"); if (oldModel != null) { Object.DestroyImmediate(oldModel.gameObject); } // 5. Создаем экземпляр модели из FBX как дочерний объект GameObject modelGo = (GameObject)PrefabUtility.InstantiatePrefab(fbxPrefab, player.transform); modelGo.name = "Model"; modelGo.transform.localPosition = new Vector3(0f, -1f, 0f); // Смещаем вниз к основанию CharacterController modelGo.transform.localRotation = Quaternion.identity; modelGo.transform.localScale = Vector3.one; // 6. Подгружаем материалы из папки Materials var matDict = new Dictionary(); string[] matGuids = AssetDatabase.FindAssets("t:Material", new[] { MaterialsFolder }); foreach (var guid in matGuids) { string p = AssetDatabase.GUIDToAssetPath(guid); var m = AssetDatabase.LoadAssetAtPath(p); if (m != null) { matDict[m.name] = m; } } // Назначаем материалы всем рендерам модели var renderers = modelGo.GetComponentsInChildren(true); foreach (var r in renderers) { var mats = r.sharedMaterials; for (int i = 0; i < mats.Length; i++) { if (mats[i] != null && matDict.TryGetValue(mats[i].name, out var foundMat)) { mats[i] = foundMat; } } r.sharedMaterials = mats; } // 7. Настраиваем Animator var animator = modelGo.GetComponent(); if (animator == null) { animator = modelGo.AddComponent(); } // Подгружаем Humanoid Avatar модели var subAssets = AssetDatabase.LoadAllAssetsAtPath(FbxPath); foreach (var a in subAssets) { if (a is Avatar av) { animator.avatar = av; break; } } animator.runtimeAnimatorController = controller; animator.applyRootMotion = false; // Движением управляет CharacterController // 8. Привязываем аниматор к PlayerMovement, PlayerAttack и PlayerGun var pm = player.GetComponent(); if (pm != null) { pm.animator = animator; EditorUtility.SetDirty(pm); } var pa = player.GetComponent(); if (pa != null) { pa.animator = animator; EditorUtility.SetDirty(pa); } var pg = player.GetComponent(); if (pg != null) { pg.playerAnimator = animator; EditorUtility.SetDirty(pg); } var echo = player.GetComponent(); if (echo != null) { echo.playerAnimator = animator; echo.FindFootBones(); EditorUtility.SetDirty(echo); } // 9. Проверяем CameraFollow на главной камере if (Camera.main != null) { var camFollow = Camera.main.GetComponent(); if (camFollow != null) { camFollow.target = player.transform; EditorUtility.SetDirty(camFollow); } } // 10. Находим кость правой руки для оружия Transform rHand = animator.GetBoneTransform(HumanBodyBones.RightHand); Transform gunHolder = rHand != null ? rHand.Find("GunHolder") : null; if (gunHolder == null) gunHolder = player.transform.Find("GunHolder"); if (gunHolder != null && rHand != null && gunHolder.parent != rHand) { gunHolder.SetParent(rHand, false); gunHolder.localPosition = new Vector3(0.08f, 0.04f, -0.02f); gunHolder.localRotation = Quaternion.Euler(279.58f, 351.41f, 180.18f); } if (gunHolder != null && gunHolder.Find("M1911 Handgun_Black") == null) { for (int i = gunHolder.childCount - 1; i >= 0; i--) { Object.DestroyImmediate(gunHolder.GetChild(i).gameObject); } var gunPrefab = AssetDatabase.LoadAssetAtPath("Assets/Nokobot/Modern Guns - Handgun/_Prefabs/Handgun Black/M1911 Handgun_Black.prefab"); if (gunPrefab != null) { var gunInst = (GameObject)PrefabUtility.InstantiatePrefab(gunPrefab, gunHolder); gunInst.name = "M1911 Handgun_Black"; gunInst.transform.localPosition = Vector3.zero; gunInst.transform.localRotation = Quaternion.identity; if (pg != null) { Transform barrel = gunInst.transform.Find("Barrel_Location"); Transform modelM = gunInst.transform.Find("M1911 Handgun_Model"); pg.muzzlePoint = barrel; pg.gunAnimator = modelM != null ? modelM.GetComponent() : null; pg.casingExitPoint = modelM != null ? modelM.Find("CasingExit") : null; pg.gunModelTransform = gunHolder; pg.muzzleFlashPrefab = AssetDatabase.LoadAssetAtPath("Assets/Nokobot/Modern Guns - Handgun/Effects/MuzzleFlash.prefab"); pg.casingPrefab = AssetDatabase.LoadAssetAtPath("Assets/Nokobot/Modern Guns - Handgun/_Prefabs/45ACP Bullet_Casing.prefab"); EditorUtility.SetDirty(pg); } } } EditorSceneManager.MarkSceneDirty(player.scene); EditorSceneManager.SaveScene(player.scene); Debug.Log("[CharacterSetup] Main hero character successfully configured on Player!"); return "SUCCESS: Main hero character successfully configured on Player!"; } }