Vuforia AR篇(九)— AR塔防下篇
目录
- 前言
- 一、搭建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);//更新金币和UIremainingCoin -= 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; // 初始化最大碰撞体数量为10colliders = 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;//游戏结束UIpublic 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(){//获取一个没有激活的slidervar 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();}//当摄像头没有扫描到图片的时候,暂停游戏并隐藏UIif (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);}}//隐藏UIpublic 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);}}
根据扫描情况开始或者暂停游戏方法赋值