AR开发实战Vuforia项目之俄罗斯方块(虚拟按钮的应用)上

一、框架视图

二、关键脚本

VBInputEvent

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Vuforia;//引用命名空间

public class VBInputEvent : MonoBehaviour,IVirtualButtonEventHandler {

    [HideInInspector]
    //0,1,2,3 表示 null left right rotate
    public int whichButton = 0; //选择的是哪一个虚拟按钮

    //单例模式
    private static VBInputEvent _instance;

    //属性
    public static VBInputEvent Instance {

        get {
            return _instance;
        }
    }


    void Awake() {

        _instance = this; //单例模式赋值
    }


    void Start () {

        //找到所有的虚拟按钮 并逐一注册虚拟按钮事件
        VirtualButtonBehaviour[] vbs = transform.GetComponentsInChildren<VirtualButtonBehaviour>();
        for (int i = 0; i < vbs.Length; i++)
        {
            vbs[i].RegisterEventHandler(this);
        }

    }
    
    
    void Update () {
        
    }

    /// <summary>
    /// 虚拟按钮点击时候触发事件
    /// </summary>
    /// <param name="vb"></param>
    public void OnButtonPressed(VirtualButtonBehaviour vb)
    {
        switch (vb.VirtualButtonName)
        {
            case "Left":
                whichButton = 1;
                break;
            case "Right":
                whichButton = 2;
                break;
            case "Rotate":
                whichButton = 3;
                break;
            default:
                break;
        }

    }

    /// <summary>
    /// 虚拟按钮松开
    /// </summary>
    /// <param name="vb"></param>
    public void OnButtonReleased(VirtualButtonBehaviour vb)
    {
        whichButton = 0;
    }
}

MyTrackableEventHandler

/*==============================================================================
Copyright (c) 2017 PTC Inc. All Rights Reserved.

Copyright (c) 2010-2014 Qualcomm Connected Experiences, Inc.
All Rights Reserved.
Confidential and Proprietary - Protected under copyright and other laws.
==============================================================================*/

using UnityEngine;
using Vuforia;

/// <summary>
///     A custom handler that implements the ITrackableEventHandler interface.
/// </summary>
public class MyTrackableEventHandler : MonoBehaviour, ITrackableEventHandler
{


    public GameObject arObject;

    #region PRIVATE_MEMBER_VARIABLES

    protected TrackableBehaviour mTrackableBehaviour;

    #endregion // PRIVATE_MEMBER_VARIABLES

    #region UNTIY_MONOBEHAVIOUR_METHODS

    protected virtual void Start()
    {
        mTrackableBehaviour = GetComponent<TrackableBehaviour>();
        if (mTrackableBehaviour)
            mTrackableBehaviour.RegisterTrackableEventHandler(this);



        //自动对焦功能
        Vuforia.CameraDevice.Instance.SetFocusMode(Vuforia.CameraDevice.FocusMode.FOCUS_MODE_CONTINUOUSAUTO);

        
    }

    #endregion // UNTIY_MONOBEHAVIOUR_METHODS

    #region PUBLIC_METHODS

    /// <summary>
    ///     Implementation of the ITrackableEventHandler function called when the
    ///     tracking state changes.
    /// </summary>
    public void OnTrackableStateChanged(
        TrackableBehaviour.Status previousStatus,
        TrackableBehaviour.Status newStatus)
    {
        if (newStatus == TrackableBehaviour.Status.DETECTED ||
            newStatus == TrackableBehaviour.Status.TRACKED ||
            newStatus == TrackableBehaviour.Status.EXTENDED_TRACKED)
        {
            Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " found");
            OnTrackingFound();
        }
        else if (previousStatus == TrackableBehaviour.Status.TRACKED &&
                 newStatus == TrackableBehaviour.Status.NOT_FOUND)
        {
            Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " lost");
            OnTrackingLost();
        }
        else
        {
            // For combo of previousStatus=UNKNOWN + newStatus=UNKNOWN|NOT_FOUND
            // Vuforia is starting, but tracking has not been lost or found yet
            // Call OnTrackingLost() to hide the augmentations
            OnTrackingLost();
        }
    }

    #endregion // PUBLIC_METHODS

    #region PRIVATE_METHODS



    protected virtual void OnTrackingFound()
    {
        arObject.SetActive(true);  //发现的时候设置为true
    }


    protected virtual void OnTrackingLost()
    {
        arObject.SetActive(false);
    }

    #endregion // PRIVATE_METHODS
}

Spanwer

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


/// <summary>
/// 方块生成器
/// </summary>
public class Spanwer : MonoBehaviour {

    public GameObject[] standbyGroup;  //跟积分面板一样 持有生成的方块一样  顺序一样  外部拖进去

    

    void Start () {
        SpanwerNext(); //一开始先调用此方法
    }
    
    

    void Update () {
        
    }


    /// <summary>
    /// 生成游戏方块 
    /// </summary>
    public void SpanwerNext() {

        //播放音效
        gameObject.transform.GetComponent<AudioSource>().Play();
        //注意获取预制体的方法  非静态也可以调用此方法
        GameObject go = Instantiate(standbyGroup[FindObjectOfType<Queue>().Next()], transform.position , Quaternion.identity);
        //设置父类对象
        go.transform.parent = this.transform;

        Debug.Log("GoName:::"+go.name);

    }
}

GameManager

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 游戏管理器
/// </summary>
public class GameManager : MonoBehaviour {

    
    private int score = 0; //定义初始分数
    public Text scoreTxt;  //分数文本

    public AudioSource bgm; //声效

    /// <summary>
    /// 加分的方法
    /// </summary>
    /// <param name="line"></param>
    public void SetScore(int line) {

        int currentAddScore=(int)Mathf.Pow(line,2);  //行数的2次方

        score += currentAddScore; //分数相加

        if (currentAddScore>0)  //判断当前分数是否大于0
        {
            gameObject.transform.GetComponents<AudioSource>()[0].Play();  //播放加分的声效
        }

        scoreTxt.text = "Score:" + '\n' + score.ToString(); //换行
    }

    /// <summary>
    /// 游戏结束的方法
    /// </summary>
    public void SetGameOver() {
        scoreTxt.text = "Game Over!";  //弹出提示
        gameObject.transform.GetComponents<AudioSource>()[1].Play(); //播放游戏结束声效
        Time.timeScale = 0; //游戏暂停
        bgm.Stop(); //声效停止播放

    }




}

MyGrid

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MyGrid : MonoBehaviour {

    //单例模式
    private static MyGrid _instance;
    //单例属性
    public static MyGrid Instance {

        get
        {

            return _instance;
        }
    }
    /// <summary>
    /// 单例赋值 
    /// </summary>
    void Awake() {
        _instance = this;
    }

    //定义长宽
    public static int w = 10;
    public static int h = 20;

    //存放所有子物体  已经生成的在游戏场景中的方块
    public Transform[,] grid = new Transform[w,h];



    /// <summary>
    /// 改变旋转
    /// </summary>
    /// <param name="pos"></param>
    /// <returns></returns>
    public Vector2 RoundVector2(Vector2 pos) {

        return new Vector2(Mathf.Round(pos.x),Mathf.Round(pos.y));

    }

    /// <summary>
    /// 判断是否在里面
    /// </summary>
    /// <param name="pos"></param>
    /// <returns></returns>
    public bool IsInside(Vector2 pos) {

        return (pos.x>=0&&pos.x<w&&pos.y>=0); //不能越出边界

    }

    /// <summary>
    /// 是否满行
    /// </summary>
    /// <param name="y"></param>
    /// <returns></returns>
    public bool IsRowFull(int y) {

        for (int x = 0; x < 2; x++)
        {
            if (grid[x,y]==null)
            {
                return false;
            }
        }

        return true;
    }

    /// <summary>
    /// 删除行
    /// </summary>
    public void DeleteRow(int y) {
        for (int x = 0; x < w; x++)
        {
            Destroy(grid[x,y].gameObject);
            grid[x,y]=null;
        }
    }


    /// <summary>
    /// 减少行 方块往下降一行
    /// </summary>
    public void DecreaseRow(int y) {
        for (int x = 0; x < w; x++)
        {
            if (grid[x,y]!=null)
            {
                grid[x, y - 1] = grid[x, y];
                grid[x, y] = null;
                grid[x, y - 1].position += new Vector3(0,-1,0); //每次下降一
            }
        }

    }


    /// <summary>
    /// 减少行
    /// </summary>
    /// <param name="y"></param>
    public void DecreaseRowAbove(int y) {

        for (int i = y; i < h; i++)
        {
            DecreaseRow(i);
        }
    }

    /// <summary>
    /// 删除满行 并调用加分的方法
    /// </summary>
    public void DeleteFullRows() {

        int currentLine = 0;

        for (int y = 0; y < h; y++)
        {
            if (IsRowFull(y))
            {
                currentLine++;
                DeleteRow(y);
                DecreaseRowAbove(y+1);
                y--;
            }
        }

        FindObjectOfType<GameManager>().SetScore(currentLine); //寻找这个类中加分的方法


    }

}

Queue

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


/// <summary>
/// 注意脚本执行顺序
/// </summary>
public class Queue : MonoBehaviour {

    public GameObject[] standbyGroup; //存贮方块的数组


    private int[] showGroup; //显示的数组

    public int sgLimit=2; //限制的数量  面板只显示两个提示

    private float timeFrame = 1.0f; //显示计时器   就是1秒走一步的意思

    //外部访问的属性
    public float TimeFrame {

        get {

            return timeFrame;
        }
    }



    void Start () {

        //设置数组的长度
        showGroup = new int[sgLimit];
        FillShowGroup(); //一开始调用 因为一开始就显示
    }
    
    
    void Update () {
        
    }

    /// <summary>
    /// 获取下一个方块坐标的函数  生成游戏对象的方法
    /// </summary>
    /// <returns></returns>
    public int Next() {

        int currentPieces = showGroup[0];  //接收第一个 并返回
        FlushShowGroup();
        FillShowGroup(sgLimit-1);
        return currentPieces;
    }


    /// <summary>
    /// 将积分面板上的第一个游戏对象显示到正式的游戏框架中去
    /// </summary>
    private void FlushShowGroup() {
        //将队列头移除到正式的游戏面板当中
        GameObject[] currentPieces = GameObject.FindGameObjectsWithTag("ShowGroup"); //找到所有方块
        Destroy(currentPieces[0]); //删除积分面板的第一个
        MoveUp(); //方块向上移
    }


    /// <summary>
    /// 向上升
    /// </summary>
    private void MoveUp(int i=0) {
        //递归 依次向上升
        if (i<sgLimit-1)
        {
            showGroup[i] = showGroup[++i];
            GameObject[] previousPieces = GameObject.FindGameObjectsWithTag("ShowGroup"); //找到所有方块
            previousPieces[i].transform.position += new Vector3(0,5.0f,0); //位置向上移5.0f 
            MoveUp(i);

        }

    }

    /// <summary>
    /// 将方块都显示在积分面板上 递归方式 把积分面板填满
    /// </summary>
    /// <param name="i"></param>
    private void FillShowGroup(int i=0) {  //for循环
        if (i<sgLimit) //判断i 是否小于2 执行加入面板的方块的函数
        {
            Debug.Log(i);
            AddToShowGroup(i);
            FillShowGroup(++i);
        }

    }


    /// <summary>
    /// 选择方块,显示在积分面板上
    /// </summary>
    /// <param name="i"></param>
    private void AddToShowGroup(int i) {

        showGroup[i] = Random.Range(0,standbyGroup.Length); //随机这数组里面的任意一下标

        GameObject go = Instantiate(standbyGroup[showGroup[i]], transform.position + new Vector3(0,i*-5,-1),Quaternion.identity); //实例化游戏物体

        go.transform.parent = this.transform; //实例化出来的方块设置父类对象
    }
}

ARGroups

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 每一个方块挂载的脚本
/// </summary>
public class ARGroups : MonoBehaviour {


    public float freezingTime = 0.5f; //冻结时间

    private float pressingButtonTime = 0f;  //松开时间

    private float lastFallTime = 0; //丢落时间

    void Start () {
        //一开始不是有效位置 先销毁   方块生成不合法 意思就是碰到生成点  游戏结束
        if (!IsValidGridPos())
        {
            FindObjectOfType<GameManager>().SetGameOver();
            Destroy(this.gameObject);
        }
    }
    
    
    void Update () {

        //监听按下的是哪个按钮
        if (VBInputEvent.Instance.whichButton==1) //按下左键
        {

            pressingButtonTime += Time.deltaTime;

            if (pressingButtonTime>freezingTime)  //判断按下的时间超过冻结时间  
            {
                //移动
                transform.position -= new Vector3(1,0,0);

                if (IsValidGridPos()) //判断是否有效位置  检查合法性   合法更新位置
                {
                    UpDateGrid();  //更新组件
                }
                else    //  不合法退回一个位置
                {
                    transform.position += new Vector3(1,0,0); //位置X轴加1
                }

                pressingButtonTime = 0; //执行完事件重新归零
            }

        }

        if (VBInputEvent.Instance.whichButton==2)   //按下右键
        {
            pressingButtonTime += Time.deltaTime;

            if (pressingButtonTime>freezingTime)
            {
                transform.position += new Vector3(1,0,0);

                if (IsValidGridPos())
                {
                    UpDateGrid();
                }
                else
                {
                    transform.position -= new Vector3(1,0,0);
                }

                pressingButtonTime = 0;
            }
        }

        if (VBInputEvent.Instance.whichButton==3)  //按下旋转按钮
        {
            pressingButtonTime += Time.deltaTime;

            if (pressingButtonTime>freezingTime)
            {
                //旋转
                transform.Rotate(0,0,-90);

                if (IsValidGridPos())
                {
                    UpDateGrid();
                }
                else
                {
                    transform.Rotate(0,0,90);
                }

                pressingButtonTime = 0;
            }


        }


        Fall(); //执行掉落的方法



    }

    /// <summary>
    /// 掉落
    /// </summary>
    void Fall() {

        //当前时间 - 上次掉落时间 是否大于设置的时间 间隔 

        if ((Time.time-lastFallTime)>FindObjectOfType<Queue>().TimeFrame)
        {

            transform.position -= new Vector3(0,1,0);  //下落一格

            if (IsValidGridPos())  //是否为有效位置  是否合法
            {
                UpDateGrid();
            }
            else    //不合法退回去一个位置 
            {
                transform.position += new Vector3(0,1,0);

                MyGrid.Instance.DeleteFullRows(); //执行删除的方法  下落之后可能满格 执行删除的方法
                FindObjectOfType<Spanwer>().SpanwerNext();  //执行生成下一个方块的函数

                enabled = false;
            }

            lastFallTime = Time.time;  //掉落时间重新赋值

        }


    }

    /// <summary>
    /// 是否有效位置
    /// </summary>
    /// <returns></returns>
    private bool IsValidGridPos() {

        foreach (Transform child in transform)
        {
            Vector2 v = MyGrid.Instance.RoundVector2(child.position);

            if (!MyGrid.Instance.IsInside(v))  //是否在边界里面  传递的参数是方块下的每一个子物体的位置
            {
                Debug.Log("检查!方法是否调用了"+this.gameObject.name);
                return false;
            }

            if (MyGrid.Instance.grid[(int)v.x,(int)v.y]!=null&&MyGrid.Instance.grid[(int)v.x,(int)v.y].parent!=this.transform)  //是否碰到其他方块
            {
                Debug.Log("不在有效范围内:"+gameObject.name);
                return false;
            }


        }
        return true;//如果上面判断没有问题就返回真的
    }

    /// <summary>
    /// 更新显示方块的位置
    /// </summary>
    private void UpDateGrid() {

        //循环数组  移除相关方块
        for (int x = 0; x < MyGrid.w; x++)
        {
            for (int y = 0; y < MyGrid.h; y++)
            {
                if (MyGrid.Instance.grid[x,y]!=null)
                {
                    if (MyGrid.Instance.grid[x,y].parent==transform)
                    {
                        MyGrid.Instance.grid[x, y] = null;
                    }
                }

            }
        }


        //重组相关方块  遍历下所有的子物体
        foreach (Transform child in transform)
        {
            Vector2 v = MyGrid.Instance.RoundVector2(child.position);

            MyGrid.Instance.grid[(int)v.x,(int)v.y]=child;
        }
    }
}

三、效果展示

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,423评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,339评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,241评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,503评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,824评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,262评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,615评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,337评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,989评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,300评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,829评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,193评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,753评论 3 230
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,970评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,708评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,295评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,207评论 2 258

推荐阅读更多精彩内容