您的位置:首页 > 健康 > 美食 > 网站运营案例_合肥飞墨设计_广告传媒公司经营范围_山东公司网站推广优化

网站运营案例_合肥飞墨设计_广告传媒公司经营范围_山东公司网站推广优化

2024/10/6 5:58:36 来源:https://blog.csdn.net/SunsetSunup/article/details/141824409  浏览:    关键词:网站运营案例_合肥飞墨设计_广告传媒公司经营范围_山东公司网站推广优化
网站运营案例_合肥飞墨设计_广告传媒公司经营范围_山东公司网站推广优化

要源码包的私信我。

简介

衔接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.HidePanel<ChooseHeroPanel>();});btnBack.onClick.AddListener(() =>{//隐藏自己UIManager.Instance.HidePanel<ChooseHeroPanel>();//播放切换摄像机动画//先得到主摄像机Camera.main.GetComponent<CameraAnimator>().TurnRight(() =>{//动画播放完后 显示开始界面UIManager.Instance.ShowPanel<BeginPanel>();});});//更新模型显示ChangeHero();}/// <summary>/// 更新场景上要显示的模型/// </summary>private void ChangeHero(){if (heroObj != null){Destroy(heroObj);heroObj = null;}//取出数据的一条 根据索引值nowRoleData = GameDataMgr.Instance.roleInfoList[nowIndex];//实例化对象 并且记录下来 用于下次切换时 删除heroObj = Instantiate(Resources.Load<GameObject>(nowRoleData.res), heroPos.position, heroPos.rotation);//根据解锁相关数据 来决定是否显示解锁按钮UpdateLockBtn();}/// <summary>///更新解锁按钮显示情况/// </summary>private 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.GetComponent<Animator>();}/// <summary>/// 初始化玩家基础属性/// </summary>/// <param name="atk"></param>/// <param name="money"></param>public 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>/// 专门用于处理刀武器攻击动作的伤害检测事件/// </summary>public 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.GetPanel<GamePanel>().UpdateMoney(money);}/// <summary>/// 提供给外部加钱的方法/// </summary>/// <param name="money"></param>public 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.GetComponent<Animator>();agent = this.GetComponent<NavMeshAgent>();}//初始化public void InitInfo(MonsterInfo info){monsterInfo = info;//状态机加载animator.runtimeAnimatorController = Resources.Load<RuntimeAnimatorController>(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 List<int> 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>/// 开始创建一波的怪物/// </summary>private void CreateWave(){//得到当前波怪物的ID是什么  用到 Unity中的随机数nowID = monsterIDs[Random.Range(0, monsterIDs.Count)];//当前波怪物有多少只nowNum = monsterNumOneWave;//创建丧尸CreateMonster();//减少波数--maxWave;}/// <summary>/// 创建怪物/// </summary>private void CreateMonster(){//直接创建丧尸//取出怪物数据MonsterInfo info = GameDataMgr.Instance.monsterInfoList[nowID - 1];//创建怪物预设体GameObject obj = Instantiate(Resources.Load<GameObject>(info.res), this.transform.position, Quaternion.identity);//为我们创建出的怪物预设体 添加怪物脚本 进行初始化MonsterObject monsterObj = obj.AddComponent<MonsterObject>();//初始化monsterObj.InitInfo(info);//创建完一只怪物后 减去要创建的怪物数量--nowNum;if (nowNum == 0){//继续利用延时函数if (maxWave > 0)Invoke("CreateWave", delayTime);}else{//延时函数 间隔的创建丧尸Invoke("CreateMonster", createOffsetTime);}}/// <summary>/// 出怪点是否出怪结束/// </summary>/// <returns></returns>public 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 List<MonsterPoint> points = new List<MonsterPoint>();//记录当前 还有多少波怪物private int nowWaveNum = 0;//记录 一共有多少波怪物private int maxWaveNum = 0;//记录当前场景上的怪物数量private int nowMonsterNum = 0;private GameLevelMgr(){}//通过该关卡脚本来连接 开始场景 和 游戏场景//1.切换到游戏场景时 我们需要动态的创建玩家public void InitInfo(SceneInfo info){//显示游戏界面UIManager.Instance.ShowPanel<GamePanel>();//玩家的创建//获取之前记录的当前选中的玩家数据RoleInfo roleInfo = GameDataMgr.Instance.nowSelRole;//首先获取到场景当中 玩家的出生位置Transform heroPos = GameObject.Find("HeroBornPos").transform;//实例化玩家预设体 然后把它的位置角度 设置为 场景当中出生点的位置 GameObject heroObj = GameObject.Instantiate(Resources.Load<GameObject>(roleInfo.res), heroPos.position, heroPos.rotation);//对玩家对象进行初始化player = heroObj.GetComponent<PlayerObject>();player.InitPlayerInfo(roleInfo.atk, info.money);//让摄像机 看向动态创建出来的玩家Camera.main.GetComponent<CameraMove>().SetTarget(heroObj.transform);//初始化 中央 保护区的血量MainTowerObject.Instance.UpdateHp(info.towerHp, info.towerHp);}//2.我们需要通过游戏管理器 来判断 游戏是否胜利//要知道场景中 是否还有怪物没有出 以及 场景中 是否还有 没有死亡的怪物//用于记录出怪点的方法public void AddMonsterPoint(MonsterPoint point){points.Add(point);}/// <summary>/// 更新一共有多少波怪/// </summary>/// <param name="num"></param>public void UpdatgeMaxNum(int num){maxWaveNum += num;nowWaveNum = maxWaveNum;//更新界面UIManager.Instance.GetPanel<GamePanel>().UpdateWaveNum(nowWaveNum, maxWaveNum);}public void ChangeNowWaveNum(int num){nowWaveNum -= num;//更新界面UIManager.Instance.GetPanel<GamePanel>().UpdateWaveNum(nowWaveNum, maxWaveNum);}/// <summary>/// 检测 是否胜利/// </summary>/// <returns></returns>public 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 name="num"></param>public 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 List<MonsterObject> targetObjs;//用于计时的 用于判断攻击间隔时间private float nowTime;//用于记录怪物位置private Vector3 monsterPos;//测试代码private void Start(){InitInfo(GameDataMgr.Instance.towerInfoList[10]);}/// <summary>/// 初始化炮台相关数据/// </summary>/// <param name="info"></param>public 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.Load<GameObject>(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.Load<GameObject>(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 List<int> chooseIDs;// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){}/// <summary>/// 建造一个塔/// </summary>/// <param name="id"></param>public 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.Load<GameObject>(info.res),this.transform.position, Quaternion.identity);towerObj.GetComponent<TowerObject>().InitInfo(info);//记录当前塔的数据nowTowerInfo = info;//塔建造完毕 更新游戏界面上的内容if (nowTowerInfo.nextLev != 0){//如果 不等于0 代表还能升级,界面就更新成要升级的炮台图标UIManager.Instance.GetPanel<GamePanel>().UpdateSelTower(this);}else{UIManager.Instance.GetPanel<GamePanel>().UpdateSelTower(null);}}//一定会用到触发器进入检测函数private void OnTriggerEnter(Collider other){//如果现在已经有塔了 就没有必要再显示升级界面 或者造塔界面了if (nowTowerInfo != null && nowTowerInfo.nextLev == 0)return;UIManager.Instance.GetPanel<GamePanel>().UpdateSelTower(this);}//触发器离开检测函数private void OnTriggerExit(Collider other){//如果不希望游戏界面下方的造塔界面显示 直接传空UIManager.Instance.GetPanel<GamePanel>().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核心实践项目

总结

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com