This commit is contained in:
Tran Hai Nam
2026-05-05 16:10:37 +07:00
38 changed files with 76982 additions and 413 deletions
+2 -2
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:329ce992197e8e90aa8ecc86ad5a2e9d0e4516ae41630b0e0fb4b68080d9ba75
size 303602
oid sha256:fdca6f6915cfc055b350581a53fbc4d2e19ab522da9eb954295d71d45d8aed58
size 303299
+2 -2
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:08dd99327890c9a20d09b1faadef16ad55b5a276d5080d744bfdc2f4191dceac
size 111628
oid sha256:7bb8ce7140f7f3652a200d0393561498e3ecdd8f4425bd2248e1ff000f225e5e
size 106979
@@ -15,6 +15,8 @@ using System.Text;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using BrewMonster.Scripts.UI.Inventory;
using CSNetwork.Protocols.RPCData;
namespace BrewMonster.Scripts.Managers
{
@@ -166,6 +168,7 @@ namespace BrewMonster.Scripts.Managers
ShowDetailPanel(false);
ShowSplitPanel(false);
RefreshAll();
RefreshCharacterModelPreview();
}
private void WireSplitUI()
@@ -688,6 +691,7 @@ namespace BrewMonster.Scripts.Managers
Debug.Log($"[InventoryUI] Equip request sent for item {currentSelectedItem.m_tid} from slot {currentSelectedSlot} to equip location {equipLocation}");
// Refresh inventory after equip
RefreshAll();
RefreshCharacterModelPreview();
});
}
@@ -713,6 +717,7 @@ namespace BrewMonster.Scripts.Managers
Debug.Log($"[InventoryUI] Unequip request sent for item {currentSelectedItem.m_tid} from equip location {equipLocation} to inventory slot {emptySlot}");
// Refresh inventory after unequip
RefreshAll();
RefreshCharacterModelPreview();
});
}
@@ -738,6 +743,7 @@ namespace BrewMonster.Scripts.Managers
// Refresh inventory after drop
RefreshAll();
RefreshCharacterModelPreview();
}
/// <summary>
@@ -1617,6 +1623,61 @@ namespace BrewMonster.Scripts.Managers
return false;
}
public void RefreshCharacterModelPreview()
{
var preview = PlayerModelPreview.Instance;
if (preview != null)
{
RoleInfo roleInfo = BuildCurrentPreviewRoleInfo();
if (roleInfo != null)
{
preview.ReloadRoleModel(roleInfo);
}
}
var inventoryPreview = FindFirstObjectByType<InventoryCharacterPreview>();
if (inventoryPreview != null)
{
inventoryPreview.QueueRefresh();
}
}
private RoleInfo BuildCurrentPreviewRoleInfo()
{
RoleInfo roleInfo = UnityGameSession.Instance != null ? UnityGameSession.Instance.GetRoleInfo() : null;
if (roleInfo == null)
return null;
RoleInfo snapshot = roleInfo.Clone();
if (snapshot.equipment == null)
snapshot.equipment = new List<GRoleInventory>();
else
snapshot.equipment.Clear();
CECHostPlayer host = CECGameRun.Instance?.GetHostPlayer();
var equipInv = host?.GetInventory(InventoryConst.IVTRTYPE_EQUIPPACK);
if (equipInv == null)
return snapshot;
int size = equipInv.GetSize();
for (int i = 0; i < size; i++)
{
EC_IvtrItem item = equipInv.GetItem(i, false);
if (item == null)
continue;
snapshot.equipment.Add(new GRoleInventory
{
id = (uint)item.GetTemplateID(),
pos = i,
count = item.GetCount(),
max_count = Math.Max(1, item.GetPileLimitInstance())
});
}
return snapshot;
}
private bool IsPointerOverDetailPanel(Vector2 screenPosition)
{
var panelRect = detailPanelRoot.transform as RectTransform;
@@ -1,5 +1,6 @@
using BrewMonster;
using BrewMonster.Network;
using BrewMonster.Scripts;
using BrewMonster.Scripts.World;
using CSNetwork;
using CSNetwork.GPDataType;
@@ -260,7 +261,8 @@ namespace PerfectWorld.Scripts.Managers
CECMatter pMatter = GetMatter(mid);
if (pMatter != null)
{
UnityEngine.Object.Destroy(pMatter.gameObject);
//UnityEngine.Object.Destroy(pMatter.gameObject);
PoolManager.Instance.Despawn(pMatter.gameObject);
m_MatterTab.Remove(mid);
}
//TODO: Might need to implement later
@@ -345,4 +347,4 @@ namespace PerfectWorld.Scripts.Managers
return null;
}
}
}
}
+185 -65
View File
@@ -324,6 +324,9 @@ namespace BrewMonster
public const int EFF_FACEPILL = 1;
}
private BaseVfxObject _levelUpVfx;
// [中文] 当前激活的状态效果 GFX,以 (路径+挂点) 为键
// [English] Currently active state-effect GFX objects, keyed by (path + hook)
private Dictionary<string, BaseVfxObject> _stateGfxObjects = new Dictionary<string, BaseVfxObject>();
protected GameObject[] m_pModels = new GameObject[(int)PLAYERMODEL_TYPE.PLAYERMODEL_MAX];
protected int[] m_aShapeID = new int[(int)PLAYERMODEL_TYPE.PLAYERMODEL_MAX];
@@ -895,91 +898,103 @@ namespace BrewMonster
// !CECOptimize::Instance().GetGFX().CanShowState(GetCharacterID(), GetClassID()))
// return;
//static const char* szBasePath = "߻\\״̬Ч\\";
// [中文] 策划联入\状态效果\ (状态效果 GFX 的基础路径)
// [English] Designer-integrated state effect GFX base path
const string szBasePath = "策划联入/状态效果/";
const int bitSize = sizeof(uint) * 8;
BMLogger.LogError($"[CECPlayer] ShowExtendStates: start: {start}, count: {count}, bitSize: {bitSize}");
for (int index = 0; index<count; index++)
for (int index = 0; index < count; index++)
{
int idState = index + start;
for (int i=0; i < bitSize; i++)
for (int i = 0; i < bitSize; i++)
{
int dwMask = 1 << i;
int dwFlag1 = (int)(m_aExtStatesShown[idState] & dwMask);
int dwFlag2 = (int)(pData[index] & dwMask);
// [中文] 两者相同(都激活或都未激活),跳过
// [English] Both unchanged (both on or both off), skip
if ((dwFlag1 == 0 && dwFlag2 == 0) || (dwFlag1 != 0 && dwFlag2 != 0))
continue;
BMLogger.LogError($"[CECPlayer] ShowExtendStates: idState: {idState}, bitIndex: {i}, i + idState*bitSize: {i + idState * bitSize}");
//TODO: Implement visible state
// const GNET::VisibleState* pvs = GNET::VisibleState::Query(m_iProfession, i + idState*bitSize);
// if (!pvs)
// continue;
// [中文] 查询可见状态(profession 参数被忽略,仅按 id 查找)
// [English] Query visible state (profession is ignored; keyed by id only)
VisibleState pvs = VisibleState.Query(m_iProfession, i + idState * bitSize);
if (pvs == null)
continue;
// AString strGFXFile = pvs->GetEffect();
// if (!strGFXFile.GetLength())
// continue;
string strEffect = pvs.GetEffect();
if (string.IsNullOrEmpty(strEffect))
continue;
// strGFXFile = szBasePath + strGFXFile;
string strGFXFile = szBasePath + strEffect;
// if (TestProcessPetCureGFX(strGFXFile, dwFlag2 != 0, i + idState*bitSize))
// continue;
// [中文] TestProcessPetCureGFX — 宠愈 GFX 系统尚未移植,跳过(等价于始终返回 false)
// [English] TestProcessPetCureGFX — pet-cure GFX system not ported yet; skip (equivalent to always false)
// if (TestProcessPetCureGFX(strGFXFile, dwFlag2 != 0, i + idState * bitSize)) continue;
if (dwFlag1 != 0)
{
// [中文] 移除旧状态效果
// [English] Remove old state GFX
if (SkillGfxMan.InstanceSub != null)
{
SkillGfxMan.InstanceSub.RemoveAllTraceTargetGfx();
}
// //TODO: Implement remove old state
// CECModel *pWeapon = NULL;
// bool bLeft (false);
// if (IsWeaponHookPos(pvs->GetHH(), &bLeft, &pWeapon))
// {
// // Ч
// if (pWeapon)
// {
// const char * gfxHook = GetWeaponGFXHookPos(pWeapon, bLeft);
// pWeapon->RemoveGfx(strGFXFile, gfxHook);
// }
// }
// else
/*bool bLeft = false;
CECModel pWeapon = null;
if (IsWeaponHookPos(pvs.GetHH(), out bLeft, out pWeapon))
{
// ģЧ
// RemoveGfx(strGFXFile, pvs->GetHH(), PLAYERMODEL_TYPEALL);
// [中文] 武器上的效果
// [English] GFX on weapon model
if (pWeapon != null)
{
string gfxHook = GetWeaponGFXHookPos(pWeapon, bLeft);
RemoveStateGfxFromModel(pWeapon, strGFXFile, gfxHook);
}
}
else
{
// [中文] 模型上的效果
// [English] GFX on player model
RemoveGfx(strGFXFile, pvs.GetHH(), (uint)PLAYERMODEL_TYPE.PLAYERMODEL_TYPEALL);
}*/
}
else
{
// [中文] 添加新状态效果
// [English] Add new state GFX
string szHH = pvs.GetHH();
float fScale;
// [中文] 根据挂点类型获取模型缩放数据
// [English] Determine GFX scale from model outer data based on hook type
CECModel majorModel = GetMajorModel()?.GetComponent<CECModel>();
if (majorModel != null && szHH.Equals("HH_Head", StringComparison.OrdinalIgnoreCase))
fScale = majorModel.GetOuterData()[0];
else if (majorModel != null && szHH.Equals("HH_Spine", StringComparison.OrdinalIgnoreCase))
fScale = majorModel.GetOuterData()[1];
else
fScale = 1.0f;
bool bLeft = false;
CECModel pWeapon = null;
if (IsWeaponHookPos(szHH, out bLeft, out pWeapon))
{
// [中文] 武器上的效果
// [English] GFX on weapon model
if (pWeapon != null)
{
string gfxHook = GetWeaponGFXHookPos(pWeapon, bLeft);
PlayStateGfxOnModel(pWeapon, strGFXFile, gfxHook, fScale);
}
}
else
{
// [中文] 玩家模型上的效果
// [English] GFX on player model
PlayGfx(strGFXFile, pvs.GetHH(), fScale, (uint)PLAYERMODEL_TYPE.PLAYERMODEL_TYPEALL, true);
}
}
// else
// {
// // Add new state
// const char* szHH = pvs->GetHH();
// float fScale;
// if (stricmp("HH_Head", szHH) == 0)
// fScale = GetMajorModel()->GetOuterData()[0];
// else if (stricmp("HH_Spine", szHH) == 0)
// fScale = GetMajorModel()->GetOuterData()[1];
// else
// fScale = 1.0f;
// CECModel *pWeapon = NULL;
// bool bLeft (false);
// if (IsWeaponHookPos(pvs->GetHH(), &bLeft, &pWeapon))
// {
// if (pWeapon)
// {
// const char *gfxHook = GetWeaponGFXHookPos(pWeapon, bLeft);
// pWeapon->PlayGfx(strGFXFile, gfxHook, fScale);
// }
// }
// else
// {
// PlayGfx(strGFXFile, pvs->GetHH(), fScale, PLAYERMODEL_TYPEALL, true);
// }
// }
}
}
@@ -996,6 +1011,68 @@ namespace BrewMonster
int bitOffset = n % bitSize;
return (m_aExtStates[index] & (1 << bitOffset)) != 0;
}
// [中文] 判断挂点是否为武器挂点,并返回对应武器模型和左右手标志
// [English] Check if hook pos is a weapon hook; output the corresponding weapon model and left-hand flag
protected bool IsWeaponHookPos(string szHH, out bool bLeft, out CECModel pWeapon)
{
bLeft = false;
pWeapon = null;
if (string.IsNullOrEmpty(szHH)) return false;
// [中文] 左手武器挂点(手持或肩部)
// [English] Left-hand weapon hook positions (held or shoulder)
if (szHH.Equals("HH_lefthandweapon", StringComparison.OrdinalIgnoreCase) ||
szHH.Equals("HH_leftsword", StringComparison.OrdinalIgnoreCase))
{
bLeft = true;
pWeapon = GetLeftHandWeapon();
return true;
}
// [中文] 右手武器挂点(手持、肩部或镰刀)
// [English] Right-hand weapon hook positions (held, shoulder, or sickle/nickle)
if (szHH.Equals("HH_righthandweapon", StringComparison.OrdinalIgnoreCase) ||
szHH.Equals("HH_rightsword", StringComparison.OrdinalIgnoreCase) ||
szHH.Equals("HH_weapon", StringComparison.OrdinalIgnoreCase))
{
pWeapon = GetRightHandWeapon();
return true;
}
return false;
}
// [中文] 获取左手武器模型(武器 CECModel 未接入时返回 null
// [English] Get left-hand weapon model (returns null until weapon CECModel tracking is wired)
protected virtual CECModel GetLeftHandWeapon() => null;
// [中文] 获取右手武器模型(武器 CECModel 未接入时返回 null
// [English] Get right-hand weapon model (returns null until weapon CECModel tracking is wired)
protected virtual CECModel GetRightHandWeapon() => null;
// [中文] 获取武器上用于挂载 GFX 的挂点名称
// [English] Get the GFX hook position name on the weapon model
protected string GetWeaponGFXHookPos(CECModel pModel, bool bLeft)
{
// [中文] 武器 CECModel 挂点逻辑未接入,暂时返回 null
// [English] Weapon CECModel hook logic not wired yet; return null for now
return null;
}
// [中文] 从玩家模型上移除状态效果 GFX
// [English] Remove a state-effect GFX from the player model
protected void RemoveGfx(string szPath, string szHook, uint iShapeTypeMask)
{
string key = szPath + szHook;
if (_stateGfxObjects.TryGetValue(key, out BaseVfxObject vfx) && vfx != null)
{
vfx.Stop();
Destroy(vfx.gameObject);
_stateGfxObjects.Remove(key);
}
}
public virtual void SetUpPlayer()
{
m_dwResFlags = 0;
@@ -1708,15 +1785,15 @@ namespace BrewMonster
//swing sfx
//workaround for sound effect delay, it need to trigger via weapon combine action
SFXManager.Instance.PlaySkillSfxAtPointAsync(soundPath, Vector3.zero,iTransTime/1000f).Forget();
szAct = EC_Utility.BuildActionName(action, weapon_type, "落");
m_pActionController.QueueNonSkillActionWithName(iAction, szAct, 0, false, bHideFX);
//hit sfx
//workaround for sound effect delay, it need to trigger via weapon combine action
//.1f is a magic number to make sure the sound effect is triggered after the action is finished
SFXManager.Instance.PlaySkillSfxAtPointAsync(hitSoundPath, Vector3.zero,iTransTime/1000f+.1f).Forget();
//PlayNonSkillActionWithName(iAction, szAct, true, 200, true, ref pActFlag, COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX);gagága
/*
if (pRightHandWeapon != null && IsUsingMagicWeapon())
@@ -2832,10 +2909,53 @@ namespace BrewMonster
// }
// }
// }
PlayLevelUpGfx(szPath);
// [中文] 路由到状态 GFX 播放(支持多 GFX 同时激活)
// [English] Route to state GFX playback (supports multiple concurrent GFX)
_ = PlayStateGfxAsync(szPath, szHook, fScale);
return false;
}
// [中文] 异步加载并播放状态效果 GFX,以 (路径+挂点) 为键去重
// [English] Asynchronously load and play a state-effect GFX; keyed by (path+hook) to deduplicate
private async Task PlayStateGfxAsync(string path, string hook, float fScale)
{
if (string.IsNullOrEmpty(path)) return;
string key = path + hook;
if (_stateGfxObjects.ContainsKey(key)) return; // [中文] 已激活,跳过 / [English] Already active, skip
GameObject prefab = await AddressableManager.Instance.LoadPrefabAsync(path);
if (prefab == null)
{
BMLogger.LogWarning($"[StateGFX] Failed to load prefab: {path}");
return;
}
// [中文] 实例化挂载在玩家 transform 下,初始位置归零
// [English] Instantiate parented to player transform with zeroed local position
BaseVfxObject vfx = Instantiate(prefab, transform).GetComponent<BaseVfxObject>();
if (vfx == null) return;
vfx.transform.localPosition = Vector3.zero;
vfx.SetScale(fScale);
vfx.Play();
_stateGfxObjects[key] = vfx;
BMLogger.Log($"[StateGFX] Playing: {path}, hook: {hook}, scale: {fScale}");
}
// [中文] 在武器 CECModel 上移除状态效果 GFX(武器挂点逻辑未接入,暂存桩)
// [English] Remove state-effect GFX from weapon CECModel (weapon hook not wired yet; stub)
private void RemoveStateGfxFromModel(CECModel model, string path, string hook)
{
// TODO: implement when weapon CECModel GFX tracking is available
}
// [中文] 在武器 CECModel 上播放状态效果 GFX(武器挂点逻辑未接入,暂存桩)
// [English] Play state-effect GFX on weapon CECModel (weapon hook not wired yet; stub)
private void PlayStateGfxOnModel(CECModel model, string path, string hook, float fScale)
{
// TODO: implement when weapon CECModel GFX tracking is available
}
private async void PlayLevelUpGfx(string path)
{
// Usage: Load the prefab asynchronously using AddressableManager
+2 -1
View File
@@ -230,7 +230,7 @@ public class CECModelStaticData
SrcBlend = A3DBLEND.A3DBLEND_SRCALPHA,
DestBlend = A3DBLEND.A3DBLEND_INVSRCALPHA
};
private readonly float[] m_OuterData = new float[CECModelConstants.OUTER_DATA_COUNT];
public float[] m_OuterData = new float[CECModelConstants.OUTER_DATA_COUNT];
private bool m_bCanCastShadow = true;
private bool m_bRenderSkinModel = true;
private bool m_bRenderEdge = true;
@@ -636,6 +636,7 @@ public class CECModel
}
return hook;
}
public float[] GetOuterData() { return m_pMapModel.m_OuterData; }
/// <summary>
/// Invalidate hook cache (call when model reloads)
@@ -161,10 +161,11 @@ namespace PerfectWorld.Scripts
var fileMatterValue = fileMatterField.GetValue(matterData);
string filePath = ByteToStringUtils.ByteArrayToCP936String((byte[])fileMatterValue);
var matterPrefab = await AddressableManager.Instance.LoadPrefabAsync(AFile.NormalizePath(filePath.ToLower(), true));
if (matterPrefab != null)
//var matterPrefab = await AddressableManager.Instance.LoadPrefabAsync(AFile.NormalizePath(filePath.ToLower(), true));
var matterObject = await PoolManager.Instance.SpawnAsync(AFile.NormalizePath(filePath.ToLower(), true), Vector3.zero, Quaternion.identity, 15f);
if (matterObject != null)
{
var matterObject = Instantiate(matterPrefab);
//var matterObject = Instantiate(matterPrefab);
matterObject.name = $"Matter {matterObject.name} {matterInfo.tid} {matterInfo.mid}";
matterObject.transform.position = new Vector3(Info.pos.x, Info.pos.y, Info.pos.z);
matterObject.transform.localScale = new Vector3(1f, 1f, 1f);
@@ -6,7 +6,7 @@ using UnityEngine;
namespace BrewMonster.Scripts
{
public class PlayerModelPreview : MonoBehaviour
public class PlayerModelPreview : MonoSingleton<PlayerModelPreview>
{
[SerializeField] private Transform modelRoot;
@@ -15,6 +15,9 @@ namespace BrewMonster.Scripts
private int _loadVersion;
private bool _hasRequestedPlayerModelId;
private int _requestedPlayerModelId;
private void Awake()
{
if (modelRoot == null)
@@ -36,10 +39,15 @@ namespace BrewMonster.Scripts
_loadVersion++;
int version = _loadVersion;
ClearModels();
if (roleInfos == null || roleInfos.Count == 0 || NPCManager.Instance == null)
{
ClearModels();
return;
}
List<GameObject> oldModels = new List<GameObject>(playerModels);
playerModels.Clear();
playerModelIds.Clear();
for (int i = 0; i < roleInfos.Count; i++)
{
@@ -51,20 +59,27 @@ namespace BrewMonster.Scripts
if (version != _loadVersion)
{
if (model != null)
Destroy(model);
Destroy(model);
return;
}
if (model == null)
continue;
model.transform.SetParent(modelRoot, false);
model.transform.localPosition = Vector3.zero;
model.transform.localRotation = Quaternion.identity;
model.SetActive(false);
playerModels.Add(model);
playerModelIds.Add(role.roleid);
ApplyRequestedModelVisibility();
}
for(int i=0; i < oldModels.Count; i++)
{
if (oldModels[i] != null)
{
Destroy(oldModels[i]);
}
}
}
@@ -73,23 +88,69 @@ namespace BrewMonster.Scripts
/// </summary>
public void ShowPlayerModel(int playerModelId)
{
//int n = Mathf.Min(playerModels.Count, playerModelIds.Count);
//for (int i = 0; i < n; i++)
//{
// GameObject go = playerModels[i];
// if (go == null)
// continue;
// go.SetActive(playerModelIds[i] == playerModelId);
//}
_hasRequestedPlayerModelId = true;
_requestedPlayerModelId = playerModelId;
ApplyRequestedModelVisibility();
}
/// <summary>
/// Applies the visibility of player models based on the requested player model id.
/// </summary>
private void ApplyRequestedModelVisibility()
{
if(!_hasRequestedPlayerModelId)
return;
int n = Mathf.Min(playerModels.Count, playerModelIds.Count);
for (int i = 0; i < n; i++)
{
GameObject go = playerModels[i];
if (go == null)
if(go == null)
continue;
go.SetActive(playerModelIds[i] == playerModelId);
go.SetActive(playerModelIds[i] == _requestedPlayerModelId);
}
}
public void HideAllPlayerModels()
{
_hasRequestedPlayerModelId = false;
for (int i = 0; i < playerModels.Count; i++)
{
if (playerModels[i] != null)
{
playerModels[i].SetActive(false);
}
}
}
public void ClearModels()
{
//for (int i = 0; i < playerModels.Count; i++)
//{
// if (playerModels[i] != null)
// {
// Destroy(playerModels[i]);
// }
//}
for (int i = 0; i < playerModels.Count; i++)
{
if (playerModels[i] != null)
{
Destroy(playerModels[i]);
}
}
playerModels.Clear();
playerModelIds.Clear();
}
@@ -98,14 +159,18 @@ namespace BrewMonster.Scripts
{
for (int i = 0; i < playerModels.Count; i++)
{
if (playerModels[i] != null)
if (modelRoot != null)
{
if (Application.isPlaying)
Destroy(playerModels[i]);
else
DestroyImmediate(playerModels[i]);
if(playerModels[i] != null)
{
if(Application.isPlaying)
Destroy(playerModels[i]);
else
DestroyImmediate(playerModels[i]);
}
}
}
playerModels.Clear();
playerModelIds.Clear();
}
@@ -114,13 +179,17 @@ namespace BrewMonster.Scripts
{
BMLogger.Log($"LoadPlayerModel: {role.roleid}");
var elemendataman = BrewMonster.ElementDataManProvider.GetElementDataMan();
GameObject prefab = await NPCManager.Instance.GetModelPlayer(role.occupation, role.gender);
if (prefab == null)
{
GameObject model = await NPCManager.Instance.GetModelPlayer(role.occupation, role.gender);
if(model == null)
return null;
}
model.SetActive(false);
BMLogger.Log($"LoadPlayerModel: {role.roleid} - prefab loaded");
GameObject model = Instantiate(prefab);
if (modelRoot != null)
{
model.transform.SetParent(modelRoot, false);
}
var playerDefaultEquipments = model.GetComponentInChildren<PlayerDefaultEquipments>();
if (playerDefaultEquipments == null)
{
@@ -310,5 +379,14 @@ namespace BrewMonster.Scripts
}
return skeletonBuilder;
}
public void ReloadRoleModel(RoleInfo roleInfo)
{
if (roleInfo == null)
return;
ShowAllPlayerModels(new List<RoleInfo> { roleInfo });
ShowPlayerModel(roleInfo.roleid);
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a86dd2e474fb3c34cb8214c766f0b941
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
namespace BrewMonster.Scripts
{
/// <summary>
/// Implement this on pooled prefab components that need to reset state between uses.
/// </summary>
public interface IPoolable
{
void OnSpawn();
void OnDespawn();
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7c09b2b8b565f2946bb4e9f09e2966af
@@ -0,0 +1,270 @@
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
namespace BrewMonster.Scripts
{
internal sealed class ObjectPool
{
private readonly string _addressableKey;
private readonly PoolManager _owner;
private readonly AddressableManager _addressableManager;
private readonly Transform _poolRoot;
private readonly Stack<GameObject> _idleInstances = new();
private readonly HashSet<GameObject> _activeInstances = new();
private readonly HashSet<GameObject> _knownInstances = new();
private readonly Dictionary<GameObject, int> _spawnVersions = new();
private GameObject _prefab;
private Task<GameObject> _loadTask;
private Coroutine _releaseCoroutine;
private float _memoryReleaseTTL;
public ObjectPool(
string addressableKey,
PoolManager owner,
AddressableManager addressableManager,
Transform poolRoot,
float memoryReleaseTTL)
{
_addressableKey = addressableKey;
_owner = owner;
_addressableManager = addressableManager;
_poolRoot = poolRoot;
_memoryReleaseTTL = Mathf.Max(0f, memoryReleaseTTL);
}
public string AddressableKey => _addressableKey;
public int ActiveCount => _activeInstances.Count;
public int IdleCount => _idleInstances.Count;
public void UpdateMemoryReleaseTTL(float memoryReleaseTTL)
{
_memoryReleaseTTL = Mathf.Max(0f, memoryReleaseTTL);
}
public async Task<GameObject> SpawnAsync(Vector3 position, Quaternion rotation, Transform parent)
{
CancelReleaseCountdown();
GameObject instance = GetIdleInstance();
if (instance == null)
{
GameObject prefab = await LoadPrefabAsync();
if (prefab == null)
{
return null;
}
instance = Object.Instantiate(prefab);
_knownInstances.Add(instance);
_spawnVersions[instance] = 0;
}
_activeInstances.Add(instance);
_spawnVersions[instance]++;
instance.transform.SetParent(parent, true);
instance.transform.SetPositionAndRotation(position, rotation);
instance.SetActive(true);
NotifyPoolablesSpawned(instance);
return instance;
}
public bool Despawn(GameObject instance)
{
if (instance == null || !_activeInstances.Remove(instance))
{
return false;
}
NotifyPoolablesDespawned(instance);
_spawnVersions[instance]++;
instance.SetActive(false);
instance.transform.SetParent(_poolRoot, false);
_idleInstances.Push(instance);
if (_activeInstances.Count == 0)
{
StartReleaseCountdown();
}
return true;
}
public bool IsActiveInstance(GameObject instance, int spawnVersion)
{
return instance != null
&& _activeInstances.Contains(instance)
&& _spawnVersions.TryGetValue(instance, out int currentVersion)
&& currentVersion == spawnVersion;
}
public int GetSpawnVersion(GameObject instance)
{
return instance != null && _spawnVersions.TryGetValue(instance, out int version) ? version : -1;
}
public IEnumerable<GameObject> GetKnownInstances()
{
return _knownInstances;
}
public void ReleaseNow()
{
CancelReleaseCountdown();
DestroyAllInstances();
ReleasePrefabAsset();
}
private GameObject GetIdleInstance()
{
while (_idleInstances.Count > 0)
{
GameObject instance = _idleInstances.Pop();
if (instance != null)
{
return instance;
}
}
return null;
}
private async Task<GameObject> LoadPrefabAsync()
{
if (_prefab != null)
{
return _prefab;
}
if (_loadTask != null)
{
return await _loadTask;
}
_loadTask = LoadPrefabInternalAsync();
GameObject prefab = await _loadTask;
if (prefab == null)
{
_loadTask = null;
}
return prefab;
}
private async Task<GameObject> LoadPrefabInternalAsync()
{
if (_addressableManager == null)
{
BMLogger.LogError($"ObjectPool: AddressableManager is not available for '{_addressableKey}'.");
return null;
}
await _addressableManager.WaitUntilInitializedAsync();
GameObject prefab = await _addressableManager.LoadPrefabAsync(_addressableKey);
if (prefab == null)
{
BMLogger.LogError($"ObjectPool: Failed to load Addressable prefab '{_addressableKey}'.");
return null;
}
_prefab = prefab;
return _prefab;
}
private void StartReleaseCountdown()
{
CancelReleaseCountdown();
_releaseCoroutine = _owner.StartCoroutine(ReleaseMemoryCountdown());
}
private void CancelReleaseCountdown()
{
if (_releaseCoroutine == null)
{
return;
}
_owner.StopCoroutine(_releaseCoroutine);
_releaseCoroutine = null;
}
private IEnumerator ReleaseMemoryCountdown()
{
if (_memoryReleaseTTL > 0f)
{
yield return new WaitForSecondsRealtime(_memoryReleaseTTL);
}
_releaseCoroutine = null;
if (_activeInstances.Count > 0)
{
yield break;
}
_owner.RemovePool(this);
DestroyAllInstances();
ReleasePrefabAsset();
}
private void DestroyAllInstances()
{
foreach (GameObject instance in _knownInstances)
{
if (instance == null)
{
continue;
}
if (_activeInstances.Contains(instance))
{
NotifyPoolablesDespawned(instance);
}
Object.Destroy(instance);
}
_idleInstances.Clear();
_activeInstances.Clear();
_knownInstances.Clear();
_spawnVersions.Clear();
if (_poolRoot != null)
{
Object.Destroy(_poolRoot.gameObject);
}
}
private void ReleasePrefabAsset()
{
if (_prefab != null && _addressableManager != null)
{
_addressableManager.ReleaseAsset(_addressableKey);
}
_prefab = null;
_loadTask = null;
}
private static void NotifyPoolablesSpawned(GameObject instance)
{
IPoolable[] poolables = instance.GetComponentsInChildren<IPoolable>(true);
for (int i = 0; i < poolables.Length; i++)
{
poolables[i].OnSpawn();
}
}
private static void NotifyPoolablesDespawned(GameObject instance)
{
IPoolable[] poolables = instance.GetComponentsInChildren<IPoolable>(true);
for (int i = 0; i < poolables.Length; i++)
{
poolables[i].OnDespawn();
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8fc34059fd203384d9c5aa022301e68f
@@ -0,0 +1,244 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
namespace BrewMonster.Scripts
{
public sealed class PoolManager : MonoSingleton<PoolManager>
{
private readonly Dictionary<string, ObjectPool> _pools = new();
private readonly Dictionary<GameObject, ObjectPool> _instanceToPool = new();
private AddressableManager _addressableManager;
private Transform _poolContainer;
protected override void Initialize()
{
base.Initialize();
_addressableManager = AddressableManager.Instance;
_addressableManager.OnDispose += ReleaseAllPools;
_poolContainer = new GameObject("Addressables Object Pools").transform;
_poolContainer.SetParent(transform, false);
}
/// <summary>
/// Spawns an Addressables prefab from its pool. The returned task completes after the prefab is loaded if needed.
/// </summary>
public async Task<GameObject> SpawnAsync(
string addressableKey,
Vector3 position,
Quaternion rotation,
float memoryReleaseTTL,
float autoDespawnTime = 0f,
Transform parent = null)
{
if (string.IsNullOrEmpty(addressableKey))
{
BMLogger.LogError("PoolManager: Cannot spawn with a null or empty Addressables key.");
return null;
}
ObjectPool pool = GetOrCreatePool(addressableKey, memoryReleaseTTL);
pool.UpdateMemoryReleaseTTL(memoryReleaseTTL);
GameObject instance = await pool.SpawnAsync(position, rotation, parent);
if (instance == null)
{
return null;
}
_instanceToPool[instance] = pool;
if (autoDespawnTime > 0f)
{
StartCoroutine(AutoDespawnAfter(pool, instance, pool.GetSpawnVersion(instance), autoDespawnTime));
}
return instance;
}
/// <summary>
/// Coroutine-friendly spawn API for callers that do not use async/await.
/// </summary>
public void Spawn(
string addressableKey,
Vector3 position,
Quaternion rotation,
float memoryReleaseTTL,
float autoDespawnTime,
Action<GameObject> onComplete,
Transform parent = null)
{
StartCoroutine(SpawnRoutine(addressableKey, position, rotation, memoryReleaseTTL, autoDespawnTime, onComplete, parent));
}
public bool Despawn(string addressableKey, GameObject instance)
{
if (string.IsNullOrEmpty(addressableKey) || instance == null)
{
return false;
}
if (!_pools.TryGetValue(addressableKey, out ObjectPool pool))
{
return false;
}
bool despawned = pool.Despawn(instance);
if (despawned)
{
_instanceToPool[instance] = pool;
}
return despawned;
}
public bool Despawn(GameObject instance)
{
if (instance == null || !_instanceToPool.TryGetValue(instance, out ObjectPool pool))
{
return false;
}
return pool.Despawn(instance);
}
public bool TryGetPoolCounts(string addressableKey, out int activeCount, out int idleCount)
{
activeCount = 0;
idleCount = 0;
if (!_pools.TryGetValue(addressableKey, out ObjectPool pool))
{
return false;
}
activeCount = pool.ActiveCount;
idleCount = pool.IdleCount;
return true;
}
public void ReleasePool(string addressableKey)
{
if (!_pools.TryGetValue(addressableKey, out ObjectPool pool))
{
return;
}
UnregisterPoolInstances(pool);
pool.ReleaseNow();
_pools.Remove(addressableKey);
}
public void ReleaseAllPools()
{
List<ObjectPool> pools = new(_pools.Values);
for (int i = 0; i < pools.Count; i++)
{
pools[i].ReleaseNow();
}
_pools.Clear();
_instanceToPool.Clear();
}
internal void RemovePool(ObjectPool pool)
{
if (pool == null)
{
return;
}
UnregisterPoolInstances(pool);
_pools.Remove(pool.AddressableKey);
}
private IEnumerator SpawnRoutine(
string addressableKey,
Vector3 position,
Quaternion rotation,
float memoryReleaseTTL,
float autoDespawnTime,
Action<GameObject> onComplete,
Transform parent)
{
Task<GameObject> spawnTask = SpawnAsync(addressableKey, position, rotation, memoryReleaseTTL, autoDespawnTime, parent);
while (!spawnTask.IsCompleted)
{
yield return null;
}
if (spawnTask.Exception != null)
{
BMLogger.LogError($"PoolManager: Spawn failed for '{addressableKey}': {spawnTask.Exception}");
onComplete?.Invoke(null);
yield break;
}
onComplete?.Invoke(spawnTask.Result);
}
private IEnumerator AutoDespawnAfter(ObjectPool pool, GameObject instance, int spawnVersion, float autoDespawnTime)
{
yield return new WaitForSeconds(autoDespawnTime);
if (pool != null && pool.IsActiveInstance(instance, spawnVersion))
{
Despawn(instance);
}
}
private ObjectPool GetOrCreatePool(string addressableKey, float memoryReleaseTTL)
{
if (_pools.TryGetValue(addressableKey, out ObjectPool pool))
{
return pool;
}
Transform poolRoot = new GameObject(GetPoolRootName(addressableKey)).transform;
poolRoot.SetParent(_poolContainer, false);
pool = new ObjectPool(addressableKey, this, _addressableManager, poolRoot, memoryReleaseTTL);
_pools[addressableKey] = pool;
return pool;
}
private void UnregisterPoolInstances(ObjectPool pool)
{
foreach (GameObject instance in pool.GetKnownInstances())
{
if (instance != null)
{
_instanceToPool.Remove(instance);
}
}
}
private static string GetPoolRootName(string addressableKey)
{
string name = addressableKey.Replace('\\', '/');
int lastSlash = name.LastIndexOf('/');
if (lastSlash >= 0 && lastSlash < name.Length - 1)
{
name = name.Substring(lastSlash + 1);
}
return $"Pool - {name}";
}
protected override void OnDestroy()
{
if (_addressableManager != null)
{
_addressableManager.OnDispose -= ReleaseAllPools;
}
ReleaseAllPools();
base.OnDestroy();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b8e790a183861dd40bd580421c793038
@@ -621,7 +621,13 @@ namespace BrewMonster.Scripts.Skills
public virtual string GetName() { return null; }
public virtual string GetHH() { return null; }
public virtual string GetEffect() { return null; }
public static VisibleState Query(int profession, int id) { return null; }
public static VisibleState Query(int profession, int id)
{
// [中文] C++ 实现同样忽略 profession,只按 id 查找
// [English] C++ impl also ignores profession; keyed by id only
GNET.VisibleState.TryGetValue(id, out var result);
return result;
}
}
public class TeamState
{
@@ -1,4 +1,5 @@
using BrewMonster.Network;
using CSNetwork;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
@@ -20,6 +21,7 @@ namespace BrewMonster.UI
private bool m_bUpdateRenderTarget = false;
[SerializeField] TextMeshProUGUI skillNameText;
[SerializeField] Image imageProgress;
[SerializeField] Button btnCancel;
public virtual void Show(bool value)
{
@@ -313,6 +315,11 @@ namespace BrewMonster.UI
public virtual void Awake()
{
m_szName = "Dialog_";
if(btnCancel != null)
{
btnCancel.onClick.RemoveAllListeners();
btnCancel.onClick.AddListener(OnClickBtnCancel);
}
}
public virtual void Start()
@@ -365,5 +372,10 @@ namespace BrewMonster.UI
{
CECUIManager.Instance.HideCurrentUIInStack();
}
public void OnClickBtnCancel()
{
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_PRESSCANCEL, MANAGER_INDEX.MAN_PLAYER, 0);
}
}
}
@@ -27,64 +27,97 @@ namespace BrewMonster.Scripts.UI.Inventory
[SerializeField] private Vector3 previewLocalPosition = Vector3.zero;
[SerializeField] private Vector3 previewLocalEuler;
[SerializeField] private Vector3 previewLocalScale = Vector3.one;
[Tooltip("Copy the preview root layer onto the cloned hierarchy so the preview camera can isolate it.")]
[SerializeField] private bool inheritPreviewLayer = true;
[Header("Camera Output")]
[SerializeField] private Camera previewCamera;
[SerializeField] private RawImage previewFrame;
[SerializeField] private RenderTexture previewRenderTexture;
[SerializeField] private bool autoFocusCamera = true;
[SerializeField] private Vector3 cameraFocusOffset = new Vector3(0f, 1.5f, 0f);
[SerializeField] private Vector3 cameraFocusOffset = new Vector3(0f, 0f, 0f);
[Header("Behaviour")]
[Tooltip("Disable PlayerVisual/Animancer components on the clone so it stays in a frozen idle pose.")]
[SerializeField] private bool freezeAnimation = true;
[Header("Camera Framing")]
[SerializeField] private float previewCameraDistance = 3.3f;
[SerializeField] private Vector3 previewCameraOffset = new Vector3(0.15f, 0.8f, 0f);
[SerializeField] private bool overrideFieldOfView = true;
[SerializeField][Range(10f, 60f)] private float previewFieldOfView = 33f;
[SerializeField] private bool autoFrameByBounds = true;
[SerializeField][Range(1f, 2f)] private float autoFramePadding = 1.15f;
[SerializeField] private float minAutoCameraDistance = 1.25f;
[SerializeField] private float maxAutoCameraDistance = 8f;
private bool _cameraStateCached;
private Vector3 _cachedCameraPosition;
private Quaternion _cachedCameraRotation;
private float _cachedCameraFov;
//[Header("Behaviour")]
//[Tooltip("Disable PlayerVisual/Animancer components on the clone so it stays in a frozen idle pose.")]
//[SerializeField] private bool freezeAnimation = true;
private GameObject _previewInstance;
private bool _refreshQueued;
private int _lastOriginModelChildCount;
private Transform _lastOriginModelRoot;
private Camera _previewCamera;
private PlayerModelPreview _playerModelPreview;
private RenderTexture _cachedTargetTexture;
private void Awake()
{
if (previewRoot == null)
{
previewRoot = transform;
}
TryBindPreviewSystem();
TryBindHostPlayer();
EnsureCameraBindings();
}
private void OnEnable()
{
TryBindPreviewSystem();
EnsureCameraBindings();
QueueRefresh();
}
private void OnDisable()
{
DestroyPreviewInstance();
RestoreCameraTargetTexture();
RestoreSharedCameraState();
ReleasePreviewReference();
}
private void LateUpdate()
{
TryBindPreviewSystem();
TryBindHostPlayer();
// Check if origin model structure has changed (equipment models are child objects)
// This detects equipment changes in realtime since all equipment is attached as children
var currentOriginRoot = ResolveSourceModelRoot();
if (currentOriginRoot != null)
Transform currentOriginRoot = ResolveSourceModelRoot();
if (currentOriginRoot == null)
{
int currentChildCount = CountAllChildren(currentOriginRoot);
// Refresh if origin model changed or child count changed (equipment added/removed)
if (_lastOriginModelRoot != currentOriginRoot || _lastOriginModelChildCount != currentChildCount)
if (_previewInstance == null)
{
_lastOriginModelRoot = currentOriginRoot;
_lastOriginModelChildCount = currentChildCount;
QueueRefresh();
}
if(_refreshQueued)
{
_refreshQueued = false;
BuildPreviewModel();
}
return;
}
int currentChildCount = CountAllChildren(currentOriginRoot);
if (_lastOriginModelRoot != currentOriginRoot || _lastOriginModelChildCount != currentChildCount)
{
_lastOriginModelRoot = currentOriginRoot;
_lastOriginModelChildCount = currentChildCount;
QueueRefresh();
}
if (_refreshQueued)
@@ -92,6 +125,11 @@ namespace BrewMonster.Scripts.UI.Inventory
_refreshQueued = false;
BuildPreviewModel();
}
if(_previewInstance != null)
{
EnsureCameraFocus(_previewInstance.transform);
}
}
/// <summary>Allows manual binding from external UI scripts.</summary>
@@ -119,6 +157,23 @@ namespace BrewMonster.Scripts.UI.Inventory
_refreshQueued = true;
}
private void TryBindPreviewSystem()
{
if (_playerModelPreview == null)
{
_playerModelPreview = PlayerModelPreview.Instance;
}
if (_playerModelPreview == null)
{
return;
}
if (_previewCamera == null)
{
_previewCamera = _playerModelPreview.GetComponent<Camera>();
}
}
private void TryBindHostPlayer()
{
@@ -136,14 +191,40 @@ namespace BrewMonster.Scripts.UI.Inventory
private void EnsureCameraBindings()
{
if (previewCamera != null && previewRenderTexture != null)
if (_previewCamera == null)
{
previewCamera.targetTexture = previewRenderTexture;
TryBindPreviewSystem();
}
if (previewFrame != null && previewCamera != null)
if (_previewCamera == null || previewFrame == null)
{
previewFrame.texture = previewCamera.targetTexture;
return;
}
if (_cachedTargetTexture == null)
{
_cachedTargetTexture = _previewCamera.targetTexture;
}
if (previewRenderTexture != null)
{
_previewCamera.targetTexture = previewRenderTexture;
}
if(previewFrame != null)
previewFrame.texture = _previewCamera.targetTexture;
}
private void RestoreCameraTargetTexture()
{
if (_previewCamera == null)
{
return;
}
if (_cachedTargetTexture != null)
{
_previewCamera.targetTexture = _cachedTargetTexture;
}
}
@@ -154,64 +235,43 @@ namespace BrewMonster.Scripts.UI.Inventory
return;
}
var sourceRoot = ResolveSourceModelRoot();
Transform sourceRoot = ResolveSourceModelRoot();
if (sourceRoot == null)
{
_previewInstance = null;
_lastOriginModelRoot = null;
_lastOriginModelChildCount = 0;
QueueRefresh();
return;
}
DestroyPreviewInstance();
if (_previewInstance != null && _previewInstance != sourceRoot.gameObject)
{
DestroyPreviewInstance();
}
_previewInstance = Instantiate(sourceRoot.gameObject, previewRoot, false);
_previewInstance.name = $"{sourceRoot.name}_Preview";
if (!sourceRoot.gameObject.activeSelf)
{
sourceRoot.gameObject.SetActive(true);
}
var instanceTransform = _previewInstance.transform;
instanceTransform.localPosition = previewLocalPosition;
// Use resolved source root, not sourceModelRoot field directly.
_previewInstance = sourceRoot.gameObject;
Transform instanceTransform = _previewInstance.transform;
if (instanceTransform.parent != previewRoot)
{
instanceTransform.SetParent(previewRoot, false);
}
instanceTransform.localPosition = previewLocalPosition + Vector3.up;
instanceTransform.localRotation = Quaternion.Euler(previewLocalEuler);
instanceTransform.localScale = previewLocalScale;
if (inheritPreviewLayer)
{
ApplyLayerRecursive(instanceTransform, previewRoot.gameObject.layer);
}
if (freezeAnimation)
{
DisableComponentInChildren<PlayerVisual>(_previewInstance);
DisableComponentInChildren<AnimancerComponent>(_previewInstance);
DisableComponentInChildren<Animator>(_previewInstance);
FreezeAnimators(_previewInstance);
}
if (cloneWholeHostHierarchy && stripRuntimeComponents)
{
StripRuntimeScripts(_previewInstance);
}
ApplyLayerRecursive(instanceTransform, previewRoot.gameObject.layer);
EnsureCameraFocus(instanceTransform);
}
private Transform ResolveSourceModelRoot()
{
if (sourceModelRoot != null)
{
return sourceModelRoot;
}
if (hostPlayer == null)
{
return null;
}
if (cloneWholeHostHierarchy)
{
sourceModelRoot = hostPlayer.transform;
return sourceModelRoot;
}
var playerVisual = hostPlayer.GetComponentInChildren<PlayerVisual>(true);
sourceModelRoot = playerVisual != null ? playerVisual.transform : hostPlayer.transform;
return sourceModelRoot;
EnsureCameraBindings();
}
private void DestroyPreviewInstance()
@@ -223,29 +283,79 @@ namespace BrewMonster.Scripts.UI.Inventory
}
}
private static void DisableComponentInChildren<T>(GameObject root) where T : Behaviour
private Transform ResolveSourceModelRoot()
{
if (root == null)
if (sourceModelRoot != null)
{
return;
return sourceModelRoot;
}
var components = root.GetComponentsInChildren<T>(true);
for (int i = 0; i < components.Length; i++)
if (_playerModelPreview == null)
{
var component = components[i];
if (component == null)
continue;
return null;
}
component.enabled = false;
if (component is PlayerVisual)
// Prefer active model.
for (int i = 0; i < _playerModelPreview.playerModels.Count; i++)
{
GameObject model = _playerModelPreview.playerModels[i];
if (model != null && model.activeSelf)
{
Destroy(component);
return model.transform;
}
}
// Fallback to first available model.
for (int i = 0; i < _playerModelPreview.playerModels.Count; i++)
{
GameObject model = _playerModelPreview.playerModels[i];
if (model != null)
{
return model.transform;
}
}
return null;
}
private void ReleasePreviewReference()
{
_previewInstance = null;
}
//private void DestroyPreviewInstance()
//{
// if (_previewInstance != null)
// {
// Destroy(_previewInstance);
// _previewInstance = null;
// }
//}
//private static void DisableComponentInChildren<T>(GameObject root) where T : Behaviour
//{
// if (root == null)
// {
// return;
// }
// var components = root.GetComponentsInChildren<T>(true);
// for (int i = 0; i < components.Length; i++)
// {
// var component = components[i];
// if (component == null)
// continue;
// component.enabled = false;
// if (component is PlayerVisual)
// {
// Destroy(component);
// }
// }
//}
private void ApplyLayerRecursive(Transform root, int layer)
{
if (root == null)
@@ -262,70 +372,113 @@ namespace BrewMonster.Scripts.UI.Inventory
private void EnsureCameraFocus(Transform modelRoot)
{
if (!autoFocusCamera || previewCamera == null || modelRoot == null)
if (_previewCamera == null)
{
return;
}
var focusPoint = modelRoot.TransformPoint(cameraFocusOffset);
if (!_cameraStateCached)
{
_cachedCameraPosition = _previewCamera.transform.position;
_cachedCameraRotation = _previewCamera.transform.rotation;
_cachedCameraFov = _previewCamera.fieldOfView;
_cameraStateCached = true;
}
// Apply FOV override independently from auto focus.
if (overrideFieldOfView)
{
_previewCamera.fieldOfView = previewFieldOfView;
}
if (!autoFocusCamera || modelRoot == null)
{
return;
}
Vector3 focusPoint = modelRoot.TransformPoint(cameraFocusOffset) + previewCameraOffset;
Vector3 viewDirection = modelRoot.forward;
viewDirection.y = 0f;
if (viewDirection.sqrMagnitude < 0.0001f)
{
viewDirection = Vector3.back;
}
viewDirection.Normalize();
_previewCamera.transform.position = focusPoint + viewDirection * previewCameraDistance;
_previewCamera.transform.LookAt(focusPoint);
}
private void StripRuntimeScripts(GameObject cloneRoot)
//private void StripRuntimeScripts(GameObject cloneRoot)
//{
// if (cloneRoot == null)
// {
// return;
// }
// var monoBehaviours = cloneRoot.GetComponentsInChildren<MonoBehaviour>(true);
// for (int i = 0; i < monoBehaviours.Length; i++)
// {
// var behaviour = monoBehaviours[i];
// if (behaviour == null)
// continue;
// Destroy(behaviour);
// }
// var colliders = cloneRoot.GetComponentsInChildren<Collider>(true);
// for (int i = 0; i < colliders.Length; i++)
// {
// var collider = colliders[i];
// if (collider == null)
// continue;
// Destroy(collider);
// }
// var rigidbodies = cloneRoot.GetComponentsInChildren<Rigidbody>(true);
// for (int i = 0; i < rigidbodies.Length; i++)
// {
// var body = rigidbodies[i];
// if (body == null)
// continue;
// Destroy(body);
// }
//}
private void RestoreSharedCameraState()
{
if (cloneRoot == null)
if (!_cameraStateCached || _previewCamera == null)
{
return;
}
var monoBehaviours = cloneRoot.GetComponentsInChildren<MonoBehaviour>(true);
for (int i = 0; i < monoBehaviours.Length; i++)
{
var behaviour = monoBehaviours[i];
if (behaviour == null)
continue;
Destroy(behaviour);
}
var colliders = cloneRoot.GetComponentsInChildren<Collider>(true);
for (int i = 0; i < colliders.Length; i++)
{
var collider = colliders[i];
if (collider == null)
continue;
Destroy(collider);
}
var rigidbodies = cloneRoot.GetComponentsInChildren<Rigidbody>(true);
for (int i = 0; i < rigidbodies.Length; i++)
{
var body = rigidbodies[i];
if (body == null)
continue;
Destroy(body);
}
_previewCamera.transform.position = _cachedCameraPosition;
_previewCamera.transform.rotation = _cachedCameraRotation;
_previewCamera.fieldOfView = _cachedCameraFov;
_cameraStateCached = false;
}
private void FreezeAnimators(GameObject cloneRoot)
{
if (cloneRoot == null)
{
return;
}
//private void FreezeAnimators(GameObject cloneRoot)
//{
// if (cloneRoot == null)
// {
// return;
// }
var animators = cloneRoot.GetComponentsInChildren<Animator>(true);
for (int i = 0; i < animators.Length; i++)
{
var animator = animators[i];
if (animator == null)
continue;
// var animators = cloneRoot.GetComponentsInChildren<Animator>(true);
// for (int i = 0; i < animators.Length; i++)
// {
// var animator = animators[i];
// if (animator == null)
// continue;
animator.speed = 0f;
}
}
// animator.speed = 0f;
// }
//}
/// <summary>
/// Counts all children recursively in the transform hierarchy.
@@ -30,7 +30,6 @@ namespace BrewMonster.UI
[SerializeField] private Button cancelButton;
[SerializeField] private Button backButton;
[SerializeField] private CDlgMessageBox messageBoxPrefab;
[SerializeField] private PlayerModelPreview playerModelPreview;
private CDlgMessageBox _messageBoxInstance;
private int _currentProfession = -1;
@@ -403,12 +402,12 @@ namespace BrewMonster.UI
}
}
playerModelPreview.ShowAllPlayerModels(_roleInfos);
PlayerModelPreview.Instance.ShowAllPlayerModels(_roleInfos);
}
private void LoadShowModel(int prof, int gender)
{
playerModelPreview.ShowPlayerModel(prof * (int)Gender.NUM_GENDER + gender);
PlayerModelPreview .Instance.ShowPlayerModel(prof * (int)Gender.NUM_GENDER + gender);
}
}
}
@@ -56,6 +56,11 @@ namespace BrewMonster.UI
{
Tech3CSDKWrapper.Instance.SetLoginCallback(OnLoginCallback);
Tech3CSDKWrapper.Instance.SetLogoutCallback(OnLogoutCallback);
if(PlayerModelPreview.Instance != null)
{
PlayerModelPreview.Instance.HideAllPlayerModels();
}
}
private void OnDisable()
@@ -102,7 +107,7 @@ namespace BrewMonster.UI
{
if (_roleInfos != null)
{
_selectCharacterScreen.InitScreen(_roleInfos, OnClickSelectCharacter, OnCreateCharacterComplete);
_selectCharacterScreen.InitScreen(_roleInfos, OnClickSelectCharacter, OnCreateCharacterComplete, OnExitCharacterSelect);
_roleInfos = null;
}
@@ -422,6 +427,21 @@ namespace BrewMonster.UI
{
BMLogger.LogError($"Logout failed -- errorCode: {errorCode}: {errorMessage}");
}
_loginInProgress = false;
if (_loginButton != null)
_loginButton.interactable = true;
}
private void OnExitCharacterSelect()
{
BMLogger.Log("Exiting character select, returning to login screen.");
if(_selectCharacterScreen != null)
_selectCharacterScreen.gameObject.SetActive(false);
_loginInProgress = false;
if(_loginButton != null)
_loginButton.interactable = true;
}
}
}
@@ -0,0 +1,209 @@
using BrewMonster.Assets.PerfectWorld.Scripts.UI;
using BrewMonster.Common;
using BrewMonster.Managers;
using BrewMonster.Scripts;
using BrewMonster.Scripts.Managers;
using BrewMonster.UI;
using CSNetwork.GPDataType;
using Cysharp.Threading.Tasks.Triggers;
using PerfectWorld.Scripts.Managers;
using System;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace BrewMonster
{
public class TestEditBodyChar : AUIDialog
{
[Header("List Btn")]
[SerializeField] private Button btn_dang_nguoi;
[SerializeField] private Button btn_face;
[SerializeField] private Button btn_eye;
[SerializeField] private Button btn_nose_and_mouth;
[SerializeField] private Button btn_hair;
[SerializeField] private Button btn_cosplay;
[Header("List Box Of Edit")]
[SerializeField] private GameObject box_dang_nguoi;
[SerializeField] private GameObject box_face;
[SerializeField] private GameObject box_eye;
[SerializeField] private GameObject box_nose_and_mouth;
[SerializeField] private GameObject box_hair;
[SerializeField] private GameObject box_cosplay;
[Header("Box Dang Nguoi")]
[SerializeField] private GameObject box_1;
[SerializeField] private GameObject box_2;
[SerializeField] private Button btn_save;
[SerializeField] private Button btn_delete_and_setup;
[SerializeField] private Button btn_random;
[SerializeField] private Button btn_setting_advancde;
[SerializeField] private Button btn_back;
[Header("Box Facae")]
[SerializeField] private GameObject box_f_1;
[SerializeField] private GameObject box_f_2;
[SerializeField] private Button btn_setting_f_advancde;
[SerializeField] private Button btn_back_f;
[Header("Box Eye")]
[SerializeField] private GameObject box_e_1;
[SerializeField] private GameObject box_e_2;
[SerializeField] private Button btn_setting_e_advancde;
[SerializeField] private Button btn_back_e;
[Header("Box Nose and Mouth")]
[SerializeField] private GameObject box_n_1;
[SerializeField] private GameObject box_n_2;
[SerializeField] private Button btn_setting_n_advancde;
[SerializeField] private Button btn_back_n;
public override void Start()
{
btn_dang_nguoi.onClick.AddListener(OnClickBtnDangNguoi);
btn_face.onClick.AddListener(OnClickBtnFace);
btn_eye.onClick.AddListener(OnClickBtnEye);
btn_nose_and_mouth.onClick.AddListener(OnClickBtnNoseAndMouth);
btn_hair.onClick.AddListener(OnClickBtnHair);
btn_cosplay.onClick.AddListener(OnClickBtnCosplay);
btn_setting_advancde.onClick.AddListener(OnClickAdvancedDangNguoi);
btn_back.onClick.AddListener(OnClickBackDangNguoi);
btn_setting_f_advancde.onClick.AddListener(OnClickAdvancedFace);
btn_back_f.onClick.AddListener(OnClickBackFace);
btn_setting_e_advancde.onClick.AddListener(OnClickAdvancedEye);
btn_back_e.onClick.AddListener(OnClickBackEye);
btn_setting_n_advancde.onClick.AddListener(OnClickAdvancedNoseAndMouth);
btn_back_n.onClick.AddListener(OnClickBackNoseAndMouth);
}
public override void OnDisable()
{
btn_dang_nguoi.onClick.RemoveListener(OnClickBtnDangNguoi);
btn_face.onClick.RemoveListener(OnClickBtnFace);
btn_eye.onClick.RemoveListener(OnClickBtnEye);
btn_nose_and_mouth.onClick.RemoveListener(OnClickBtnNoseAndMouth);
btn_hair.onClick.RemoveListener(OnClickBtnHair);
btn_cosplay.onClick.RemoveListener(OnClickBtnCosplay);
btn_setting_advancde.onClick.RemoveListener(OnClickAdvancedDangNguoi);
btn_back.onClick.RemoveListener(OnClickBackDangNguoi);
btn_setting_f_advancde.onClick.RemoveListener(OnClickAdvancedFace);
btn_back_f.onClick.RemoveListener(OnClickBackFace);
btn_setting_e_advancde.onClick.RemoveListener(OnClickAdvancedEye);
btn_back_e.onClick.RemoveListener(OnClickBackEye);
btn_setting_n_advancde.onClick.RemoveListener(OnClickAdvancedNoseAndMouth);
btn_back_n.onClick.RemoveListener(OnClickBackNoseAndMouth);
}
private void OnClickBtnDangNguoi()
{
box_dang_nguoi.SetActive(true);
box_face.SetActive(false);
box_eye.SetActive(false);
box_nose_and_mouth.SetActive(false);
box_hair.SetActive(false);
box_cosplay.SetActive(false);
}
private void OnClickBtnFace()
{
box_dang_nguoi.SetActive(false);
box_face.SetActive(true);
box_eye.SetActive(false);
box_nose_and_mouth.SetActive(false);
box_hair.SetActive(false);
box_cosplay.SetActive(false);
}
private void OnClickBtnEye()
{
box_dang_nguoi.SetActive(false);
box_face.SetActive(false);
box_eye.SetActive(true);
box_nose_and_mouth.SetActive(false);
box_hair.SetActive(false);
box_cosplay.SetActive(false);
}
private void OnClickBtnNoseAndMouth()
{
box_dang_nguoi.SetActive(false);
box_face.SetActive(false);
box_eye.SetActive(false);
box_nose_and_mouth.SetActive(true);
box_hair.SetActive(false);
box_cosplay.SetActive(false);
}
private void OnClickBtnHair()
{
box_dang_nguoi.SetActive(false);
box_face.SetActive(false);
box_eye.SetActive(false);
box_nose_and_mouth.SetActive(false);
box_hair.SetActive(true);
box_cosplay.SetActive(false);
}
private void OnClickBtnCosplay()
{
box_dang_nguoi.SetActive(false);
box_face.SetActive(false);
box_eye.SetActive(false);
box_nose_and_mouth.SetActive(false);
box_hair.SetActive(false);
box_cosplay.SetActive(true);
}
private void OnClickAdvancedDangNguoi()
{
box_1.SetActive(false);
box_2.SetActive(true);
}
private void OnClickBackDangNguoi()
{
box_1.SetActive(true);
box_2.SetActive(false);
}
private void OnClickAdvancedFace()
{
box_f_1.SetActive(false);
box_f_2.SetActive(true);
}
private void OnClickBackFace()
{
box_f_1.SetActive(true);
box_f_2.SetActive(false);
}
private void OnClickAdvancedEye()
{
box_e_1.SetActive(false);
box_e_2.SetActive(true);
}
private void OnClickBackEye()
{
box_e_1.SetActive(true);
box_e_2.SetActive(false);
}
private void OnClickAdvancedNoseAndMouth()
{
box_n_1.SetActive(false);
box_n_2.SetActive(true);
}
private void OnClickBackNoseAndMouth()
{
box_n_1.SetActive(true);
box_n_2.SetActive(false);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7ed6d61372d1789479a2ac3ad8eaf022
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 951489a2cb8c576469f156d0397b2811
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+31 -163
View File
@@ -500,10 +500,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 7166820878650541780}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 588.35, y: -59.5}
m_SizeDelta: {x: 85, y: 85}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &9112205963990496432
CanvasRenderer:
@@ -907,10 +907,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 7166820878650541780}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 479.18, y: -59.5}
m_SizeDelta: {x: 85, y: 85}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &3997873020355048166
CanvasRenderer:
@@ -1069,138 +1069,6 @@ MonoBehaviour:
m_FillOrigin: 2
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &633515748786992396
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5275130098495308601}
- component: {fileID: 7874153745862569389}
- component: {fileID: 3332719603249310962}
m_Layer: 5
m_Name: PreviewCamera
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &5275130098495308601
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 633515748786992396}
m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
m_LocalPosition: {x: 0, y: 0, z: 103}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 6778274724352405780}
m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
m_AnchorMin: {x: 1, y: 1}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 1}
m_SizeDelta: {x: 100, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!20 &7874153745862569389
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 633515748786992396}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 2
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_Iso: 200
m_ShutterSpeed: 0.005
m_Aperture: 16
m_FocusDistance: 10
m_FocalLength: 50
m_BladeCount: 5
m_Curvature: {x: 2, y: 11}
m_BarrelClipping: 0.25
m_Anamorphism: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 0.3
far clip plane: 1000
field of view: 38
orthographic: 1
orthographic size: 1
m_Depth: 0
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingPath: -1
m_TargetTexture: {fileID: 8400000, guid: 42c3c43cd0c3b704cb6cf0dd1051d9ff, type: 2}
m_TargetDisplay: 0
m_TargetEye: 3
m_HDR: 1
m_AllowMSAA: 1
m_AllowDynamicResolution: 0
m_ForceIntoRT: 0
m_OcclusionCulling: 1
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!114 &3332719603249310962
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 633515748786992396}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
m_Name:
m_EditorClassIdentifier:
m_RenderShadows: 1
m_RequiresDepthTextureOption: 2
m_RequiresOpaqueTextureOption: 2
m_CameraType: 0
m_Cameras: []
m_RendererIndex: -1
m_VolumeLayerMask:
serializedVersion: 2
m_Bits: 1
m_VolumeTrigger: {fileID: 0}
m_VolumeFrameworkUpdateModeOption: 2
m_RenderPostProcessing: 0
m_Antialiasing: 0
m_AntialiasingQuality: 2
m_StopNaN: 0
m_Dithering: 0
m_ClearDepth: 1
m_AllowXRRendering: 1
m_AllowHDROutput: 1
m_UseScreenCoordOverride: 0
m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0}
m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0}
m_RequiresDepthTexture: 0
m_RequiresColorTexture: 0
m_Version: 2
m_TaaSettings:
m_Quality: 3
m_FrameInfluence: 0.1
m_JitterScale: 1
m_MipBias: 0
m_VarianceClampScale: 0.9
m_ContrastAdaptiveSharpening: 0
--- !u!1 &644319246446733122
GameObject:
m_ObjectHideFlags: 0
@@ -1370,10 +1238,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 7166820878650541780}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 370.01, y: -59.5}
m_SizeDelta: {x: 85, y: 85}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &4479228720555407959
CanvasRenderer:
@@ -5061,10 +4929,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 7166820878650541780}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 42.5, y: -59.5}
m_SizeDelta: {x: 85, y: 85}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &7052320096474271168
CanvasRenderer:
@@ -11166,8 +11034,7 @@ RectTransform:
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 5275130098495308601}
m_Children: []
m_Father: {fileID: 3289674559629147232}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@@ -11231,13 +11098,14 @@ MonoBehaviour:
previewLocalPosition: {x: 0, y: 0, z: 100}
previewLocalEuler: {x: 0, y: 0, z: 0}
previewLocalScale: {x: 1, y: 1, z: 1}
inheritPreviewLayer: 1
previewCamera: {fileID: 7874153745862569389}
previewFrame: {fileID: 6707240765686952970}
previewRenderTexture: {fileID: 8400000, guid: 42c3c43cd0c3b704cb6cf0dd1051d9ff, type: 2}
autoFocusCamera: 1
cameraFocusOffset: {x: 0, y: 0, z: 0}
freezeAnimation: 1
previewCameraDistance: 3.3
previewCameraOffset: {x: 0.06, y: 1.07, z: 0}
overrideFieldOfView: 1
previewFieldOfView: 44
--- !u!1 &5473020210238587200
GameObject:
m_ObjectHideFlags: 0
@@ -14202,10 +14070,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 7166820878650541780}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 151.67, y: -59.5}
m_SizeDelta: {x: 85, y: 85}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &4452551064742165538
CanvasRenderer:
@@ -19649,10 +19517,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 7166820878650541780}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 260.84, y: -59.5}
m_SizeDelta: {x: 85, y: 85}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &5155025384962724770
CanvasRenderer:
@@ -20239,7 +20107,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 7209086543831860202, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.y
value: -928.02
value: -836.15
objectReference: {fileID: 0}
- target: {fileID: 8894405194986632892, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMax.y
@@ -20259,7 +20127,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 8894405194986632892, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.y
value: -464.01
value: -418.075
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
+259
View File
@@ -32,6 +32,7 @@ RectTransform:
- {fileID: 1925327431716214704}
- {fileID: 7830969636268911684}
- {fileID: 529978810702632783}
- {fileID: 1961303628433326950}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@@ -53,6 +54,7 @@ MonoBehaviour:
m_EditorClassIdentifier:
skillNameText: {fileID: 4156282290771635373}
imageProgress: {fileID: 7818904766065936941}
btnCancel: {fileID: 4979337629572737255}
--- !u!1 &1961198748621413970
GameObject:
m_ObjectHideFlags: 0
@@ -189,6 +191,142 @@ MonoBehaviour:
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1 &3105178767150005615
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1173275105690546560}
- component: {fileID: 2270321894074679265}
- component: {fileID: 3782302284108987037}
m_Layer: 0
m_Name: Text (TMP)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1173275105690546560
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3105178767150005615}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1961303628433326950}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &2270321894074679265
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3105178767150005615}
m_CullTransparentMesh: 1
--- !u!114 &3782302284108987037
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3105178767150005615}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: Cancel
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
m_sharedMaterial: {fileID: 9092487103257209053, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4281479730
m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 24
m_fontSizeBase: 24
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
m_HorizontalAlignment: 2
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 0
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1 &4998799320591488598
GameObject:
m_ObjectHideFlags: 0
@@ -339,3 +477,124 @@ MonoBehaviour:
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &8568909860233813620
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1961303628433326950}
- component: {fileID: 1490671430424278683}
- component: {fileID: 4272345749132423406}
- component: {fileID: 4979337629572737255}
m_Layer: 0
m_Name: BtnCancel
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1961303628433326950
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8568909860233813620}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1173275105690546560}
m_Father: {fileID: 3062421795583818898}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -329, y: -243}
m_SizeDelta: {x: 160, y: 30}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &1490671430424278683
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8568909860233813620}
m_CullTransparentMesh: 1
--- !u!114 &4272345749132423406
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8568909860233813620}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!114 &4979337629572737255
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8568909860233813620}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_WrapAround: 0
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Colors:
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
m_SpriteState:
m_HighlightedSprite: {fileID: 0}
m_PressedSprite: {fileID: 0}
m_SelectedSprite: {fileID: 0}
m_DisabledSprite: {fileID: 0}
m_AnimationTriggers:
m_NormalTrigger: Normal
m_HighlightedTrigger: Highlighted
m_PressedTrigger: Pressed
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 4272345749132423406}
m_OnClick:
m_PersistentCalls:
m_Calls: []
+1 -1
View File
@@ -989,8 +989,8 @@ MonoBehaviour:
parentItems: {fileID: 2643174602035272289}
createCharacterButton: {fileID: 2685968509838782728}
_btnEnterGame: {fileID: 5173447691193350084}
_btnExit: {fileID: 1166159884785039946}
createCharacterScreen: {fileID: 0}
playerModelPreview: {fileID: 0}
--- !u!1 &7510180475820570348
GameObject:
m_ObjectHideFlags: 0
+7 -7
View File
@@ -1,5 +1,12 @@
{
"items": [
{
"header": 1992,
"param": 0,
"hasParam": false,
"describe": "Buff rage",
"lastUsedUtcTicks": 639134653927186702
},
{
"header": 1988,
"param": 0,
@@ -20,13 +27,6 @@
"hasParam": true,
"describe": "NoCooldown",
"lastUsedUtcTicks": 639123542141980400
},
{
"header": 1992,
"param": 0,
"hasParam": false,
"describe": "Buff rage",
"lastUsedUtcTicks": 639119244650372830
}
]
}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e06e8db90216c9745b7135d4c896cdcf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,344 @@
# szActName dump — pre-computed for all skills of this profession
# Profession: 0 (PROF_WARRIOR)
# Time: 2026-05-04 16:25:37
# Total entries: 339
虎击_吟唱_单手短
虎击_施放起_单手短
虎击_施放落_单手短
虎击_吟唱_双手短
虎击_施放起_双手短
虎击_施放落_双手短
虎击_吟唱_双手长
虎击_施放起_双手长
虎击_施放落_双手长
虎击_吟唱_空拳
虎击_施放起_空拳
虎击_施放落_空拳
寸力_吟唱_单手短
寸力_施放起_单手短
寸力_施放落_单手短
寸力_吟唱_双手短
寸力_施放起_双手短
寸力_施放落_双手短
寸力_吟唱_双手长
寸力_施放起_双手长
寸力_施放落_双手长
寸力_吟唱_空拳
寸力_施放起_空拳
寸力_施放落_空拳
虎跃_吟唱_单手短
虎跃_施放起_单手短
虎跃_施放落_单手短
虎跃_吟唱_双手短
虎跃_施放起_双手短
虎跃_施放落_双手短
虎跃_吟唱_双手长
虎跃_施放起_双手长
虎跃_施放落_双手长
虎跃_吟唱_空拳
虎跃_施放起_空拳
虎跃_施放落_空拳
狮子吼_吟唱_通用
狮子吼_施放起_通用
狮子吼_施放落_通用
狮子吼_吟唱_双手长
狮子吼_施放起_双手长
狮子吼_施放落_双手长
龙现_吟唱_单手短
龙现_施放起_单手短
龙现_施放落_单手短
龙现_吟唱_双手短
龙现_施放起_双手短
龙现_施放落_双手短
龙现_吟唱_双手长
龙现_施放起_双手长
龙现_施放落_双手长
龙现_吟唱_空拳
龙现_施放起_空拳
龙现_施放落_空拳
流水_吟唱_单手短
流水_施放起_单手短
流水_施放落_单手短
流水_吟唱_双手短
流水_施放起_双手短
流水_施放落_双手短
流水_吟唱_双手长
流水_施放起_双手长
流水_施放落_双手长
流水_吟唱_空拳
流水_施放起_空拳
流水_施放落_空拳
狂龙斩_吟唱_单手短
狂龙斩_施放起_单手短
狂龙斩_施放落_单手短
狂龙斩_吟唱_双手短
狂龙斩_施放起_双手短
狂龙斩_施放落_双手短
狂龙斩_吟唱_双手长
狂龙斩_施放起_双手长
狂龙斩_施放落_双手长
狂龙斩_吟唱_空拳
狂龙斩_施放起_空拳
狂龙斩_施放落_空拳
横扫千军_吟唱_单手短
横扫千军_施放起_单手短
横扫千军_施放落_单手短
横扫千军_吟唱_双手短
横扫千军_施放起_双手短
横扫千军_施放落_双手短
横扫千军_吟唱_双手长
横扫千军_施放起_双手长
横扫千军_施放落_双手长
横扫千军_吟唱_空拳
横扫千军_施放起_空拳
横扫千军_施放落_空拳
断岩斩_吟唱_单手短
断岩斩_施放起_单手短
断岩斩_施放落_单手短
断岩斩_吟唱_双手短
断岩斩_施放起_双手短
断岩斩_施放落_双手短
断岩斩_吟唱_双手长
断岩斩_施放起_双手长
断岩斩_施放落_双手长
断岩斩_吟唱_空拳
断岩斩_施放起_空拳
断岩斩_施放落_空拳
劈空掌_吟唱_单手短
劈空掌_施放起_单手短
劈空掌_施放落_单手短
劈空掌_吟唱_双手短
劈空掌_施放起_双手短
劈空掌_施放落_双手短
劈空掌_吟唱_双手长
劈空掌_施放起_双手长
劈空掌_施放落_双手长
劈空掌_吟唱_空拳
劈空掌_施放起_空拳
劈空掌_施放落_空拳
无影脚_吟唱_单手短
无影脚_施放起_单手短
无影脚_施放落_单手短
无影脚_吟唱_双手短
无影脚_施放起_双手短
无影脚_施放落_双手短
无影脚_吟唱_双手长
无影脚_施放起_双手长
无影脚_施放落_双手长
无影脚_吟唱_空拳
无影脚_施放起_空拳
无影脚_施放落_空拳
风卷残云_吟唱_单手短
风卷残云_施放起_单手短
风卷残云_施放落_单手短
风卷残云_吟唱_双手短
风卷残云_施放起_双手短
风卷残云_施放落_双手短
风卷残云_吟唱_双手长
风卷残云_施放起_双手长
风卷残云_施放落_双手长
风卷残云_吟唱_空拳
风卷残云_施放起_空拳
风卷残云_施放落_空拳
云龙九现_吟唱_单手短
云龙九现_施放起_单手短
云龙九现_施放落_单手短
云龙九现_吟唱_双手短
云龙九现_施放起_双手短
云龙九现_施放落_双手短
云龙九现_吟唱_双手长
云龙九现_施放起_双手长
云龙九现_施放落_双手长
云龙九现_吟唱_空拳
云龙九现_施放起_空拳
云龙九现_施放落_空拳
疾风霹雳_吟唱_单手短
疾风霹雳_施放起_单手短
疾风霹雳_施放落_单手短
疾风霹雳_吟唱_双手短
疾风霹雳_施放起_双手短
疾风霹雳_施放落_双手短
疾风霹雳_吟唱_双手长
疾风霹雳_施放起_双手长
疾风霹雳_施放落_双手长
回马枪_吟唱_单手短
回马枪_施放起_单手短
回马枪_施放落_单手短
回马枪_吟唱_双手短
回马枪_施放起_双手短
回马枪_施放落_双手短
回马枪_吟唱_双手长
回马枪_施放起_双手长
回马枪_施放落_双手长
流星赶月_吟唱_单手短
流星赶月_施放起_单手短
流星赶月_施放落_单手短
流星赶月_吟唱_双手短
流星赶月_施放起_双手短
流星赶月_施放落_双手短
流星赶月_吟唱_双手长
流星赶月_施放起_双手长
流星赶月_施放落_双手长
刃域_吟唱_单手短
刃域_施放起_单手短
刃域_施放落_单手短
刃域_吟唱_双手短
刃域_施放起_双手短
刃域_施放落_双手短
刃域_吟唱_双手长
刃域_施放起_双手长
刃域_施放落_双手长
霸王龙飞_吟唱_单手短
霸王龙飞_施放起_单手短
霸王龙飞_施放落_单手短
霸王龙飞_吟唱_双手短
霸王龙飞_施放起_双手短
霸王龙飞_施放落_双手短
霸王龙飞_吟唱_双手长
霸王龙飞_施放起_双手长
霸王龙飞_施放落_双手长
霸王献鼎_吟唱_单手短
霸王献鼎_施放起_单手短
霸王献鼎_施放落_单手短
霸王献鼎_吟唱_双手短
霸王献鼎_施放起_双手短
霸王献鼎_施放落_双手短
霸王献鼎_吟唱_双手长
霸王献鼎_施放起_双手长
霸王献鼎_施放落_双手长
霸王断岳_吟唱_单手短
霸王断岳_施放起_单手短
霸王断岳_施放落_单手短
霸王断岳_吟唱_双手短
霸王断岳_施放起_双手短
霸王断岳_施放落_双手短
霸王断岳_吟唱_双手长
霸王断岳_施放起_双手长
霸王断岳_施放落_双手长
霸王暴怒_吟唱_单手短
霸王暴怒_施放起_单手短
霸王暴怒_施放落_单手短
霸王暴怒_吟唱_双手短
霸王暴怒_施放起_双手短
霸王暴怒_施放落_双手短
霸王暴怒_吟唱_双手长
霸王暴怒_施放起_双手长
霸王暴怒_施放落_双手长
忘情式_吟唱_单手短
忘情式_施放起_单手短
忘情式_施放落_单手短
忘情式_吟唱_双手短
忘情式_施放起_双手短
忘情式_施放落_双手短
忘情式_吟唱_双手长
忘情式_施放起_双手长
忘情式_施放落_双手长
追魂诀_吟唱_单手短
追魂诀_施放起_单手短
追魂诀_施放落_单手短
追魂诀_吟唱_双手短
追魂诀_施放起_双手短
追魂诀_施放落_双手短
追魂诀_吟唱_双手长
追魂诀_施放起_双手长
追魂诀_施放落_双手长
剑气纵横_吟唱_单手短
剑气纵横_施放起_单手短
剑气纵横_施放落_单手短
剑气纵横_吟唱_双手短
剑气纵横_施放起_双手短
剑气纵横_施放落_双手短
剑气纵横_吟唱_双手长
剑气纵横_施放起_双手长
剑气纵横_施放落_双手长
万剑诀_吟唱_单手短
万剑诀_施放起_单手短
万剑诀_施放落_单手短
万剑诀_吟唱_双手短
万剑诀_施放起_双手短
万剑诀_施放落_双手短
万剑诀_吟唱_双手长
万剑诀_施放起_双手长
万剑诀_施放落_双手长
金钟罩_吟唱_通用
金钟罩_施放起_通用
金钟罩_施放落_通用
金钟罩_吟唱_双手长
金钟罩_施放起_双手长
金钟罩_施放落_双手长
回城术_吟唱_通用
回城术_施放起_通用
回城术_施放落_通用
爆气_吟唱_通用
爆气_施放起_通用
爆气_施放落_通用
蓄气_吟唱_通用
蓄气_施放起_通用
蓄气_施放落_通用
挑衅_吟唱_通用
挑衅_施放起_通用
挑衅_施放落_通用
仙元爆发_吟唱_通用
仙元爆发_施放起_通用
仙元爆发_施放落_通用
魔元爆发_吟唱_通用
魔元爆发_施放起_通用
魔元爆发_施放落_通用
雷霆震击_吟唱_通用
雷霆震击_施放起_通用
雷霆震击_施放落_通用
无影_吟唱_单手短
无影_施放起_单手短
无影_施放落_单手短
无影_吟唱_双手短
无影_施放起_双手短
无影_施放落_双手短
一闪_吟唱_单手短
一闪_施放起_单手短
一闪_施放落_单手短
一闪_吟唱_双手短
一闪_施放起_双手短
一闪_施放落_双手短
群体物经_吟唱_通用
群体物经_施放起_通用
群体物经_施放落_通用
狂风_吟唱_双手短
狂风_施放起_双手短
狂风_施放落_双手短
碎颅_吟唱_双手长
碎颅_施放起_双手长
碎颅_施放落_双手长
回旋击_吟唱_双手短
回旋击_施放起_双手短
回旋击_施放落_双手短
龙爪手_施放起_空拳
龙爪手_施放落_空拳
虎击改_施放起_单手短
虎击改_施放落_单手短
虎击改_施放起_双手短
虎击改_施放落_双手短
虎击改_施放起_双手长
虎击改_施放落_双手长
虎击改_施放起_空拳
虎击改_施放落_空拳
横扫千军改_施放起_单手短
横扫千军改_施放落_单手短
横扫千军改_施放起_双手短
横扫千军改_施放落_双手短
横扫千军改_施放起_双手长
横扫千军改_施放落_双手长
横扫千军改_施放起_空拳
横扫千军改_施放落_空拳
凌风改_吟唱_单手短
凌风改_施放起_单手短
凌风改_施放落_单手短
凌风改_吟唱_双手短
凌风改_施放起_双手短
凌风改_施放落_双手短
凌风改_吟唱_双手长
凌风改_施放起_双手长
凌风改_施放落_双手长
凌风改_吟唱_空拳
凌风改_施放起_空拳
凌风改_施放落_空拳
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2aefef74167a14447b703fe58796fc14
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 98a4892172bb4864d8e92a4f17b94d63
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -517,6 +517,7 @@ namespace BrewMonster
if (ui != null)
{
ui.RefreshAll();
ui.RefreshCharacterModelPreview();
}
UpdateEquipSkins();
+34
View File
@@ -578,6 +578,7 @@ namespace BrewMonster
case EC_MsgDef.MSG_HST_SETMOVESTAMP: OnMsgHstSetMoveStamp(Msg); break;
case EC_MsgDef.MSG_HST_SANCTUARY: OnMsgHstSanctuary(Msg); break;
case EC_MsgDef.MSG_HST_RECEIVEEXP: OnMsgHstReceiveExp(Msg); break;
case EC_MsgDef.MSG_HST_ROOTNOTIFY: OnMsgHstRootNotify(Msg) ; break;
default:
// Uncomment to debug unhandled messages
// Debug.LogWarning($"[CECHostPlayer] ProcessMessage: Unhandled message {msg}");
@@ -604,6 +605,39 @@ namespace BrewMonster
}*/
}
private void OnMsgHstRootNotify(in ECMSG Msg)
{
if ((int)Msg.dwParam2 == CommandID.HOST_NOTIFY_ROOT)
{
cmd_host_notify_root pCmd = GPDataTypeHelper.FromBytes<cmd_host_notify_root>((byte[])Msg.dwParam1);
m_dwLIES |=(uint) (1 << pCmd.type);
if (pCmd.type != 3)
{
// Force pull host to specified position
SetPos(EC_Utility.ToVector3(pCmd.pos));
}
if (IsRooting())
{
if (m_pWorkMan.IsFollowing()){
m_pWorkMan.FinishRunningWork(Host_work_ID.WORK_FOLLOW);
}
if (m_pWorkMan.IsMovingToPosition()){
m_pWorkMan.FinishRunningWork(Host_work_ID.WORK_MOVETOPOS);
}
if (m_pWorkMan.IsTracing()){
m_pWorkMan.FinishRunningWork(Host_work_ID.WORK_TRACEOBJECT);
}
}
}
else if ((int)Msg.dwParam2 == CommandID.HOST_DISPEL_ROOT)
{
cmd_host_dispel_root pCmd = GPDataTypeHelper.FromBytes<cmd_host_dispel_root>((byte[])Msg.dwParam1);
m_dwLIES &=(uint) ~(1 << pCmd.type);
}
}
private void OnMsgHstPickupMoney(ECMSG msg)
{
var data = msg.dwParam1 as byte[];
+36 -14
View File
@@ -18,12 +18,13 @@ namespace BrewMonster.UI
[SerializeField] private RectTransform parentItems;
[SerializeField] private Button createCharacterButton;
[SerializeField] private Button _btnEnterGame;
[SerializeField] private Button _btnExit;
[SerializeField] private CreateCharacterScreen createCharacterScreen;
[SerializeField] private PlayerModelPreview playerModelPreview;
private CharacterItemUI _selectingCharacterItemUI;
private Action<RoleInfo> _onClickItemChar;
private Action<RoleInfo> _onCreateCharacterComplete;
private Action _onExit;
private List<RoleInfo> _roleInfos;
private Coroutine _showModelReadyCoroutine;
@@ -34,13 +35,18 @@ namespace BrewMonster.UI
_btnEnterGame.onClick.AddListener(OnClickedEnterGame);
_btnEnterGame.gameObject.SetActive(false);
if(_btnExit != null)
{
_btnExit.onClick.AddListener(OnClickedExit);
}
if (_roleInfos != null && _roleInfos.Count > 0)
{
playerModelPreview?.ShowAllPlayerModels(_roleInfos);
PlayerModelPreview.Instance?.ShowAllPlayerModels(_roleInfos);
}
else
{
playerModelPreview?.ClearModels();
PlayerModelPreview.Instance?.ClearModels();
}
}
@@ -58,10 +64,11 @@ namespace BrewMonster.UI
}
}
public void InitScreen(List<RoleInfo> roleInfos, Action<RoleInfo> OnClickItemChar, Action<RoleInfo> onCreateCharacterComplete = null)
public void InitScreen(List<RoleInfo> roleInfos, Action<RoleInfo> OnClickItemChar, Action<RoleInfo> onCreateCharacterComplete = null, Action onExit = null)
{
_onClickItemChar = OnClickItemChar;
_onCreateCharacterComplete = onCreateCharacterComplete;
_onExit = onExit;
// Clear existing items
if (parentItems != null)
@@ -111,7 +118,7 @@ namespace BrewMonster.UI
addButton.onClick.AddListener(OnCreateCharacterClicked);
}
}
}
else
{
@@ -121,14 +128,14 @@ namespace BrewMonster.UI
createCharacterButton.gameObject.SetActive(true);
}
}
// Load player preview 3D models
playerModelPreview?.ShowAllPlayerModels(roleInfos);
PlayerModelPreview.Instance?.ShowAllPlayerModels(roleInfos);
}
else
{
if (playerModelPreview != null)
playerModelPreview.ShowAllPlayerModels(null);
if (PlayerModelPreview.Instance != null)
PlayerModelPreview.Instance.ShowAllPlayerModels(null);
// If roleInfos is null, show createCharacterButton
if (createCharacterButton != null)
{
@@ -144,7 +151,7 @@ namespace BrewMonster.UI
return;
}
if(_selectingCharacterItemUI!=null)
if (_selectingCharacterItemUI != null)
{
_selectingCharacterItemUI.SetFocus(false);
}
@@ -152,9 +159,11 @@ namespace BrewMonster.UI
_selectingCharacterItemUI = characterItemUI;
_selectingCharacterItemUI.SetFocus(true);
_btnEnterGame.gameObject.SetActive(true);
_btnEnterGame.interactable = false; // Disable the button until the model is ready
if (playerModelPreview == null || characterItemUI.RoleInfo == null)
if (PlayerModelPreview.Instance == null || characterItemUI.RoleInfo == null)
{
_btnEnterGame.interactable = true; // If we can't show the model, allow entering the game anyway
return;
}
@@ -168,21 +177,23 @@ namespace BrewMonster.UI
private IEnumerator ShowSelectedModelWhenReady(int roleId)
{
while (isActiveAndEnabled && playerModelPreview != null)
while (isActiveAndEnabled && PlayerModelPreview.Instance != null)
{
if (_pendingShowModelRoleId != roleId)
{
yield break; // Role changed, stop this coroutine
}
if (playerModelPreview.playerModelIds != null && playerModelPreview.playerModelIds.Contains(roleId))
if (PlayerModelPreview.Instance.playerModelIds != null && PlayerModelPreview.Instance.playerModelIds.Contains(roleId))
{
playerModelPreview.ShowPlayerModel(roleId);
PlayerModelPreview.Instance.ShowPlayerModel(roleId);
_btnEnterGame.interactable = true;
_showModelReadyCoroutine = null;
yield break; // Model is ready, show it and stop this coroutine
}
yield return null;
}
_btnEnterGame.interactable = true;
_showModelReadyCoroutine = null;
}
@@ -227,5 +238,16 @@ namespace BrewMonster.UI
BMLogger.LogError("No role selected");
}
}
private void OnClickedExit()
{
_onExit?.Invoke();
gameObject.SetActive(false);
if(PlayerModelPreview.Instance != null)
{
PlayerModelPreview.Instance.HideAllPlayerModels();
}
}
}
}
+201
View File
@@ -0,0 +1,201 @@
# AddressableManager Public API
`AddressableManager` is a Unity `MonoSingleton` wrapper around Unity Addressables. It initializes the Addressables system, loads selected asset types, caches loaded handles, and exposes methods for delayed or immediate release.
This document lists only the public functions from `Assets/PerfectWorld/Scripts/Addressable/AddressableManager.cs`.
## `bool IsInitialized()`
Returns whether the Addressables initialization callback has completed successfully.
Use this when another system needs to quickly check if `AddressableManager` is ready before requesting assets. It returns `true` only after `Addressables.InitializeAsync()` finishes with `AsyncOperationStatus.Succeeded`.
If initialization fails, this method remains `false`.
## `UniTask WaitUntilInitializedAsync()`
Waits until `AddressableManager` has finished initializing Unity Addressables.
If initialization has already completed, this returns `UniTask.CompletedTask` immediately. Otherwise, it waits on the internal initialization completion source that is resolved by the Addressables initialization callback.
Call this before loading assets from systems that may start before Addressables is ready.
Example usage:
```csharp
await AddressableManager.Instance.WaitUntilInitializedAsync();
```
## `Task<TextAsset> LoadTextAssetAsync(string assetPath)`
Loads a `TextAsset` asynchronously through Unity Addressables.
`assetPath` must match a valid Addressables key, normally the asset address. The method first increases the internal reference count for this path and removes the path from the delayed-release list so the asset will not be released while it is being used again.
If the text asset was already loaded and the cached handle is still valid, the method returns the cached `TextAsset` immediately. If no valid cached handle exists, it calls:
```csharp
Addressables.LoadAssetAsync<TextAsset>(assetPath)
```
After loading completes, the handle is stored in the text asset cache and the loaded `TextAsset` is returned.
If loading throws an exception, the method logs an error and returns `null`.
Important behavior:
- The loaded asset is cached by `assetPath`.
- Repeated calls for the same valid path reuse the cached handle.
- The method may return `null` when the asset cannot be loaded.
- Release is not automatic after loading. Call a release method when the asset is no longer needed.
## `Task<GameObject> LoadPrefabAsync(string assetPath)`
Loads a prefab asynchronously through Unity Addressables and returns it as a `GameObject`.
`assetPath` must match a valid Addressables key for a `GameObject` prefab. Before loading, the method removes the path from the delayed-release list. It then calls `KeyExists(assetPath, typeof(GameObject))`; if the key is considered missing, it logs a warning and returns `null`.
If the prefab is already cached and the cached handle is valid, the method returns the cached prefab immediately. Otherwise, it calls:
```csharp
Addressables.LoadAssetAsync<GameObject>(assetPath)
```
After loading completes successfully, the handle is stored in the prefab cache and the prefab `GameObject` is returned.
If the Addressables operation reports an exception, or if an exception is thrown while loading, the method logs the failure and returns `null`. In the Unity Editor, failed paths are added to an invalid-path cache so later `KeyExists` checks can reject them quickly.
Important behavior:
- This loads the prefab asset itself, not an instantiated scene object.
- Callers that need an instance should instantiate the returned prefab separately.
- The loaded prefab handle is cached by `assetPath`.
- The method may return `null` when the key does not exist or loading fails.
## `Task<AudioClip> LoadAudioClipAsync(string assetPath)`
Loads an `AudioClip` asynchronously through Unity Addressables.
`assetPath` must match a valid Addressables key for an audio asset. The method removes the path from the delayed-release list before checking the cache.
If the audio clip is already cached and the cached handle is valid, the method returns the cached `AudioClip` immediately. Otherwise, it calls:
```csharp
Addressables.LoadAssetAsync<AudioClip>(assetPath.Trim())
```
After loading completes successfully, the handle is stored in the audio cache and the loaded `AudioClip` is returned.
If the Addressables operation reports an exception, or if an exception is thrown while loading, the method logs the failure and returns `null`.
Important behavior:
- The Addressables load uses `assetPath.Trim()`.
- The cache key is the original `assetPath` string.
- Repeated calls for the same cached path can return immediately.
- The method may return `null` when loading fails.
## `bool TryGetCachedAudioClip(string assetPath, out AudioClip clip)`
Attempts to retrieve an already loaded `AudioClip` from the internal audio cache without starting a new Addressables load.
If `assetPath` is null or empty, the method returns `false` and sets `clip` to `null`.
If the audio cache contains a valid handle for `assetPath` and the handle has a non-null result, the method assigns that cached `AudioClip` to `clip` and returns `true`.
If no valid cached audio clip exists, the method returns `false` and leaves `clip` as `null`.
Use this when a caller wants to play audio immediately if it is already loaded, while avoiding an async load path.
## `void ReleaseAsset(string assetPath)`
Marks an asset path for delayed release.
This method does not immediately call `Addressables.Release`. Instead, it records a timestamp in the delayed-release dictionary. During `Update`, once the configured timeout has elapsed, the manager force releases the asset by path.
If the same path is released multiple times before it is force released, the earliest release timestamp is kept.
Important behavior:
- This is a delayed release request.
- The asset remains cached until the release timeout expires.
- If the asset is loaded again before timeout, load methods remove it from the delayed-release list.
- The path must match the same key used to cache the asset.
## `void ForceReleaseAsset(string assetPath)`
Immediately releases a cached asset by path.
The method checks the prefab cache first, then the text asset cache, then the audio cache. If the path exists in one of those caches and the stored handle is valid, it calls:
```csharp
Addressables.Release(handle)
```
After releasing, the method removes the path from the matching cache. For audio assets, it also logs that the audio asset was force released.
Important behavior:
- This releases immediately, unlike `ReleaseAsset(string assetPath)`.
- Only one matching cache entry is released because the method uses an `if / else if` chain.
- If the path is not found in any cache, the method does nothing.
## `void ReleaseAsset(AsyncOperationHandle<GameObject> handle)`
Releases a specific `GameObject` Addressables operation handle directly.
If the provided handle is valid, the method calls:
```csharp
Addressables.Release(handle)
```
This overload does not remove entries from `AddressableManager`'s internal prefab cache because it does not know which `assetPath` the handle belongs to. Prefer releasing by `assetPath` when the asset was loaded through `AddressableManager` and should also be removed from its cache.
## `void ReleaseAllAssets()`
Immediately releases all cached prefab and audio assets.
The method iterates through the prefab cache and releases each valid prefab handle. It then clears the prefab cache. After that, it iterates through the audio cache, releases each valid audio handle, and clears the audio cache.
Important behavior:
- This releases prefab and audio caches.
- It does not currently release cached text assets from `_loadedTextAssets`.
- It logs `"AddressableManager: Released all assets"` after completing.
- `OnDestroy` calls this method automatically.
## `bool IsAssetLoaded(string assetPath)`
Checks whether a prefab asset path is currently loaded in the prefab cache.
The method returns `true` only when `_loadedPrefabAssets` contains `assetPath` and the stored handle is valid.
Important behavior:
- This checks only loaded prefab assets.
- It does not check cached text assets.
- It does not check cached audio assets.
## `static bool KeyExists(object key, System.Type type = null)`
Checks whether an Addressables key exists.
In non-editor builds, the method iterates through all loaded Addressables resource locators and calls:
```csharp
locator.Locate(key, type, out var locations)
```
It returns `true` when a locator finds at least one matching resource location. If no locator finds a match, it returns `false`.
The optional `type` parameter can restrict the lookup to a specific asset type, such as `typeof(GameObject)`.
In the Unity Editor, this method currently returns `true` unless the key string exists in the manager's `_invalidAssetPaths` set. That means editor behavior is based on paths that previously failed to load, not a full catalog lookup.
Important behavior:
- Runtime builds perform a locator-based catalog lookup.
- Editor builds use the invalid-path cache.
- `LoadPrefabAsync` uses this method before attempting to load a prefab.
- Passing a type helps avoid matching an address that exists for a different asset kind.
+74
View File
@@ -0,0 +1,74 @@
# Pooling Manager Execution Flow
## Purpose
The pooling system recycles Addressables-backed `GameObject` instances instead of repeatedly calling `Instantiate` and `Destroy`. Each Addressables key owns one runtime pool that tracks active objects, idle objects, and a memory-release timer.
`PoolManager` loads prefab assets through `AddressableManager`, so it uses the same Addressables initialization path and loaded-prefab cache as the rest of the project. Runtime instances are ordinary instantiated `GameObject`s owned by the pool.
## Runtime Files
- `IPoolable.cs`: prefab-side lifecycle interface.
- `ObjectPool.cs`: owns instances and the loaded prefab reference for one Addressables key.
- `PoolManager.cs`: singleton service used by gameplay, server packet handlers, UI code, and effects systems.
## Spawn Flow
1. Call `PoolManager.Instance.SpawnAsync(...)` or `PoolManager.Instance.Spawn(...)`.
2. `PoolManager` finds or creates an `ObjectPool` for the Addressables key.
3. The pool updates its `memoryReleaseTTL` from the spawn parameter.
4. If a memory release countdown is running, the pool cancels it.
5. The pool reuses an idle object when available.
6. If no idle object exists, the pool waits for `AddressableManager` initialization, loads the prefab with `AddressableManager.LoadPrefabAsync()`, then instantiates it.
7. The instance is parented, positioned, rotated, activated, and all `IPoolable.OnSpawn()` hooks are called.
8. If `autoDespawnTime > 0`, `PoolManager` starts a version-checked auto-despawn coroutine.
9. The spawned `GameObject` is returned to the caller.
## Despawn Flow
1. Call `PoolManager.Instance.Despawn(gameObject)` or `PoolManager.Instance.Despawn(addressableKey, gameObject)`.
2. The target pool validates that the object is currently active.
3. All `IPoolable.OnDespawn()` hooks are called.
4. The object is deactivated, parented under the pool root, and pushed into the idle stack.
5. When the active count reaches zero, the pool starts the memory release countdown.
## Memory Release Flow
1. The countdown waits for the latest `memoryReleaseTTL` value supplied by spawn calls for that key.
2. A new spawn for the same key cancels the countdown and keeps the prefab plus idle instances available.
3. If the countdown completes while active count is still zero, the pool:
- unregisters itself from `PoolManager`;
- destroys all pooled instances;
- calls `AddressableManager.ForceReleaseAsset(addressableKey)` to release the cached prefab asset;
- destroys the pool root object.
## Auto Despawn Safety
Auto despawn stores the instance spawn version when the coroutine starts. If the object is manually despawned and reused before the timer completes, the version changes and the old coroutine will not despawn the new lifecycle.
## Shutdown Flow
`PoolManager` subscribes to `AddressableManager.OnDispose`. When the Addressables manager is disposed, all pools release their instances and prefab assets before `AddressableManager.ReleaseAllAssets()` runs. `PoolManager.OnDestroy()` also releases all pools as a fallback.
## Example
```csharp
GameObject fx = await PoolManager.Instance.SpawnAsync(
"effects/fireball.prefab",
hitPosition,
Quaternion.identity,
memoryReleaseTTL: 15f,
autoDespawnTime: 2f);
```
For coroutine-based callers:
```csharp
PoolManager.Instance.Spawn(
"effects/fireball.prefab",
hitPosition,
Quaternion.identity,
memoryReleaseTTL: 15f,
autoDespawnTime: 2f,
onComplete: spawned => { /* use spawned */ });
```