如何在unity中制作塔防游戏 —— 完结篇

给怪兽子弹——很多很多的子弹!

将Images/Objects/Bullet1 从Project Browser拖拽到场景中。将z 位置设置为-2 ——x 和 y 位置不重要,因为在运行时每次实例化一个新的子弹都需要设置它们。

添加一个名为BulletBehavior的新脚本,并在MonoDevelop中添加以下变量。

Public float speed =10;
Public int damage;
public GameObject target;
public Vector3 startPosition;
public Vector3 targetPosition;
  
privatefloat distance;
privatefloat 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 设置为当前时间并计算起始位置和目标位置间的距离。还要向往常一样获取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);
}
  1. 通过用Vector3.Lerp 获取起始位置和结束位置的差值来计算新子弹的位置。
  2. 如果子弹到达了targetPosition,要核实target 仍然存在。
  3. 检索目标的HealthBar 组件并根据子弹的伤害减掉它的生命值。
  4. 如果敌人的生命值将为零,销毁它,播放一段声音效果并为击中敌人奖励玩家。

保存文件并回到unity。

获取更大的子弹
如果怪兽在高等级发生更大的子弹是不是很酷?——是的,必须的!辛运的是,这个很容易实现。
将Bullet1 游戏对象从 Hierarchy 拖拽到Project 面板来创建一个子弹的预制件。将原来的对象从场景移除——不再需要它了。

复制Bullet1 两次。为复制件分别命名Bullet2 和Bullet3。选择Bullet2。在Inspector中,将Sprite Renderer 组件的Sprite 区域设置为Images/Objects/Bullet2。这标志着Bullet2看起来比Bullet1大一些。
重复这步来设置Bullet3 预制件的Sprite 为Images/Objects/Bullet3。

接下来,在Bullet Behavior中设置子弹带来的伤害值是多少。在Project 标签选择Bullet1 预制件。在Inspector 中可以看到Bullet Behavior (Script),并在那里为Bullet1的 Damage 设置为 10 ,为Bullet2设置为15 ,为Bullet3 设置为20——或者想要设置多少就设置多少。

注:设置好值以便在更高等级中,每次带来的伤害会更高。这抵消了升级允许玩家在最佳位置提升怪兽的事实。

子弹预制件——随着等级而变大

瞄准子弹

为不同等级的怪兽分配不同的子弹,所以更强壮的怪兽会更快的杀掉敌人。

在MonoDevelop中打开MonsterData.cs ,为MonsterLevel添加以下变量:

public GameObject bullet;
public float fireRate;

这些会为每个怪兽等级设置子弹预制件和发射速度。保存文件并返回unity完成怪兽的设置。
在Project Browser选择Monster 预制件。在Inspector中,在Monster Data (Script) 组件扩展Levels 。为每个元素将Fire Rate 设置为1 。然后分别将Elements 0,,1 和2的Bullet 设置为Bullet1, Bullet2 和 Bullet3,

怪兽等级应该被配置为像下边显示这样:

开火

在MonoDevelop中打开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);
}
  1. 获取子弹起始位置和目标位置。为bulletPrefab设置z方向位置。之前,设置了子弹预制件的z方向位置来确保子弹出现在发射它的怪兽后边,不过要在敌人前边。
  2. 为MonsterLevel用bulletPrefab 实例化一个新的子弹。为子弹分配startPosition 和targetPosition 。
  3. 使游戏更有趣:无论什么时候怪兽射击都运行一个射击动画并播放给激光声音。

将所有汇总

现在是时候将所有都连接到一起了。确定目标并让怪兽看向它。

还是在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));
}

一步一步看这些代码。

  1. 确定怪兽的目标。在minimalEnemyDistance中以最大可能的距离开始。遍及范围内的所有怪兽并且如果敌人距离曲奇的距离比当前的最小值更小时将敌人作为新的目标。
  2. 如果过去的时间大于怪兽的射击速度则呼叫Shoot 并将lastShotTime 设置为当前时间。
  3. 计算怪兽和它的目标间的旋转角度。将怪兽旋转这个角度。现在它总是面向目标了。

保存文件并在unity中播放游戏。怪兽努力的保护着曲奇。现在就全部做完啦!

何去何从

哇,现在已经按照两个教程做了好多事,并且还有一个酷酷的游戏可以展示。如果从这个教程创建了另一个新的游戏,我们愿意去玩——所以在评论分享一下链接和你的畅想。

在这个采访中会发现很多关于塔防游戏的有趣想法。

感谢大家花费时间来看这些教程。期盼看到你们超级棒的想法并杀掉很多怪兽。

标签: 新手入门, unity, unity2d, unity制作塔防游戏

?>