/******************************************************************************** * 文件名: ObjManager.cs * 全路径: \Script\Obj\ObjManager.cs * 创建人: 李嘉 * 创建时间:2013-10-25 * * 功能说明:游戏逻辑Obj管理器,对游戏中所有Obj提供创建,移除和管理 * 修改记录: * //2013-11-18 修改list结构为Dictionary结构, *********************************************************************************/ using System; using System.Collections.Generic; using Games.GlobeDefine; using Games.LogicObj; using GCGame.Table; using Module.Log; using UnityEngine; public class OtherPlayerVisibility { public readonly Obj_OtherPlayer character; public readonly Collider collider; public bool isHide; public OtherPlayerVisibility(Obj_OtherPlayer character, bool isHide) { this.character = character; collider = character.GetComponent(); this.isHide = isHide; } public void SetHide(bool hide) { if (isHide != hide) { isHide = hide; if (collider != null) collider.enabled = !isHide; character.SetHideByMask(GetHideMask(isHide)); } } public static int GetHideMask(bool hide) { return hide ? int.MaxValue : PlayerPreferenceData.OtherPlayerHideMask; } } /// /// 死亡Npc延迟回收装置 /// // Npc死亡后,如果收到死亡删除协议,客户端将延迟回收Npc public class DeadNpcObjHolder { private readonly List _npcData = new List(); public DeadNpcObjHolder() { GameManager.AddLateUpdate(OnLateUpdate, GameManager.deadNpcUpdateOrder); } public void Destroy() { GameManager.RemoveLateUpdate(OnLateUpdate, GameManager.deadNpcUpdateOrder); } public bool TryAddObj(Obj obj) { var objNpc = obj as Obj_NPC; if (objNpc != null && objNpc.IsDie() && objNpc.ModelNode.model != null && objNpc.ObjEffectLogic != null && objNpc.BaseAttr != null) { var roleBaseAttr = TableManager.GetRoleBaseAttrByID(objNpc.BaseAttr.RoleBaseID, 0); if (roleBaseAttr != null && roleBaseAttr.CharModelID > GlobeVar.INVALID_ID && (roleBaseAttr.RampType == 2 || roleBaseAttr.RampType == 3)) { var charModel = TableManager.GetCharModelByID(roleBaseAttr.CharModelID, 0); if (charModel != null && charModel.DieShadetime > 0f) { var effectTable = TableManager.GetEffectByID(GlobeVar.deathEffectId, 0); if (effectTable != null) { objNpc.ObjEffectLogic.PlaySpecialEffect(effectTable, charModel.DieShadetime); _npcData.Add(new DeadNpcData(objNpc, Time.realtimeSinceStartup + charModel.DieShadetime)); } return true; } } } return false; } public void Clear() { for (var i = 0; i < _npcData.Count; i++) if (_npcData[i].objNpc != null) _npcData[i].objNpc.DestroyObj(); _npcData.Clear(); } private void OnLateUpdate() { for (var i = _npcData.Count - 1; i >= 0; i--) if (_npcData[i].removeTime < Time.realtimeSinceStartup) { var npcData = _npcData[i]; _npcData.RemoveAt(i); if (npcData.objNpc != null) npcData.objNpc.DestroyObj(); } } private struct DeadNpcData { public readonly Obj_NPC objNpc; public readonly float removeTime; public DeadNpcData(Obj_NPC objNpc, float removeTime) { this.objNpc = objNpc; this.removeTime = removeTime; } } } public class ObjManager : Singleton { /// /// 在角色创建前,收到的特效数据将会统一保存在这里 /// private readonly List _effectCacheList = new List(); // OtherPlayer隐藏逻辑 // 创建时,如果当前服务器创建角色小于限制,记录数据并创建Obj;否则只记录数据不创建。 // 删除时,删除数据,如果角色有Obj,则为最早无Obj数据创建Obj。 // 暂时没有限制数值改变时,处理主动删除和主动创建 private readonly List _otherList = new List(); // 当前客户端带ServerId的Obj池,使用Obj(场景中)名字(唯一)索引,对应Obj的数据 //动态的,注意使用上限 public readonly Dictionary ObjPools = new Dictionary(); // 采集物等无ServerId物体的保存位置 public readonly Dictionary otherObjPools = new Dictionary(); // 死亡Npc的延迟回收器 public readonly DeadNpcObjHolder deadNpcObjHolder = new DeadNpcObjHolder(); //怪物归属缓存 public List m_Belongs = new List(); //当前客户端主角色 public Obj_MainPlayer MainPlayer { get; private set; } public void AddPoolObj(Obj obj) { try { Obj contain; if (ObjPools.TryGetValue(obj.ServerID, out contain)) { // 试图第二次创建一个物体,直接忽略 if (contain != obj) { // 重复创建ServerId相同的物体,报错,然后按之前逻辑摧毁物体 LogModule.ErrorLog(string.Format("试图创建ServerId重复的物体{0}", obj.ServerID)); if (contain != null) contain.DestroyObj(); ObjPools[obj.ServerID] = obj; } } else { ObjPools.Add(obj.ServerID, obj); } } catch (Exception ex) { LogModule.ErrorLog(string.Format("ObjManager AddObj {0} Error: ", obj.ServerID) + ex); } } public ObjManager() { PlayerPreferenceData.ShowOtherPlayerCount.onSettingUpdate += RefreshOtherPlayerCount; } public void RemoveMainPlayer() { MainPlayer = null; } public bool RemovePoolObj(int serverId, bool isDead) { var result = false; // 注:存在其他玩家Obj未建立,但是数据存在的情况 RemoveHidePlayerByServerId(serverId); GetEffectCache(serverId, ListGetMode.remove); Obj obj; if (ObjPools.TryGetValue(serverId, out obj)) { ObjPools.Remove(serverId); if (!deadNpcObjHolder.TryAddObj(obj)) DestroyObj(obj); result = true; } return result; } // 已经不再维护这个接口! //public bool RemovePoolObj(Obj removeObj) //{ // var result = false; // // 注:存在其他玩家Obj未建立,但是数据存在的情况 // RemoveHidePlayerByServerId(removeObj.ServerID); // GetEffectCache(removeObj.ServerID, ListGetMode.remove); // Obj obj; // if (ObjPools.TryGetValue(removeObj.ServerID, out obj)) // { // if (removeObj != obj) // { // LogModule.ErrorLog(string.Format("试图移除ServerId为{0}的物体,但是实际ObjManager中存的是另一个物体!", removeObj.ServerID)); // DestroyObj(obj); // } // ObjPools.Remove(removeObj.ServerID); // DestroyObj(removeObj); // result = true; // } // return result; //} public ObjEffectCache GetEffectCache(int serverId, ListGetMode mode) { ObjEffectCache result = null; for (var i = 0; i < _effectCacheList.Count; i++) if (_effectCacheList[i].serverId == serverId) { result = _effectCacheList[i]; if (mode == ListGetMode.remove) _effectCacheList.RemoveAt(i); break; } if (mode == ListGetMode.create && result == null) { result = new ObjEffectCache(serverId); _effectCacheList.Add(result); } return result; } private void DestroyObj(Obj obj) { if (obj != null) obj.DestroyObj(); } private void RemoveHidePlayerByServerId(int serverId) { // 注:存在其他玩家Obj未建立,但是数据存在的情况 // 删除一个其他玩家后,重新监视是否拥有足够的其他玩家。注:其他玩家排序正常应该是严格队列,前面部分都有Obj,后面部分都没有Obj var playerIndex = _otherList.FindIndex(a => a.character.ServerID == serverId); if (playerIndex >= 0) { var otherPlayer = _otherList[playerIndex]; _otherList.RemoveAt(playerIndex); if (!otherPlayer.isHide && !IsOtherFull()) { if (MainPlayer != null) { OtherPlayerVisibility visibility = null; var minDistSqr = float.PositiveInfinity; for (var i = 0; i < _otherList.Count; i++) if (_otherList[i].isHide) { var distSqr = (_otherList[i].character.Position - MainPlayer.Position).sqrMagnitude; if (distSqr < minDistSqr) { visibility = _otherList[i]; minDistSqr = distSqr; } } if (visibility != null) visibility.SetHide(false); } } } } public void CleanSceneObj(bool isCleanMainPlayer = false) { foreach (var otherObj in otherObjPools.Values) if (otherObj != null) otherObj.DestroyObj(); otherObjPools.Clear(); if (isCleanMainPlayer) MainPlayer = null; foreach (var obj in ObjPools.Values) // 不删除主角时,不摧毁主角的Obj,清空字典后再将主角Obj添加回去 if (MainPlayer == null || obj != MainPlayer) DestroyObj(obj); ObjPools.Clear(); deadNpcObjHolder.Clear(); if (!isCleanMainPlayer && MainPlayer != null) AddPoolObj(MainPlayer); _otherList.Clear(); _effectCacheList.Clear(); } public bool IsOtherFull() { var count = 0; for (var i = 0; i < _otherList.Count; i++) if (!_otherList[i].isHide) count++; return count >= PlayerPreferenceData.ShowOtherPlayerCount; } //如果先创建NPC后创建主角的话,NPC名字颜色等会表现异常,服务器大爷说他们是先发的创建的主角的协议的,没办法客户端这边先处理一下吧 public void OnCreateMainPlayer() { var valus = new List(ObjPools.Values); for (var i = 0; i < valus.Count; i++) { var Character = valus[i] as Obj_Character; if (Character != null) Character.OptForceChange(); } } public void SetMainPlayer(ObjParent player) { var mainPlayer = player as Obj_MainPlayer; if (mainPlayer != null) MainPlayer = mainPlayer; } public ObjParent NewCharacterObj(GameDefine_Globe.OBJ_TYPE type, ObjParent_Init_Data initData) { ObjParent newObj = null; // 注:非服务器记录的Obj在读盘期间应该正常响应 if (NeedCreate(initData as Obj_Init_Data)) switch (type) { case GameDefine_Globe.OBJ_TYPE.OBJ_MAIN_PLAYER: { if (MainPlayer == null) { MainPlayer = ObjParent.Create(initData, "Prefab/Model/PlayerRoot"); newObj = MainPlayer; if (MainPlayer != null) { //Object.DontDestroyOnLoad(MainPlayer.gameObject); //m_bBeginAsycCreateMainPlayer = true; AddPoolObj(MainPlayer); OnCreateMainPlayer(); ApplyCachedEffect(MainPlayer, GlobeVar.INVALID_ID); ApplyCachedEffect(MainPlayer); } } else { MainPlayer.Init(initData); } } break; case GameDefine_Globe.OBJ_TYPE.OBJ_NPC: { if (initData is Obj_Init_Data) newObj = CreateCharacter(initData, "Prefab/Model/NPCRoot"); } break; case GameDefine_Globe.OBJ_TYPE.OBJ_FELLOW: { newObj = CreateCharacter(initData, "Prefab/Model/FellowRoot"); } break; case GameDefine_Globe.OBJ_TYPE.OBJ_OTHER_PLAYER: { var otherPlayerData = initData as Obj_Player_Init_Data; if (otherPlayerData != null) { var isHide = IsOtherFull(); otherPlayerData.m_hideMask = OtherPlayerVisibility.GetHideMask(isHide); var otherPlayer = CreateOtherPlayer(otherPlayerData); var visibilityData = new OtherPlayerVisibility(otherPlayer, isHide); _otherList.Add(visibilityData); newObj = otherPlayer; } } break; case GameDefine_Globe.OBJ_TYPE.OBJ_ZOMBIE_PLAYER: { newObj = CreateCharacter(initData, "Prefab/Model/OtherPlayerRoot"); } break; case GameDefine_Globe.OBJ_TYPE.OBJ_DROP_ITEM: { newObj = ObjParent.Create(initData, "Prefab/Model/DropItemInfo"); } break; case GameDefine_Globe.OBJ_TYPE.OBJ_GHARRY: { newObj = CreateCharacter(initData, "Prefab/Model/FellowRoot"); } break; case GameDefine_Globe.OBJ_TYPE.OBJ_WEDDINGCAR: { newObj = CreateCharacter(initData, "Prefab/Model/NPCRoot"); } break; case GameDefine_Globe.OBJ_TYPE.OBJ_CHILD: { newObj = CreateCharacter(initData, "Prefab/Model/NPCRoot"); } break; case GameDefine_Globe.OBJ_TYPE.OBJ_SNARE: { newObj = ObjParent.Create(initData, "Prefab/Model/SnareRoot"); } break; case GameDefine_Globe.OBJ_TYPE.OBJ_COLLECTITEM: { newObj = ObjParent.Create(initData, "Prefab/Model/CollectItemRoot"); } break; case GameDefine_Globe.OBJ_TYPE.OBJ_TELEPORTPOINT: { newObj = ObjParent.Create(initData, "Prefab/Model/Teleport"); } break; case GameDefine_Globe.OBJ_TYPE.OBJ_JUMPPOINT: { newObj = ObjParent.Create(initData, "Prefab/Model/Jump"); } break; case GameDefine_Globe.OBJ_TYPE.OBJ_CLIENTNPC: { newObj = CreateCharacter(initData, "Prefab/Model/NPCRoot"); } break; } return newObj; } //根据ServerID来查找Obj public Obj FindObjInScene(int nServerID) { Obj result; ObjPools.TryGetValue(nServerID, out result); return result; } //根据ServerID来查找Obj_Character public Obj_Character FindObjCharacterInScene(int nServerID) { return FindObjInScene(nServerID) as Obj_Character; } public Obj_OtherPlayer FindOtherPlayerByGuid(ulong guid) { if (MainPlayer != null && MainPlayer.GUID == guid) return MainPlayer; var visibility = _otherList.Find(a => a.character.GUID == guid); return visibility == null ? null : visibility.character; } //根据Obj_Character的BaseAttr中的RoleID来查找NPC //遍历,不推荐反复使用 public Obj_Character FindObjCharacterInSceneByRoleID(int roleID, bool bIsAlive = true,bool isNearest = false) { Obj_Character objTarget = null; float distance = -1; foreach (var obj in ObjPools.Values) { var objChar = obj as Obj_NPC; if (objChar && objChar.BaseAttr != null && objChar.BaseAttr.RoleBaseID == roleID) { //是否要寻找非死亡目标 if (bIsAlive && objChar.IsDie()) continue; if(isNearest==false) return objChar; else { float dis = Vector3.Distance(Singleton.Instance.MainPlayer .Position, objChar.Position); if (distance == -1 || dis < distance) { distance = dis; objTarget = objChar; } } } } return objTarget; } //根据Obj_Character的BaseAttr中的名字来查找NPC //遍历,不推荐反复使用 public Obj_Character FindObjCharacterInSceneByName(string szBaseAttrName, bool bIsAlive = true) { Obj_Character objTarget = null; var minDistanceSqr = 8f * 8f; foreach (var obj in ObjPools.Values) { var objChar = obj as Obj_NPC; if (objChar == null) continue; string roleName = objChar.BaseAttr.RoleName; if (objChar.ObjType == GameDefine_Globe.OBJ_TYPE.OBJ_NPC && objChar.BaseAttr.RoleData != null && string.IsNullOrEmpty(objChar.BaseAttr.RoleData.Name) == false) roleName = objChar.BaseAttr.RoleData.Name; if (objChar && objChar.BaseAttr != null && string.Equals(roleName, szBaseAttrName, StringComparison.InvariantCulture)) { //是否要寻找非死亡目标 if (bIsAlive && objChar.IsDie()) continue; var nType = Reputation.GetObjReputionType(objChar); if (nType == CharacterDefine.REPUTATION_TYPE.REPUTATION_INVALID || nType == CharacterDefine.REPUTATION_TYPE.REPUTATION_FRIEND) { objTarget = objChar; break; } if (MainPlayer != null) { var distanceSqr = (MainPlayer.Position - objChar.Position).sqrMagnitude; if (distanceSqr < minDistanceSqr) { minDistanceSqr = distanceSqr; objTarget = objChar; } } } } return objTarget; } //查找某个玩家 public Obj_OtherPlayer FindOtherPlayerInScene(ulong guid) { return FindOtherPlayerByGuid(guid); } public bool NeedCreate(Obj_Init_Data initData) { var result = true; if (initData != null) { var obj = FindObjCharacterInScene(initData.m_ServerID); if (obj != null) { obj.Init(initData); result = false; } } return result; } public void AddPoolOtherGameObj(string name, Obj obj) { if (!otherObjPools.ContainsKey(name)) otherObjPools.Add(name, obj); else LogModule.ErrorLog("ObjManager AddPoolOtherGameObj already contain obj:" + name); } //根据Gameobject的Name在非Obj池查找 public Obj FindOtherGameObj(string strName) { Obj result; otherObjPools.TryGetValue(strName, out result); return result; } public void UpdateHideMask(int hideMask) { for (var i = 0; i < _otherList.Count; i++) if (!_otherList[i].isHide && _otherList[i].character != null) _otherList[i].character.SetHideByMask(hideMask); } /// /// 强制刷新其他玩家数目 /// public void RefreshOtherPlayerCount(int current) { int previous = 0; for (var i = 0; i < _otherList.Count; i++) if (!_otherList[i].isHide && _otherList[i].character != null) previous++; var delta = current - previous; if (delta > 0) { for (var i = 0; i < _otherList.Count; i++) { if (_otherList[i].isHide && _otherList[i].character != null) { _otherList[i].SetHide(false); delta--; if (delta <= 0) break; } } } else { for (var i = _otherList.Count - 1; i >= 0; i--) { if (!_otherList[i].isHide && _otherList[i].character != null) { _otherList[i].SetHide(true); delta++; if (delta >= 0) break; } } } } private Obj_OtherPlayer CreateOtherPlayer(ObjParent_Init_Data initData) { return CreateCharacter(initData, "Prefab/Model/OtherPlayerRoot"); } private T CreateCharacter(ObjParent_Init_Data initData, string prefabPath) where T : Obj_Character { var character = ObjParent.Create(initData, prefabPath); ApplyCachedEffect(character); return character; } private void ApplyCachedEffect(Obj_Character character, int? serverId = null) { var id = serverId ?? character.ServerID; var cache = GetEffectCache(id, ListGetMode.remove); if (cache != null) { if (cache.shieldCache != null) character.CreateShield(cache.shieldCache); for (var i = 0; i < cache.effectList.Count; i++) { var effect = cache.effectList[i]; character.PlayEffect(effect.effectId, time: effect.startTime, handle: effect.handle); } } } /// /// 在没有完整的数据层之前,暂时使用这个数据来处理一些Obj协议接收顺序问题 /// public class ObjEffectCache { public readonly List effectList = new List(); public readonly int serverId; public ObjEffectCache(int serverId) { this.serverId = serverId; } public ObjShieldData shieldCache { get; private set; } public void AddEffect(int effectId, int? handle = null) { if (effectId > GlobeVar.INVALID_ID) { var effect = new ObjEffectCacheData(effectId, handle, Time.time); effectList.Add(effect); } } public void RemoveEffect(int effectId) { for (var i = effectList.Count - 1; i >= 0; i--) if (effectList[i].effectId == effectId) effectList.RemoveAt(i); } public void RemoveEffectByHandle(int handle) { for (var i = effectList.Count - 1; i >= 0; i--) if (effectList[i].handle != null && effectList[i].handle.Value == handle) effectList.RemoveAt(i); } public void AddShield(ObjShieldData shieldData) { shieldCache = shieldData; } public void UpdateShield(long handle, long health) { if (shieldCache != null && shieldCache.handle == handle) shieldCache.health = health; } public void RemoveShield(int handle) { if (shieldCache != null && shieldCache.handle == handle) shieldCache = null; } } public class ObjEffectCacheData { public readonly int effectId; public readonly int? handle; public readonly float startTime; public ObjEffectCacheData(int effectId, int? handle, float startTime) { this.effectId = effectId; this.handle = handle; this.startTime = startTime; } } }