东莞企石网站建设北京优质网站制作

当前位置: 首页 > news >正文

东莞企石网站建设,北京优质网站制作,公司网站建设制作商,上海最繁华的五个区要源码包的私信我。 简介 衔接Unity核心学习后的实操小项目 需求分析 准备工作 面板基类 为了能够控制一画布整体的透明度#xff0c;所以需要给每个面板都添加一个 CanvasGroup组件 UI管理器 UGUI方面的参数设置 开始场景 场景搭建 直接用资源包搭建好的场景#xff1a;…要源码包的私信我。 简介 衔接Unity核心学习后的实操小项目 需求分析 准备工作 面板基类 为了能够控制一画布整体的透明度所以需要给每个面板都添加一个 CanvasGroup组件 UI管理器 UGUI方面的参数设置 开始场景 场景搭建 直接用资源包搭建好的场景Demo1  PC端 Demo2_mobile 是移动端的 将场景Demo1 复制到 Scenes文件夹下 删除 调整好相机 这样即可 开始界面 拼界面 拖入两个僵尸创建动画状态机拖入啃食动画和倒下动画到状态机里注意要拖入循环动画再把动画状态机拖入僵尸模型的Animator 组件中 界面逻辑 Main主路口 设置界面 拼界面 背景音乐数据 创建Data 数据文件夹 创建 BkMusic 用于管理背景音乐  界面逻辑 摄像机动画逻辑 先为摄像机做四个动画idle上下缓动、turnLeft左转摄像机、turnRight右转摄像机、leftIdle左上下缓动 创建 CameraAnimator 脚本 BeginPanel  代码添加 人物选择界面 拼界面 资源准备 1.准备好人物模型 2.给人物都配好相应的武器 3.创建人物的动画状态机 双击进入 设置一些参数 分析出有9个动作 设置值和匹配动画 拖入翻滚动画 用到动画遮罩知识点创建人物攻击动画 创建人物蹲下动画 一个人物的证套动作就完成了 运用 动画状态机复用功能为其他人物创建动画状态机 绑定相应动画就可以  数据准备 创建人物数据 转Json  创建玩家数据类 界面逻辑 先添加一个购买按钮 ChooseHeroPanel  逻辑 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI;public class ChooseHeroPanel : BasePanel {//左右键public Button btnLeft;public Button btnRight;//购买按钮public Button btnUnLock;public Text txtUnLock;//开始和返回public Button btnStart;public Button btnBack;//左上角拥有的钱public Text txtMoney;//角色信息public Text txtName;//英雄预设体需要创建在的位置private Transform heroPos;//当前场景中显示的对象private GameObject heroObj;//当前使用的数据private RoleInfo nowRoleData;//当前使用数据的索引private int nowIndex;public override void Init(){//一开始就找到场景中 放置对象预设体的位置heroPos GameObject.Find(HeroPos).transform;//更新左上角玩家拥有的钱txtMoney.text GameDataMgr.Instance.playerData.haveMoney.ToString();btnLeft.onClick.AddListener(() {–nowIndex;if (nowIndex 0)nowIndex GameDataMgr.Instance.roleInfoList.Count - 1;//模型的更新ChangeHero();});btnRight.onClick.AddListener(() {nowIndex;if (nowIndex GameDataMgr.Instance.roleInfoList.Count)nowIndex 0;//模型更新ChangeHero();});btnUnLock.onClick.AddListener(() {//点击解锁按钮的逻辑PlayerData data GameDataMgr.Instance.playerData;//当有钱时if (data.haveMoney nowRoleData.lockMoney){//购买逻辑//减去花费data.haveMoney - nowRoleData.lockMoney;//更新界面显示txtMoney.text data.haveMoney.ToString();//记录购买的iddata.buyHero.Add(nowRoleData.id);//保存数据GameDataMgr.Instance.SavePlayerData();//更新解锁按钮UpdateLockBtn();//提示面板 显示购买成功print(购买成功);}else{//提示面板 显示 金钱不足print(金币不足);}});btnStart.onClick.AddListener(() {//第一 是记录当前选择的角色//因为 GameDataMgr 是单例模式 所以就算切场景了数据也不会删除它是唯一的//后面我们可以通过单例模式的对象去获取里面的信息相当于将数据传递到了 GameDataMgr中间接的帮我们存储数据GameDataMgr.Instance.nowSelRole nowRoleData;//第二 是隐藏自己 显示场景选择界面UIManager.Instance.HidePanelChooseHeroPanel();});btnBack.onClick.AddListener(() {//隐藏自己UIManager.Instance.HidePanelChooseHeroPanel();//播放切换摄像机动画//先得到主摄像机Camera.main.GetComponentCameraAnimator().TurnRight(() {//动画播放完后 显示开始界面UIManager.Instance.ShowPanelBeginPanel();});});//更新模型显示ChangeHero();}/// summary/// 更新场景上要显示的模型/// /summaryprivate void ChangeHero(){if (heroObj ! null){Destroy(heroObj);heroObj null;}//取出数据的一条 根据索引值nowRoleData GameDataMgr.Instance.roleInfoList[nowIndex];//实例化对象 并且记录下来 用于下次切换时 删除heroObj Instantiate(Resources.LoadGameObject(nowRoleData.res), heroPos.position, heroPos.rotation);//根据解锁相关数据 来决定是否显示解锁按钮UpdateLockBtn();}/// summary///更新解锁按钮显示情况/// /summaryprivate void UpdateLockBtn(){//如果该角色 需要解锁 并且没有解锁的话 就应该显示解锁按钮 并且隐藏开始按钮if (nowRoleData.lockMoney 0 !GameDataMgr.Instance.playerData.buyHero.Contains(nowRoleData.id)){//更新解锁按钮显示 并更新上面的钱btnUnLock.gameObject.SetActive(true); // 显示true 隐藏falsetxtUnLock.text nowRoleData.lockMoney;//隐藏开始按钮 因为该角色没有解锁btnStart.gameObject.SetActive(false);}else{btnUnLock.gameObject.SetActive(false);btnStart.gameObject.SetActive(true);}}public override void HideMe(UnityAction callBack){base.HideMe(callBack);//每次隐藏自己时 要把当前显示的3D模型角色 删除掉if (heroObj ! null){DestroyImmediate(heroObj); //马上删除不用等到下一帧heroObj null;}}}其他补充逻辑 提示界面 拼界面 界面逻辑 场景选择界面 拼界面 数据准备 准备场景数据 创建Excel表格 转成 Json 创建图片数据 创建 场景数据类 GameDataMgr 中添加场景数据 界面逻辑 上节课遗留 要把图片资源的纹理类型Texture Type改为 精灵图片 ChooseScenePanel 面板逻辑 调用 小总结所有的面板都是数据的体现。 游戏场景 场景搭建 遗留ChooseHeroPanel 面板中 显示人物名字 自行添加对应的逻辑 场景搭建 因为丧尸要自动寻路所以先烘焙地图 1.打开导航 2.烘焙前先设置一下——烘焙静态 不需要设置连接点因为该地图没有断开的点 Off Mesh Link Generation 3.回到导航窗口Navigation–打开 烘焙页签Bake–点击 Back 烘焙 记得打开 辅助功能 – Gizmos 调整 游戏界面 拼界面 界面逻辑 GamePanel 逻辑 创建组合控件的脚本 摄像机跟随逻辑 创建 CameraMove 脚本挂载到主摄像机上 玩家逻辑 玩家的控制其实就是调用动画的播放 分析 玩家的一些属性 给人物添加 角色碰撞器 添加怪物层 为每个武器添加一个开火点 给开火动画添加事件 PlayerObject 逻辑 using System.Collections; using System.Collections.Generic; using UnityEngine;public class PlayerObject : MonoBehaviour {//为获得玩家身上的动画组件private Animator animator;//1.玩家属性的初始值//玩家攻击力private int atk;//玩家拥有的钱public int money;//旋转的速度private float roundSpeed 50;//持枪对象才有的开火点public Transform gunPoint;//2.移动变化 动作变化//3.攻击动作的不同处理//4.金币变化的逻辑// Start is called before the first frame updatevoid Start(){//得到自己依附的 Animator组件animator this.GetComponentAnimator();}/// summary/// 初始化玩家基础属性/// /summary/// param nameatk/param/// param namemoney/parampublic void InitPlayerInfo(int atk, int money){this.atk atk;this.money money;//更新界面上金币的数量UpdateMoney();}// Update is called once per framevoid Update(){//2.移动变化 动作变化//移动动作的变换 由于动作有位移 我们也应用了动作的位移 所以只要改变这两个值 就会有动作的变化 和 速度的变化animator.SetFloat(VSpeed, Input.GetAxis(Vertical));animator.SetFloat(HSpeed, Input.GetAxis(Horizontal));//旋转this.transform.Rotate(Vector3.up, Input.GetAxis(Mouse X) * roundSpeed * Time.deltaTime);//下蹲if (Input.GetKeyDown(KeyCode.LeftShift)){//当按下 Shift键时 把编号为1的动画层级权重改为1animator.SetLayerWeight(1, 1);}else if (Input.GetKeyUp(KeyCode.LeftShift)){//当抬起 Shift键时 把编号为1的动画层级权重改为0animator.SetLayerWeight(1, 0);}//按下R 播放打滚动画if (Input.GetKeyDown(KeyCode.R))animator.SetTrigger(Roll);//鼠标左键 开火if (Input.GetMouseButtonDown(0))animator.SetTrigger(Fire);}//3.攻击动作的不同处理/// summary/// 专门用于处理刀武器攻击动作的伤害检测事件/// /summarypublic void KnifeEvent(){//伤害检测 返回一个碰撞器数组Collider[] colliders Physics.OverlapSphere(this.transform.position this.transform.forward this.transform.up, 1, 1 LayerMask.NameToLayer(Monster));//暂时无法继续写逻辑了 因为 我们没有怪物对应的脚本for (int i 0; i colliders.Length; i){//得到碰撞到的对象上的怪物脚本 让其受伤}}public void ShootEvent(){//进行摄像机检测//前提是需要有开火点RaycastHit[] hits Physics.RaycastAll(new Ray(gunPoint.position, gunPoint.forward), 1000, 1 LayerMask.NameToLayer(Monster));for (int i 0; i hits.Length; i){//得到对象上的怪物脚本 让其受伤}}//4.金币变化的逻辑public void UpdateMoney(){//间接的更新界面上 钱的数量UIManager.Instance.GetPanelGamePanel().UpdateMoney(money);}/// summary/// 提供给外部加钱的方法/// /summary/// param namemoney/parampublic void AddMoney(int money){//加金币this.money money;UpdateMoney();} }保护区域逻辑 选择地图上一片区域为保护区 加入一个合适的特效 注意因为一些草的贴图丢失导致画面有面片感 处理点击地图里的 地形——Terrain 点地形组件 Terrain 中页签中的 花的图标 按住 Shift 点击地图上的区域消除即可 给特效区域添加球形碰撞并勾选触发器 MainTowerObject 逻辑主保护区域相关逻辑 挂载到保护区特效上 怪物逻辑 状态机准备 创建动画状态机 创建丧尸模型 数据准备 创建Excel表 再转成 Json 数据 然后申明 对应的数据结构 在 GameDataMgr 中去读取它 逻辑处理 创建 MonsterObject 脚本 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI;public class MonsterObject : MonoBehaviour {//动画相关private Animator animator;//位移相关 寻路组件private NavMeshAgent agent;//一些不变的基础数据private MonsterInfo monsterInfo;//当前血量private int hp;//怪物是否死亡public bool isDead false;//上一次攻击的时间private float frontTime;//出生过后再移动//移动————寻路组件//攻击————伤害检测//受伤//死亡//初始化// Start is called before the first frame updatevoid Awake(){//得到丧尸对象上挂载的动画组件和寻路组件animator this.GetComponentAnimator();agent this.GetComponentNavMeshAgent();}//初始化public void InitInfo(MonsterInfo info){monsterInfo info;//状态机加载animator.runtimeAnimatorController Resources.LoadRuntimeAnimatorController(monsterInfo.animator);//要改变的当前血量,一定要取出来用不能改变数据里的数据hp info.hp;//速度初始化//速度和加速度赋值 之所以赋值一样 是希望没有明显的加速度 而是一个匀速运动agent.speed agent.acceleration info.moveSpeed;//旋转速度agent.angularSpeed info.roundSpeed;}//受伤public void Wound(int dmg){//减少血量hp - dmg;//播放受伤动画animator.SetTrigger(Wound);if (hp 0){//死亡}else{//每死亡 是受伤 播放受伤音效}}//死亡public void Dead(){isDead true;//停止移动agent.isStopped true;//播放死亡动画animator.SetBool(Dead, true);//播放音效//加金币 ———— 我们之后通过关卡管理类 来管理游戏中的对象 通过它来让玩家加钱}//死亡动画播放完毕后 会调用的事件方法public void DeadEvent(){//死亡动画播放完毕移除对象//之后有了关卡管理器再来处理}//移动 ———— 寻路组件//出生过后再移动public void BornOver(){//出生结束后 再让怪物朝目标点移动agent.SetDestination(MainTowerObject.Instance.transform.position);//播放移动动画animator.SetBool(Run, true);}// 攻击void Update(){//检测什么时候停下来攻击if (isDead)return;//根据速度 来决定动画播放什么//agent.velocity 是指对象三个方向的速度animator.SetBool(Run, agent.velocity ! Vector3.zero);//检测和目标点到达移动条件时 就攻击if (Vector3.Distance(this.transform.position, MainTowerObject.Instance.transform.position) 5 Time.time - frontTime monsterInfo.atkOffset){//记录这次攻击时的时间frontTime Time.time;animator.SetTrigger(Atk);}}//伤害检测public void AtkEvent(){//范围检测 进行伤害判断 用圆形范围检测//Physics.OverlapSphere 第一个参数是 位置 第二个参数是半径第三个参数是能够攻击到的层级Collider[] colliders Physics.OverlapSphere(this.transform.position this.transform.forward this.transform.up, 1, 1 LayerMask.NameToLayer(MainTower));for (int i 0; i colliders.Length; i){if (colliders[i].gameObject MainTowerObject.Instance.gameObject){//让保护区受到伤害MainTowerObject.Instance.Wound(monsterInfo.atk);}}} }补充1.给所有丧尸模型添加 寻路组件—— Nav Mesh Agent 2.添加事件丧尸死亡后移除模型自行给每个死亡动画都添加上 3.添加层级 MainTower 层 4.给每个丧尸模型都调整好预设体调到适当的大小和 添加碰撞盒——胶囊碰撞盒后面玩家要攻击 出怪点逻辑 选择适合的出怪点特效 创建 MonsterPoint (挂载到出怪特效上 using System.Collections; using System.Collections.Generic; using UnityEngine;public class MonsterPoint : MonoBehaviour {//怪物有多少波public int maxWave;//每波怪物有多少只public int monsterNumOneWave;//用于记录 当前波的怪物还有多少只没有创建private int nowNum;//怪物ID 允许有多个 这样就可以随机创建不同的怪物 更具多样性public Listint monsterIDs;//用于记录 当前波 要创建什么ID的怪物private int nowID;//单只怪物创建间隔时间public float createOffsetTime;//波与波之间的间隔时间public float delayTime;//第一波怪物创建的间隔时间public float firstDelayTime;// Start is called before the first frame updatevoid Start(){//利用延时函数 Invok//第一波 延时Invoke(CreateWave, firstDelayTime);}/// summary/// 开始创建一波的怪物/// /summaryprivate void CreateWave(){//得到当前波怪物的ID是什么 用到 Unity中的随机数nowID monsterIDs[Random.Range(0, monsterIDs.Count)];//当前波怪物有多少只nowNum monsterNumOneWave;//创建丧尸CreateMonster();//减少波数–maxWave;}/// summary/// 创建怪物/// /summaryprivate void CreateMonster(){//直接创建丧尸//取出怪物数据MonsterInfo info GameDataMgr.Instance.monsterInfoList[nowID - 1];//创建怪物预设体GameObject obj Instantiate(Resources.LoadGameObject(info.res), this.transform.position, Quaternion.identity);//为我们创建出的怪物预设体 添加怪物脚本 进行初始化MonsterObject monsterObj obj.AddComponentMonsterObject();//初始化monsterObj.InitInfo(info);//创建完一只怪物后 减去要创建的怪物数量–nowNum;if (nowNum 0){//继续利用延时函数if (maxWave 0)Invoke(CreateWave, delayTime);}else{//延时函数 间隔的创建丧尸Invoke(CreateMonster, createOffsetTime);}}/// summary/// 出怪点是否出怪结束/// /summary/// returns/returnspublic bool CheckOver(){return nowNum 0 maxWave 0;} }游戏关卡管理器 创建 GameLevelMgr 管理器 using System.Collections; using System.Collections.Generic; using UnityEngine;public class GameLevelMgr {private static GameLevelMgr instance new GameLevelMgr();public static GameLevelMgr Instance instance;public PlayerObject player;//所有的出怪点private ListMonsterPoint points new ListMonsterPoint();//记录当前 还有多少波怪物private int nowWaveNum 0;//记录 一共有多少波怪物private int maxWaveNum 0;//记录当前场景上的怪物数量private int nowMonsterNum 0;private GameLevelMgr(){}//通过该关卡脚本来连接 开始场景 和 游戏场景//1.切换到游戏场景时 我们需要动态的创建玩家public void InitInfo(SceneInfo info){//显示游戏界面UIManager.Instance.ShowPanelGamePanel();//玩家的创建//获取之前记录的当前选中的玩家数据RoleInfo roleInfo GameDataMgr.Instance.nowSelRole;//首先获取到场景当中 玩家的出生位置Transform heroPos GameObject.Find(HeroBornPos).transform;//实例化玩家预设体 然后把它的位置角度 设置为 场景当中出生点的位置 GameObject heroObj GameObject.Instantiate(Resources.LoadGameObject(roleInfo.res), heroPos.position, heroPos.rotation);//对玩家对象进行初始化player heroObj.GetComponentPlayerObject();player.InitPlayerInfo(roleInfo.atk, info.money);//让摄像机 看向动态创建出来的玩家Camera.main.GetComponentCameraMove().SetTarget(heroObj.transform);//初始化 中央 保护区的血量MainTowerObject.Instance.UpdateHp(info.towerHp, info.towerHp);}//2.我们需要通过游戏管理器 来判断 游戏是否胜利//要知道场景中 是否还有怪物没有出 以及 场景中 是否还有 没有死亡的怪物//用于记录出怪点的方法public void AddMonsterPoint(MonsterPoint point){points.Add(point);}/// summary/// 更新一共有多少波怪/// /summary/// param namenum/parampublic void UpdatgeMaxNum(int num){maxWaveNum num;nowWaveNum maxWaveNum;//更新界面UIManager.Instance.GetPanelGamePanel().UpdateWaveNum(nowWaveNum, maxWaveNum);}public void ChangeNowWaveNum(int num){nowWaveNum - num;//更新界面UIManager.Instance.GetPanelGamePanel().UpdateWaveNum(nowWaveNum, maxWaveNum);}/// summary/// 检测 是否胜利/// /summary/// returns/returnspublic bool CheckOver(){for (int i 0; i points.Count; i){//只要有一个出怪点 还没有出完怪 那么就证明游戏还没有胜利if (!points[i].CheckOver())return false;}//要所有的怪都死亡了if (nowMonsterNum 0)return false;Debug.Log(游戏胜利);return true;}/// summary/// 改变当前场景上怪物的数量/// /summary/// param namenum/parampublic void ChangeMonsterNum(int num){nowMonsterNum num;} }补充1.报错 2.创建一个玩家出生位置 3.给玩家角色都绑定好开火点 4.更改 SceneInfo 数据参数 4.将两个场景添加到 Build Settings 中 5.MonsterPoint 脚本 逻辑添加 5. MonsterObject 中 逻辑添加 6.PlayerObject  添加逻辑 游戏结束面板 1.拼面板 2. GameOverPanel 逻辑 补充1.在游戏关卡管理器(GameLevelMgr)中 提供一个清除数据的方法 2.在MonsterObject 中检测是否胜利的逻辑并给玩家获得金币奖励 3.在进入游戏后 锁定鼠标显示 结束面板时 解除锁定 音效特效添加 1.在 GameDataMgr 中给外部提供一个播放音效的方法因为GameDataMgr中数据多所以在这里写这个方法方便些 2.在 PlayerObject 中播放音效 3.怪物受伤音效 创建特效相关 1. PlayerObject 中创建打击特效 其他相关特效可以自行添加 防御塔逻辑 数据模型准备 1.创建模型 从资源包里取出炮台模型并重命名 图片资源也得准备自己截图 2.配置数据 先创 Excel 表 —— 转Json 数据 —— 创建对应结构体数据 —— GameDataMgr 中读取出来 防御塔逻辑 创建 TowerObject 类 using System.Collections; using System.Collections.Generic; using UnityEngine;public class TowerObject : MonoBehaviour {//炮台头部 用于旋转 指向目标public Transform head;//开火点 用于释放攻击的位置public Transform gunPoint;//炮台头部旋转速度 可以写死 也可以配在表中private float roundSpeed 20;//炮台关联的数据private TowerInfo info;//当前要攻击的目标private MonsterObject targetObj;//当前要攻击的群体目标private ListMonsterObject targetObjs;//用于计时的 用于判断攻击间隔时间private float nowTime;//用于记录怪物位置private Vector3 monsterPos;//测试代码private void Start(){InitInfo(GameDataMgr.Instance.towerInfoList[10]);}/// summary/// 初始化炮台相关数据/// /summary/// param nameinfo/parampublic void InitInfo(TowerInfo info){this.info info;}// Update is called once per framevoid Update(){//单体攻击逻辑if (info.atkType 1){//没有目标 或者 目标死亡 或者 目标超出攻击距离 就继续寻找其他目标if (targetObj null || targetObj.isDead || Vector3.Distance(this.transform.position, targetObj.transform.position) info.atkRange){targetObj GameLevelMgr.Instance.FindMonster(this.transform.position, info.atkRange);}//如果没有找到任何可以攻击的对象 那么炮台就不应该旋转if (targetObj null)return;//得到怪物位置偏移Y的目标位置是希望 炮台头部不要上下倾斜monsterPos targetObj.transform.position;monsterPos.y head.position.y;//让炮台头部旋转起来head.rotation Quaternion.Slerp(head.rotation, Quaternion.LookRotation(monsterPos - head.position), roundSpeed * Time.deltaTime);//Vector3.Angle() 这个方法可以得到两个向量的夹角//判断 两个对象之间的夹角 小于一定范围时 才能让目标受伤 并且攻击间隔条件要满足if (Vector3.Angle(head.forward, monsterPos - head.position) 5 Time.time - nowTime info.offsetTime){//让目标受伤//提示这里为什么不用射线检测//因为当这些条件满足时射线检测也一定是能够打中敌方的所以就直接让敌方受伤就行targetObj.Wound(info.atk);//播放音效GameDataMgr.Instance.PlaySound(Music/Tower);//创建开火特效GameObject effObj Instantiate(Resources.LoadGameObject(info.eff), gunPoint.position, gunPoint.rotation);//延迟移除特效Destroy(effObj, 0.2f);//记录开火时间nowTime Time.time;}}//群体攻击逻辑else{targetObjs GameLevelMgr.Instance.FindMonsters(this.transform.position, info.atkRange);if (targetObjs.Count 0 Time.time - nowTime info.offsetTime){//创建开火特效GameObject effObj Instantiate(Resources.LoadGameObject(info.eff), gunPoint.position, gunPoint.rotation);//延迟移除特效Destroy(effObj, 0.2f);//让目标们受伤for (int i 0; i targetObjs.Count; i){targetObjs[i].Wound(info.atk);}//记录开火时间nowTime Time.time;}}} }补充1.创建炮台预设体和图片资源 2.记录丧尸的数量用于后面进行攻击 在 关卡管理器GameLevelMgr中去申明要把原申明丧尸数量的数据替换了 再添加两个方法添加丧尸数量、减少丧尸数量 其他地方逻辑有错也要进行对应的修改 3.在 关卡管理器GameLevelMgr中添加 满足攻击条件的丧尸并传出去 4.创建一些开火、伤害特效 5.给所有炮台拖入 TowerObject 脚本并添加开火点 最后把测试代码注释了 造塔点逻辑 1.创建 造塔点特效 在资源包里找到合适的即可 2.给造塔点 加上碰撞器要勾选触发器 3.创建 TowerPoint 类 using System.Collections; using System.Collections.Generic; using UnityEngine;public class TowerPoint : MonoBehaviour {//造塔点关联的 塔对象private GameObject towerObj null;//造塔点关联的 塔的数据public TowerInfo nowTowerInfo null;//可以建造的三个塔的ID是多少public Listint chooseIDs;// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){}/// summary/// 建造一个塔/// /summary/// param nameid/parampublic void CreateTower(int id){TowerInfo info GameDataMgr.Instance.towerInfoList[id - 1];//如果钱不够 就不用建造if (info.money GameLevelMgr.Instance.player.money)return;//扣钱GameLevelMgr.Instance.player.AddMoney(-info.money);//创建塔//先判断之前是否有塔 如果有 就删除if(towerObj ! null){Destroy(towerObj);towerObj null;}//实例化塔对象towerObj Instantiate(Resources.LoadGameObject(info.res),this.transform.position, Quaternion.identity);towerObj.GetComponentTowerObject().InitInfo(info);//记录当前塔的数据nowTowerInfo info;//塔建造完毕 更新游戏界面上的内容if (nowTowerInfo.nextLev ! 0){//如果 不等于0 代表还能升级界面就更新成要升级的炮台图标UIManager.Instance.GetPanelGamePanel().UpdateSelTower(this);}else{UIManager.Instance.GetPanelGamePanel().UpdateSelTower(null);}}//一定会用到触发器进入检测函数private void OnTriggerEnter(Collider other){//如果现在已经有塔了 就没有必要再显示升级界面 或者造塔界面了if (nowTowerInfo ! null nowTowerInfo.nextLev 0)return;UIManager.Instance.GetPanelGamePanel().UpdateSelTower(this);}//触发器离开检测函数private void OnTriggerExit(Collider other){//如果不希望游戏界面下方的造塔界面显示 直接传空UIManager.Instance.GetPanelGamePanel().UpdateSelTower(null);}}4.在 GamePanel 中添加更新炮塔图标类的界面方法 5.在 TowerBtn 中添加 初始化炮台的方法 6.记得把炮台的图片资源调为 Sprite图片 7.报错的一个要点运行时为什么会不显示游戏界面 因为在 GamePanel 中我们重写了 Update() , 将 BasePanel 里的 Update()覆盖了而BasePanel里的 Updata() 有界面的淡入淡出覆盖后就没有了。 解决办法将 BasePanel 里的 Update 写成虚函数让GamePanel 去重写。 8.给每个人物都加上 角色碰撞器 9.在 GamePanel 中添加 检测输入造塔的逻辑 补充这里要在 GamePanel 中添加一个 是否检测输入的标识 10.在 MonsterObject 中添加怪物死亡后加金币的逻辑 这里发现 丧尸会多次死亡导致我们会多加好多钱添加一些条件判断死亡加金币 PlayerObject 中的条件也得改一下 细节完善 1.丧尸死亡后还在向前移动 关闭寻路即可这里是将寻路组件失活 2.射线检测的改动 玩家、丧尸、炮台、保护区的参数数组都自己合理更改。 到这里游戏的基本逻辑都已实现接下来要自己把人物开枪、打击特效添加好将三副地图设计好还有各个数值设置合理。 完成展示 Unity核心实践项目 总结