Vuforia AR篇(九)— AR塔防下篇

前言

在增强现实(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);
    }

}

在这里插入图片描述

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

在这里插入图片描述

三、效果

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨染青枫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值