笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。
视频网址:http://edu.csdn.net/lecturer/144
前面已经给读者介绍了两个类,这两个类并没有具体对象的行为表示,给人的感觉就是一个抽象的类,接下来它们的孩子类的编写,也就是具体的类了。在这里介绍一下设计思路,游戏中的角色和怪物也有共同的属性,比如Buff、Debuff、伤害、移动等等,将这些共同的属性可以放在我们已经规划好的BaseCharacter类中,该类主要实现的就是英雄和怪物的基础属性和方法,代码如下所示:
public class BaseCharacter:BaseActor { public GameObject m_MPoint; /// <summary> /// 攻击特效最长持续时间 /// </summary> protected float m_AttackedEffTimeMax; /// <summary> /// /// </summary> protected bool m_bCameraFrustum; /// <summary> /// 标识碰撞后是否移动 /// </summary> protected bool m_bCollisionMove; protected bool m_bMute; protected int m_BulletUidCount; /// <summary> /// /// </summary> protected CapsuleCollider m_CapsuleCol; /// <summary> /// 移动朝向 /// </summary> protected Vector3 m_MoveDirection; /// <summary> /// 移动至向量位置 /// </summary> protected Vector3 m_MoveToPos; /// <summary> /// 屏幕向量 /// </summary> protected Vector3 m_ScreenPos; /// <summary> /// 打昏特效 /// </summary> protected GameObject m_StunEff; /// <summary> /// /// </summary> protected float m_StunTimeMax; /// <summary> /// 队伍Id /// </summary> protected int m_TeamId; protected float m_TurnTimeMax; /// <summary> /// 角色状态 /// </summary> protected CharStat m_Stat = new CharStat(); /// <summary> /// Buff 列表 /// </summary> protected List<BaseBuff> m_Buffs = new List<BaseBuff>(); /// <summary> /// DeBuff 列表 /// </summary> protected List<BaseDeBuff> m_Debuffs = new List<BaseDeBuff>(); public override void Init(int uid) { base.Init(uid); this.m_bMute = false; this.m_FBX.SendMessage("SetParent", gameObject); this.m_CapsuleCol = this.gameObject.GetComponent<CapsuleCollider>(); } public override void Enable(Vector3 pos) { base.Enable(pos); this.m_MoveDirection = Vector3.zero; this.m_BulletUidCount = 0; this.m_bCameraFrustum = true; this.m_AttackedEffTimeMax = 0f; this.m_StunTimeMax = 0f; this.m_bCollisionMove = true; this.m_Buffs.Clear(); this.m_Debuffs.Clear(); } protected override void UpdateObj(float delta) { base.UpdateObj(delta); this.UpdateBuffs(delta); this.UpdateDebuffs(delta); this.CheckInCameraFrustum(); //if(this.GetCurState() != 13 && this.m_bCollisionMove && // Application.loadedLevelName != "03_LobbyScene" && // gameObject.GetComponent<Rigidbody>().velocity.magnitude <= 0.1f) //{ // gameObject.rigidbody.velocity = GetDirection() * delta * 3f; //} } public override void OnObjectDestroy() { this.m_StunEff = null; base.OnObjectDestroy(); } /// <summary> /// 更新增益buff /// </summary> /// <param name="delta"></param> protected void UpdateBuffs(float delta) { for (int i = 0; i < this.m_Buffs.Count; i++) { this.m_Buffs[i].OnUpdate(delta); } for (int j = this.m_Buffs.Count - 1; j >= 0; j--) { BaseBuff buff = this.m_Buffs[j]; buff.m_Time -= delta; if (this.m_Buffs[j].m_Time < 0f) { this.m_Buffs[j].OnDestroy(); this.m_Buffs.RemoveAt(j); } } } /// <summary> /// 更新减益buff /// </summary> /// <param name="delta"></param> protected void UpdateDebuffs(float delta) { for (int i = 0; i < this.m_Debuffs.Count; i++) { this.m_Debuffs[i].OnUpdate(delta); } for (int j = this.m_Debuffs.Count - 1; j >= 0; j--) { BaseDeBuff debuff = this.m_Debuffs[j]; debuff.m_Time -= delta; if (this.m_Debuffs[j].m_Time < 0f) { this.m_Debuffs[j].OnDestroy(); this.m_Debuffs.RemoveAt(j); } } } public void EnableCapsuleCol(bool flag) { if (this.m_CapsuleCol != null) this.m_CapsuleCol.enabled = flag; } /// <summary> /// 获取英雄角色的身体半径 /// </summary> /// <returns></returns> public float GetBodyRadius() { if (this.m_CapsuleCol != null) return this.m_CapsuleCol.radius; return 0f; } /// <summary> /// /// </summary> /// <returns></returns> public int GetBulletUid() { return this.m_BulletUidCount++; } public void SetTeamId(int teamId) { this.m_TeamId = teamId; } public int GetTeamId() { return this.m_TeamId; } /// <summary> /// 设定等级 /// </summary> /// <param name="level"></param> public void SetLevel(int level) { if (m_ObjType == EnumDefine.ObjType.Mon) { } this.SetGrowth(EnumDefine.GrowthT.Min, level); this.SetStatusByLevel(level); } /// <summary> /// 增加buff /// </summary> /// <param name="type"></param> /// <param name="time"></param> /// <param name="value"></param> protected void AddBuff(EnumDefine.BuffType type, float time, float value) { BaseBuff buff = this.m_Buffs.Find(x => x.GetBuffType() == type); if (buff != null) { buff.OnInit(type, time, this, value); } else { BaseBuff instance = BaseBuff.GetInstance(type); instance.OnInit(type, time, this, value); this.m_Buffs.Add(instance); } ; } /// <summary> /// 增加Debuff /// </summary> /// <param name="type"></param> /// <param name="time"></param> /// <param name="value"></param> protected void AddDebuff(EnumDefine.DeBuffT type, float time, float value) { BaseDeBuff debuff = this.m_Debuffs.Find(x => x.GetDebuffType() == type); if (debuff != null) { debuff.OnInit(type, time, this, value); } else { BaseDeBuff instance = BaseDeBuff.GetInstance(type); instance.OnInit(type, time, this, value); this.m_Debuffs.Add(instance); } } /// <summary> /// 获取Buff 值 /// </summary> /// <param name="type"></param> /// <returns></returns> protected float GetBuffValue(EnumDefine.BuffType type) { BaseBuff buff = this.m_Buffs.Find(x => x.GetBuffType() == type); if (buff != null) return buff.GetValue(); return 0f; } /// <summary> /// 获取 DeBuff 值 /// </summary> /// <param name="type"></param> /// <returns></returns> protected float GetDeBuffValue(EnumDefine.DeBuffT type) { BaseDeBuff debuff = this.m_Debuffs.Find(x => x.GetDebuffType() == type); if (debuff != null) return debuff.GetValue(); return 0f; } /// <summary> /// 检查是否在摄像机视锥体范围内 /// (视锥体(frustum),是指场景中摄像机的可见的一个锥体范围。它有上、下、左、右、近、远,共6个面组成。在视锥体内的景物可见,反之则不可见。为提高性能,只对其中与视锥体有交集的对象进行绘制。) /// </summary> protected void CheckInCameraFrustum() { this.m_ScreenPos = Camera.main.WorldToScreenPoint(gameObject.transform.localPosition); if (this.m_ScreenPos.z <= 0f) { this.m_ScreenPos.x *= -1f; this.m_ScreenPos.y *= -1f; } if ((this.m_ScreenPos.x > Screen.width) || (this.m_ScreenPos.x < 0f)) { this.m_bCameraFrustum = false; } else { this.m_bCameraFrustum = true; } } /// <summary> /// 获得屏幕向量 /// </summary> /// <returns></returns> public Vector3 GetScreenPos() { return this.m_ScreenPos; } /// <summary> /// /// </summary> /// <returns></returns> public bool IsInnerCameraFrustum() { return this.m_bCameraFrustum; } /// <summary> /// 是否可以被攻击 /// </summary> /// <returns></returns> public virtual bool IsPossibleAttacked() { if (m_CurState == 1) // { return false; } if (m_CurState == 13) //死亡状态 return false; return true; } /// <summary> /// 根据当前状态判定是否在Move /// </summary> /// <returns></returns> public virtual bool IsPossibleMoveTo() { switch (m_CurState) { case 1: case 13: //Die case 9: //Slide 滑动 case 10: //Down 打到 case 12: //Stun 打昏 case 7: //CriticalStiff return false; } return true; } /// <summary> /// 是否在使用技能 /// </summary> /// <returns></returns> public virtual bool IsPossibleSkill() { switch (m_CurState) { case 1: case 13: case 9: case 10: case 12: case 7: return false; } return true; } /// <summary> /// /// </summary> /// <param name="flag"></param> public void SetCollisionMove(bool flag) { this.m_bCollisionMove = flag; } /// <summary> /// /// </summary> /// <param name="growthT"></param> /// <returns></returns> protected bool CheckGrowth(EnumDefine.GrowthT growthT) { if ((growthT >= EnumDefine.GrowthT.Min) && (growthT < EnumDefine.GrowthT.Max)) { return true; } return false; } /// <summary> /// 设置成长属性 /// </summary> /// <param name="growthT"></param> /// <param name="value"></param> public void SetGrowth(EnumDefine.GrowthT growthT, int value) { if (this.CheckGrowth(growthT)) { this.m_Stat.GrowthEnc[(int)growthT] = AhHelper.XEncInt(value); } } /// <summary> /// 获取成长 /// </summary> /// <param name="growthT"></param> /// <returns></returns> public virtual int GetGrowth(EnumDefine.GrowthT growthT) { if (!this.CheckGrowth(growthT)) { return 0; } return AhHelper.XDecToInt(this.m_Stat.GrowthEnc[(int)growthT]); } /// <summary> /// /// </summary> /// <param name="baseStatT"></param> /// <param name="value"></param> public void SetStatusBase(EnumDefine.BaseStatT baseStatT,float value) { if (this.CheckStatusBase(baseStatT)) { this.m_Stat.BaseStr[(int)baseStatT] = AhHelper.EncFloat(value); } } /// <summary> /// 设定等级状态 /// </summary> /// <param name="level"></param> protected virtual void SetStatusByLevel(int level) { //Debug.Log("Virtual Func"); } public void SetStatusOption(EnumDefine.OptT optT, float value) { if (CheckStatusOption(optT)) { this.m_Stat.OptStr[(int)optT] = AhHelper.EncFloat(value); } } /// <summary> /// /// </summary> /// <param name="baseStateType"></param> /// <returns></returns> protected bool CheckStatusBase(EnumDefine.BaseStatT baseStateType) { if ((baseStateType >= EnumDefine.BaseStatT.MIN) && (baseStateType < EnumDefine.BaseStatT.MAX)) { return true; } return false; } protected bool CheckStatusOption(EnumDefine.OptT optionType) { if (optionType >= EnumDefine.OptT.Min && optionType < EnumDefine.OptT.Max) { return true; } return false; } /// <summary> /// 获取基础状态属性 /// </summary> /// <param name="baseType"></param> /// <returns></returns> public virtual float GetStatusBase(EnumDefine.BaseStatT baseType) { if (!this.CheckStatusBase(baseType)) { return 0f; } float num = AhHelper.DecToFloat(this.m_Stat.BaseStr[(int)baseType]); float statusBaseP = 0f; if (PlayerHelper.IsPvPMode()) { statusBaseP = this.GetStatusBaseP(baseType, true); } else { statusBaseP = this.GetStatusBaseP(baseType, false); } if (baseType == EnumDefine.BaseStatT.MPC) { return (num + statusBaseP); } return num + (num * (statusBaseP / 100f)); } /// <summary> /// /// </summary> /// <param name="baseType"></param> /// <returns></returns> public virtual float GetStatusBaseOrg(EnumDefine.BaseStatT baseType) { if (!this.CheckStatusBase(baseType)) { return 0f; } return AhHelper.DecToFloat(this.m_Stat.BaseStr[(int)baseType]); } /// <summary> /// 获取基础状态(PVP) /// </summary> /// <param name="baseType"></param> /// <param name="addPvp"></param> /// <returns></returns> public virtual float GetStatusBaseP(EnumDefine.BaseStatT baseType, bool addPvp = false) { if (!CheckStatusBase(baseType)) { return 0f; } float statusOption = 0f; switch (baseType) { case EnumDefine.BaseStatT.MPC: return this.GetStatusOption(EnumDefine.OptT.MPA); case EnumDefine.BaseStatT.MHP: statusOption = this.GetStatusOption(EnumDefine.OptT.HPU); if (addPvp) { statusOption += this.GetStatusOption(EnumDefine.OptT.HPU_PVP); } return statusOption; case EnumDefine.BaseStatT.MMP: return this.GetStatusOption(EnumDefine.OptT.MPU); case EnumDefine.BaseStatT.ATT: statusOption = this.GetStatusOption(EnumDefine.OptT.ATTR); if (addPvp) statusOption += this.GetStatusOption(EnumDefine.OptT.ATTR_PVP); return statusOption; case EnumDefine.BaseStatT.DEF: statusOption = this.GetStatusOption(EnumDefine.OptT.DEFR); if (addPvp) statusOption += this.GetStatusOption(EnumDefine.OptT.DEFR_PVP); return statusOption; case EnumDefine.BaseStatT.MS: return this.GetStatusOption(EnumDefine.OptT.MSR); } return statusOption; } /// <summary> /// /// </summary> /// <param name="optionType"></param> /// <returns></returns> public virtual float GetStatusOption(EnumDefine.OptT optionType) { if (!this.CheckStatusOption(optionType)) return 0f; float num = AhHelper.DecToFloat(this.m_Stat.OptStr[(int)optionType]); switch (optionType) { case EnumDefine.OptT.ATTR: num += this.GetBuffValue(EnumDefine.BuffType.MIN); break; case EnumDefine.OptT.DEFR: num += this.GetBuffValue(EnumDefine.BuffType.DEF_UP); break; case EnumDefine.OptT.ASR: num += this.GetBuffValue(EnumDefine.BuffType.AS_UP); break; case EnumDefine.OptT.MSR: num += this.GetDeBuffValue(EnumDefine.DeBuffT.IC); break; } if(HasDebuff(EnumDefine.DeBuffT.CS) && optionType == EnumDefine.OptT.DEFR){ num+= this.GetDeBuffValue(EnumDefine.DeBuffT.CS); } return num; } /// <summary> /// /// </summary> /// <param name="outDmgAttT"></param> /// <param name="outValue"></param> public void GetDmgAttT(out EnumDefine.DmgAttT outDmgAttT, out float outValue) { float statusOption = 0f; statusOption = GetStatusOption(EnumDefine.OptT.Min); if (statusOption > 0f) { outDmgAttT = EnumDefine.DmgAttT.PS; outValue = statusOption; } else { statusOption = this.GetStatusOption(EnumDefine.OptT.IDR); if (statusOption > 0f) { outDmgAttT = EnumDefine.DmgAttT.IC; outValue = statusOption; } else { statusOption = this.GetStatusOption(EnumDefine.OptT.FDR); if (statusOption > 0f) { outDmgAttT = EnumDefine.DmgAttT.FR; outValue = statusOption; } else { statusOption = this.GetStatusOption(EnumDefine.OptT.TDR); if (statusOption > 0f) { outDmgAttT = EnumDefine.DmgAttT.TD; outValue = statusOption; } else { outDmgAttT = EnumDefine.DmgAttT.Min; outValue = 0f; } } } } } public virtual EnumDefine.AttackedEffT ResearchAttEffType(EnumDefine.AttackedEffT srcType) { return srcType; } protected bool HasDebuff(EnumDefine.DeBuffT type) { return (this.m_Debuffs.Find(x => x.GetDebuffType() == type) != null); } public bool IsDead() { return m_CurState == 13; } /// <summary> /// 播放受击效果 /// </summary> /// <param name="attackerPos"></param> /// <param name="effId"></param> /* public void PlayAttackedEff(Vector3 attackerPos, int effId) { Vector3 vector = gameObject.transform.position - attackerPos; vector.Normalize(); Vector3 pos = gameObject.transform.position - this.m_CapsuleCol.radius * vector; pos += this.m_CapsuleCol.center; FxManager.PlayEff(effId, pos, 0f); ShineEff(0.15f); } */ public virtual void PlayAttackedEff(Vector3 pos, int effId, EnumDefine.EffT efft = EnumDefine.EffT.HeroEffect) { if (m_MPoint != null) { GameObject effObj = FxManager.PlayEff(effId, GetDirection(), m_MPoint.transform.position, 0.5f, efft); effObj.transform.parent = m_MPoint.transform; effObj.transform.localPosition = Vector3.zero; effObj.transform.localEulerAngles = Vector3.zero; //DebugLog.Log("PlayAttackedEff:" + effId); } } /// <summary> /// 自残 /// 一般用在Debuff状态下 /// </summary> /// <param name="damage"></param> /// <param name="resultHp"></param> /// <param name="effId"></param> /// <param name="numEffId"></param> public virtual void SelfDamage(float damage, float resultHp, int effId, int numEffId) { this.PlayAttackedEff(gameObject.transform.position, effId, EnumDefine.EffT.PublicEffect); } public enum eState { None, Sleep, Advent, Idle, Move, MoveTo, Turn, CriticalStiff, //至命最后一击 Stiff, Slide, //滑动 Down, StunSlide, Stun, Die, Max } } 在主要的函数中都有代码注释,读者可以参考,给出类的实现,是给读者设计时做一个参考使用的。