目录
- 前言
- 一、搭建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);
}
}
根据扫描情况开始或者暂停游戏方法赋值