• Unity3dAR小游戏

    2018-06-20 12:52:58
    Unity3dAR小游戏 游戏简介 一个控制小飞龙躲避障碍物的小型AR跑酷游戏,有两个虚拟按钮可以控制飞龙向上或向下移动。 效果 静态图 动态图(太大传不上,见视频) AR模型识别 配置步骤 ...

    Unity3d之AR小游戏


    游戏简介

    一个控制小飞龙躲避障碍物的小型AR跑酷游戏,有两个虚拟按钮可以控制飞龙向上或向下移动。

    效果

    • 静态图

    这里写图片描述

    • 动态图(太大传不上,见视频)

    AR模型识别

    配置步骤

    这里网上教程很多,就不详细阐述了,大致过程就是去高通(vuforia)官网注册一个账号同意相关协议,下载sdk并导入。然后创建一个app关联数据库,数据库中上传待识别的图像,然后等vuforia分析完识别图的特征点之后,将target作为一个unity editor的选项下载下来后也导入unity项目。最后是项目内的配置,需要配置根据之前创建app时密钥配置unity项目,然后将预制ImageTarget的目标设置成你要识别的图片。运行项目时将待识别的图片放置在电脑摄像头范围内即可。

    注意点

    • 图像的星级
      我们会注意到将识别图上传到数据库时,target项右侧会有星级显示,星级越高代表识别图的质量越高。你可能会问,何为识别图质量?识别图质量有什么用?答案是,你踩过坑之后就知道了。经试验,影响识别图最主要的因素就是待识别图的对比度,这里应该要极力避免使用有很多连续相同或相似大色块的图片,因为分析识别图特征点的原理是 根据色块边缘 来决定的,色彩变化越丰富,色块边缘、棱角就越多(如果棱角分布均匀且每个色块都很小那就再好不过了),进而特征点就越多,而特征点越多就意味着识别图质量越高。另外,以下三点则是我总结的识别图质量最主要影响的三个方面:
      • 星级越高越容易识别
      • 星级越高识别速度越快
      • 星级越高更不容易出现抖动
    • 模型的抖动问题(比较棘手)
      识别出来的模型抖动问题一直是AR图像识别中存在的难题,这一定程度上和识别算法有关,很多识别工具也还没有做得很完善。因此我们能做的就是采取一些比较初级的措施来尽可能地避免模型抖动,高级的方法当然还有待研究。初级的方法我们就是要大致了解图像识别的原理,换句话说,就是尽可能地让图像容易识别和模型容易被渲染。我的话虽然识别图是五星的,但是一开始使用了一个比较复杂的模型,导致渲染模型时抖动比较剧烈,网上找了好多有关防抖动的原因,总结起来切入点主要有以下几个(一般推荐前两个):

      • 提高识别图的星级,尽量使用容易识别的图(前面已经提到过)
      • 使用更简单的模型,减少模型的面数,会使降低渲染难度
      • 烘培场景,调整灯光。因为现场实时识别最大的问题就是光线和角度的不稳定或不恰当,导致模型会不停抖动。
      • 优化识别算法(不在我们考虑范围内)
    • 虚拟按钮的检测(尤其注意)
      虚拟按钮第一次接触的话,听起来是个很神奇的东西。因此感觉用虚拟按钮做什么都很有意思。但是这里有个很大的坑,就是 虚拟按钮必须在识别图范围内,否则无论怎么点都点不到!!! 一开始没有注意到,还以为是点击方式的问题,盲目实现导致花费了很多时间到头来发现实现不了,可以说这次项目基本时间都花在这个坑里了,因此最后也就只好交了一个比较简单的版本,这是由于没有弄清楚虚拟按钮的原理导致的。在网上搜了很多技术博客以及逛了许多技术论坛,有关的博客少之又少,因为往年没有布置过AR的作业,所以也没有师兄博客可以参考,总之就是几乎没有发现有人提过类似的问题,于是就不得已只好将目光投向虚拟按钮的实现原理。虚拟按钮的触发原理是,根据识别图被遮挡的特征点所在的位置来判断是否点击了按钮。为什么叫遮挡,遮挡了什么?这时才反应过来是虚拟按钮的点击是通过检测识别图的对应位置是否被遮挡了,所以按钮一定要在识别图范围内,更准确地说,是在特征点分布的范围内。这样就又带来一个问题,如何使在遮挡虚拟按钮时不要让识别目标丢失。因为一开始我用的识别图虽然是五星的,但特征点整体集中在中间区域,也就是我的图有很多留白,总体来说特征点太集中,遮挡一部分时虚拟按钮都还没响应就失去目标了,这是我遇到的又一个问题,至此才真正发现问题所在(当时以为把虚拟按钮放在识别图范围内了还没有效果,一度以为是触发方式的问题)。于是我就将识别图换成了一幅特征点分布比较均匀的图,并且将待识别图载体由原来用手机换成了平板(面积更大),或许还可以将待识别图打印到一张A4的彩印纸上。


    地图的动态维护

    地图结构

    分为好多间隔相同的列,每列有三个障碍物位置,不能全放满(要留条活路),因此允许放0~2个障碍物,这些位置的障碍物都是随机生成的。

    地图初始化(生成)

    一开始初始化足够多的列,这里我是选择初始化5列,只要能保证移动的时候能衔接自然即可。然后针对每列的随机算法是,对每列的三个位置,随机不重复地挑选至多两个位置,然后在该位置上,随机决定该位置是否应该有障碍物。这样就可以完全使地图随机生成了。

    地图维护

    为了减少运行开销,对于移出视野的地图,应该进行销毁。另外,为了生成无尽的地图,应该也要在移动过程中动态生成新的地图拼接在原地图后面。这里我是采用一个队列来维护整个地图以及实现地图的移动,因为队列是不允许直接遍历的,所以我的遍历是将队列头部元素重新插入到队尾来遍历。

    地图部分完整代码

    public class Map : MonoBehaviour {
    
        private const float ydis = 0.5f;//y间隔    
        private float[] ypos;//y位置    
        private const float zdis = 2;//z间隔    
        private float[] zpos;//z位置
        //private GameObject[] barriers;//所有障碍物
        private Queue<GameObject> barriers;//所有障碍物
        private float delta;//移动距离
    
        // Use this for initialization
        void Start () {
            //初始化队列
            barriers = new Queue<GameObject>();     
    
            //初始化y位置
            ypos = new float[3];
            for (int i = 0; i < 3; i++)
            {
                ypos[i] = ydis * i + 0.3f; //0.3是偏移
            }
    
            //初始化z位置
            zpos = new float[5];
            for (int i = 0; i < 5; i++)
            {            
                zpos[i] = zdis * (i + 1f); //保证障碍物全在视野外
            }
    
            Debug.Log(ypos);
            Debug.Log(zpos);
    
            //移入视野前产生5列障碍物,确保移动过程中能无缝衔接
            for (int i = 0; i < 5; i++)
            {
                int count = 0; //每列的障碍物数量
    
                //每列的三个位置
                bool[] state = { false, false, false };
                for (int j = 0; j < 3; j++)
                {
                    //随机一个位置
                    int rpos = (int)Random.Range(0, 3);
                    //该位置必须没有放置过
                    while (state[rpos])
                    {
                        rpos = (int)Random.Range(0, 3);
                    }
                    state[rpos] = true;
    
                    //在该位置随机产生或不产生障碍物
                    float rand = Random.Range(0, 1);
                    if (rand < 0.5)
                    {
                        //加载预制障碍物
                        GameObject barrier = (GameObject)Instantiate(Resources.Load("Barrier", typeof(GameObject)), 
                            new Vector3(0, ypos[rpos], zpos[i]), Quaternion.identity, null);                    
                        barrier.transform.parent = this.transform;
                        barrier.transform.position *= 0.03f;
                        barrier.transform.localScale *= 0.03f;
                        barrier.GetComponent<BoxCollider>().center = barrier.transform.position;
                        barriers.Enqueue(barrier);
                        count++;
                    }
    
                    //每列的障碍物不能超过2个
                    if (count >= 2) break;
                }
            }
    
            //初始化距离
            delta = 0;
        }
    
        // Update is called once per frame
        void Update () {
            //移动
    
            //遍历队列
            int len = barriers.Count;
            while (len > 0)
            {
                //取出第一个
                GameObject front = barriers.Peek();
                front.transform.position -= new Vector3(0, 0, 0.0002f);                       
                barriers.Dequeue();
    
                //Debug.Log("front pos: " + front.transform.position);
    
                //消除移出视野的障碍物                       
                if (front.transform.position.z < -1 * 0.03f)
                {
                    Destroy(front);
                }
                //否则插回队列尾部
                else
                {
                    barriers.Enqueue(front);
                }
    
                len--;
            }
    
            //移动距离增加,注意相对坐标和绝对坐标的转换
            delta += 0.0002f / 0.03f;
    
            //如果移动了一个z间距,那么并产生新一列放在最后 
            if (delta >= zdis)
            {
                Debug.Log(delta);
                int count = 0; //每列的障碍物数量
    
                //每列的三个位置
                bool[] state = { false, false, false}; 
                for (int j = 0; j < 3; j++)
                {
                    //随机一个位置
                    int rpos = (int)Random.Range(0, 3);
                    //该位置必须没有放置过
                    while (state[rpos])
                    {
                        rpos = (int)Random.Range(0, 3);
                    }
                    state[rpos] = true;
                    Debug.Log(rpos);
    
                    //在该位置随机产生或不产生障碍物
                    float rand = Random.Range(0, 1);
                    if (rand < 0.5)
                    {
                        //加载预制障碍物
                        GameObject barrier = (GameObject)Instantiate(Resources.Load("Barrier", typeof(GameObject)),
                            new Vector3(0, ypos[rpos], zpos[4]), Quaternion.identity, null);
                        barrier.transform.parent = this.transform;
                        barrier.transform.position *= 0.03f;
                        barrier.transform.localScale *= 0.03f;
                        barrier.GetComponent<BoxCollider>().center = barrier.transform.position;
                        barriers.Enqueue(barrier);
                        //Debug.Log(barrier);
                        count++;
                    }
    
                    //每列的障碍物不能超过2个
                    if (count >= 2) break;
                }
    
                delta = 0;
            }
    
        }    
    }

    其他

    其他实现如按钮的控制和移动范围的判断,以及按钮的点击放大效果等,比较简单就不列出来了,调参也是比较需要耐心,花时间的。


    展开全文
  • 引言:2016年的AR游戏Pokemon GO火遍全球(除了中国),让我第一次了解到AR的世界。神奇的虚拟对象出现在现实世界中,感觉小时候的游戏王这类的动画可以成为现实。 最近小生学习了高通的Vuforia,本篇着重介绍入门操作...

    引言:2016年的AR游戏Pokemon GO火遍全球(除了中国),让我第一次了解到AR的世界。神奇的虚拟对象出现在现实世界中,感觉小时候的游戏王这类的动画可以成为现实。 最近小生学习了高通的Vuforia,本篇着重介绍入门操作。

    开发版本:Unity 2018.1.3f1

    适合人群:对U3D有基础认识,想要学习AR的童鞋


    一、什么是增强现实?

    增强现实(Augmented Reality,简称 AR)是指把现实世界中某一区域原本不存在的信息,基于某种介质并经过仿真后再叠加到真实世界,被人类感官所感知的技术。

    特点:真实世界和虚拟世界的信息集成,无缝衔接   ;具有实时交互性    ;在三维空间中定位虚拟物体


    二、Vuforia学习

    1、工作原理

    用摄像头拍摄现实场景,通过计算机视觉技术捕获识别标记,实时记录它的位置和方向,数据平台中存储的虚拟3D模型对象与真实场景相叠加。

    P.S. Unity2017.2版本开始将Vuforia内置,在安装时即可下载


    2、Vuforia识别机制

    通过检测自然特征点的匹配来完成,将识别图检测出的特征点保存在数据库中,然后将实时检测出真实图像中的特征点与数据库中识别图的特征点数据进行匹配

    1、服务器对上传图片进行灰度处理,图片变为黑白图像

    2、提取黑白图像的特征点

    3、将特征点数据打包

    4、程序运行时,对比特征点数据包

    注意:图片中尖锐可辨,轮廓清晰的地方就是特征点,例如矩形四个角就是四个特征点,而圆形没有特征点

    识别卡片需要材质较硬,不要有褶皱,轮廓清晰,丰富的细节,较高的对比度

    特别注意:整幅图片的8%的区域作为功能排斥缓冲区,该区域不会被识别


    3、实现步骤

    <1>申请密钥

    现在Vuforia的官网注册开发者账号,并申请项目密钥(License Key),免费开发者每月有1000次的扫描次数,而且左下角有水印。不过,用于开发学习也足够了。


    创建完项目后,即可获取密钥,密钥是需要复制到项目中的配置文件中的

    添加数据库,用于保存识别图的数据

    添加识别图,点击Add Target按钮

    将识别图上传至网站,生成识别图的特征点数据包,点击右侧Download Database按钮,下载Unity数据包
    P.S. Rating一列中,星级越高说明特片的特征点越多,越容易识别,建议不低于三颗星

    <2>前期设置

    打开Player setting窗口 勾选开发VR选项

    发布平台需要改为Android

    Player Setting中Other Setting一栏中需要取消勾选Android TV Compatibility,因为Vuforia不支持Android TV的发布。


    将官网申请的秘钥复制到Resources文件夹中VuforiaConfiguration的App license key一栏


    属性:

    camera device mode :设置默认识别速度优先还是质量优先

    max simultaneous tracked images :最大识别图数量

    max simultaneous tracked objects :最大识别对象数量

    camera direction :默认摄像机是前置还是后置

    device type :默认为手持handhelds


    <3>导入Vuforia

    如果是Unity 2017.2之前的版本,需要在官网下载Vuforia插件,然后导入Unity即可。

    而这之后的版本,选择GameObject-Vuforia-AR Camera,导入AR Camera的同时,会自动导入Vuforia相关内容。

    将之前在官网下载的Unity数据包导入到Unity项目中

    导入Image Target,选择GameObject-Vuforia-Image


    将Image Target的数据库选择为之前导入的Unity包内的数据库


    将一个3D模型作为ImageTarget的子物体


    然后,打包运行即可!
    下图为编辑器模式下演示:

    结束语:至此,您可以实现单卡显示的功能,之后,会继续记录Vuforia的其他功能,还请关注我哦!


    展开全文
  • 原创文章如需转载请注明:转载自 脱莫柔Unity3D学习之旅 QQ群:【Unity3D(AR/VR) 334163814】【Unity3D(游戏) 119706192】 本文链接地址: Unity3D 一些基础的方向、距离算法 最近做一款一款3D“跑酷”游戏终于要...

    原创文章如需转载请注明:转载自 脱莫柔Unity3D学习之旅 QQ群:【Unity3D(AR/VR) 334163814】【Unity3D(游戏) 119706192】 本文链接地址:Unity3D 一些基础的3D图形学算法

    最近做一款一款3D“跑酷”游戏终于要上线了,很开森,忙里偷闲分享点常用的一些基础算法。

    另外感兴趣的朋友可以去上网下载下来玩玩 ,游戏叫《让小编飞》,嘿嘿、

    ps:最近使用中文编程~强迫症们,来咬我啊!

    1..A点正前方,10米位置的坐标点


    代码:
    public Transform A;
    public Transform Z;
    
    
    void Start () 
    {
        正前方();
        Debug.Log("Distance:" + Vector3.Distance(A.transform.position, Z.transform.position));
    }
    
    void 正前方()
    {
        Z.transform.position = A.transform.position + Vector3.forward * 10f;
    }
    运行结果:

    2.A点前方Y轴45°,10米位置的坐标点

    直接上代码:
    void 斜前方()
    {
        Quaternion q = Quaternion.Euler(0, 45, 0);
        Z.transform.position = A.transform.position + q * Vector3.forward * 10f;
    }

    运行结果:

    3.A点到B点方向,10米位置的坐标点


    柱子是B点。
    这个例子用3D视角来做。
    代码:
    void 到B方向正前方()
    {
        Vector3 方向 = (B.transform.position - A.transform.position).normalized;
        Quaternion q = Quaternion.LookRotation(方向);
        Z.transform.position = A.transform.position + q * Vector3.forward * 10f;
    
        Debug.Log("Distance A-B:" + Vector3.Distance(A.transform.position, B.transform.position));
    }
    运行结果:

    4.A点到B点方向Y轴偏移45°,10米位置的坐标点

    这个例子将B点与A点放在了同一高度,继续使用2D视角。
    代码:
    void 到B方向斜前方()
    {
        Vector3 方向 = (B.transform.position - A.transform.position).normalized;
        Quaternion q = Quaternion.LookRotation(方向) * Quaternion.Euler(0,45,0);
        Z.transform.position = A.transform.position + q * Vector3.forward * 10f;
    
        Debug.Log("Distance A-B:" + Vector3.Distance(A.transform.position, B.transform.position));
    }
    运行结果:


    到此展示完毕~
    谢谢参观~!

    展开全文
  • Unity3d Ar Vuforia +zxing 实现二维码扫描

    最近在写Ar项目,研究了一段时间Vuforia+zxing实现二维码扫描,走了许多弯路,浪费了很多时间,因此,分享一下代码,方便那些想要用vuforia+zxing来实现二维码扫描同学。


    直接贴代码:

    using UnityEngine;
    using FairyGUI;
    
    
    using ZXing;
    using ZXing.Client.Result;
    using Vuforia;
    
    using System;
    
    //扫描二维码
    public class UIScanning : MonoBehaviour
    {
        private GObject _ArVerifyQRUI_btn_back_name;
        private GComponent _mainView;
        private GLoader _mGloader;
    
        private GTextInput _ArVerifyQRUI_et_code_name;
        private Vuforia.Image.PIXEL_FORMAT m_PixelFormat = Vuforia.Image.PIXEL_FORMAT.RGB888;
        private bool m_RegisteredFormat = false;
        private bool m_LogInfo = true;
    
    
        public VuforiaBehaviour vuforia;
    
    
        public static event Action<ParsedResult, string> Scanned = delegate { };
        public static event Action<string> ScannedQRCode = delegate { };
        public static event Action<string> ScannedBarCode = delegate { };
    
        bool decoding;
        bool init;
        BarcodeReader barcodeReader = new BarcodeReader();
    
    
        void Awake()
        {
            UIPackage.AddPackage("fairyui_scan_main/scense_2/ArScanMain");
        }
    
    
        void Start()
        {
           
            Global.seleteScene(5);
            _mainView = this.GetComponent<UIPanel>().ui;
            _ArVerifyQRUI_btn_back_name = _mainView.GetChild("ArVerifyQRUI_btn_back_name");
            _ArVerifyQRUI_btn_back_name.visible = true;
            _ArVerifyQRUI_btn_back_name.onClick.Add(onClickBack);
    
            _mGloader = _mainView.GetChild("ArVerifyQRUI_Iimages").asLoader;
            _ArVerifyQRUI_et_code_name = _mainView.GetChild("ArVerifyQRUI_et_code_name").asTextInput;
    
    
            vuforia = GameObject.FindGameObjectsWithTag("ARScanningCamera")[0].GetComponent<VuforiaBehaviour>();
    
        }
    
        void Update()
        {
            if (vuforia == null)
                return;
            if (vuforia.enabled && !init)
            {
                //Wait 1/4 seconds for the device to initialize (otherwise it seems to crash sometimes)
                init = true;
                
                Loom.QueueOnMainThread(() =>
                {
                    init = CameraDevice.Instance.SetFrameFormat(Vuforia.Image.PIXEL_FORMAT.RGB888, true);
                }, 0.25f);
            }
            if (vuforia.enabled && CameraDevice.Instance != null && !decoding)
            {
                var image = CameraDevice.Instance.GetCameraImage(Vuforia.Image.PIXEL_FORMAT.RGB888);
                if (image != null)
                {
                    decoding = true;
    
                    Loom.RunAsync(() =>
                    {
                        try
                        {
                            var data = barcodeReader.Decode(image.Pixels, image.BufferWidth, image.BufferHeight, RGBLuminanceSource.BitmapFormat.RGB24);
                            if (data != null)
                            {
                                Loom.QueueOnMainThread(() =>
                                {
                                    if (data.BarcodeFormat == BarcodeFormat.QR_CODE)
                                    {
                                        ScannedQRCode(data.Text);
                                        if (_ArVerifyQRUI_et_code_name != null)
                                        {
                                            _ArVerifyQRUI_et_code_name.text = data.Text;
                                            
                                        }
                                    }
                                    if (data.BarcodeFormat != BarcodeFormat.QR_CODE)
                                    {
                                        ScannedBarCode(data.Text);
                                        if (_ArVerifyQRUI_et_code_name != null)
                                        {
                                            _ArVerifyQRUI_et_code_name.text = data.Text;
    
                                        }
    
                                    }
                                   // var parsedResult = ResultParser.parseResult(data);
                                   // if (target != null)
                                   // {
                                   //     target.SendMessage("Scanned", parsedResult, SendMessageOptions.DontRequireReceiver);
                                    //}
                                   // Scanned(parsedResult, data.Text);
                                });
                            }
                        }
                        finally
                        {
                            decoding = false;
                        }
                    });
                }
            }
        }
    
    
        //返回按钮
        private void onClickBack()
        {
            Application.LoadLevel(Global.sceneHome);
    
        }
    
    
    
    
    
       
    
    }
    


    Loom 线程队列管理:

    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    using System;
    using System.Threading;
    using System.Linq;
    
    //Loom handles threading
    public class Loom : MonoBehaviour
    {
        private static Loom _current;
    
        public static Loom Current
        {
            get
            {
                if (_current == null && Application.isPlaying)
                {
    
                    var g = GameObject.Find("Loom");
                    if (g == null)
                    {
                        g = new GameObject("Loom");
                    }
    
                    _current = g.GetComponent<Loom>() ?? g.AddComponent<Loom>();
                }
    
                return _current;
            }
        }
    
        void Awake()
        {
            if (_current != null && _current != this)
            {
                Destroy(gameObject);
            }
            else
            {
                _current = this;
            }
        }
    
        private List<Action> _actions = new List<Action>();
        public class DelayedQueueItem
        {
            public float time;
            public Action action;
            public string name;
        }
        private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
    
        public static void QueueOnMainThread(Action action, float time, string name)
        {
            lock (Current._delayed)
            {
                if (Current._delayed.Any(d => d.name == name))
                    return;
                QueueOnMainThread(action, time);
            }
        }
    
        public static void QueueOnMainThread(Action action, string name)
        {
            QueueOnMainThread(action, 0, name);
        }
    
        public static void QueueOnMainThread(Action action, float time)
        {
            if (time != 0)
            {
                lock (Current._delayed)
                {
                    Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action });
                }
            }
            else
            {
                lock (Current._actions)
                {
                    Current._actions.Add(action);
                }
            }
        }
    
        public static void QueueOnMainThread(Action action)
        {
            lock (Current._actions)
            {
                Current._actions.Add(action);
            }
        }
    
        public static void RunAsync(Action a)
        {
            var t = new Thread(RunAction);
            t.Priority = System.Threading.ThreadPriority.Normal;
            t.Start(a);
        }
    
        private static void RunAction(object action)
        {
            ((Action)action)();
        }
    
    
        List<Action> toBeRun = new List<Action>();
        List<DelayedQueueItem> toBeDelayed = new List<DelayedQueueItem>();
    
        void Update()
        {
            //Process the non-delayed actions
            lock (_actions)
            {
                toBeRun.AddRange(_actions);
                _actions.Clear();
            }
            foreach (var a in toBeRun)
            {
                try
                {
                    a();
                }
                catch (Exception e)
                {
                    Debug.LogError("Queued Exception: " + e.ToString());
                }
            }
            toBeRun.Clear();
            lock (_delayed)
            {
                toBeDelayed.AddRange(_delayed);
            }
            foreach (var delayed in toBeDelayed.Where(d => d.time <= Time.time))
            {
                lock (_delayed)
                {
                    _delayed.Remove(delayed);
                }
                try
                {
                    delayed.action();
                }
                catch (Exception e)
                {
                    Debug.LogError("Delayed Exception:" + e.ToString());
                }
            }
            toBeDelayed.Clear();
    
        }
    }


    展开全文
  • AR相机的实现,功能包括: 1.双指进行放大缩小操作; 2.单指水平滑动时,水平旋转; 3.单指垂直滑动时,垂直旋转。

    AR相机的实现,功能包括:

    1.双指进行放大缩小操作;

    2.单指水平滑动时,水平旋转;

    3.单指垂直滑动时,垂直旋转。

    代码如下:

    using UnityEngine;
    using System.Collections;
    using UnityEngine.EventSystems;
    public class ARCamera : MonoBehaviour
    {
    	//目标
    	public GameObject target;
    	//当前camera距离
    	public float distance = 3.5f;
    	//camera 最远距离
    	public float maxDistance=5f;
    	//camera 最近距离
    	public float minDistance=2f;
    	//camera 移动速度
    	public float moveSpeed = 0.3f;
    	//水平移动速度
    	public float xSpeed = 250.0f;
    	//垂直移动速度
    	public float ySpeed = 120.0f;
    	//垂直旋转最小角度
    	public float yMinLimit = -20;
    	//垂直旋转最大角度
    	public float yMaxLimit = 80;
    
    	private float x = 0.0f;
    	private float y = 0.0f;
    	//前一帧手指位置
    	private Vector2 oldPosition1;
    	private Vector2 oldPosition2;
    
    	private Vector3 tempPositon;
    
    	//PC端测试模式
    	public bool bDebug=false;
    
    	void Start()
    	{
    		Vector2 angles = transform.eulerAngles;
    		x = angles.y;
    		y = angles.x;
    		//确保刚体不会改变旋转
    		if (GetComponent<Rigidbody>())
    			GetComponent<Rigidbody>().freezeRotation = true;
    	}
    	void Update()
    	{
    		//PC端测试
    		if (bDebug) {
    			if (Input.GetAxis ("Mouse ScrollWheel") > 0.01f) {
    				if (distance > minDistance) {
    					distance -= moveSpeed;
    				} 
    			} else if(Input.GetAxis ("Mouse ScrollWheel") < -0.01f){
    				if (distance < maxDistance) {
    					distance += moveSpeed;
    				}
    			}
    			if (Input.GetMouseButton (0)) {
    				x += Input.GetAxis ("Mouse X") * xSpeed * 0.02f;
    				y -= Input.GetAxis ("Mouse Y") * ySpeed * 0.02f;
    				y = ClampAngle (y, yMinLimit, yMaxLimit);
    			}
    		} else {
    			if (Input.touchCount == 1) {
    				if (Input.GetTouch (0).phase == TouchPhase.Moved) {
    					x += Input.GetAxis ("Mouse X") * xSpeed * 0.02f;
    					y -= Input.GetAxis ("Mouse Y") * ySpeed * 0.02f;
    					y = ClampAngle (y, yMinLimit, yMaxLimit);
    				}
    			}
    			if (Input.touchCount > 1) {
    				if (Input.GetTouch (0).phase == TouchPhase.Moved || Input.GetTouch (1).phase == TouchPhase.Moved) {
    					Vector3 tempPosition1 = Input.GetTouch (0).position;
    					Vector3 tempPosition2 = Input.GetTouch (1).position;
    					if (IsEnlarge (oldPosition1, oldPosition2, tempPosition1, tempPosition2)) {
    						if (distance > minDistance) {
    							distance -= moveSpeed;
    						} 
    					} else {
    						if (distance < maxDistance) {
    							distance += moveSpeed;
    						}
    					}
    					oldPosition1 = tempPosition1;
    					oldPosition2 = tempPosition2;
    				}
    			}
    		}
    	}
    	void LateUpdate()
    	{
    		if (target)
    		{
    			ClampAngle(y, yMinLimit, yMaxLimit);
    			Quaternion rotation = Quaternion.Euler(y, x, 0);
    			tempPositon.Set(0.0f, 0.0f, (-1) * distance);
    			Vector3 position = rotation * tempPositon + target.transform.position - Vector3.down * 0.5f;
    			transform.rotation = rotation;
    			transform.position = position;
    		}
    	}
    	//检测是否放大还是缩小
    	private bool IsEnlarge(Vector2 oldPos1, Vector2 oldPos2, Vector2 newPos1, Vector2 newPos2)
    	{
    		float leng1 = Vector2.Distance (oldPos1, oldPos2); 
    		float leng2 = Vector2.Distance (newPos1, newPos2);
    		if (leng1 < leng2)
    		{
    			return true;
    		}
    		else
    		{
    			return false;
    		}
    	}
    	private float ClampAngle(float angle, float min, float max)
    	{
    		if (angle < -360)
    			angle += 360;
    		if (angle > 360)
    			angle -= 360;
    		return Mathf.Clamp(angle, min, max);
    	}
    }
    挂载在相机上即可。生气
    展开全文
  • 话不多说,直接上干货 1.实现的效果: 2.使用步骤: ...特别说明,此处target分为图片,3d实体模型,以及立方体,柱状物,其中图片需要是24位RGB的png、jpg或者8位灰度图,大小不大于2M。 3d
  • 基于安卓平台,利用unity3d引擎,实现vuforia插件实现3D物体的识别应用
  • unity AR3D物体识别

    2017-11-21 10:15:29
    上篇讲到了各种AR插件的一些对比 因为上个项目需求用到3D物体追踪。所以使用了EasyAR和Vuforia两种进行了测试对比。 因为如果需要AR识别,都需要有识别点,大致都是基于物体材质纹理来进行识别。 1.先讲一下...
  • 大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.csdn.net/qinyuanpei。 不知从什么时候开始,国产RPG单机游戏开始出现换装,仙剑系列中第一部实现了换装的游戏是仙剑奇侠传四,后来原上海软星团队,...
  • 一:千锋Unity3d游戏图形学从理论到实战精讲 图形学是游戏开发的重点和难点。理解图形学是做好一款游戏的必备技能。 目前有很少的视频教程能够清楚的讲明白图形学,本视频通过大量的游戏实例讲解,从底层原理到...
  • 目前,Unity3D应用范围非常广泛,从手机游戏到联网的大型游戏,从严肃游戏到电子商务,再到VR虚拟现实均可完美呈现。Unity3D是一软专业3D游戏引攀,其具备跨平台发布、离效能优化、高性价比,AAA级游戏画面演染效果...
  • 一般使用Vuforia开发AR应用,涉及到3D模型时,目前最主要的开发工具还是Unity3D。对于Android和iOS原生开发的3D模型的处理,比较复杂,而且效果不好。  另外,可以在Android上实现使用第三方的游戏引擎来渲染3D...
  • 在使用Unity3D这个引擎做科研或者工程的过程中,有时候需要获得某一个虚拟摄像机实时拍到的画面并保存为图片。这里给出一种简单的实现方法。原理很简单,先将虚拟摄像机的图像转移到一个RenderTexture上,然后使用...
  • 最近在研究一个问题,就是通过Unity平台做出AR导航功能,类似于地图的样子,但不是通过GPS定位,室内应用的那种。 大概的效果图是: 现在功能还没有实现,有同在做这方面的小伙伴加群学习:663515959 .....
  • 一、前言关于下载EasyAR和配置在这里就不说了,可以参考文章:Unity3D+EasyAR实现VR效果的案例 实验的3D模型我都是自己用各个组件拼接的,有需要用的FBX的模型可以下载,给大家提供一个3DUnity模型下载的网址(我的...
  • Unity 3D俄罗斯方块

    2013-05-08 22:11:58
    学习unity也有一段时间了,从一开始的懵懵懂懂到现在的学有所小成,心里挺是高兴的,不过目前还是处于初学者阶段,很多东西还等着自己去发掘去学习。 最近做了一个3D俄罗斯方块小游戏作为练手,花了有两个多星期吧...
  • 增强现实(Augmented Reality,简称AR)。...Vuforia + Unity3d SDK开发-原理,前述 Vuforia + Unity3d SDK开发-hello world1、什么是Vuforia? Vuforia是一个收费的增强现实(AR)软件开发引擎。Vufo
  • 增强现实(Augmented Reality,简称AR)。补充:AR和AV(Augmented ...Vuforia + Unity3d SDK开发-原理,前述 Vuforia + Unity3d SDK开发-hello worldHello World: 使用unity3d新建工程,导入Vuforia提供的库和云
  • EasyAR最新4.0版本加了很多新功能,本文主要讲一下EasyARColoring3D 这个功能的原理 一、模型和识别图关系 首先识别图应该是作为模型的贴图使用的,因此在做模型UV的时候,要根据识别图设置 红框部分属于模型...
  • UnityAR书籍翻页效果

    2018-04-19 18:21:43
    阅读学习Vuforia for Unity开发,实现手机平台上的AR效果. 2.自主思考编写用于AR中视频背景shader,用于AR书籍中有效覆盖书页中特定部分。第3-4周完成实验1,打分方法,在自己的手机上实现基本AR效果填写基本AR功能...
1 2 3 4 5 ... 20
收藏数 563
精华内容 225