如何在unity中制作塔防游戏 —— 完善敌人

如果你喜欢我们的教程,欢迎加入泰然网Unity交流群201505161

创建敌人标签

Select the Enemy prefab inthe Project Browser. At the top of the Inspector, click on the Tag dropdownand selectAdd Tag.

在工程浏览视口选择Enemy 预制件。在Inspector顶部,点击Tag 下拉菜单并选择Add Tag。

创建一个名为Enemy的Tag。

选择Enemy 预制件,在Inspector中设置它的Tag 为Enemy。

定义敌人的波数

现在需要定义一个敌人的波数。在MonoDevelop中打开SpawnEnemy.cs ,在SpawnEnemy之前添加以下类实现:

[System.Serializable]
publicclass Wave {
public GameObject enemyPrefab;
publicfloat spawnInterval =2;
publicint maxEnemies =20;
}

Wave 控制一个enemyPrefab,列举这一波敌人的基础,一个spawnInterval,几秒钟内每一波敌人之间的时间间隔,还有 maxEnemies,即这一波敌人的数量。这个类是Serializable,这就意味着可以在监视面板改变这些值。
在SpawnEnemy 类中添加以下变量:

public Wave[] waves;
publicint timeBetweenWaves =5;
private GameManagerBehavior gameManager;
privatefloat lastSpawnTime;
privateint enemiesSpawned =0;

这个为大量产生设置了一些类似于如何让敌人沿着路径点移动的变量。
可以在waves中定义游戏中一波波不同的敌人并追踪生成敌人的数量,当在enemiesSpawned 和 lastSpawnTime中大量生成时,请分开做。玩家在把他们全部杀掉后需要休息一下,因此将 timeBetweenWaves 设置为5秒。
用下边代码替换Start() 中的内容。

将lastSpawnTime 设置为当前时间,当场景加载完就会开始执行脚本。然后用同样的方法检索GameManagerBehavior 。
将这个添加到Update():

int currentWave = gameManager.Wave;
if(currentWave < waves.Length){
// 2
float timeInterval = Time.time- lastSpawnTime;
float spawnInterval = waves[currentWave].spawnInterval;
if(((enemiesSpawned ==0&& timeInterval > timeBetweenWaves)||
       timeInterval > spawnInterval)&&
      enemiesSpawned < waves[currentWave].maxEnemies){
// 3  
    lastSpawnTime = Time.time;
    GameObject newEnemy =(GameObject)
        Instantiate(waves[currentWave].enemyPrefab);
    newEnemy.GetComponent<MoveEnemy>().waypoints= waypoints;
    enemiesSpawned++;
}
// 4 
if(enemiesSpawned == waves[currentWave].maxEnemies&&
      GameObject.FindGameObjectWithTag("Enemy")==null){
    gameManager.Wave++;
    gameManager.Gold= Mathf.RoundToInt(gameManager.Gold* 1.1f);
    enemiesSpawned =0;
    lastSpawnTime = Time.time;
}
// 5 
}else{
  gameManager.gameOver=true;
  GameObject gameOverText = GameObject.FindGameObjectWithTag("GameWon");
  gameOverText.GetComponent<Animator>().SetBool("gameOver", true);
}

一步一步看一下这个代码:

  1. 获取当前这波敌人的索引,判断是不是最后一个。
  2. 如果是,计算最后一个敌人生成需要的时间并且计算是否到了生成敌人的时间。这里需要考虑两点。如果它是这波敌人中的第一个,检查timeInterval 是否大于timeBetweenWaves。否则,检查timeInterval 是否大于这一波敌人的spawnInterval。无论哪种情况,确认没有在这一波中生成所有的敌人。
  3. 如果有必要,通过实例化一个enemyPrefab来生成敌人。这样也增加了enemiesSpawned计数。
  4. 在屏幕中检查敌人的数量。如果没有并且它是这波敌人中的最后一个,那么就生成下一波敌人。在这波敌人的最后要给玩家所有剩余金币的百分之十。
  5. 打败最后一波敌人运行游戏胜利动画。

设置生成间隔

保存文件并切换到unity。在Hierarchy中选择Road 。在Inspector中,设置Waves 的Size 为4。
现在,为Enemy 所有四个元素设置Enemy Prefab 。如下设置Spawn Interval 和 Max Enemies 区域:
Element 0:Spawn Interval: 2.5, Max Enemies: 5
Element 1:Spawn Interval: 2, Max Enemies: 10
Element 2:Spawn Interval: 2, Max Enemies: 15
Element 3:Spawn Interval: 1, Max Enemies: 5
最终设置应该是下边截图的样子。

当然,可以自己尝试这些设置来增加或减少杀害。
运行游戏。哈哈!虫子们向着曲奇进军啦。

可选:添加不同类型的敌人

没有哪一个塔防游戏里只有一种类型的敌人。幸运的是,Prefabs 文件夹包含了另一个选择,Enemy2.
在Inspector 中选择Prefabs\Enemy2 并将MoveEnemy 脚本添加给它。
将它的Speed 设置为3 ,Tag 为Enemy。现在可以用这个快速的虫子来让玩家保持警觉了!

更新玩家生命值——温柔地杀掉我吧

即使虫子们成群结队的向着曲奇奔来,玩家确没有受到伤害。不过不再会是这样了。当敌人蚕食了曲奇玩家应该受到影响。

在MonoDevelop中打开GameManagerBehavior.cs 。添加下边两个变量:

public Text healthLabel;
public GameObject[] healthIndicator;

用healthLabel 来访问玩家的生命值读取数据,用healthIndicator 来访问五个绿色的吃曲奇的小怪兽——它们只是用一个比标准生命条更有趣的方式代表玩家的生命值。

管理生命值

接下来,在GameManagerBehavior中添加属性来维护玩家的生命值。

public int Health {
  get {return health;}
  set {
// 1
if(value < health){
      Camera.main.GetComponent<CameraShake>().Shake();
}
// 2
    health = value;
    healthLabel.text="HEALTH: "+ health;
// 3
if(health <=0&&!gameOver){
      gameOver =true;
      GameObject gameOverText = GameObject.FindGameObjectWithTag("GameOver");
      gameOverText.GetComponent<Animator>().SetBool("gameOver", true);
}
// 4 
for(int i =0; i < healthIndicator.Length; i++){
if(i < Health){
        healthIndicator.SetActive(true);
}else{
        healthIndicator.SetActive(false);
}
}
}
}

这个用来管理玩家的生命值。再一次,大部分的代码在setter方法中:

  1. 如果正在减少玩家的生命值,用CameraShake 组件来创建一个漂亮的抖动效果。这个脚本已经在项目中了,这里不再介绍。
  2. 更新私有变量和屏幕左上角的生命条。
  3. 如果生命值降到0并且游戏还没有结束,将gameOver 设置为true 并触发GameOver 动画。
  4. 从曲奇上边移走一个怪兽。如果仅仅是禁用了它们,这样写起来会更简单些,不过当生命值增加的时候还可以重用它们。

在Start()中初始化Health :

Health =5;

当屏幕开始播放时将Health 设置为5.
有了这个属性,现在无论什么时候虫子到达了曲奇都可以更新玩家的生命值。保存这个文件,还是在MonoDevelop中切换到MoveEnemy.cs。

更新生命值

为了更新玩家的生命值,在Update() 中找到为// TODO: deduct health 的注释并用下边的代码替换它:

GameManagerBehavior gameManager =
    GameObject.Find("GameManager").GetComponent<GameManagerBehavior>();
gameManager.Health-=1;

这个获取到GameManagerBehavior 并且从生命值减1。
保存文件并切换到unity。
在Hierarchy 中选择GameManager 并将其 Health Label 设置为HealthLabel。
在Hierarchy 中扩大Cookie ,将它的五个小怪兽拖拽到GameManager的Health Indicator 数组中——健康指示是这五个绿色小怪兽在开心地吃他们的曲奇呢。
播放场景并等待虫子到达曲奇。直到失败什么都不要做。

怪兽之战:怪兽的复仇

怪兽在位置上吗?检查一下。敌人在前进吗?检查一下——他们看起来真卑鄙!现在是时候攻击它们啦!
这步需要一下这些:
一个生命条,由此玩家可以知道哪些敌人强哪些敌人弱。
在怪兽的范围内检测敌人。
决策点——射击哪个敌人。
大量的子弹。

敌人的生命条

用两个图片来实现生命条,一个用作黑色背景,另一个浅绿色的条来伸缩匹配敌人的生命值。
将Prefabs\Enemy 从Project Browser拖入到场景中。
然后将Images\Objects\HealthBarBackground 拖拽到Hierarchy的Enemy 上作为子集。
在Inspector中,将HealthBarBackground 的Position 设置为(0, 1, -4)。接下来在Project Browser 中选择Images\Objects\HealthBar 并确保它的Pivot 设置为了Left。然后,在 Hierarchy 中将它作为Enemy 的子集添加并将它的Position 设置为(-0.63, 1, -5)。将它的X Scale 设置为125。
为HealthBar 游戏对象添加一个命名为HealthBar 的C# 脚本。之后,会编辑它来调整生命条的长度。
随着在Hierarchy选择了Enemy ,确保它的位置是(20, 0, 0)。点击Inspector 顶部的Apply 来将刚才的改变保存为预制件的一部分。最后,从Hierarchy.中删除Enemy 。

现在重复这些步骤来为Prefabs\Enemy2添加生命条。

调整生命条的长度

在MonoDevelop中打开HealthBar.cs ,添加以下变量:

publicfloat maxHealth =100;
publicfloat currentHealth =100;
privatefloat originalScale;

maxHealth 存储敌人的最大生命点,currentHealth 追踪生命值的剩余量。最后,originalScale 记录记录生命条的原始长度。
在Start()中存储对象的originalScale。

originalScale = gameObject.transform.localScale.x;

保存localScale的x值。
通过将下边代码添加到Update()中来设置生命条的缩放:

Vector3 tmpScale = gameObject.transform.localScale;
tmpScale.x= currentHealth / maxHealth * originalScale;
gameObject.transform.localScale= tmpScale;

将localScale 复制出一个临时变量,因为不可能只适应它的x 值。然后,基于虫子当前的生命值计算一个新的x缩放值,并将临时变量设置回localScale。
保存文件并在unity中运行游戏。就会在敌人的上方看到生命条啦。

标签: unity, unity2d, unity制作塔防游戏

?>