219 lines
8.6 KiB
C#
219 lines
8.6 KiB
C#
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<GameObject>(FbxPath);
|
|
if (fbxPrefab == null)
|
|
{
|
|
Debug.LogError($"[CharacterSetup] FBX model not found at {FbxPath}");
|
|
return "FBX model not found!";
|
|
}
|
|
|
|
var controller = AssetDatabase.LoadAssetAtPath<RuntimeAnimatorController>(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<PlayerMovement>();
|
|
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<MeshRenderer>();
|
|
if (meshRenderer != null)
|
|
{
|
|
meshRenderer.enabled = false;
|
|
}
|
|
|
|
// Отключаем лишний CapsuleCollider, так как CharacterController сам является коллайдером
|
|
var capCol = player.GetComponent<CapsuleCollider>();
|
|
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, Material>();
|
|
string[] matGuids = AssetDatabase.FindAssets("t:Material", new[] { MaterialsFolder });
|
|
foreach (var guid in matGuids)
|
|
{
|
|
string p = AssetDatabase.GUIDToAssetPath(guid);
|
|
var m = AssetDatabase.LoadAssetAtPath<Material>(p);
|
|
if (m != null)
|
|
{
|
|
matDict[m.name] = m;
|
|
}
|
|
}
|
|
|
|
// Назначаем материалы всем рендерам модели
|
|
var renderers = modelGo.GetComponentsInChildren<SkinnedMeshRenderer>(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<Animator>();
|
|
if (animator == null)
|
|
{
|
|
animator = modelGo.AddComponent<Animator>();
|
|
}
|
|
|
|
// Подгружаем 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<PlayerMovement>();
|
|
if (pm != null)
|
|
{
|
|
pm.animator = animator;
|
|
EditorUtility.SetDirty(pm);
|
|
}
|
|
|
|
var pa = player.GetComponent<PlayerAttack>();
|
|
if (pa != null)
|
|
{
|
|
pa.animator = animator;
|
|
EditorUtility.SetDirty(pa);
|
|
}
|
|
|
|
var pg = player.GetComponent<PlayerGun>();
|
|
if (pg != null)
|
|
{
|
|
pg.playerAnimator = animator;
|
|
EditorUtility.SetDirty(pg);
|
|
}
|
|
|
|
var echo = player.GetComponent<EchoTrigger>();
|
|
if (echo != null)
|
|
{
|
|
echo.playerAnimator = animator;
|
|
echo.FindFootBones();
|
|
EditorUtility.SetDirty(echo);
|
|
}
|
|
|
|
// 9. Проверяем CameraFollow на главной камере
|
|
if (Camera.main != null)
|
|
{
|
|
var camFollow = Camera.main.GetComponent<CameraFollow>();
|
|
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<GameObject>("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<Animator>() : null;
|
|
pg.casingExitPoint = modelM != null ? modelM.Find("CasingExit") : null;
|
|
pg.gunModelTransform = gunHolder;
|
|
pg.muzzleFlashPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Nokobot/Modern Guns - Handgun/Effects/MuzzleFlash.prefab");
|
|
pg.casingPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("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!";
|
|
}
|
|
}
|