目录
- 前言
- 一、搭建UI
- 二、创建脚本
前言
在增强现实(AR)技术快速发展的今天,Vuforia作为一个强大的AR开发平台,为开发者提供了许多便捷的工具和功能。在本篇博客中,我们将介绍如何使用Vuforia在Unity中创建一个简单的塔防游戏。通过结合Vuforia的图像识别和增强现实技术,我们可以将传统的塔防游戏带入一个全新的维度。
一、搭建UI
创建Canvas,并设置16:9

创建button,然后设置图片

在素材中选择一个合适的塔,然后做成自己的预制体

设置所有炮塔基座的tag和layer

设置敌人的tag和layer

设置所有炮塔基座的碰撞器

创建敌人显示的UI
创建一个image,然后选择上一期素材的图片

在图片下面创建一个text用于显示血量的数值

复制几份,然后显示不同的数值


创建敌人血条
创建一个Slider,用于制作血条显示,把图片修改成line并且删除子物体

创建一个子image,把图片也改成line,修改图片的填充选项,然后血条就完成了


设置Scrollbar的Handle Rect,至此血条UI设计完成

二、创建脚本
GameManager脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }
    public List<Transform> pointList;
    //玩家当前点击的塔
    public int currentChoice;
    //存储塔的预制体
    public List<GameObject> towerList;
    //存储塔的费用
    public List<int> towerCostList;
    //玩家总金币
    public int totalCoin;
    //玩家剩下的金币
    public int remainingCoin;
    //敌人血量
    public int maxHealth;
    public int currentHealth;
    private GameUi _gameUi;
    private SpawnManager _sm;
    //需要创建的敌人数量
    public int totalCreateEnemys;
    //击杀的敌人数量
    public int hasKilledEnemys;
    //当前游戏状态
    public bool isFinish;
    public bool isPause;
    public bool isPlant;
    void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
        }
        else
        {
            Destroy(gameObject);
        }
        _sm = GetComponent<SpawnManager>();
        //初始化
        totalCreateEnemys = 4;
        _sm.createNum = 4;
        totalCoin = remainingCoin = 100;
        maxHealth = currentHealth = 5;
    }
    void Start()
    {
        //开启敌人生成携程
        StartCoroutine(_sm.CreateEnemyCoroutine(3));
    }
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            if (Camera.main != null)
            {
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                RaycastHit hit;
                bool isHit = Physics.Raycast(ray, out hit);
                if (isHit)
                {
                    if (hit.transform.CompareTag("BaseBox"))
                    {
                        //如果点击到了炮台基座
                        if (hit.transform.childCount == 0)
                        {
                            if (remainingCoin >= towerCostList[currentChoice])
                            {
                                if (isPlant)
                                {
                                    GameObject go = Instantiate(towerList[currentChoice], hit.transform.position,
                                        Quaternion.identity);
                                    go.transform.SetParent(hit.transform);
                                    go.transform.localPosition = new Vector3(0, 0.02f, 0);
                                    //更新金币和UI
                                    remainingCoin -= towerCostList[currentChoice];
                                    _gameUi.SetCoinUI();
                                }
                            }
                        }
                    }
                }
            }
        }
        if (currentHealth <= 0 || hasKilledEnemys >= totalCreateEnemys)
        {
            GameEnd();
        }
    }
    //游戏结束
    private void GameEnd()
    {
        isFinish = true;
        StopAllCoroutines();
        _gameUi.ExitPanelShow();
    }
    //根据扫描情况开始或者暂停游戏
    public void PauseOrContinueGame()
    {
        Time.timeScale = !isPause ? 0f : 1f;
        isPause = !isPause;
    }
}

创建塔的脚本
using System; 
using System.Collections; 
using System.Collections.Generic; 
using UnityEngine; 
public class Tower : MonoBehaviour
{
    // 存储检测到的碰撞体数组
    public Collider[] colliders; 
    public int maxSize;
    public float rotationSpeed;
    //攻击间隔
    public float attackInternal;
    //攻击开始的时间
    public float startTime;
    
    //tower的特效
    public GameObject hitEffect;
    public GameObject firePoint1;
    public GameObject firePoint2;
    private void Awake()
    {
        maxSize = 10; // 初始化最大碰撞体数量为10
        colliders = new Collider[maxSize]; // 初始化碰撞体数组
        rotationSpeed = 100;
        attackInternal = 0.5f;
    }
    private void Update()
    {
        //获取最近的敌人
        GameObject go = LookForTarget();
        if (go!=null)
        {
            //修改炮台方向
            RotateTower(go);
        }
        startTime += Time.deltaTime;
        if (startTime>attackInternal)
        {
            NormalAttack();
            startTime = 0;
        }
    }
    // 检测塔范围内的敌人
    public virtual GameObject LookForTarget()
    {
        // 清空数据
        Array.Clear(colliders,0,colliders.Length);
        // 使用OverlapSphereNonAlloc方法检测塔范围内的敌人,结果存储在colliders数组中
        Physics.OverlapSphereNonAlloc(transform.position, 1, colliders, LayerMask.GetMask("Enemy"));
        GameObject closestObj = null; 
        float distance = float.MaxValue; 
        for (int i = 0; i < colliders.Length; i++) 
        {
            if (colliders[i]!=null)
            {
                GameObject currentObj = colliders[i].gameObject; 
                // 计算当前对象与塔的距离
                float tempDistance = Vector3.Distance(transform.position, currentObj.transform.position); 
                // 如果当前对象距离小于已知最小距离
                if (tempDistance < distance) 
                {
                    distance = tempDistance; 
                    closestObj = colliders[i].gameObject; 
                }
            }
        }
        return closestObj; 
    }
    
    // 控制塔朝向敌人
    public void RotateTower(GameObject go)
    {
        Vector3 dir = go.transform.position - transform.position;
        Vector3 rotationDir = Vector3.RotateTowards(transform.forward, dir, 0.5f, 360f);
        Quaternion targetRotation = Quaternion.LookRotation(rotationDir);
        transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
    }
    
    // 塔的攻击
    public void NormalAttack()
    {
        GameObject target = LookForTarget();    
        if (target!=null)
        {
            Enemy enemy = target.GetComponent<Enemy>();
            
            if (enemy!=null)
            {
                //生成攻击特效
                var o = Instantiate(hitEffect, firePoint1.transform.position, transform.rotation);
                //删除特效
                Destroy(o,0.2f);
                enemy.Hurt();
            }
        }
    }
}

创建UI的脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameUi : MonoBehaviour
{
    public Button towerBtn;
    public Text enemyText;
    public Text coinText;
    
    public Text healthText;
    //存储血条
    public List<Slider> sliders;
    //游戏结束UI
    public Image gameOverUI;
    //游戏结束按钮
    public Button gameOverBtn;
    void Awake()
    {
        towerBtn.onClick.AddListener(CreateTower);
        gameOverBtn.onClick.AddListener(ExitGame);
        SetBloodPositionInfo();
        SetGamOverShow();
    }
    void Start()
    {
    }
    void Update()
    {
    }
    private void CreateTower()
    {
        GameManager.Instance.isPlant = true;
        GameManager.Instance.currentChoice = 0;
    }
    public void SetEnemyUI()
    {
        enemyText.text = $"{GameManager.Instance.hasKilledEnemys}/{GameManager.Instance.totalCreateEnemys}";
    }
    public void SetCoinUI()
    {
        coinText.text = $"{GameManager.Instance.remainingCoin}/{GameManager.Instance.totalCoin}";
    }
    public void SetHealthUI()
    {
        healthText.text = $"{GameManager.Instance.currentHealth}/{GameManager.Instance.maxHealth}";
    }
    //设置血条的位置信息
    public void SetBloodPositionInfo()
    {
        for (int i = 0; i < sliders.Count; i++)
        {
            var rectTransform = sliders[i].GetComponent<RectTransform>();
            rectTransform.position = new Vector3(-10000f, 0f, 0f);
            sliders[i].gameObject.SetActive(false);
        }
    }
    //查找到没有激活的血条然后显示出来
    public Slider FindNoActivationBloodShow()
    {
        //获取一个没有激活的slider
        var slider = sliders.Find(x => !x.gameObject.activeSelf);
        slider.gameObject.SetActive(true);
        return slider;
    }
    public void SetGamOverShow()
    {
        var rectTransform = gameOverUI.GetComponent<RectTransform>();
        rectTransform.position = new Vector3(-10000f, 0f, 0f);
    }
    public void ExitPanelShow()
    {
        var rectTransform = gameOverUI.GetComponent<RectTransform>();
        rectTransform.position = new Vector3(Screen.width / 2f, Screen.height / 2f, 0f);
    }
    //退出游戏
    public void ExitGame()
    {
        Application.Quit();
    }
}

创建SpawnManager脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnManager : MonoBehaviour
{
    public GameObject enemyPrefab;
    public GameObject parent;
    
    //需要创建的敌人数量
    public int createNum;
    void Awake()
    {
        createNum = 4;
    }
    void Start ()
    {
        CreateEnemy();
    }
	
    void Update ()
    {
    }
    public void CreateEnemy()
    {
        GameObject enemy = Instantiate(enemyPrefab, parent.transform);
        enemy.transform.SetParent(parent.transform);
        enemy.transform.localPosition = new Vector3(-1f, 0.25f, 4f);
    }
    public IEnumerator CreateEnemyCoroutine(float duration)
    {
        int count = 0;
        while (true)
        {
            if (count<createNum)
            {
                yield return new WaitForSeconds(duration);
                CreateEnemy();
                count++;
            }
            else
            {
                break;
            }
        }
    }
}

创建Enemy脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Enemy : MonoBehaviour
{
    public int currentIndex;
    public float moveSpeed;
    //血量
    public int maxHp;
    public int currentHp;
    private Slider _slider;
    private RectTransform _rt;
    //血条跟随偏移
    public float xOffest;
    public float yOffest;
    
    private GameUi _gameUi;
    void Awake()
    {
        moveSpeed = 0.2f;
        currentIndex = 0;
        currentHp = maxHp = 10;
        _gameUi = GameObject.Find("Canvas").GetComponent<GameUi>();
        _slider = _gameUi.FindNoActivationBloodShow();
        _rt = _slider.GetComponent<RectTransform>();
        xOffest = 5;
        yOffest = 5;
    }
    void Start()
    {
    }
    void Update()
    {
        if (!GameManager.Instance.isFinish)
        {
            BloodFollow();
            Move();
        }
        //当摄像头没有扫描到图片的时候,暂停游戏并隐藏UI
        if (GameManager.Instance.isPause)
        {
            SetSliderInfoWhenPause();
        }
    }
    public void Move()
    {
        int nextPoint = currentIndex + 1;
        if (GameManager.Instance.pointList.Count <= nextPoint)
        {
            ReachFinalPoint();
            return;
        }
        Vector3 v3 = transform.InverseTransformPoint(GameManager.Instance.pointList[nextPoint].position);
        transform.Translate(v3 * (Time.deltaTime * moveSpeed));
        if (IsArrive(GameManager.Instance.pointList[nextPoint]))
        {
            currentIndex++;
        }
    }
    bool IsArrive(Transform t)
    {
        float distance = Vector3.Distance(transform.position, t.position);
        if (distance < 0.05f)
        {
            return true;
        }
        return false;
    }
    
    // 受伤的方法
    public void Hurt(int damge = 2)
    {
        currentHp -= damge;
        _slider.value = currentHp * 1.0f / (maxHp * 1.0f);
        if (currentHp<=0)
        {
            Die();
        }
    }
    //死亡效果
    public void Die()
    {
        _rt.position = new Vector3(-10000f, 0f, 0f);
        _slider.value = 1;
        _slider.gameObject.SetActive(false);
        GameManager.Instance.hasKilledEnemys += 1;
        _gameUi.SetEnemyUI();
        Destroy(gameObject);
    }
    //血条跟随
    public void BloodFollow()
    {
        if (Camera.main != null)
        {
            Vector2 point = Camera.main.WorldToScreenPoint(transform.position);
            _rt.position = point + new Vector2(xOffest, yOffest);
        }
    }
    //隐藏UI
    public void SetSliderInfoWhenPause()
    {
        if (_rt!=null)
        {
            _rt.position = new Vector3(-10000f, 0, 0);
        }
    }
    //移动到最后一个点处理方法
    public void ReachFinalPoint()
    {
        _rt.position = new Vector3(-10000f, 0, 0);
        _slider.value = 1;
        _slider.gameObject.SetActive(false);
        GameManager.Instance.currentHealth -= 1;
        _gameUi.SetHealthUI();
        GameObject.Destroy(gameObject);
    }
}

根据扫描情况开始或者暂停游戏方法赋值




















