2025-11-13 16:24:49 +08:00
|
|
|
|
using System;
|
2025-11-18 18:45:14 +08:00
|
|
|
|
using System.IO;
|
2025-11-13 16:24:49 +08:00
|
|
|
|
using System.Reflection;
|
2025-11-18 18:45:14 +08:00
|
|
|
|
using Cysharp.Threading.Tasks;
|
2025-11-13 16:24:49 +08:00
|
|
|
|
using Duckov.Utilities;
|
2025-11-18 18:45:14 +08:00
|
|
|
|
using ItemStatsSystem;
|
2025-11-13 16:24:49 +08:00
|
|
|
|
using Saves;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using UnityEngine.SceneManagement;
|
|
|
|
|
|
using Object = UnityEngine.Object;
|
|
|
|
|
|
|
|
|
|
|
|
namespace CharacterPreview
|
|
|
|
|
|
{
|
|
|
|
|
|
public class ModBehaviour : Duckov.Modding.ModBehaviour
|
|
|
|
|
|
{
|
2025-11-18 18:45:14 +08:00
|
|
|
|
private static CharacterMainControl characterControl;
|
|
|
|
|
|
public static ModelMove modelMove;
|
2025-11-13 16:24:49 +08:00
|
|
|
|
private const string characterFaceSaveKey = "CustomFace_MainCharacter";
|
2025-11-18 18:45:14 +08:00
|
|
|
|
private const string characterItemSaveKey = "MainCharacterItemData";
|
2025-11-13 16:24:49 +08:00
|
|
|
|
|
|
|
|
|
|
private const string ModelName = "CharacterPreviewModel";
|
|
|
|
|
|
|
2025-11-18 18:45:14 +08:00
|
|
|
|
public static Config config;
|
|
|
|
|
|
|
|
|
|
|
|
private static GameObject cameraModelObject;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void OnDestroy()
|
|
|
|
|
|
{
|
|
|
|
|
|
config.Save();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-13 16:24:49 +08:00
|
|
|
|
protected override void OnAfterSetup()
|
|
|
|
|
|
{
|
2025-11-18 18:45:14 +08:00
|
|
|
|
var path=Path.Combine(info.path,"config.json");
|
|
|
|
|
|
config = Config.Load(path);
|
|
|
|
|
|
SceneManager.sceneLoaded+=OnSceneLoaded;
|
|
|
|
|
|
AddListen();
|
|
|
|
|
|
|
2025-11-13 16:24:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void OnBeforeDeactivate()
|
|
|
|
|
|
{
|
2025-11-18 18:45:14 +08:00
|
|
|
|
SceneManager.sceneLoaded -= OnSceneLoaded;
|
|
|
|
|
|
RemoveModel();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (scene.name == "MainMenu")
|
2025-11-13 16:24:49 +08:00
|
|
|
|
{
|
2025-11-18 18:45:14 +08:00
|
|
|
|
AddListen();
|
|
|
|
|
|
// _ = CreateCharacterModel();
|
2025-11-13 16:24:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-18 18:45:14 +08:00
|
|
|
|
private void AddListen()
|
2025-11-13 16:24:49 +08:00
|
|
|
|
{
|
2025-11-18 18:45:14 +08:00
|
|
|
|
var canvasObj = GameObject.Find("Canvas");
|
|
|
|
|
|
if (canvasObj == null)
|
2025-11-13 16:24:49 +08:00
|
|
|
|
{
|
2025-11-18 18:45:14 +08:00
|
|
|
|
Debug.Log("Canvas not found");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
var mainMenu=GameObjectUtils.FindObjectByPath(canvasObj, "MainMenuContainer");
|
|
|
|
|
|
if (!mainMenu)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.Log("MainMenuContainer not found");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var listen = mainMenu.GetComponent<ShowListen>();
|
|
|
|
|
|
if (!listen)
|
|
|
|
|
|
{
|
|
|
|
|
|
mainMenu.AddComponent<ShowListen>();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void RemoveModel()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (characterControl)
|
|
|
|
|
|
{
|
|
|
|
|
|
Destroy(characterControl.gameObject);
|
|
|
|
|
|
characterControl = null;
|
|
|
|
|
|
modelMove = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static async UniTask CreateCharacterModel()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (characterControl)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("[CreateCharacterModel] CharacterModel 已经被创建,跳过重复创建。");
|
2025-11-13 16:24:49 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (SceneManager.GetActiveScene().name != "MainMenu")
|
|
|
|
|
|
{
|
2025-11-18 18:45:14 +08:00
|
|
|
|
Debug.LogWarning(
|
|
|
|
|
|
$"[CreateCharacterModel] 当前场景为 \"{SceneManager.GetActiveScene().name}\",非主菜单界面 \"MainMenu\",无法创建角色模型。");
|
2025-11-13 16:24:49 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-18 18:45:14 +08:00
|
|
|
|
|
|
|
|
|
|
Item item = await ItemSavesUtilities.LoadItem(characterItemSaveKey);
|
|
|
|
|
|
if (item == null)
|
2025-11-13 16:24:49 +08:00
|
|
|
|
{
|
2025-11-18 18:45:14 +08:00
|
|
|
|
Debug.Log($"[CreateCharacterModel] 未找到已保存的 {characterItemSaveKey},将使用默认角色模板创建新角色。");
|
|
|
|
|
|
|
|
|
|
|
|
var defaultTypeID = GameplayDataSettings.ItemAssets.DefaultCharacterItemTypeID;
|
|
|
|
|
|
item = await ItemAssetsCollection.InstantiateAsync(defaultTypeID);
|
|
|
|
|
|
if (item == null)
|
2025-11-13 16:24:49 +08:00
|
|
|
|
{
|
2025-11-18 18:45:14 +08:00
|
|
|
|
Debug.LogError("[CreateCharacterModel] 无法通过默认角色类型 ID 实例化角色物品,请确认资源是否存在且配置正确。");
|
2025-11-13 16:24:49 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-18 18:45:14 +08:00
|
|
|
|
|
|
|
|
|
|
// 获取角色模型预制体
|
|
|
|
|
|
var model = GetCharacterModelPrefab_Reflection();
|
|
|
|
|
|
if (model == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("[CreateCharacterModel] 通过反射获取角色模型预制体失败,请检查 GetCharacterModelPrefab_Reflection 方法实现。");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
characterControl = CreateCharacter(
|
|
|
|
|
|
item, model, Vector3.zero, Quaternion.identity);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (characterControl == null)
|
2025-11-13 16:24:49 +08:00
|
|
|
|
{
|
2025-11-18 18:45:14 +08:00
|
|
|
|
Debug.LogError("[CreateCharacterModel] 角色创建失败,返回的 characterControl 为 null。");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
characterControl.enabled = false;
|
|
|
|
|
|
characterControl.gameObject.name = ModelName;
|
|
|
|
|
|
|
|
|
|
|
|
// 加载自定义面部设置
|
|
|
|
|
|
var customFaceSettingData = SavesSystem.Load<CustomFaceSettingData>(characterFaceSaveKey);
|
|
|
|
|
|
if (!customFaceSettingData.savedSetting)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("[CreateCharacterModel] 未能加载有效的 CustomFaceSettingData,将使用默认预设。");
|
|
|
|
|
|
|
|
|
|
|
|
if (GameplayDataSettings.CustomFaceData?.DefaultPreset?.settings == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("[CreateCharacterModel] 默认面部预设也为空!无法应用面部设置。");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2025-11-13 16:24:49 +08:00
|
|
|
|
{
|
|
|
|
|
|
customFaceSettingData = GameplayDataSettings.CustomFaceData.DefaultPreset.settings;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-18 18:45:14 +08:00
|
|
|
|
|
|
|
|
|
|
if (characterControl.characterModel?.CustomFace != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
characterControl.characterModel.CustomFace.LoadFromData(customFaceSettingData);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("[CreateCharacterModel] 跳过面部数据加载:CustomFace 组件或数据为空。");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加移动组件
|
|
|
|
|
|
modelMove = characterControl.gameObject.GetComponent<ModelMove>();
|
|
|
|
|
|
if (modelMove == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
modelMove = characterControl.gameObject.AddComponent<ModelMove>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Debug.LogWarning("这个是模型的动画组件报错,由于这个是部分初始化,导致内容不完全,不影响(其实是因为没影响就懒得一个个分析了,欸嘿(*^▽^*))");
|
|
|
|
|
|
HideCamera();
|
|
|
|
|
|
// SetModelShow(false);
|
2025-11-13 16:24:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-18 18:45:14 +08:00
|
|
|
|
private static void SetModelShow(bool show)
|
2025-11-13 16:24:49 +08:00
|
|
|
|
{
|
2025-11-18 18:45:14 +08:00
|
|
|
|
if (characterControl)
|
2025-11-13 16:24:49 +08:00
|
|
|
|
{
|
2025-11-18 18:45:14 +08:00
|
|
|
|
characterControl.gameObject.SetActive(show);
|
2025-11-13 16:24:49 +08:00
|
|
|
|
}
|
2025-11-18 18:45:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static void HideCamera()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!cameraModelObject)
|
2025-11-13 16:24:49 +08:00
|
|
|
|
{
|
2025-11-18 18:45:14 +08:00
|
|
|
|
var camera = FindObjectOfType<Camera>();
|
|
|
|
|
|
if (camera == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("[CreateCharacterModel] 场景中未找到 Camera 对象。");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
cameraModelObject = GameObjectUtils.FindObjectByPath(camera.gameObject, "Camera01_prefab");
|
|
|
|
|
|
if (cameraModelObject == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("[CreateCharacterModel] 在 Camera 下未找到路径为 \"Camera01_prefab\" 的子对象。");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (cameraModelObject)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (config.data.hideCamera)
|
|
|
|
|
|
{
|
|
|
|
|
|
cameraModelObject.SetActive(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
cameraModelObject.SetActive(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("[CreateCharacterModel] 未找到摄像机模型对象,无法控制其显示状态。");
|
2025-11-13 16:24:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-18 18:45:14 +08:00
|
|
|
|
|
2025-11-13 16:24:49 +08:00
|
|
|
|
private static CharacterModel GetCharacterModelPrefab_Reflection()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 获取 LevelManager 实例
|
|
|
|
|
|
var lm = GameplayDataSettings.Prefabs.LevelManagerPrefab;
|
|
|
|
|
|
if (lm == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("LevelManager 实例未找到!");
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 使用反射获取 LevelManager 中的 characterModel 字段
|
|
|
|
|
|
var field = typeof(LevelManager).GetField("characterModel", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
|
|
|
|
if (field == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("characterModel 字段在 LevelManager 中未找到!");
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
2025-11-18 18:45:14 +08:00
|
|
|
|
|
2025-11-13 16:24:49 +08:00
|
|
|
|
// 获取字段值并转换为 CharacterModel
|
|
|
|
|
|
var modelPrefab = field.GetValue(lm) as CharacterModel;
|
|
|
|
|
|
if (modelPrefab == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("characterModel 字段的值为空!");
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
return modelPrefab;
|
|
|
|
|
|
}
|
2025-11-18 18:45:14 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 检查指定全限定类名的类型是否存在于已加载的程序集中。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="fullTypeName">完整类名,如 "DuckovCustomModel.ModBehaviour"</param>
|
|
|
|
|
|
/// <returns>如果存在返回 true,否则 false</returns>
|
|
|
|
|
|
public static bool IsTypeLoaded(string fullTypeName)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(fullTypeName))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
// 先尝试用 Type.GetType(适用于 mscorlib 和当前程序集)
|
|
|
|
|
|
var type = Type.GetType(fullTypeName, throwOnError: false, ignoreCase: false);
|
|
|
|
|
|
if (type != null)
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历所有已加载的程序集
|
|
|
|
|
|
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
|
|
|
|
|
foreach (var assembly in assemblies)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
type = assembly.GetType(fullTypeName, throwOnError: false, ignoreCase: false);
|
|
|
|
|
|
if (type != null)
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 某些程序集可能无法读取(如动态生成、权限问题等),跳过
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static CharacterMainControl CreateCharacter(
|
|
|
|
|
|
Item itemInstance,
|
|
|
|
|
|
CharacterModel modelPrefab,
|
|
|
|
|
|
Vector3 pos,
|
|
|
|
|
|
Quaternion rotation)
|
|
|
|
|
|
{
|
|
|
|
|
|
var character = Instantiate(GameplayDataSettings.Prefabs.CharacterPrefab, pos, rotation);
|
|
|
|
|
|
var _characterModel = Instantiate(modelPrefab);
|
|
|
|
|
|
character.SetCharacterModel(_characterModel);
|
|
|
|
|
|
if (itemInstance == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ((bool) (Object) character)
|
|
|
|
|
|
Destroy(character.gameObject);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
character.SetItem(itemInstance);
|
|
|
|
|
|
return character;
|
|
|
|
|
|
}
|
2025-11-13 16:24:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|