Unity塔防游戏

本教程详细讲解了使用Unity开发塔防游戏的全过程,包括制作预制体、地图、路径、摄像机控制、敌人生成、炮台设计、子弹系统、特效、寻路算法、UI交互等多个方面。通过学习,你可以掌握Unity游戏开发的基本技能和流程。
摘要由CSDN通过智能技术生成

Unity塔防游戏

B站教学视频链接:传送门

视频传送门:https://www.bilibili.com/video/BV15W411976h/?spm_id_from=333.999.0.0&vd_source=a9be95fd9abd5e6f7a6dd9c91565dd34
素材下载地址:https://pan.baidu.com/s/1bd8mM-8MxtPUAuVUS2g5BQ 密码:pb6i

优化列表

  1. 炮塔攻击的时候, 先隐藏一下炮弹这个节点。更像是炮弹发射出去的感觉。

第一课:制作预制体、材质

  1. Unity的Layout采用Tall布局
  2. Project面板采用One Column Layout
  3. 在Project中创建Scenes文件夹,存放主场景MainScene以及后续的副本场景。
  4. 在Project中创建Material文件夹,存放各种材质。
  5. Hierarchy中创建空物体,重命名为Plane作为整个游戏的地表。
  6. Material文件夹中,创建一个Plane的材质,赋予给主场景中的Plane,调节Plane材质颜色即可修改地表的颜色,我这里调整设置为金黄色的地表。
  7. 地面有点儿反光,调节Plane材质属性smoothness,设置为0或者1,我选择设置为1,地面就不会出现反光现象。
  8. Inspector窗口去掉主场景中Plane的Mesh Collider组件,因为该塔防游戏,我们不处理碰撞检测
  9. 新建Prefab文件夹,存放后续的各种预制体。新建一个cube,调整合适的大小、高度,单击选中从Hierarchy窗口拖到Project窗口下,就可以把cube设置为一个prefab预制体。
    预制体简介:可以复用,在游戏运行时,可以重复创建。
    这里把这个cube作为一格地板,后续会点击该cube,在地板上面制造炮台。整个地图大小是
    30*15,总计450个地板(这个可以自己调整修改大小),地板铺在地表上面。场景1
图1. Uity设置 创建地表 地表材质

第二课:制作地图

  1. 将上述的cube重命名为MapCube,在场景中会创建很多的MapCube。新建一个空物体命名为Map,用来挂载管理所有的MapCube。将所有的地块MapCube拖放到Map层级下。这样通过在世界中查找Map节点,就可以获取到所有的MapCube地块。
  2. ctrl+d复制1个MapCube,因为MapCube大小是4*4,两个cube之间计划设置为间隔1m距离。
  3. 按住ctrl键点击cube拖动,每次拖动距离是1m。如果不按住ctrl键,拖动距离的精度是任意的
  4. 花费一些时间,拖动创建出15*15的Map,然后下节课设计地图元素:起点、路径、终点
    在这里插入图片描述
图2. 创建地图Map层级、创建地图块MapCube

第三课:制作路径

  1. 在Map上面,挖出一条路径(删除路径所经过的所有MapCube即可)
  2. 制作一个RoadCube,将RoadCube制作为Prefab。
  3. 制作RoadCube的材质,将该材质设置给RoadCube
  4. 创建一个空物体,重命名为Road,将RoadCube归类到Road下。(所有的MapCube归类到Map下管理;所有的RoadCube归类到Road管理),这样归类的目的是方便之后查找。
  5. 把挖出来的路径,从起点到终点,用RoadCube填充一遍
  6. 制作起点(绿色的正方体)和终点(红色的正方体)。(有个小知识点:因为要从Map中扣掉一个MapCube,即从Map层级中,使一个MapCube脱离该层级。可以通过GameObject=>Break Prefab Instance来实现该操作)
    在这里插入图片描述
图3. 移除MapCube,从起点(绿色正方体)到终点(红色正方体)创建一条路径Road

第四课:摄像机

  1. 左上角,点击对应按钮,可以切换摄像机的移动旋转模式。在这里插入图片描述
    上下左右四个箭头的图标:功能是控制预制体位置的。两个循环的箭头图标:功能是控制预制体旋转的)
  2. 给摄像机添加移动功能、给地图添加缩小放大功能:
    1. 因为已经对摄像机做了旋转,所以移动时用世界坐标(摄像机移动其实是前、后、左、右修改摄像机坐标)
    2. 摄像头在垂直方向上的上下移动,就是地图的放大和缩小功能。
  3. 开始制作功能:选中Main Camera点击Add Component输入Script,然后点击New script给摄像机添加一个c#脚本文件,编码控制摄像机坐标。
  4. 键盘的上下左右或wasd按键。接口:Input.GetAxis(“Horizontal”)获取水平轴滑动;Input.GetAxis(“Vertical”)获得垂直轴滑动。
  5. 在c#脚本Update()函数中获取轴的滑动值。假设Update()函数1min跑60帧,Time.deltaTime可以获取当前Update()处于第几帧。当然,实际上一分钟可以跑超多的帧数,和机器的硬件配置有关。
  6. 因为摄像机经过了旋转,所以transform.Translate()移动相机时,要用世界坐标系Space.World,而不能使用自身坐标系Space.Self
  7. 获取的轴滑动值范围是[-1,1]
  8. 鼠标滚轮控制地图的放大缩小。获取鼠标滚轮滑动值的函数:Input.GetAxis(“Mouse ScrollWheel”)
    在这里插入图片描述
图4. 创建控制摄像机脚本:获取水平轴、垂直轴的滑动值,在Update中实现摄像机前、后、左、右

第五课:配置路点

配置路点来实现怪物的寻路功能

  1. 添加路径点:首先创建一个空物体Way,在Way层级下面挂载路径点WayPoint(因为没有其它功能,所以每一个WayPoint就是一个空物体)。点击Inspector下的椭圆体,可以给WayPoint修改颜色。
  2. Way这个节点,新增一个c#脚本组件:WayPointController.cs,用来管理所有挂载在Way上面的路径点。
  3. 脚本在Awake()函数中,获取节点下挂载的所有WayPoint。代码如下:
wayPointsPosition = new Transform[transform.childCount];
for (int idx=0; idx<wayPointsPosition.Length;++idx)
{
    wayPointsPosition[idx] = transform.GetChild(idx);
}

获取子节点GetChild()
在这里插入图片描述

图5. 地图寻路路径:WayPoint1 ~ WayPoint11

第六课:创建敌人,并且移动。

  1. 首先,我们制作一个预制体,命名为Enemy(敌人)。
  2. Enemy预制体上创建c#脚本:Enemy.cs,来控制敌人的行为:移动、攻击等。
  3. 移动的思路:获取Way节点下所有子物体,放到一个数组中,这个数组保存了整张地图的所有路点。然后让敌人顺序移动到数组中路点的位置。就能从起点移动到终点。
  4. 控制物体移动的函数:transform.Translate(移动的向量),举例:
    transform.Translate((positions[index].position - transform.position).normalized * Time.deltaTime * 	speed);
    
  5. 判断物体是否达到某个路点:Vector3.Distance(位置1,位置2)<0.2f
    Vector3.Distance(positions[index].position,transform.position)<0.2f
    
    在这里插入图片描述
图6. 制作的预制体Enemy1、游戏游戏时在场景中创建的敌人Enemy1(Clone)

第七课:创建敌人孵化器

  1. 在空物体GameManager上面,挂载一个Enemy Spawner脚本,来管理敌人的孵化、生成。
  2. 在Start()函数中启动协程来创建怪物:StartCoroutine(SpawnEnemy());
  3. 怪物被记录到class Wave中,这个类不能继承MonoBehaviour,是个独立的类。
  4. Wave记录了一波怪物的属性:类型、数量、出生间隔。目前设定一波怪物中,只能出现一种怪物。
  5. 孵化器脚本,根据波数控制脚本,来创建怪物。创建的接口:GameObject.Instantiate(对象,位置,旋转)
  6. 暂停n秒:yield return new WaitForSeconds(间隔)
    在这里插入图片描述
图7. 敌人孵化器

第八课:改进敌人的生成策略

  1. 改进后:前一波的敌人到达终点,消失以后,才会创建下一波敌人。

第九课:创建三种炮台的Prefab

第十课:创建炮台选择的UI

  1. bug:3个单选按钮,会全部被选中,显示黑色遮挡。
    1. 解决方案:把3个Toggle设置为一个group,这样每次选中显示1个黑色遮挡。

第十一课:创建炮台的数据类(保存炮台数据)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum TurrentType
{
	LasserTurret,	// 激光炮台;
	MissleTurret,	// 炮弹炮台;
	StandTurret,	// 标准炮台;
}

public class TurrentData{
	public TurrentType 	type;				// 炮台类型;
	public GameObject 	turretPrefab;		// 武器对应的prefab;
	public int 			createCost;			// 武器创建消耗;
	public GameObject 	turretUpLevPrefab;	// 武器升级后的prefab;
	public int 			UpLevCost;			// 升级消耗;
}

第十二课:监听炮台选择的事件(保存选择的炮台)

  1. 在UI的togger上,可以区别图标是否被点击。会抛出OnValueChange事件。
  2. 在BuildManager上实现监听函数,并绑定到UI上。
    请添加图片描述
  3. 阿萨德

第十三课:检测点击哪个MapCube

  1. 射线检测,点击了哪个方块。先检查下方块上是否有炮台,有炮台弹出销毁界面,没有则创建。
  2. 给方块Cube添加一个Layer,这样射线检测的时候,只检测和MapCube的碰撞。在这里插入图片描述在这里插入图片描述
	void Update()
	{
		do
		{
			if(!Input.GetMouseButtonDown(0)){
				break;	// 鼠标没有按下;
			}
			if(EventSystem.current.IspointerOverGameObject()){
				break;	// 属性按在了UI上;(检测鼠标调用该函数不需要传入参数,但是手机上的话,需要传入参数 Help=>Manual查看Api接口说明)
			}
			// 射线判断点击在哪个方块上;
			Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);	// 将鼠标点击的位置转换为一条射线;
			RaycastHit hit;
			// p1:射线 p2:碰撞结果 p3:最大距离 p4:需要检测的layer层,如果不传参数,就和所有的层做检测;
			bool isCollider = Physice.Raycast(ray,out hit,1000,LayerMask.GetMack("MapCube"));
			if(!isCollider){
				break;
			}
			GameObject mapCube = hit.collider.GameObject;	// 得到碰撞到的MapCube;
			// &&& 开始炮台的建设;
		}
		while(false);
	}

第十四课:检查是否足够可以创建炮塔

第十五课:资源管理(金钱消费和更新UI)

  1. Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 
    

报错NullReferenceException,原因是之前在场景中加了两个摄像机Camera,后来我删除了一个, 应该是把MainCamera删除了。解决办法是:把剩下的那个摄像机的Tag修改为MainCamera即可。

第十六课:钱不够,制作动画提示效果

  1. 制作Animations动画:
    1. 选中需要制作动画的文本,然后点击Window>Animation,打开Animation执制作器。在这里插入图片描述在这里插入图片描述

    2. 拖动红色的竖线,右键Add Key就可以添加动画的关键帧。

    3. 制作好以后,Food>Text下面就会挂上一个Animator组件,其中的controller指向了刚刚制作的动画。双击打开Text动画的状态机。在这里插入图片描述

    4. 动画有一个Entry状态,这是动画的入口。原先Entry默认指向food_flicker。因为需求是钱不够才会播放food_flicker闪烁动画。所以右键新建一个Empty状态,Entry默认指向Empty状态。然后新增两条线路,分别指向Emptyfood_flicker

第十七课:炮台的实例化

第十八课:做特效

  1. 在场景中新建空物体,重命名为BuildEffect。在BuildEffect下新建一个Particle System粒子系统。

  2. 旋转特效,设置x为-90,使其特效朝上运动 。

  3. 点击BuildEffect>RendererRenderer就是每一个粒子的效果),将RenderMode修改为MeshMesh就是一个网格。这里的游戏例子使用Cube网格。

  4. 然后为Mesh创建一个Material(材质),修改一下材质的颜色属性Albedo。保存命名为BuildEffectMaterialSmoothness控制材质是否反光。

  5. 将新创建的材质,拖动到特效BuildEffectRenderer中的Material。至此,我们完成了一个从下往上发散的特效。但是我们的建造特效,希望是从建筑中心,向四周发散的。所以我们需要调整一下参数。
    在这里插入图片描述

  6. 首先,调节Particle System>Start Size,设置为0.6。再把Particle System>ShapeShape属性设置为Circle,就可以看到特效向水平方向四周发散。
    在这里插入图片描述

  7. 特效默认是循环的,这里我们只在创建的时候,发散一次就行。

    7.1 修改Particle System>Emmission,将Rate over Time设置为0。
    7.2 Bursts设置为一次发散30个cube小方块。
    7.3 特效的持续时间,修改Start LifeTime这里设置为1秒。
    7.4 修改Duration1,表示每次循环时长为1秒。
    7.5 修改Size over Lifetime,特效从开始到结束,是从大逐渐变小的。
    在这里插入图片描述 在这里插入图片描述

  8. 这里做完特效,是水平扩散的。如果想要特效还是稍微朝上发散的话。需要修改Particle System>Shape>Shape的模式改为Cone(圆锥体),整个特效就是下面这种效果。在这里插入图片描述

  9. 特效有个属性Looping,控制其是否循环播放。

第十九课:炮台搜敌

  1. 修改Missile炮弹的属性:Smoothness控制透明度,Metalic控制金属性。
  2. 通过触发器来检测,哪些敌人进入到了攻击范围之内。
    • 点击预制体>Add Component>Sphere Collider添加一个球形触发器。
    • 勾上属性Is Trigger,表示该物体是个触发器。
    • 修改Radius,调整大小,就可以看到如下触发器。在这里插入图片描述
  3. 触发器要能正常检测敌人进入范围,需要添加刚体。
    • 给预制体加上Rigidbody属性,去掉Use Gravity(使用重力)属性。
    • 在代码中定义敌人列表,如果敌人进入触发器范围,会回调函数void OnTriggerEnter(Collider col);同理,离开触发器范围,会回调函数void OnTriggerEnter(Collider col)
  4. 如何识别是敌人进入了触发器范围?
    • 给所有的敌人添加一个Enemy的标签。
      在这里插入图片描述
    • 如果遇到炮台没有检测到敌人,可能是敌人没有添加碰撞器组件,检查一下对应的prefab,给敌人的prefab也添加上Sphere Collider即可。
  5. 碰撞检测的效率优化:Edit>Project Settings>Physics中,可以配置指定的层1层2做物理的碰撞检测。给所有的敌人添加EnemyLayer,给所有的武器添加WeaponLayer。然后在Physics中如下配置:
    在这里插入图片描述

第二十课:制作子弹

  1. 在炮台的枪管前面,创建一个空物体,作为射击开火点,随后的子弹初始化位置会被设置在这里。
  2. 建造一个子弹的预制体,然后在Attack()函数被调用的时候,将子弹创建在fire_pos位置。 在这里插入图片描述

第二十一课:子弹飞行

第二十二课:子弹碰撞盒

  1. 创建一个球体作为标准炮台的子弹,调整子弹的缩放。
  2. 标准炮台子弹创建材质,添加刚体和碰撞盒。
  3. 给子弹添加Bullet.cs脚本,通过触发函数OnTriggerEnter来检测和敌人的碰撞。
void OnTriggerEnter(Collider col)
{
   if(col.tag == "Enemy")
   {
   	col.GetComponent<Enemy>().Damage(damage);									   // 1.让敌人掉血;
   	GameObject.Instantiate(explosionEffect,transform.position,transform.rotation); // 2.播放受击特效;
   	Destroy(this.gameObject);													   // 3.销毁子弹
   }
}

第二十三课:创建爆炸特效

  1. 创建Particle System,调整Renderer>RenderMode>Mesh>Cube
  2. 调整Shape(发散属性),勾选Sphere表示球体,向四周发散。
  3. 只要播放一次,设置Emission>Rate over Time为0。Rate over Distance添加一个范围数量60~80的方块。
  4. 爆发的速度:Start Speed 调大一点,则特效播放的速度也会加快。
  5. Start Lifetime控制存在的时长。
  6. Size over Lifetime拖动曲线,可以控制特效的方块由大到小,或者由小到大。
  7. 不需要循环播放的特效取消Looping
  • 子弹的速度如果太快了,子弹的刚体可能和敌人的刚体,检测不到。(可以通过设置刚体的属性Collision Detection>Continuous来提高检测的精度)
  • 子弹之前到达敌人身上,不会爆炸,原因是没有勾上Weapon层,因为在工程设置中,敌人的碰撞体只会和Weapon层做碰撞检测。
  • 特效实例化以后,一定记得要销毁。
GameObject effect = GameObject.Instantiate(explosionEffect,transform.position,transform.rotation);
Destroy(effect,1);	// 1s后销毁;

第二十四课:fixbug修复敌人销毁时,子弹销毁

第二十五课:fixbug修复敌人销毁时,炮台的引用

第二十六课:敌人死亡时,销毁特效

第二十七课:手动配置一下所有敌人的销毁特效

第二十八课:控制炮管指向敌人射击子弹

  1. 在turret.cs中持有一个炮管的变量(Transform turretHead;),在Update()函数中让该变量LookAt()敌人的位置即可。
  2. 代码完成以后,炮管会进行旋转。但是发现炮管旋转的中心是炮管的1/2处,并不是以炮台的基座为中心旋转点。
  3. 在StandTurret中新建一个空物体,保证空物体的中心和炮台基座的中心点是一致的,然后将炮管拖动到空物体节点下。这样炮管旋转就以基座为中心了。
  4. 修改以后,炮管确实围绕基座为中心进行旋转。但是炮管的初始朝向还是不正确。5.在这里插入图片描述
  5. 我们是以y轴为中心轴,旋转炮管。所以保证蓝色的z轴在初始状态是和炮管的朝向是一致的。所以修改head节点的旋转(旋转90度) ,使蓝色z轴和炮管朝向一致。
    在这里插入图片描述

第二十九课:

第三十一课:敌人添加血条

  1. 新建一个Canvas(画布),里面新建一个Slider(滑动条)来实现血条的功能。
  2. 这里Slider不需要交互,所以去掉选项Interactable
  3. Slider所处的Canvas和原先的Canvas重合了,准备调整一下Canvas的大小,发现无法调节。需要修改Render Mode为World Space。点击这个小正方形,可以调整血条Canvas的大小。在这里插入图片描述
  4. 因为不需要血条的那个小圆圈,所以移除Handle Slide Area。血条的前背景图片,不能完整的覆盖后背景,需要调节一下节点Fill Area,使得前后背景的大小一致。调节一下Slider的前背景为绿色。在这里插入图片描述
  5. 将血条Canvas移动到坐标(1,1,1)处,并且绑定到Enemy的节点下面。
  6. 在敌人脚本中,用Slider类声明变量hpSlider。然后修改控制hpSlider.value的值,来动态显示血条的百分比。
  7. 制作多个敌人的血条的时候,可以将已经制作好的预制体1的参数,copy复制到预制体2的控件上。在这里插入图片描述

第三十二课:制作一个ui

第三十三课:给按钮添加动画

  1. button的Transition设置为Animation,可以为按钮添加动画
  2. button的Transition可以点击Auto Gen Animation,会自动会按钮生成4个动画,分别对应:
    1. normal:常规时按钮的动画
    2. 鼠标移动到按钮上的动画
    3. 鼠标点击按钮的动画
    4. 按钮失效后的动画

第三十四课:给按钮添加动画

第三十五课:给按钮添加动画

第三十六课:给升级面板添加动画

  1. 实现的效果是从小到大,慢慢从0到1的比例缩放。(可以用这种方式给UI做动效的效果,比如UI从左到右慢慢滑动消失
  2. Animation是动画,一个按钮可以设计多个动画。然后用Animator状态机来通过trigger条件来控制不同动画之间的切换。
  3. 先失效再生效,可以修正:点击不同炮塔的时候,没有播放show动画。

第三十七课:处理升级和销毁的点击

第三十八课:处理升级和销毁

第三十九课:升级特效、销毁特效

第四十课:2级标准炮塔

第四十一课:火箭弹炮塔

第四十二课:激光炮塔

第四十三课:激光炮塔的发射(LineRenderer)

第四十四课:激光伤害和伤害特效

第四十五课:2级激光炮塔

第四十六课:炮塔消耗

第四十七课:游戏结束时候的UI

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值