VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 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);
        }
复制代码
  1. 计算出子弹的当前位置,这里我们还是使用 Vector3.Lerp 这个方法。
  2. 当子弹击中目标的时候,我们会先验证目标是否还存在。
  3. 获取了目标的 HealthBar 组件,按子弹造成的伤害来削减目标的生命值。
  4. 当一个敌人的生命值减到零的时候,需要销毁这个敌人对象,然后播放一个音效,最后给予玩家金币奖励。

  保存好脚本,返回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的BulletBullet1Bullet2Bullet3

  配置好后的结果如下图所示:

  

开火

  打开脚本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. 获取了子弹的初始坐标和目标所在坐标,将这两个坐标的Z坐标设置为 bulletPrefab的Z坐标。之前我们设置bullet prefab的Z坐标的原因是为了表现一种层次感,子弹所处的位置要比小怪兽和敌人更低。
  2. 方法开头从MonsterData中获取了bulletPrefab,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设置为float.MaxValue,这样就不会有比它更大的数出现了。遍历集合中的所有敌人,当循环结束的时候,我们就可以找出距离饼干最近的敌人。
  2. 当前时间与小怪兽上次开火的时间间隔大于射击速率的时候,调用Shoot方法, 再将lastShotTime设置为当前时间。
  3. 计算出小怪兽和目标之间的当前角度,然后旋转小怪兽,让小怪兽能够一直面对着目标。

  保存好脚本,启动游戏。看你的小怪兽们正在奋力地保护你的饼干。好样的,现在我们完成了整个工程。

  

从这个项目中我们学到了什么

  从这里可以下载完整的项目。

  现在我们这个教程就要结束了,我们完成了一个很棒的塔防游戏。

  这个游戏我们还可以做出以下扩展:

   1. 添加更多种类的敌人和小怪兽

   2. 为敌人建立更多的通往饼干的道路

   3. 为小怪兽们设置更多的级别

  这些小小的扩展可以令我们的游戏更好玩。假如你以此教程为基础创造出了属于自己的新游戏,请在评论中分享你的链接,让大家都能够好好地体验一回。

  在这里你可以发现更多有趣的关于塔防游戏的想法。

   感谢大家抽出时间来完成这篇教程。希望大家能够提出更多好的想法,祝大家都能够愉快地杀敌。


相关教程
关于我们--广告服务--免责声明--本站帮助-友情链接--版权声明--联系我们       黑ICP备07002182号