Files
JJBB/Assets/Project/Script/Obj/ObjManager.cs
2024-08-23 15:49:34 +08:00

725 lines
25 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/********************************************************************************
* 文件名: 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<Collider>();
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;
}
}
/// <summary>
/// 死亡Npc延迟回收装置
/// </summary>
// Npc死亡后如果收到死亡删除协议客户端将延迟回收Npc
public class DeadNpcObjHolder
{
private readonly List<DeadNpcData> _npcData = new List<DeadNpcData>();
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<ObjManager>
{
/// <summary>
/// 在角色创建前,收到的特效数据将会统一保存在这里
/// </summary>
private readonly List<ObjEffectCache> _effectCacheList = new List<ObjEffectCache>();
// OtherPlayer隐藏逻辑
// 创建时如果当前服务器创建角色小于限制记录数据并创建Obj否则只记录数据不创建。
// 删除时删除数据如果角色有Obj则为最早无Obj数据创建Obj。
// 暂时没有限制数值改变时,处理主动删除和主动创建
private readonly List<OtherPlayerVisibility> _otherList = new List<OtherPlayerVisibility>();
// 当前客户端带ServerId的Obj池使用Obj场景中名字唯一索引对应Obj的数据
//动态的,注意使用上限
public readonly Dictionary<int, Obj> ObjPools = new Dictionary<int, Obj>();
// 采集物等无ServerId物体的保存位置
public readonly Dictionary<string, Obj> otherObjPools = new Dictionary<string, Obj>();
// 死亡Npc的延迟回收器
public readonly DeadNpcObjHolder deadNpcObjHolder = new DeadNpcObjHolder();
//怪物归属缓存
public List<int> m_Belongs = new List<int>();
//当前客户端主角色
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<Obj>(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<Obj_MainPlayer>(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<Obj_NPC>(initData, "Prefab/Model/NPCRoot");
}
break;
case GameDefine_Globe.OBJ_TYPE.OBJ_FELLOW:
{
newObj = CreateCharacter<Obj_Fellow>(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<Obj_ZombieUser>(initData, "Prefab/Model/OtherPlayerRoot");
}
break;
case GameDefine_Globe.OBJ_TYPE.OBJ_DROP_ITEM:
{
newObj = ObjParent.Create<Obj_DropItem>(initData, "Prefab/Model/DropItemInfo");
}
break;
case GameDefine_Globe.OBJ_TYPE.OBJ_GHARRY:
{
newObj = CreateCharacter<Obj_GuildGharry>(initData, "Prefab/Model/FellowRoot");
}
break;
case GameDefine_Globe.OBJ_TYPE.OBJ_WEDDINGCAR:
{
newObj = CreateCharacter<Obj_WeddingCar>(initData, "Prefab/Model/NPCRoot");
}
break;
case GameDefine_Globe.OBJ_TYPE.OBJ_CHILD:
{
newObj = CreateCharacter<Obj_Child>(initData, "Prefab/Model/NPCRoot");
}
break;
case GameDefine_Globe.OBJ_TYPE.OBJ_SNARE:
{
newObj = ObjParent.Create<Obj_Snare>(initData, "Prefab/Model/SnareRoot");
}
break;
case GameDefine_Globe.OBJ_TYPE.OBJ_COLLECTITEM:
{
newObj = ObjParent.Create<Obj_CollectItem>(initData, "Prefab/Model/CollectItemRoot");
}
break;
case GameDefine_Globe.OBJ_TYPE.OBJ_TELEPORTPOINT:
{
newObj = ObjParent.Create<Obj_TeleportPoint>(initData, "Prefab/Model/Teleport");
}
break;
case GameDefine_Globe.OBJ_TYPE.OBJ_JUMPPOINT:
{
newObj = ObjParent.Create<Obj_JumpPoint>(initData, "Prefab/Model/Jump");
}
break;
case GameDefine_Globe.OBJ_TYPE.OBJ_CLIENTNPC:
{
newObj = CreateCharacter<Obj_ClientNPC>(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<ObjManager>.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);
}
/// <summary>
/// 强制刷新其他玩家数目
/// </summary>
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<Obj_OtherPlayer>(initData, "Prefab/Model/OtherPlayerRoot");
}
private T CreateCharacter<T>(ObjParent_Init_Data initData, string prefabPath) where T : Obj_Character
{
var character = ObjParent.Create<T>(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);
}
}
}
/// <summary>
/// 在没有完整的数据层之前暂时使用这个数据来处理一些Obj协议接收顺序问题
/// </summary>
public class ObjEffectCache
{
public readonly List<ObjEffectCacheData> effectList = new List<ObjEffectCacheData>();
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;
}
}
}