Vuforia开发AR卡牌对战游戏(二):实现
接着上一篇,我们再继续实现我们的卡牌对战游戏,上一次忘记了将血条的样式改为水平的Filed样式。
这里我们需要几个Tag:KnightAI,RemoteAI,KnightWeapen,RemoteBullet,分别给到近战,远程,近战的武器,远程的子弹上。
由于我们有两个角色,一个远程一个近战,所以我们要写两个脚本。
首先写近战的脚本:KnightCtrl,先说一下大致思路:近战角色有血量,要获得自身血条的引用,自己在被子弹击中时会减血,还需要判断敌人是否死亡,敌人死亡需要嘲讽,自己是否死亡;需要每帧去获得远程敌人的引用(因为不知道啥时候会识别到敌人识别图,所以不能在Start中获得),到了一定的距离可以向着敌人移动,到了攻击范围就去攻击敌人。下面是主要代码和注释:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class KnightCtrl : MonoBehaviour
{
public float m_Life = 100f; //血量
public float sightRange = 20f; //视野范围,到了视野范围就向着对方移动
public float attackRange = 2f; //攻击范围
public Image bloodImage; //血条
public static float weaponDamage = 20f; //自己的武器伤害值
private Animator m_Animator;//动画机
private bool enemyHasDie; //敌人是否死亡
// Use this for initialization
void Start ()
{
m_Animator = this.GetComponent();
//待机
m_Animator.SetBool("Run", false);
m_Animator.SetBool("NextAttack", false);
enemyHasDie = false;
}
// Update is called once per frame
void Update ()
{
GameObject remoteEnemy = GameObject.FindGameObjectWithTag("RemoteAI");
if (remoteEnemy != null && !enemyHasDie && m_Life > 0f) //识别图加载出来后而且敌人没有死而且自己还活着
{
float dis = Vector3.Distance(this.transform.position, remoteEnemy.transform.position);
Debug.Log(dis);
if (dis < sightRange) //如果在视野范围之内
{
if (dis > attackRange) //如果在攻击范围之外,就向着目标移动
{
//向着敌人移动
m_Animator.SetBool("Run", true);
m_Animator.SetBool("NextAttack", false);
this.transform.position = Vector3.MoveTowards(this.transform.position, remoteEnemy.transform.position, 0.1f);
this.transform.LookAt(remoteEnemy.transform);
}
else //到了攻击范围,攻击
{
m_Animator.SetBool("Run", false);
m_Animator.SetBool("NextAttack", true);
if (remoteEnemy.GetComponent().m_Life <= 0f) //获得远程人物的RemoteCtrl组件中的生命值,如果把对面打死了
{
enemyHasDie = true;
m_Animator.SetTrigger("Raise Dead"); //嘲讽
//待机
m_Animator.SetBool("Run", false);
m_Animator.SetBool("NextAttack", false);
}
}
}
}
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "RemoteBullet") //如果打来的是子弹
{
this.m_Life -= Bullet_R.bulletDam; //子弹伤害
bloodImage.fillAmount = m_Life / 100f;
Destroy(other.gameObject);
}
if ( m_Life <= 0f) //如果被打死了
{
m_Animator.SetTrigger("Die");
Destroy(this.gameObject, 5f); //5s后销毁
}
}
}
然后是远程的脚本RemoteCtrl,思路和上面相似,只不过多了一个开火的位置和动态生成子弹:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RemoteCtrl :MonoBehaviour
{
public float m_Life = 100f;//血量
public float sightRange = 20f; //视野范围,到了视野范围就向着对方移动
public float attackRange = 5f; //攻击范围
public float attackInterval = 1f; //攻击间隔
public Image bloodImage;//血条
public Transform fireTrans;//开火位置
public GameObject bulletPerfab;//子弹的预制件
private Animator m_Animator;//动画机
private bool enemyHasDie; //敌人是否死亡
private float totalTime; //子弹的生成间隔
// Use this for initialization
void Start ()
{
m_Animator = this.GetComponent();
m_Animator.SetBool("Run", false);
m_Animator.SetBool("NextAttack",false);
enemyHasDie = false;
totalTime = attackInterval;
}
// Update is called once per frame
void Update ()
{
GameObject knightEnemy = GameObject.FindGameObjectWithTag("KnightAI");
if (knightEnemy != null && !enemyHasDie && m_Life > 0f)
{
float dis = Vector3.Distance(this.transform.position, knightEnemy.transform.position);
if (dis < sightRange) //如果在视野范围之内
{
if (dis > attackRange)//如果在攻击范围之外
{
//向着敌人移动
m_Animator.SetBool("Run",true);
m_Animator.SetBool("NextAttack",false);
this.transform.position =Vector3.MoveTowards(this.transform.position, knightEnemy.transform.position, 0.1f);
this.transform.LookAt(knightEnemy.transform);
totalTime = attackInterval;
}
else //到了开火位置
{
if (totalTime >= attackInterval)
{
m_Animator.SetBool("NextAttack",true);
GenerateBullet(); //生成子弹
totalTime = 0f;
m_Animator.SetBool("NextAttack",false);
}
totalTime += Time.deltaTime; //更新总时间
if (knightEnemy.GetComponent().m_Life <= 0f)
{
//如果敌人死了
enemyHasDie = true;
m_Animator.SetTrigger("Raise Dead");
//待机
m_Animator.SetBool("Run",false);
m_Animator.SetBool("NextAttack",false);
}
}
}
}
}
void GenerateBullet()
{
GameObject bulletObj = Instantiate(bulletPerfab);
bulletObj.transform.position = fireTrans.position; //在开火位置生成
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "KnightWeapen") //如果近战的武器攻击我了
{
this.m_Life -= KnightCtrl.weaponDamage; //子弹伤害
bloodImage.fillAmount = m_Life / 100f;
}
if (m_Life <= 0f) //如果被打死了
{
m_Animator.SetTrigger("Die");
Destroy(this.gameObject, 5f);//5s后销毁
}
}
}
接下来在写一个子弹的脚本Bullet_R,由于我们是追踪的子弹,所以脚本大致是:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet_R :MonoBehaviour
{
public static float bulletDam = 20f;
private GameObject knightGameObj;
private Transform knightTrans;
// Use this for initialization
void Start ()
{
knightGameObj = GameObject.FindGameObjectWithTag("KnightAI");
knightTrans = knightGameObj.transform;
Destroy(this.gameObject, 8f); //8s后销毁
}
// Update is called once per frame
void Update ()
{
if (knightGameObj != null)
{
//射击
this.transform.position = Vector3.MoveTowards(this.transform.position, knightTrans.position, 5f);
}
}
}
把所有的脚本都写好后,我们再在My_DefaultTrackableEventHandler2中修改脚本,这次在识别到后让他动态加载,丢失后销毁:
添加 public GameObject knightOrRemotePerfab; //动态加载的预制件
private GameObject knightOrRemoteObj; //加载的GameObject的引用
修改两个方法:
//识别到的时候
//动态加载
private void OnTrackingFound()
{
//动态加载
knightOrRemoteObj = Instantiate<GameObject>(knightOrRemotePerfab);
knightOrRemoteObj.transform.parent = this.transform;
knightOrRemoteObj.transform.position = this.transform.position + this.transform.up * 2f;
}
//识别丢失的时候
//销毁
private void OnTrackingLost()
{
if (knightOrRemoteObj != null)
{
Destroy(knightOrRemoteObj);
}
}
OK,这样就差不多了,然后我们再在远程的人物身上加一个开获得位置。
然后把KnightCtrl赋给近战,RemoteCtrl付给远程,Bullet_R赋给子弹,然后赋值
最后将近战和远程的的人物全部弄成预制件(拖到世界空间下再Apply),然后在ImageTarget下赋值这几个预制件。具体参数需要自己调整
测试如下:
没有到攻击距离的时候:
向着对方移动:
攻击中:
死亡一个:
基本功能已经实现了,具体的一些细节啥的还是需要细化,基本的AR卡牌对战框架就是这样。
本文内容部分参考自Think加速想象力出版的《AR与VR开发实战》教程,更多学习资料也请关注www.arvrthink.com。