首页 > Python基础教程 >
-
C#教程之使用Unity创建塔防游戏(Part2)(2)
public float distanceToGoal() { float distance = 0; distance += Vector3.Distance( gameObject.transform.position, waypoints[currentWaypoint + 1].transform.position); for (int i = currentWaypoint + 1; i < waypoints.Length - 1; i++){ Vector3 startPosition = waypoints[i].transform.position; Vector3 endPosition = waypoints[i + 1].transform.position; distance += Vector3.Distance(startPosition, endPosition); } return distance; }
这个方法计算出了敌人尚未走完的路有多长。我们使用了Distatnce这个方法来计算两个Vector3之间的距离。
·通过这个方法来决定小怪兽的攻击目标。但是,现在你的小怪兽们无法攻击敌人,什么事都做不了,这个问题在下一步中解决。
保存好脚本,返回Unity中,我们需要为小怪兽们配备射击敌人的子弹。
为小怪兽们配备无尽的子弹
将 Images/Objects/Bullet1 拖拽到场景视图中。将它的Z坐标设置为-2,在游戏过程中,我们需要不断地产生新的子弹,X和Y坐标是在子弹产生时候设置的。
为Bullet1添加一个名为 BulletBehavior 的C#脚本组件,将下面的变量添加到脚本中:
public float speed = 10; public int damage; public GameObject target; public Vector3 startPosition; public Vector3 targetPosition; private float distance; private float startTime; private GameManagerBehavior gameManager;
变量 speed 指的是子弹的飞行速度,damage 指的是子弹对敌人造成的伤害。
Target、startPosition、 targetPosition 分别指的是:子弹的目标、初始坐标、目标的坐标。
distance 和 startTime 这两个变量决定了子弹的当前坐标。当玩家消灭一个敌人的时候,我们通过操作 gameManager 这个变量来给予玩家奖励。
在 Start() 方法中为这些变量赋值:
startTime = Time.time; distance = Vector3.Distance(startPosition, targetPosition); GameObject gm = GameObject.Find("GameManager"); gameManager = gm.GetComponent<GameManagerBehavior>();
我们将 startTime 设置为当前时间;distance变量的值为 startPosition 和 targetPosition 之间的距离;最后,我们获取了GameManagerBehavior的实例。
在Update()方法中,添加下面的代码来控制子弹的运动轨迹:
// 1 float timeInterval = Time.time - startTime; gameObject.transform.position = Vector3.Lerp(startPosition, targetPosition, timeInterval * speed / distance); // 2 if (gameObject.transform.position.Equals(targetPosition)) { if (target != null){ // 3 Transform healthBarTransform = target.transform.FindChild("HealthBar"); HealthBar healthBar = healthBarTransform.gameObject.GetComponent<HealthBar>(); healthBar.currentHealth -= Mathf.Max(damage, 0); // 4 if (healthBar.currentHealth <= 0) { Destroy(target); AudioSource audioSource = target.GetComponent<AudioSource>(); AudioSource.PlayClipAtPoint(audioSource.clip, transform.position); gameManager.Gold += 50; } } Destroy(gameObject); }
-
计算出子弹的当前位置,这里我们还是使用
Vector3.Lerp
这个方法。 - 当子弹击中目标的时候,我们会先验证目标是否还存在。
-
获取了目标的
HealthBar
组件,按子弹造成的伤害来削减目标的生命值。 - 当一个敌人的生命值减到零的时候,需要销毁这个敌人对象,然后播放一个音效,最后给予玩家金币奖励。
保存好脚本,返回Unity中。
来些更大的子弹
假如等级高的小怪兽能发射较大的子弹,这是不是很酷呢?是的,我们能做到,因为这很简单。
将 Hierarchy 视图中的 Bullet1 拖拽到Project 视图中的Prefab文件夹下,创造出一个子弹的prefab。删除场景中的子弹对象,我们已经不再需要它。
利用 Bullet1 prefab再创建两个prefab,分别命名为 Bullet2 和 Bullet3 。传统的CTRL + C,CTRL + V命令在这里行不通。选中Bullet1后,按下快捷键CTRL + D,(duplicate 复制的意思),按下CTRL + D 两次后,创建 Bullet2 和 Bullet3。因为Bullet2 和 Bullet3都是比较大的子弹,接下来,我们要为这两个prefab设置新的子弹图片。
选中Bullet2 ,在Inspector面板中,设置 Sprite Renderer 组件的Sprite为 Images/Objects/Bullet2。这样,Bullet2的样子会比Bullet1更大一些。
同上,将Bullet3 prefab的sprite设置为 Images/Objects/Bullet3。
之前在编写Bullet Behavior脚本的时候,没有进行设置 Damage 这个变量的值,接下来,分别设置这三种子弹造成的伤害值。
在Inspector面板中,对Bullet1 、Bullet2 、Bullet3 的Damage进行赋值,分别为10、15、20,或者随你的便。
注意:级别越高的子弹造成的伤害越大。玩家需要将金币花在刀刃上,优先升级那些位置好的小怪兽们。
子弹的大小与小怪兽的等级成正比。
提升子弹的威力
为不同等级的小怪兽分配威力不同的子弹,这样小怪兽越强,就能越快地消灭敌人。
打开脚本 MonsterData.cs ,为 MonsterLevel 添加下面的变量:
public GameObject bullet; public float fireRate;
前者是指子弹的 prefab,后者是指小怪兽发射子弹的速率。保存好脚本,返回Unity,让我们完成对小怪兽的配置。
在Project视图中选中Monster prefab。在Inspector面板中,展开Monster Data脚本组件中的Levels数组,将所有元素的Fire Rate都设置为1,分别设置Elements0、Elements1、Elements2的Bullet为Bullet1、Bullet2、Bullet3。
配置好后的结果如下图所示:
开火
打开脚本ShootEnemies.cs,添加下面的变量:
private float lastShotTime; private MonsterData monsterData;
像这两个变量名所显示的那样,前者记录了小怪兽上一次开火的时间,后者的类型为MonsterData,这里包含了该小怪兽的子弹类型,发射速率等等数据。
在Start()方法中为这两个变量赋值:
lastShotTime = Time.time;
monsterData = gameObject.GetComponentInChildren<MonsterData>();
这里,我们设置lastShotTime为当前时间,然后获取了该游戏对象的MonsterData 组件。
再添加下面的代码,令小怪兽能够对敌人开火:
void Shoot(Collider2D target) { GameObject bulletPrefab = monsterData.CurrentLevel.bullet; // 1 Vector3 startPosition = gameObject.transform.position; Vector3 targetPosition = target.transform.position; startPosition.z = bulletPrefab.transform.position.z; targetPosition.z = bulletPrefab.transform.position.z; // 2 GameObject newBullet = (GameObject)Instantiate(bulletPrefab); newBullet.transform.position = startPosition; BulletBehavior bulletComp = newBullet.GetComponent<BulletBehavior>(); bulletComp.target = target.gameObject; bulletComp.startPosition = startPosition; bulletComp.targetPosition = targetPosition; // 3 Animator animator = monsterData.CurrentLevel.visualization.GetComponent<Animator>(); animator.SetTrigger("fireShot"); AudioSource audioSource = gameObject.GetComponent<AudioSource>(); audioSource.PlayOneShot(audioSource.clip); }
-
获取了子弹的初始坐标和目标所在坐标,将这两个坐标的Z坐标设置为
bulletPrefab的Z坐标
。之前我们设置bullet prefab的Z坐标的原因是为了表现一种层次感,子弹所处的位置要比小怪兽和敌人更低。 -
方法开头从MonsterData中获取了bulletPrefab,再
以bulletPrefab创建出一个子弹对象。
将startPosition
和targetPosition
赋值给我们创建出来的子弹对象。 - 让游戏更生动:当小怪兽开火的时候播放一个射击的动画和音效。
整合所有的模块
现在是时候该整合一切了,让你的小怪兽能够准确地朝着目标开火。
往ShootEnemies.cs脚本的Update()方法中添加下面的代码:
GameObject target = null; // 1 float minimalEnemyDistance = float.MaxValue; foreach (GameObject enemy in enemiesInRange) { float distanceToGoal = enemy.GetComponent<MoveEnemy>().distanceToGoal(); if (distanceToGoal < minimalEnemyDistance) { target = enemy; minimalEnemyDistance = distanceToGoal; } } // 2 if (target != null) { if (Time.time - lastShotTime > monsterData.CurrentLevel.fireRate){ Shoot(target.GetComponent<Collider2D>()); lastShotTime = Time.time; } // 3 Vector3 direction = gameObject.transform.position - target.transform.position; gameObject.transform.rotation = Quaternion.AngleAxis( Mathf.Atan2(direction.y, direction.x) * 180 / Mathf.PI, new Vector3(0, 0, 1)); }
让我们一步一步地来看这些代码:
-
决定小怪兽开火的目标,这里我们采用了寻找最小数的算法。先将
minimalEnemyDistance设置为float.MaxValue,这样就不会有比它更大的数出现了。遍历集合中的所有敌人,当循环结束的时候,我们就可以找出距离饼干最近的敌人。
-
当前时间与小怪兽上次开火的时间间隔大于射击速率的时候,调用
Shoot方法,
再将lastShotTime
设置为当前时间。 - 计算出小怪兽和目标之间的当前角度,然后旋转小怪兽,让小怪兽能够一直面对着目标。
保存好脚本,启动游戏。看你的小怪兽们正在奋力地保护你的饼干。好样的,现在我们完成了整个工程。
从这个项目中我们学到了什么
从这里可以下载完整的项目。
现在我们这个教程就要结束了,我们完成了一个很棒的塔防游戏。
这个游戏我们还可以做出以下扩展:
1. 添加更多种类的敌人和小怪兽
2. 为敌人建立更多的通往饼干的道路
3. 为小怪兽们设置更多的级别
这些小小的扩展可以令我们的游戏更好玩。假如你以此教程为基础创造出了属于自己的新游戏,请在评论中分享你的链接,让大家都能够好好地体验一回。
在这里你可以发现更多有趣的关于塔防游戏的想法。
感谢大家抽出时间来完成这篇教程。希望大家能够提出更多好的想法,祝大家都能够愉快地杀敌。