• http://blog.csdn.net/fzhlee/article/details/8667251
    展开全文
  • 一、目录 【Unity3D从入门到进阶】文章目录及设置这个专栏的初衷

    一、目录

    【Unity3D从入门到进阶】文章目录及设置这个专栏的初衷

    这篇文章介绍如何使用EasyAR.unitypackage配置EasyAR

    二、参考资料

    1、EasyAR 初学者入门指南
    http://forum.easyar.cn/portal.php?mod=view&aid=2
    2、EasyAR入门
    https://www.easyar.cn/doc_sdk/cn/Getting-Started/Getting-Started-with-EasyAR.html
    3、Unity – 使用easyAR的基础教程
    https://www.cnblogs.com/mafeng/p/7600172.html
    4、EasyAR 社区教程
    http://forum.easyar.cn/portal.php?mod=view&aid=5

    三、EasyAR入门

    EasyAR是好用免费的全平台AR(Augmented Reality,增强现实)引擎。

    EasyAR支持使用平面目标的AR,支持1000个以上本地目标的流畅加载和识别,支持基于硬解码的视频(包括透明视频和流媒体)的播放,支持二维码识别,支持多目标同时跟踪。

    EasyAR支持PC和移动设备等多个平台,EasyAR不会显示水印,也没有识别次数限制。

    在拿到EasyAR package或EasyAR样例之后,你需要一个key才能使用。请确保在使用EasyAR之前阅读以下内容。

    四、免费注册

    使用EasyAR之前需要使用邮箱在www.easyar.cn注册

    *如果邮箱已经在视+官网(www.sightp.com )注册,可以直接登录。

    五、KEY的获取

    准备好识别图之后,我们需要到官网(http://www.easyar.cn/view/open/app.html)来为我们的AR APP申请key。首先点击 “开发中心”
    这里写图片描述
    点击 “添加 SDK license Key” 按钮,选择Basic 版本
    这里写图片描述
    接下来填写应用详情,填写你的应用名字与打包移动平台时必填的package name
    这里写图片描述
    确定好后,我们可以查看我们的Key
    这里写图片描述
    1.可以对应用名称进行修改
    2.可以对Bundle ID 进行修改
    3.若使用的是1.0的sdk,可以查看1.0的 Key

    六、导入EasyAR的SDK

    我们到EasyAR官网(http://www.easyar.cn/view/download.html)上下载"EasyAR2.0 package(for unity)"
    这里写图片描述
    当然也可以在这里直接下载整理好的
    http://pan.baidu.com/s/1dFGaHGH
    解压之后,我们将"EasyAR_SDK_2.0.0_Basic.unitypackage"导入到unity中
    这里写图片描述
    导入之后,效果如图:
    这里写图片描述
    使用步骤

    1. 找一张图片当做识别图,自己的照片也可以哦,推荐颜色不要单一的识别图,不然一种颜色识别不到就尴尬了。然后在Unity里创建一个名叫StreamingAssets的文件夹,把图片拖在这里。另外再拖一次放在Assets下。
      这里写图片描述这里写图片描述

    2. 接下来我们删除原有场景的"Main Camera",然后打开EasyAR文件夹,把Prefabs文件夹下的EasyAR_Startup预设体拖到面板
      这里写图片描述这里写图片描述

    3. 中面板上的EasyAR_Startup,修改它的属性,把我们之前复制的key粘贴进去
      这里写图片描述
      这里写图片描述

    4. 找到Primitives文件夹下的ImageTarget预设,把它也拖到面板。把ImageTarget上的ImageTargetBehaviour脚本删掉,新建一个脚本EasyImageTargetBehaviour,拖到ImageTarget物体上

    5. 编写脚本EasyImageTargetBehaviour

    using UnityEngine;
    
    namespace EasyAR
    {
        public class EasyImageTargetBehaviour : ImageTargetBehaviour
        {
            protected override void Awake()
            {
                base.Awake();
                TargetFound += OnTargetFound;
                TargetLost += OnTargetLost;
                TargetLoad += OnTargetLoad;
                TargetUnload += OnTargetUnload;
            }
    
            void OnTargetFound(TargetAbstractBehaviour behaviour)
            {
                Debug.Log("Found: " + Target.Id);
            }
    
            void OnTargetLost(TargetAbstractBehaviour behaviour)
            {
                Debug.Log("Lost: " + Target.Id);
            }
    
            void OnTargetLoad(ImageTargetBaseBehaviour behaviour, ImageTrackerBaseBehaviour tracker, bool status)
            {
                Debug.Log("Load target (" + status + "): " + Target.Id + " (" + Target.Name + ") " + " -> " + tracker);
            }
    
            void OnTargetUnload(ImageTargetBaseBehaviour behaviour, ImageTrackerBaseBehaviour tracker, bool status)
            {
                Debug.Log("Unload target (" + status + "): " + Target.Id + " (" + Target.Name + ") " + " -> " + tracker);
            }
        }
    }
    

    6、 接下来,我们填写如下信息
    这里写图片描述

    • Path: 识别图的路径
    • Name:识别图的名字
    • Size:识别图的大小
      这里写图片描述这里写图片描述这里写图片描述
      注意,我们一定要将Storage 的格式修改为Assets

    关于Storage:
    这里写图片描述

    7.建一个Cube,颜色改为红色,Cube的位置在识别图上方,然后把它拖在ImageTarget下当它的子物体。
    现在运行游戏,激活ImageTarget,Cube就显现出来了。一个简单的AR就做成了。(EasyAR就这点比较好,可以在Unity里面看效果)。
    在以后的开发里也可以通过控制ImageTarget的激活和不激活让物体显现和消失,抑或怎么去显现。
    这里写图片描述
    这里写图片描述

    8.打包成APK,File–buildsetings,场景拖进去,选择,点击Playerseting,设置一下参数:
    我们填写好信息,注意Compang Name 与我们申请key时的公司或团队名相同(比如我申请时填的是:mars),Product Name 也要和我们申请key时填的应用名相同(本次的项目演示为:HelloAR)
    这里写图片描述
    我们的Bundle Identifier 也要修改为
    这里写图片描述
    最后是最关键的一部分:我们的Graphics API 使用的是 OpenGLES2
    这里写图片描述

    9.OK,现在把打包好的APK安装在Android手机上,运行,扫面这张识别图,你的模型就出来了。
    怎么样小伙伴们,你会了吗?

    七、案例

    EasyAR应用-多图识别

    这里写图片描述
    开发资源:
    源码:https://pan.baidu.com/s/1cYaJmnBTqFcVMG2bggQaTQ 密码:br4d

    Step 1:新建项目导入sdk

    我们新建一个unity项目,命名为"ARMultiTarget"
    这里写图片描述
    接着导入我们的EasyAR 2.0 package并进行基本环境的搭建,首先我们像上次操作一样,在unity中新建一个文件夹,命名为"StreamingAssets",将我们的识别图导入到该文件目录下
    这里写图片描述
    删除原有的"Main Camera",将我们的"EasyAR_ImageTracker-1-MultiTarget" 拖到面板中
    这里写图片描述
    接着我们到官网申请Key填写到相机上
    这里写图片描述

    Step 2:处理相机

    我们要编写段脚本来处理EasyAR 的多图识别功能,在"EasyAR_ImageTracker-1-MultiTarget" 组件上新建一个脚本"HelloARTarget"
    这里写图片描述
    脚本下载: https://pan.baidu.com/s/12tf0aEVwW9Z2AUjK4qJR6Q 密码:wg2n
    脚本具体内容如下:

    using UnityEngine;
    using EasyAR;
    
    namespace EasyARSample
    {
        public class HelloARTarget : MonoBehaviour
        {
            private const string title = "Please enter KEY first!";
            private const string boxtitle = "===PLEASE ENTER YOUR KEY HERE===";
            private const string keyMessage = ""
                + "Steps to create the key for this sample:\n"
                + "  1. login www.easyar.com\n"
                + "  2. create app with\n"
                + "      Name: HelloARMultiTarget-SameImage (Unity)\n"
                + "      Bundle ID: cn.easyar.samples.unity.helloarmultitarget.si\n"
                + "  3. find the created item in the list and show key\n"
                + "  4. replace all text in TextArea with your key";
    
            private void Awake()
            {
                if (FindObjectOfType<EasyARBehaviour>().Key.Contains(boxtitle))
                {
    #if UNITY_EDITOR
                    UnityEditor.EditorUtility.DisplayDialog(title, keyMessage, "OK");
    #endif
                    Debug.LogError(title + " " + keyMessage);
                }
            }
        }
    }
    

    Step 3: 处理ImageTarget

    我们准备两张识别图
    这里写图片描述这里写图片描述
    接下来我们拖动一个"ImageTarget"组件到面板中
    这里写图片描述
    我们像之前最基础操作的那样处理好ImageTarget,使得可以显示一个model(不懂的可以看看之前的教程:EasyAR基础入门之显示模型),我们在其下面新建一个cube,具体效果如下图:
    这里写图片描述
    我们再建一个ImageTarget,改变识别图和cube的材质,效果如图:
    这里写图片描述
    这里写图片描述
    最后我们Build 测试就可以实现预览的效果了。
    这里写图片描述

    EasyAR多图识别简单案例—双卡battle1.0

    本次的案例是双卡battle1.0,适合AR开发初学者,主要目的是帮助大家更进一步了解EasyAR 多图识别的应用场景,在往后的技术分享我们会推出完整的AR battle 案例。

    预览:
    这里写图片描述
    开发功能描述:
    当两张识别图相碰时,出现“战斗开始”的提示,两个怪物播放各自的动画

    开发素材

    源码:链接: https://pan.baidu.com/s/1jHNOZ6e 密码: n2hy

    NGUI插件:链接: https://pan.baidu.com/s/1eRG8KN4 密码: 8mf9

    Step 1:开发环境搭建

    我们在前面已经了解了如何用EasyAR SDK来开发多图识别,本次的案例是在此基础上进行开发的,当然了我们也可以在EasyAR的官方案例进行开发(两种方法大同小异).上次我们的项目框架如图:
    这里写图片描述
    将所需的模型资源导入到我们的项目中,目录结构为:
    这里写图片描述

    Step 2:模型的设置

    将我们准备好的模型分别替换Cube,并适当修改它们的Scale 与 rotation,效果如图:
    这里写图片描述
    我们分别对两个模型进行参数设置,首先对ImageTarget 下的模型设置Scale 与 Rotation
    这里写图片描述
    我们还需要对它的Animation修改,在本次案例中,我们只需要"n2017_idle" 与"n2017_skill_2"。效果如图:
    这里写图片描述
    还需将"n2017_skill_2" 这个Animation 的"Wrap Mode “设置为"Loop”
    这里写图片描述
    另外一个模型的设置也是这样,大家自行设置,所需的Animation 为"Standby"和"Attack3"
    这里写图片描述
    然后接着为两个模型添加Box Collider,并勾选Is Trigger,在这里,我们需要设置Box Collider 的大小,使得长度稍稍大于图片的宽度,方便我们的碰撞检测,给大家一个参考的数值:
    这里写图片描述
    为了使用OnTriggerEnter() 方法,我们还需在一个模型身上挂一个RigidBody 组件
    这里写图片描述

    Step 3:编写脚本

    首先当我们的两张识别图靠近时,我们显示一个UI,提示“战斗开始”,这里我们用NGUI来实现。
    PS:对于NGUI不熟悉的可以看看这些教程:
    http://www.taikr.com/course/445
    http://www.taikr.com/course/34

    我们创建个label
    这里写图片描述
    修改label文字内容为“战斗开始”
    这里写图片描述
    效果如图:
    这里写图片描述
    然后我们在Label 上添加一个Tween-Alpha 脚本来
    这里写图片描述
    我们调整我们Alpha 从0变到1,并且设置动画时长为2s。
    这里写图片描述
    接着我们编写新建代码"PlayAnim",实现当两张识别图靠近时,出现这个title,即label,首先我们要将label 设为不可见:
    这里写图片描述
    然后编写代码:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PlayAnim : MonoBehaviour {
    
        public TweenAlpha label;
        // Use this for initialization
        void Start () {
            
        }  
        // Update is called once per frame
        void Update () {    
        }
        void OnTriggerEnter(Collider col) 
        {    
            label.gameObject.SetActive (true);
            label.PlayForward ();
        }
    
    }
    

    通过碰撞检测,我们处理label 的显示------首先是设置label为可见,接着播放它的Tween–Alpha 动画,即Alpha 在2s内从0–1,即两张识别图靠近之后,“战斗开始”这几个字在2s内出现。

    当显示完后,我们不希望它一直出现,所以我们需要处理它的隐藏。我们在这个脚本基础上写一个方法:

    public void Hide()
        {
            label.gameObject.SetActive (false);
        }
    

    然后调用,操作方法类似unity 给Button添加方法。
    这里写图片描述
    最后我们再来实现动画的交互,代码相对简单,属于unity最基本东西

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PlayAnim : MonoBehaviour {
        private Animation anim;
        // Use this for initialization
        void Start () {
            anim = GetComponent<Animation> ();
        }  
        // Update is called once per frame
        void Update () {    
        }
        void OnTriggerEnter(Collider col) 
        {
            anim.Play ("n2017_skill_2");
            col.gameObject.GetComponent<Animation> ().Play ("Attack3");
        }
    }
    

    完整脚本地址:链接: https://pan.baidu.com/s/1kVmP095 密码: 4kzx
    原文链接:http://forum.easyar.cn/portal.php?mod=view&aid=4

    EasyAR开发技巧—模型交互操作

    AR 开发中常用的交互功能总结

    我们在EasyAR 初学者入门系列的第一篇教程中展现了EasyAR 最基本的功能,使得一个模型以AR技术呈现在我们面前,实在炫酷。扫描识别图之后展现一个模型,如果仅仅是静态的,体验效果也不是很好,所以根据市面上的常见AR APP,给大家总结了几种常见的AR模型的交互方式。我们在最基础的 HelloAR 这个项目的基础上进行开发,前提是大家已经掌握好了如何基础性的搭建EasyAR+unity 的开发方式,不懂得伙伴可以跳转到:EasyAR 初学者入门指南(1)—显示模型 阅读。
    关于交互方式,在这里主要给大家提供思路以及脚本文件。

    源码:链接: https://pan.baidu.com/s/1pKSy5jP 密码: yy2b

    Step 1:导入项目

    我们以后的开发都在EasyAR 的官方项目"HelloAR" 的基础上进行,首先我们需要到官网上下载并导入unity中
    这里写图片描述

    下载好后,我们将HelloAR 在unity 中打开
    这里写图片描述
    下载好后,我们将HelloAR 在unity 中打开
    这里写图片描述

    Step 2:点击模型本身交互

    预览:
    这里写图片描述
    实现功能:点击Cube(扫描识别图出现的模型)我们可以更换它的颜色。

    我们先删除另外两个不用的ImageTarget,只在"ImageTarget-JsonFile-idback" 身上做文章
    这里写图片描述
    我们新建一个Material,命名为"blue"
    这里写图片描述
    然后在Cube 上新建一个脚本"ChangeColor",来实现点击时cube 颜色的改变,这段脚本属于unity 最基本的知识,主要是定义两个Material ,然后在OnMouseDown() 方法中进行修改替换,同时我们也定义了一个布尔类的标识位。

    脚本地址:链接: https://pan.baidu.com/s/1miidEOS 密码: 5x6d

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class ChangeColor : MonoBehaviour {
        public Material blue;
        public Material id;
        private bool isClick = false;
        // Use this for initialization
        void Start () {    
        }
        // Update is called once per frame
        void Update () {   
        }
        void OnMouseDown()
        {
            if (!isClick) {
                this.gameObject.GetComponent<MeshRenderer> ().material = blue;
                isClick = true;
            } else {
                this.gameObject.GetComponent<MeshRenderer> ().material = id;
                isClick = false;
            }
        }
    }
    

    这里写图片描述

    Step 2:双手缩放

    预览:
    这里写图片描述
    在开发AR APP功能开发时,双手缩放是最常见的功能,这也是一种最自然的交互手段,实现这样的功能也非常的简单,我们在Cube 上挂一个脚本,命名为"Gesture"

    脚本地址:链接: https://pan.baidu.com/s/1geDLOPl 密码: ykwf
    这里写图片描述

    using UnityEngine;
    using System.Collections;
    public class Gesture : MonoBehaviour {
        private Touch oldTouch1;  //上次触摸点1(手指1)
        private Touch oldTouch2;  //上次触摸点2(手指2)
        void Update()
        {
            //没有触摸,就是触摸点为0
            if (Input.touchCount <= 0)
            {
                return;
            }     
            //多点触摸, 放大缩小
            Touch newTouch1 = Input.GetTouch(0);
            Touch newTouch2 = Input.GetTouch(1);
            //第2点刚开始接触屏幕, 只记录,不做处理
            if (newTouch2.phase == TouchPhase.Began)
            {
                oldTouch2 = newTouch2;
                oldTouch1 = newTouch1;
                return;
            }
            //计算老的两点距离和新的两点间距离,变大要放大模型,变小要缩放模型
            float oldDistance = Vector2.Distance(oldTouch1.position, oldTouch2.position);
            float newDistance = Vector2.Distance(newTouch1.position, newTouch2.position);
            //两个距离之差,为正表示放大手势, 为负表示缩小手势
            float offset = newDistance - oldDistance;
            //放大因子, 一个像素按 0.01倍来算(100可调整)
            float scaleFactor = offset / 100f;
            Vector3 localScale = transform.localScale;
            Vector3 scale = new Vector3(localScale.x + scaleFactor,
                localScale.y + scaleFactor,
                localScale.z + scaleFactor);
            //在什么情况下进行缩放
            if (scale.x >= 0.05f && scale.y >=0.05f && scale.z >= 0.05f)
            {
                transform.localScale = scale;
            }
            //记住最新的触摸点,下次使用
            oldTouch1 = newTouch1;
            oldTouch2 = newTouch2;
        }
    }
    
    

    Step 3:任意拖动

    预览:
    这里写图片描述
    脚本地址:链接: https://pan.baidu.com/s/1pL7Je9l 密码: s4g5

    这样的功能在市面上的AR APP 中也很常见,比如视+ APP,我们可以快速的将模型拖动到任何位置。我们同样的在Cube新建段脚本,命名为"Drag"

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Drag : MonoBehaviour {
        private Vector3 _vec3TargetScreenSpace;// 目标物体的屏幕空间坐标
    
        private Vector3 _vec3TargetWorldSpace;// 目标物体的世界空间坐标
    
        private Transform _trans;// 目标物体的空间变换组件
    
        private Vector3 _vec3MouseScreenSpace;// 鼠标的屏幕空间坐标
    
        private Vector3 _vec3Offset;// 偏移
    
        void Awake() { _trans = transform; }
    
        IEnumerator OnMouseDown()
    
        {
    
            // 把目标物体的世界空间坐标转换到它自身的屏幕空间坐标
    
            _vec3TargetScreenSpace = Camera.main.WorldToScreenPoint(_trans.position);
    
            // 存储鼠标的屏幕空间坐标(Z值使用目标物体的屏幕空间坐标)
    
            _vec3MouseScreenSpace = new Vector3(Input.mousePosition.x, Input.mousePosition.y, _vec3TargetScreenSpace.z);
    
            // 计算目标物体与鼠标物体在世界空间中的偏移量
    
            _vec3Offset = _trans.position - Camera.main.ScreenToWorldPoint(_vec3MouseScreenSpace);
    
            // 鼠标左键按下
    
            while (Input.GetMouseButton(0))
    
            {
    
                // 存储鼠标的屏幕空间坐标(Z值使用目标物体的屏幕空间坐标)
    
                _vec3MouseScreenSpace = new Vector3(Input.mousePosition.x, Input.mousePosition.y, _vec3TargetScreenSpace.z);
    
                // 把鼠标的屏幕空间坐标转换到世界空间坐标(Z值使用目标物体的屏幕空间坐标),加上偏移量,以此作为目标物体的世界空间坐标
    
                _vec3TargetWorldSpace = Camera.main.ScreenToWorldPoint(_vec3MouseScreenSpace) + _vec3Offset;
    
                // 更新目标物体的世界空间坐标
                _trans.position = _vec3TargetWorldSpace;
                // 等待固定更新
                yield return new WaitForFixedUpdate();
    
            }
    
        }
    }
    

    AR交互操作实例—玩转僵尸

    原文链接:http://forum.easyar.cn/portal.php?mod=view&aid=6

    通过视+ APP上的一个案例来了解熟悉AR开发常用的交互技巧

    在上一篇的EasyAR 开发技巧—模型交互操作 我们熟悉了比较流行的移动端AR的交互技巧,今天我们在此基础上继续深入的了解,通过一个类似视+ APP 的一个实例来复习或熟悉AR开发的交互技巧。

    预览:

    相关资源:

    iTween 插件:链接: https://pan.baidu.com/s/1nuNMajn 密码: b9nv

    Step 1:开发准备

    下载EasyAR SDK,搭建EasyAR 开发的最基本环境。(前面有基础教程:EasyAR 初学者入门指南(1)—显示模型
    ok,接下来我们删除unity原有的Main Camera,把EasyAR_Startup的摄像机拖入到面板中。
    这里写图片描述
    接着把导入的怪物模型拖入面板中(注意:我们这里并没有用到Imagetarget,因为不需要识别功能。大家还可以脑洞大开,来为模型的展现增加个缓冲显示效果,在这里我就不实现了,主要把AR 移动端的核心知识给大家分享一下)
    这里写图片描述

    Step 2:修改相关参数

    先修改AR相机的角度,使其X值旋转270度
    这里写图片描述
    接下来使怪物Y值旋转180度,并放大2倍,修改默认动画(也可以不修改,只不过使的看起来效果更惊艳)。
    这里写图片描述
    给怪物添加BoxCollider,并勾选is Trigger

    Step 3:实现点击怪物播放动画实现交互

    首先给物体再加一个Animation,根据你自己的喜爱添加相应的Animation
    这里写图片描述
    接下来新建一段代码实现动画交互,代码十分简单,我们在上一篇(交互操作)上讲过,大家套用框架就好。

    using UnityEngine;
    using System.Collections; 
    public class PlayAnim : MonoBehaviour 
    {        
    public Animation anim;        
    void Start()        
    {                
    anim = GetComponent();
    }
    void Update()
    {
    if (!anim.isPlaying) {
    anim.Play ("2HitCombo");
    }
    }
    void OnMouseDown()
    {
    anim.Play ("jumpAttack_RM");
    }
    }
    

    然后我们要实现的是双指实现缩放,单指任意拖动,这部分的代码在前一篇文章(EasyAR开发技巧—模型交互操作)中讲过,以后可以把这些当作常用代码来使用,会比较方便,直接拖动到模型身上即可。

    step 4:点哪走哪

    在这里,我只提供自己的一种实现方法,当然实现这种效果可以有很多方法。
    首先,我们先建一个plane,修改大小为(2,2,2)
    这里写图片描述
    这里写图片描述
    然后,接下来,修改其Tag为Ground
    这里写图片描述
    最重要的一部分,关闭其Renderer,使其不显示,在这里我们只要Mesh Collider
    这里写图片描述
    这里写图片描述
    我们先在这个模型新建个脚本,在这里我们用射线检测的方法来实现。
    我们用Input.touchCount 先判断是否有触摸事件,然后获取Input.GetTouch(0).position ,触摸手机屏幕的位置,然后射线检测,实现移动,完整代码如下:

    using UnityEngine;
    using System.Collections;
    public class Player : MonoBehaviour {
    private Vector3 clickPosion;
    public float speed = 5f;
    
    void Start()
    {
    	clickPosion = transform.position;
    }
    void Update()
    {
    	if (Input.touchCount > 0) {
    	Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch (0).position);
    	RaycastHit hit;
    	Physics.Raycast(ray, out hit);
    	try
    	{
    		if (hit.collider.tag == "Ground") //获取点击位置的世界坐标
    		{
    		Vector3 v = hit.point;
    		clickPosion = new Vector3(v.x, transform.position.y, v.z);
    		transform.LookAt(clickPosion);
    		}
    	}
    	catch
    	{
    	}
    	iTween.MoveTo(gameObject, clickPosion, 4f);
    	}
    }
    

    关于iTween 知识:
    1.http://edu.manew.com/course/6
    2.http://www.xuanyusong.com/archives/2052

    EasyAR 二维码+AR的应用

    EasyAR实现二维码+AR的应用第一篇章

    二维码在我们生活总早已是司空见惯了,当AR碰撞上二维码,一定可以产生好玩的效果。EasyAR对于二维码的识别与支持是相当不错的,所以在这一篇与下一篇的教程中,我们会分享如何从零开发二维码+AR 的应用。

    Step 1:开发环境

    我们需要新建一个unity项目,并将"EasyAR_SDK_2.0.0_Basic" 的unitypackage导入,关于EasyAR+unity 这些基础操作不懂的可以看看之前的文章“EasyAR 初学者入门指南(1)—显示模型”,在这里我一笔带过。导入之后,我们的unity目录界面应该是这样的:

    这里写图片描述
    我们删除原有的Main Camera,将"EasyAR_ImageTracker-1_QRCode-1" 拖到面板中。并将官网申请的Key填好。
    这里写图片描述
    观察"EasyAR_ImageTracker-1_QRCode-1"这个预制体,对比与我们之前常用的"EasyAR_Startup",发现多了一个"BarCodeScanner" 的部分。
    这里写图片描述
    这里写图片描述
    这里写图片描述
    它上面所挂的脚本"QRCodeScannerBehaviour"使用来实现二维码的扫描与识别功能的。这是对于它的具体描述:
    这里写图片描述
    这里写图片描述
    这里写图片描述

    Step 2:制作二维码资源

    我们需要准备二维码的图片,有许多网站都可以来制作自己的二维码。我制作的内容大致如下:
    这里写图片描述
    大家也可以发挥自己的脑洞,随意写些内容,目前我们先实现通过EasyAR SDK 来实现扫描二维码 显示文字的功能。

    Step 3:编辑代码

    我们准备好了二维码,接下来就是在unity里编辑代码来实现功能,首先我们在"EasyAR_ImageTracker-1_QRCode-1" 下新建一个脚本,命名为"ARIsEasyBehaviour",
    这里写图片描述
    脚本下载地址:链接: https://pan.baidu.com/s/1dF5tigx 密码: 9ag5

    using System.Collections;
    using UnityEngine;
    
    namespace EasyAR
    {
        public class ARIsEasyBehaviour : MonoBehaviour
        {
            private const string title = "Please enter KEY first!";
            private const string boxtitle = "===PLEASE ENTER YOUR KEY HERE===";
            private const string keyMessage = ""
                + "Steps to create the key for this sample:\n"
                + "  1. login www.easyar.com\n"
                + "  2. create app with\n"
                + "      Name: HelloARQRCode (Unity)\n"
                + "      Bundle ID: cn.easyar.samples.unity.helloarqrcode\n"
                + "  3. find the created item in the list and show key\n"
                + "  4. replace all text in TextArea with your key";
    
            private bool startShowMessage;
            private bool isShowing;
            private string textMessage;
    
            private void Awake()
            {
                var EasyARBehaviour = FindObjectOfType<EasyARBehaviour>();
                if (EasyARBehaviour.Key.Contains(boxtitle))
                {
    #if UNITY_EDITOR
                    UnityEditor.EditorUtility.DisplayDialog(title, keyMessage, "OK");
    #endif
                    Debug.LogError(title + " " + keyMessage);
                }
                EasyARBehaviour.Initialize();
                foreach (var behaviour in ARBuilder.Instance.ARCameraBehaviours)
                {
                    behaviour.TargetFound += OnTargetFound;
                    behaviour.TargetLost += OnTargetLost;
                    behaviour.TextMessage += OnTextMessage;
                }
                foreach (var behaviour in ARBuilder.Instance.ImageTrackerBehaviours)
                {
                    behaviour.TargetLoad += OnTargetLoad;
                    behaviour.TargetUnload += OnTargetUnload;
                }
            }
    
            void OnTargetFound(ARCameraBaseBehaviour arcameraBehaviour, TargetAbstractBehaviour targetBehaviour, Target target)
            {
                Debug.Log(" Found: " + target.Id);
            }
    
            void OnTargetLost(ARCameraBaseBehaviour arcameraBehaviour, TargetAbstractBehaviour targetBehaviour, Target target)
            {
                Debug.Log(" Lost: " + target.Id);
            }
    
            void OnTargetLoad(ImageTrackerBaseBehaviour trackerBehaviour, ImageTargetBaseBehaviour targetBehaviour, Target target, bool status)
            {
                Debug.Log(" Load target (" + status + "): " + target.Id + " (" + target.Name + ") " + " -> " + trackerBehaviour);
            }
    
            void OnTargetUnload(ImageTrackerBaseBehaviour trackerBehaviour, ImageTargetBaseBehaviour targetBehaviour, Target target, bool status)
            {
                Debug.Log(" Unload target (" + status + "): " + target.Id + " (" + target.Name + ") " + " -> " + trackerBehaviour);
            }
    
            private void OnTextMessage(ARCameraBaseBehaviour arcameraBehaviour, string text)
            {
                textMessage = text;
                startShowMessage = true;
                Debug.Log("got text: " + text);
            }
    
            IEnumerator ShowMessage()
            {
                isShowing = true;
                yield return new WaitForSeconds(2f);
                isShowing = false;
            }
    
            private void OnGUI()
            {
                if (startShowMessage)
                {
                    if (!isShowing)
                        StartCoroutine(ShowMessage());
                    startShowMessage = false;
                }
    
                if (isShowing)
                    GUI.Box(new Rect(10, Screen.height / 2, Screen.width - 20, 30), textMessage);
            }
        }
    }
    

    我们在这段脚本文件实现的是首先Target 的识别然后是扫描二维码之后接收结果并实现绘制在屏幕上,对于Target 的found与load等方法相信大家已经很熟悉了。对于OnTextMessage()接收返回结果然后赋值给textMessage,并由OnGUI()进行绘制。我们Build测试,会实现如下的效果:
    这里写图片描述
    PS:在实际的开发中,我们不会像这样从零来搭建AR+二维码的开发环境,一般是直接在EasyAR官网的实例进行二次开发,这样会大大提高我们的效率。下一篇我们会实现二维码+AR的一个实例。

    我们在此基础上继续完善demo
    预览:
    这里写图片描述
    资源:

    NGUI插件:链接: https://pan.baidu.com/s/1bMgGn8 密码: uviy
    资源地址:链接: https://pan.baidu.com/s/1kVCBiBX 密码: b3eg
    代码地址:链接: https://pan.baidu.com/s/1pKFAATX 密码: x7r9

    Step 1:准备

    首先是关于识别图的准备,在这次的案例演示中我使用了如下的图片(二维码可以自己制作):
    这里写图片描述
    在unity中我们新建一个文件夹"StreamingAssets",将识别图导入。并且新建文件夹“Scripts”,导入提前准备好的资源,框架图如下:
    这里写图片描述

    Step 2:制作ImageTarget

    将ImageTarget拖入到面板中
    这里写图片描述
    remove掉原来挂在ImageTarget上的脚本,将我们导入的"EasyImageTargetBehaviour" 挂在上面(这部分属于EasyAR 图像识别最基本的操作,不懂的可以看看:EasyAR 初学者入门指南(1)—显示模型
    这里写图片描述
    填写识别图信息,将我们导入的那张带有二维码的识别图名字与size配置好
    这里写图片描述
    这里写图片描述
    为了能在unity中看到识别图的具体信息,我们建立一个材质球使其显示。新建Material,模式设置为Mobile/Diffuse.效果如图:
    这里写图片描述
    这样在unity编辑器中就可以显示了,方便我们设置Scan扫描特效的配置
    这里写图片描述

    Step 3:制作扫描特效

    将prefab scan 拖到ImageTarget 下面
    这里写图片描述
    适当调整Scan的位置与scale,这里大家自行调整使其达到一个合适的位置
    这里写图片描述
    编写脚本"Move",实现扫描效果。脚本的思路其实很简单,就是在Update里不断更新Scan材质球的texture 的offset。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Move : MonoBehaviour
    {
    	public float offset_y = 0.5f;
    	
    	void Awake(){}
    
    	void Start(){}
    
    	void Update()
    	{
    		offset_y -= Time.deltaTime;
    		this.GetComponent<MeshRenderer>().material.SetTextureOffset("_MainTex",new Vector2(0,offset_y));
    	}
    }
    

    Step 4:制作UI显示二维码内容

    我们使用NGUI来完成ui的制作。在这里的思路是根据你的二维码文字内容建立相应的label(我们在代码实现是通过逗号来分割内容分别显示在不同的label上)在本次的案例演示,我建立两个label(分别显示EasyAR与Cool)和一个Button(点击跳转网页)。具体的ui位置配置大家可以自行调试,效果如图:
    这里写图片描述
    这里写图片描述
    我是将三个UI控件(label,button)放在一个Empty GameObject下面,即"b"下面。我们将"b"添加个Tween 动画,使其演示效果更加炫酷,在这里我用的是Tween/Scale这一模式
    这里写图片描述
    这里写图片描述
    最后,将"b"这一GameObject 设为不可见
    这里写图片描述

    Step 5:编写代码

    首先我们修改完善"Move" 这个脚本。思路是这样的,我们设置一个扫描时长,当达到这个时长时,我们设置Scan 这一object不可见,并且把我们准备好的UI控件显示,完整代码如下:
    脚本下载地址:链接: https://pan.baidu.com/s/1pKNFKMn 密码: xh6m

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Move : MonoBehaviour
    {
    	public float offset_y = 0.5f;
    	public GameObject btns;
    	
    	void Awake(){}
    
    	void Start(){}
    
    	void Update()
    	{
    		if(offset_y <= -3.0f)
    		{
    			btns.SetActive(true);
    			btns.GetComponent<TweenScale>().PlayForward();
    			this.gameObject.SetActive(false);
    		}
    		else
    		{
    			offset_y -= Time.deltaTime;
    			this.GetComponent<MeshRenderer>().material.SetTextureOffset("_MainTex",new Vector2(0,offset_y));
    		}
    	}
    }
    

    在unity操作界面将"b"赋值到脚本里
    这里写图片描述
    然后我们修改绑定在"EasyAR_ImageTracker-1_QRCode-1" 上的"ARIsEasyBehaviour" 脚本文件。主要是实现获得二维码内容text后将它显示在我们准备好的UI上。

    在开头定义UIlabel

    public UILabel text1;
    public UILabel text2;
    

    然后在OnTextMessage()方法里实现

    private void OnTextMessage(ARCameraBaseBehaviour arcameraBehaviour, string text)
    {
                textMessage = text;
                text1.text = textMessage.Split (',')[0];
                text2.text = textMessage.Split (',') [1];
                startShowMessage = true;
                Debug.Log("got text: " + text);
    }
    

    最后注释掉原有的OnGUI方法即可。
    完整代码地址:链接: https://pan.baidu.com/s/1jIwvMFs 密码: c8t8

    对于按钮的交互,我们实现的是点击之后跳转到EasyAR SDK2.0的网页,实现起来相当简单,一句代码即可:Application.OpenURL (“http://www.easyar.cn/view/sdk.html”);

    实现到这里,我们基本上就可以完成案例效果了。

    EasyAR 开发实例—AR红包(初级)

    原文链接:http://forum.easyar.cn/portal.php?mod=view&aid=10

    用EasyAR SDK 来实现一个AR红包功能

    分享一篇如何用EasyAR SDK来开发一个简单的AR红包的功能。

    预览:
    这里写图片描述
    开发资源:
    粒子特效:链接: https://pan.baidu.com/s/1i5swzLN 密码: dwtr
    NGUI插件: 链接: https://pan.baidu.com/s/1qYAyYL2 密码: 7jb2

    Step 1:开发环境搭建

    首先我们下载EasyAR SDK (unity版本)并导入到unity中,并到官网申请开发时所用到的Key值,在unity中,删除原有的Camera,将EasyAR_Startup拖入到面板中,并将key之填入。注意:在这里我们并没有用到识别功能,因此没必要用ImageTarget。
    这里写图片描述
    接下来,我们准备红包模型,有些人在导入红包模型的过程中可能会遇到贴图丢失的情况,在这里,我们只需将红包贴图重新挂到材质上即可。
    这里写图片描述
    在这里,我们准备两个红包预制体,来实现不同的交互。并修改它们的大小以便区分。在这里我给他们命名分别为Hong,HongBao。具体详细参数如下
    Hong:
    这里写图片描述
    HongBao:
    这里写图片描述
    接下来,我们给两个红包添加Tag,分别为Hong,HongBao。
    这里写图片描述
    这里写图片描述
    这里写图片描述
    为两个红包预制体添加BoxCollider,并勾选Trigger。大小自己调节。

    最后,我们为我们所交互的那个红包HongBao添加个动画。选中它,并在菜单栏Window-Animation,打开后,点击Create,并保存命名。
    这里写图片描述
    接着点Add Property,选Transfrom-Scale
    这里写图片描述
    接着点Add Property,选Transfrom-Scale
    这里写图片描述

    Step 2:产生红包

    首先我们先创建几个随机点,分别命名point1,point2,point3,这是红包所降落的位置。参考数值如下:大家可以自行设置

    point1:
    这里写图片描述
    point2:
    这里写图片描述
    point3:
    这里写图片描述
    接下来,我们创建一段代码来使得红包可以降落,在这里用Translate来实现,当然大家可以用其他方法,比如添加Rigidbody,给个受力也可以,不过那样有点麻烦。(补充:当红包的Z坐标小于-8时,就销毁)

    using UnityEngine;
    
    using System.Collections;
    
    using UnityEngine.UI;
    
    public class Move : MonoBehaviour {
    
    public GameObject par;
    
    // Use this for initialization
    
    void Start () {
    
    }
    
    // Update is called once per frame
    
    void Update () {
    
    	transform.Translate (-transform.forward*2f*Time.deltaTime);
    
    	if (transform.position.z < -8f) {
    
    	Destroy (this.gameObject);
    
    }
    
    }
    
    }
    

    接下来,创建CreateHong空物体,在上面挂上CreateHong.cs脚本,实现随机产生红包。

    using UnityEngine;
    
    using System.Collections;
    
    public class CreateHong : MonoBehaviour {
    
    public Transform[] points;
    
    public GameObject[] hongbaos;
    
    private int index;
    
    // Use this for initialization
    
    void Start () {
    
    InvokeRepeating ("CreateHongbao",1f,1f);
    
    }
    
    // Update is called once per frame
    
    void Update () {
    
    }
    
    void CreateHongbao(){
    
    int i = Random.Range (0, 10);
    
    if (i > 1) {
    
    index = 0;
    
    } else {
    
    index = 1;
    
    }
    
    GameObject go = GameObject.Instantiate (hongbaos [index], points [Random.Range (0, points.Length)].position + new Vector3 (Random.Range (-0.5f, 0.5f), 0, 0), Quaternion.identity) as GameObject;
    
    go.transform.Rotate (new Vector3 (270, 180, 0));
    
    }
    
    }
    
    }
    

    Step 3:交互

    当点击抖动红包时我们产生炫酷的粒子特效,将如下方法添加到Move.cs中

    void OnMouseDown(){
    
    if (gameObject.tag == "Hong") {
    
    Debug.Log ("ddd");
    
    } else if(gameObject.tag=="HongBao") {
    
    CreateHong._instace.isCreate = false;
    
    GameObject go=GameObject.Instantiate (par,gameObject.transform.position,Quaternion.identity) as GameObject;
    
    Destroy (go,2f);
    
    }
    
    }
    

    并在2s后销毁该粒子

    好了,接下来,我们用NGUI插件实现产生优惠卷或红包(这不重要,重要的是实现思路与方法)

    效果如下:
    这里写图片描述
    首先,我们创建Sprite
    这里写图片描述
    接着添加TweenScale
    这里写图片描述
    注意:
    这里写图片描述
    这里写图片描述
    接下来我们使用单例模式在CreateHong.cs脚本中实现:

    首先声明:

    publicstaticCreateHong_instace;
    

    接着:

    void Awake()
    
    {
    
    _instace = this;
    
    }
    

    然后实现方法供外界调用

    public void PlayScale()
    
    {
    
    daxiao.gameObject.SetActive (true);
    
    daxiao.PlayForward ();
    
    }
    

    在Move.CS中实现:

    void OnMouseDown()
    
    {
    
    if (gameObject.tag == "Hong") {
    
    Debug.Log ("ddd");
    
    } else if(gameObject.tag=="HongBao") {
    
    CreateHong._instace.isCreate = false;
    
    GameObject go=GameObject.Instantiate (par,gameObject.transform.position,Quaternion.identity) as GameObject;
    
    Destroy (go,2f);
    
    CreateHong._instace.PlayScale ();
    
    }
    
    }
    

    完整代码:Move.cs:链接: https://pan.baidu.com/s/1qYLS77Y 密码: 9n1u

    CreateHong.cs:链接: https://pan.baidu.com/s/1jIBVt4q 密码: 483i

    EasyAR 开发实例—AR礼物

    原文链接:http://forum.easyar.cn/portal.php?mod=view&aid=11

    EasyAR 开发出一个炫酷的节日礼物效果

    预览:
    这里写图片描述
    这里写图片描述
    开发资源:

    链接: https://pan.baidu.com/s/1mkWL7Ev7CWOyfp6s4_8JQw 密码:pznt

    Step 1:开发环境

    关于用EasyAR SDK 搭建AR 开发环境的文章,不懂得朋友可以看下"EasyAR 初学者入门指南(1)—显示模型"。我们直接讲解本次的核心内容。

    我们下好资源后,导入到unity,搭建好基本AR环境。如图:
    这里写图片描述

    Step 2:准备模型

    我们将准备好的资源–礼物与二次元女生导入到unity中,并将三个礼物盒子与女主角拖入到ImageTarget 充当子物体,礼物盒的模型位置在
    这里写图片描述
    女主角的模型位置在
    这里写图片描述
    拖入之后,根据自己的需求修改其位置,实现其如下效果:
    这里写图片描述
    这里写图片描述

    Step 3:编写脚本

    首先为礼物盒添加Box Collider,并勾选Trigger
    这里写图片描述
    新建脚本,名字随便起,先实现点击礼物盒后,礼物盒消失二次元女生出现,这里用到了一个最巧但最常用方便的方法Void OnMouseDown(),使用这个方法前提是该物体挂了个Collider

    void OnMouseDown()
    
    {
    
    Destroy(this.gameObject);
    
    }
    

    Step 4:添加粒子特效

    使用粒子特效来使得更令人惊喜的礼物效果,粒子特效的资源位置在
    这里写图片描述
    接下来,编写脚本,脚本比较简单,基本思路就是在点击礼物盒子后,盒子销毁,创建粒子特效,代码如下:

    using UnityEngine;
    
    using System.Collections;
    
    public class Explore : MonoBehaviour {
    
    public GameObject explore1;
    
    public GameObject explore2;
    
    public GameObject explore3;
    
    public AudioSource sound;
    
    // Use this for initialization
    
    void Start () {
    
    }
    
    // Update is called once per frame
    
    void Update () {
    
    }
    
    void OnMouseDown()
    
    {
    
    Destroy (this.gameObject);
    
    Instantiate (explore1,transform.position,transform.rotation);
    
    Instantiate (explore2, transform.position, transform.rotation);
    
    Instantiate (explore3,transform.position,transform.rotation);
    
    }
    
    }
    

    粒子的选择与自己的喜好来选择,不一定和我一样,这样大家可以实现不同的效果。

    ###Step 5:添加音效

    音效对一个应用或游戏给人的用户体验影响还是很大的,给礼物盒子添加AudioSource
    这里写图片描述

    using UnityEngine;
    
    using System.Collections;
    
    public class Explore : MonoBehaviour {
    
    public GameObject explore1;
    
    public GameObject explore2;
    
    public GameObject explore3;
    
    public AudioSource sound;
    
    // Use this for initialization
    
    void Start () {
    
    }
    
    // Update is called once per frame
    
    void Update () {
    
    }
    
    void OnMouseDown()
    
    {
    
    Destroy (this.gameObject);
    
    sound.Play ();
    
    Instantiate (explore1,transform.position,transform.rotation);
    
    Instantiate (explore2, transform.position, transform.rotation);
    
    Instantiate (explore3,transform.position,transform.rotation);
    
    }
    
    }
    

    OK,就是这样,用很简单的代码就可以用EasyAR SDK 开发出惊艳的应用。

    EasyAR 开发实例—Pokemon Go

    原文链接:http://forum.easyar.cn/portal.php?mod=view&aid=14

    EasyAR+Pokemon Go

    Pokemon Go 作为去年最火爆的AR游戏除了让用户体验到AR的神奇外,也让开发者兴奋不已。所以了今天给大家分享如何用EasyAR SDK 来构建类似Pokemon Go 的AR+LBS+IP 的项目。

    对于这个较为庞大的项目打算分几期来分享,主要功能或教程目录如下:

    1.实现最基础的Pokemon Go 的抛掷效果

    2.集成AR录屏功能

    3.拍照截屏(录屏)分享朋友圈功能

    4.LBS部分,考虑用百度地图/高德地图(或Mapbox)来集成

    5.添加语音功能

    6.UI部分的设计

    7.添加对战功能(精灵PK.即AR联机对战)

    目前的策划是这样的,当然大家有什么好的想法也可以在下面评论。今天我们来实现第一部分-----抛掷精灵球并捕获皮卡丘。

    开发资源:

    皮卡丘模型:链接: https://pan.baidu.com/s/1mdhfpFnLc6SQFsubz9qn3w 密码:2mnj

    游戏音效:链接: https://pan.baidu.com/s/1bphIupp 密码: xpbe

    Step 1:开发环境

    我们将开发资源与EasyAR 2.0 unitypackage 一起导入unity中。框架如图:
    这里写图片描述
    在这里为大家准备了很多的PokeBalls 的模型,大家可以自由选择:
    这里写图片描述
    把 EasyAR_Startup (我们这里没有运用到识别图片之后展现AR模型,所以不需要ImageTarget),皮卡丘模型,pokeballs 都拖入面板中,效果如图:
    这里写图片描述
    修改皮卡丘位置与旋转角度(为了获取在移动端的最好体验),大家可以在不断测试中调出合适的数值,例如:
    这里写图片描述
    修改Pokeballs 位置(十分重要):
    这里写图片描述
    接下来,在皮卡丘上挂上Box Collider,并为其添加Tag(命名为Pika)。
    这里写图片描述
    这里写图片描述
    为Pokeballs 添加rigidbody 与 Sphere Collider。
    这里写图片描述

    Step 2:实现抛掷

    我们思路是这样的:点击pokeball ,进行抛物线运动。现在有两种方法来实现,一种是通过Rigidbody来施力实现,另一种是通过transform 来合成加速度实现。

    第一种:

    在Pokeball 上新建一段脚本:

    public class ClickSound : MonoBehaviour 
    {
    	bool drawing = false;
    	public ParticleSystem par;
    	float distance;
    	public float ThrowSpeed;
    	public float ArcSpeed;
    	public float Speed;
    	
    	void OnMouseDown()
    	{
    		distance = Vector3.Distance (transform.position,Camera.main.transform.position);
    		drawing = true;
    	}
    	void OnMouseUp()
    	{
    		this.GetComponent<Rigidbody>().useGravity = true;
    		this.GetComponent<Rigidbody>().velocity += this.transform.forward * ThrowSpeed;
    		this.GetComponent<Rigidbody>().velocity += this.transform.up * ArcSpeed;
    		drawing = false;
    	}
    	void Update()
    	{
    		if(drawing)
    		{
    			Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    			Vector3 rayPoint = ray.GetPoint(distance);
    			transform.position = Vector3.Lerp(transform.position,rayPoint,Time.deltaTime * Speed);
    		}
    	}
    }
    

    最主要的是判断点击抬起之后,为其添加向前的推力与向上的动力,来实现其运动。接着,实时判断通过射线来插值的方式实现其运动。

    我们设置好向前与向上的速度后,可以实现抛掷效果,但是缺点是不够灵活,很能与皮卡丘进行碰撞检测。

    第二种:

    借鉴:http://blog.csdn.net/hiramtan/article/details/51753448
    第二种我们通过transform 的方法来实现(直接绑定我们需要与之碰撞的对象即可):

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Throw : MonoBehaviour {
        public const float g = 9.8f;
    
        public GameObject target;
        public float speed = 10;
        private float verticalSpeed;
        private Vector3 moveDirection;
    
        private float angleSpeed;
        private float angle;
    
        bool drawing=false;
        void Start()
        {
            float tmepDistance = Vector3.Distance(transform.position, target.transform.position);
            float tempTime = tmepDistance / speed;
            float riseTime, downTime;
            riseTime = downTime = tempTime / 2;
            verticalSpeed = g * riseTime;
            transform.LookAt(target.transform.position);
    
            float tempTan = verticalSpeed / speed;
            double hu = Mathf.Atan(tempTan);
            angle = (float)(180 / Mathf.PI * hu);
            transform.eulerAngles = new Vector3(-angle, transform.eulerAngles.y, transform.eulerAngles.z);
            angleSpeed = angle / riseTime;
    
            moveDirection = target.transform.position - transform.position;
        }
    
    
        void OnMouseDown()
        {
            drawing = true;
    
        }
    
        private float time;
        void Update()
        {
            if (drawing) {
                time += Time.deltaTime;
                float test = verticalSpeed - g * time;
                transform.Translate (moveDirection.normalized * speed * Time.deltaTime, Space.World);
                transform.Translate (Vector3.up * test * Time.deltaTime, Space.World);
                float testAngle = -angle + angleSpeed * time;
                transform.eulerAngles = new Vector3 (testAngle, transform.eulerAngles.y, transform.eulerAngles.z);
            }
        }
    
    }
    
    

    然后,将对象拖给这段脚本即可:
    这里写图片描述

    Step 3:实现碰撞交互

    当pokeball 与皮卡丘相碰撞时,我们定义为捕获操作,这是销毁皮卡丘鱼pokeball并播放音效。

    首先我们在 pokeball 下面添加“AudioSource”组件
    这里写图片描述
    然后在脚本里添加方法:

    void OnTriggerEnter(Collider col)
        {
            if (col.tag == "Pika") {
                sound.Play ();
                Destroy (col.gameObject);
                Destroy (this.gameObject,3f);
            }
    
        }
    

    完整代码:

    链接: https://pan.baidu.com/s/1dFMpIcp 密码: 57bf
    捕获的功能现在就可以实现了

    展开全文
  • 你可以制作很多精细的小场景、小地形,离的进了,就依次...当然,你也可以不用unity自带的地形,直接使用模型; 这里有一个例子: http://www.youtube.com/watch?v=mXTxQko-JH0 你可以看看这个,以便当做参



    就是类似图片里这样的大场景地形 怎么做的啊 要用什么插件吗 在u3d里做这样的地形 太卡了 怎么办啊






    你可以制作很多精细的小场景、小地形,离的进了,就依次加载进来;离得远了,就把那些精细的地形给销毁掉。

    你可以创建好几个地形,设置他们的高度图分辨率,分辨率越高就越精细,当然也就更占资源;
    当然,你也可以不用unity自带的地形,直接使用模型;




    这里有一个例子:
    http://www.youtube.com/watch?v=mXTxQko-JH0
    你可以看看这个,以便当做参考。
    这是坎巴拉太空计划的开发人员在Unite大会上讲的。
    坎巴拉太空计划也是用unity制作的,他们在这个游戏里塞下了一整个恒星系统,其中有7颗行星和数颗卫星……


    从这个视频的30分钟50秒开始看,你可以看到他们是如何把一颗直径600KM的行星给放进去的~~
    他们把一颗行星分成好几层,
    第0层的分辨率为75000米,整个行星的模型将只会有1536个顶点,
    第5层的分辨率为2344米,整个行星的模型将会有1572864个顶点,
    第10层的分辨率为73米,整个行星的模型将会有1610612736个顶点…… 


    当你的飞行器在这颗行星上飞行、或是着陆在这个行星上的时候,离摄像机最近的地方,将会使用第10层的模型。
    但是并不是整个,否则1610612736个顶点将会够呛,
    因此,离摄像机最近的地方,将会使用第10层的模型的一小部分;
    离摄像机稍远的地方将会使用第9层的模型;
    更远的地方使用第8层的模型,以此类推,
    大部分较远的地方使用第0层的模型,也就是那个只有1536个顶点的模型,以便节省资源……

    展开全文
  • Unity3D游戏框架设计

    2018-07-03 20:00:09
    Unity框架设计将Unity Api、.NetFramework Api(4.6)以及部分原生库和托管库封装到一个抽象层,游戏本身的业务仅依赖于该抽象层从而提高业务逻辑的独立性和可维护性。 框架部分提供项目中使用的基础设施,包括资源...

    Unity框架设计

    将Unity Api、.NetFramework Api(4.6)以及部分原生库和托管库封装到一个抽象层,游戏本身的业务仅依赖于该抽象层从而提高业务逻辑的独立性和可维护性。 框架部分提供项目中使用的基础设施,包括资源管理、网络通信、UI框架、消息管理、场景管理、数据解析及存取等。

    1.   资源管理

    资源管理模块负责按照划分场景的颗粒度将所有游戏资源均打包至AssetBundle并在游戏中动态更新与加载,打包前需要将相资源索引文件和二进制资源(AB)放在StreamingAssetsPath路径下,在游戏初次运行时将所有资源拷贝至可读写路径PersistentDataPath下,在游戏更新阶段从服务器下载更新配置文件并根据本地资源的MD5更新资源文件,场景载入阶段异步加载场景需要的AssetBundle,资源加载时根据资源索引文件加载资源,离开场景时卸载相关AssetBundle,下图是流程示意图。

    除了使用AssetBundle,Unity还支持使用Resources的动态加载资源方案,对比之下使用AssetBundle的主要优势为:可以较好控制游戏资源所占内存;可以从外部可读写路径加载所以支持热更新;可以实现边下边玩减小初始安装包体大小。AssetBundle的劣势为:资源之间的依赖关系难以处理,容易造成资源冗余(自己在处理这部分时遇到很多问题,处理不当场景中引用的资源也会和AssetBundle资源重复); 需要额外的编辑器扩展支持增加复杂度。考虑到该项目为联网游戏所以选择使用AssetBundle进行游戏资源管理。

    资源管理策略:

    资源索引文件是一个对称加密的哈希表,作为配置文件存放在本地的某个可读写目录,内容为由资源名和资源所在AssetBundle名构成的键值对。资源管理模块初始化时会加载该资源索引文件并在整个游戏的生命周期中保持资源名和AssetBundle名的映射,资源管理模块也会一直保持已加载AssetBundle镜像的引用,但是这些对业务层均是不可见的。

    考虑到AssetBundle的占用内存较大,且资源之间引用比较复杂,默认情况下,在加载AssetBundle的同时也加载其内所有资源,且均使用异步加载。 卸载AssetBundle时会一并卸载其对应的所有资源。

    考虑到通用资源的使用频率较高,资源管理模块提供一种策略,在加载通用AssetBundle时可以选择同时加载其内的所有资源并在整个游戏生命周期保留其引用,之后便卸载AssetBundle镜像,这样在没有增大游戏使用内存的前提下加快这部分资源的访问速度。

    该方案的缺点是不同AssetBudle中的资源不允许重名,因而带来的优势为加载资源时只需提供资源名和资源类型(因为资源名和AssetBundle名存在一对一映射关系)从而隐藏了AssetBundle的复杂性。

    ResourceManager

    负责资源管理模块初始化,和服务器比对资源配置文件更新本地AB,更新资源索引文件,异步加载AssetBundle,卸载AssetBundle,加载游戏资源等功能。

    AssetBundleHelper

    作为Editor扩展, 负责生成本地资源索引文件和资源配置文件,上传资源文件至服务器。

    AssetFileInfo

    记录资源文件的基本信息,包括资源名、MD5值、下载路径、文件夹路径、资源大小等。

     

    不足和反思:

    a.    当前框架的更新是不可配置的,也就是将各种url,文件夹路径全都写死在了代码里,这在很大程度上限制了其通用性和扩展性,理想情况下最好将各种资源的配置以文件的形式存储,然后资源管理模块通过读取该文件进行初始化。

    b.    这种方案要求不同AssetBundle中不允许重名资源,这一点如果在多人协作的时候很不容易保证,就算只有一个人管理也会不小心触发(Unity资源目录不同文件夹下的资源允许重名), 所以需要一个统一的资源命名规范,甚至需要一个工具来检查。

    c.    有部分资源没有纳入资源管理模块的体系,如果场景引用了某个图集中的图片,那么在打包时会在本地拷贝整个图集会造成很大的资源冗余,所以只能选择拷贝一份重复的图片在场景中加以引用,但是如果这样同样会造成一部分资源冗余,同时这部分资源也无法热更新,目前仍没找到较好的解决方案。

    d.    AssetBundleHelper的Editor扩展部分比较坑,需要运行游戏才能进行上传资源文件等联网操作(因为调用了网络模块),并且该操作也是不可取消的,这一部分有较大优化空间,可以单独写一个客户端来提供这部分功能。

    e.    本地化问题,当项目做大的时候本地化是一个比较棘手的问题,当前的资源管理框架完全没有考虑本地化相关问题,该模块应当提供一种友好的方式来区分不同语言文化的资源并控制它们的加载。

    f.     最大的不足是没有引入脚本资源的热更新,因为当时考虑到学习成本和开发成本,没有在框架中加入这部分内容,但是对于联网游戏来说代码的热更新十分重要,比较成熟的热更框架据说有uLua,xlua,ILRuntime,后续还需要继续学习。

     

    2.   网络通信

    网络通信模块负责向业务层提供短连接服务(Http请求)和Tcp长连接服务。

    Http请求:

    网络通信模块的Http请求使用了System.Net.Http.dll库,这个托管库是微软对HttpWebRequest封装的一层Http请求接口,网络通信模块在此之上进行封装实现异步Get/Post请求、下载文件等静态方法。

    当然Unity本身也提供了很多Http请求的实现方式,例如WWW, UnityWebRequest均可以进行Http请求,但这两者都是基于协程实现的并发而不是并行,网络请求操作仍然是在主线程中执行。该项目使用Unity2017(.Net 4.6)开发,对多线程的支持已经比较完备,同时支持基于任务的异步编程模型,考虑将网络和I/O放在线程池中执行可以减小主线程压力,同时充分利用多核设备性能提高执行效率,所以设计网络通信模块时没有选用WWW和UnityWebRequest而是使用System.Net.Http.HttpClient。

    HttpRequestClient:

    实现并重载Http请求的多种静态异步方法,包括GetAsync(Get请求)、PostAsync(Post请求)、DownloadFileAsync(下载文件请求)。

    HttpResponseData:

    Http请求返回的数据格式(和后端约定),包括错误码、数据、消息等。

    Tcp客户端:

    网络通信模块基于System.Net.Sockets.TcpClient封装了异步阻塞模式的Tcp客户端(抽象基类BaseTcpClient),并提供了Tcp客户端常用方法的默认实现,包括连接、关闭连接、自动心跳、发送消息、包序排列、粘包分包处理、断线重连、断线回调、接收消息回调等功能。子类构造方法中需要提供IP地址/域名和端口即可创建一个Tcp客户端实例,一般情况下子类应当重写断线回调方法和接收消息回调方法,同时定义了默认的通信数据类型(前后端通信数据协议默认使用json)以支持Tcp客户端实现。

    每个Tcp客户端实例内部会维护两个线程,一个线程用于定时发送自定义心跳包,一个线程用于阻塞等待接收消息包。Tcp客户端基类没有继承自MonoBehaviour,这样设计的初衷是希望让Tcp客户端在于游戏流程之外作为一个更封闭更独立的模块存在,Tcp客户端的生命周期管理方式也和Unity GameObject的生命周期不同,所以没必要继承MonoBehaviour。在实例化并连接成功之后,Tcp客户端的功能就是推动游戏流程,业务层应当订阅Tcp客户端中感兴趣的事件,当Tcp客户端收到消息包时便会把该消息包的处理方法派发到主线程消息队列,在主线程中执行订阅该事件的方法(因为绝大多数Unity Api只能在主线程中调用)  因为没有继承MonoBehaviour所以设计心跳包功能时没有选择在游戏周期的使用协程定时发送,而是单独开一个线程。

    在处理粘包分包问题上,网络通信模块使用自定义包格式(包头+包体)的方式来处理。前后端通信的每个包在发送之前都会在原有数据之前添加4个字节的包头表示包体大小,客户端通过计算包头来获取每个包体,不完整的包则临时存储在缓冲区。

    BaseTcpClient:

    Tcp客户端基类,实现了常用方法,包括ConnectAsync(异步连接)、Close(关闭连接)、Reconnect(端线重连)、Send(发送消息)、OnReceived(接收消息回调)、OnLoseConnect(断线回调)等。

    TcpRecvData:

    默认实现的接收消息包的数据类型(和后端约定),包括包序、类型、数据(object,业务层解析)、错误码、消息。

    TcpSendData:

    默认实现的发送消息包的数据类型(和后端约定),包括用户ID、类型、数据(object,业务层解析)。

     

    不足和反思:

    a.    下载文件需要断点续传,这部分功能还需要进行优化,尤其是在更新资源包大小较大的时候,如果出现网络波动导致下载的进度丢失会给玩家造成较差的游戏体验。

    b.    很多大佬都在建议使用Protobuf进行数据的结构化存储,但是该框架整体的数据存储和通信均使用json,Protobuf有更优的性能,占用更小的内存,所以将整个框架中的json数据存储方案替换为Protobuf存储方案会提高一些性能。

    c.    目前仍没有实现UDP客户端,当前的网络通信模块没有实现一个UDP客户端基类,不排除项目之后有使用UDP协议通信的可能。

    d.    异步方法的取消,整个框架中的大部分异步方法都是不支持取消的,尤其是网络和I/O部分有大量的异步方法,对于玩家来讲等待时间过久而又无法取消的体验是极不友好的,参照网上的方案可以在一些类型中实现接口以支持取消操作。

    e.    关于自动发送心跳包和业务层发送消息引发的线程同步问题,这个问题目前还不是很清晰,因为业务层发送消息在主线程,自动发送心跳包在线程池,二者有可能同时执行发送消息的操作,而且网络流Netw orkStream好像不是线程安全的(不确定),在实践中后端反应过出过问题,但是把所有心跳操作都派发到主线程执行又违背了设计的初衷(封闭和独立),降低了这部分模块的扩展性(没法拿到别处用)。

     

    3.   UI框架

    UI框架基于UGUI简单封装了UI窗体的各种属性和方法,同时提供UI管理器作为业务层调用的接口。

    把游戏中所有的2D可视元素均可以看作为窗体或窗体的一部分,例如一个设置界面、一个消息确认界面、一条吐司通知都可以抽象为一个窗体Form。但是在Unity中设计这样一个界面需要很多游戏物体组合,所以为了方便管理,将该窗体本身的显示元素均置于一个空的游戏物体StaticLayout。然后还要引入父窗体、子窗体、兄弟窗体的概念,因为考虑一个窗体在交互时会很可能产生隶属于该窗体的其它窗体,如果生成的窗体的生命周期应当小于等于原窗体的生命周期,那么它们就有逻辑上的父子关系,此时生成的窗体是原窗体的子窗体,原窗体是生成窗体的父窗体,拥有同一父窗体的窗体互为兄弟窗体,单独拿出每一个子窗体同样是一个独立的窗体整体。为了方便管理子窗体,所有子窗体都根据显示类型置于StackNode或NormalNode下。这三个节点(StaticLayout, StakcNode, NormalNode)统一挂在同一游戏物体下,并将Form组件添加到该游戏物体上,此时这个游戏物体在逻辑上就相当于一个窗体,将该游戏物体做成预制体资源,可以方便的在Inspector面板配置该窗体的属性(如显示类型, 预制名称 预制路径)和特性(如支持拖动, 模态显示, 固定显示, 全局唯一, 等),而窗体名称则需要在实例化该窗体时指定,下图是窗体层次结构示意图:

    根据该层次结构也引入一个概念“虚拟层级路径”,是一个字符串,通过拼接根窗体和该窗体之间所有层次的窗体名称来作为唯一标识该窗体的索引。窗体在创建的时候有两种显示模式:堆栈和普通,任何时候堆栈模式的所有子窗体里只有一个子窗体是具有焦点的(可操作),而除此之外的其它子窗体均处在冻结状态,堆栈模式的子窗体任何时候只有栈顶的窗体具有焦点,当点击任一窗体或创建了新的子窗体时对应窗体移至栈顶。而普通模式的子窗体均是可操作的。

    UIBaseForm:

    是所有自定义窗体的基类,实现了一些窗体的特性和方法并提供了默认实现,子类可以重写InitContent(初始化窗体)、Show(显示窗体)、Freezed(冻结窗体)、Destroy(销毁窗体)、Recovery(从冻结状态恢复)等方法以便在UI窗体状态改变时更新其界面。

    UIManager:

    UI管理器,其内部有多个数据结构维护所有存在窗体的父子/兄弟关系,对外公开了CreateForm(创建窗体)、CloseForm(销毁窗体)等方法。

     

    不足和反思:

    a.    层级路径允许使用一个字符串来直接获取某个窗体的引用,虽然这样设计可以为获取一个窗体的引用提供了更灵活的方式。但是实际上父窗体直接拥有其所有子窗体的引用和控制权限而不必通过虚拟层级路径来获取,做同样一件事却有超过一种的方法,这在一定程度上违背了设计原则。本来这个UI框架中是没有虚拟层级路径的,但是个人能力有限,在实际开发的过程中发现很多时候业务层不易获取到UI窗体的引用,所以引入了虚拟层级路径这个概念,算一定意义的委曲求全。

    b.    界面的显示逻辑和交互逻辑(也就是View层和Controller层)没有分开,因为毕竟当前该UI框架只是进行较简单的封装,继承BaseUIForm的自定义窗体类中,既有处理交互的代码,也有处理显示的代码,处理输入的部分和处理显示的部分耦合性较强。重新屡了下代码觉得这部分可以做如下优化:

    l  任何一个继承自BaseUIForm的自定义窗体类都需要再实现一个对应的FormController类,并且均作为组件挂在窗体顶层上。以便取消窗体类的任何主动性,仅接收交互逻辑和业务逻辑的通知来更新UI,这样窗体类更轻了仅仅负责显示的功能,其处理交互部分的逻辑均在FormController类中实现。

    c.    关于UI窗体的复用。复用重复创建的UI窗体可以提高性能, 虽然目前的UI框架支持使用对象池,但是和对象池系统整合得比较松散,理想情况下业务层对于创建UI窗体的复用过程应该是不可感知的,但是目前每个自定义窗体都需要实现对象池定义的接口,并且创建UI窗体之前需要手动判断在对象池中是否存在可复用对象显得很繁琐,可以想办法将这部分整合到UI管理器内部。

    d.    关于UI根节点。该UI框架有个硬伤就是一定要存在某个根节点,也就是所有UI窗体的祖宗节点一定要是这个根节点,甚至需要单独为根结点挂载特殊的脚本,在处理UI逻辑中存在这样一个特例引入了一定的复杂性,在切换场景之后也要重新初始化根节点,可以优化取消根节点,只需在游戏载入阶段初始化一次UI框架即可。

     

    4.   消息管理

    为了降低模块之间的耦合性,使用一套消息通信机制很有必要,消息管理模块基于观察者模式提供了订阅、注销、发送消息等功能,同时实现了一个线程安全的类型来维护派发至主线程的消息队列。

    虽然Unity本身提供了消息机制,如SendMessage、BroadcastMessage等方法,但是经过了解这种方法局限性较大:首先这样发送消息严重依赖字符串而无法实现编译阶段的类型安全,它也可以调用私有方法破坏类型的封装性,并且只有继承MonoBehaviour的类型才可以调用,这些应该都是该机制内部使用反射而带来的问题。所以有必要自己实现一套消息管理机制。

    消息管理模块的核心消息管理器(MessageCenter),这是一个泛型静态类型,类型参数是一类枚举用来划分不同模块的消息,不同的类型参数有各自的消息队列,维护各自内部的消息处理。比如为了传递系统类型消息,设计一个SystemMessage枚举,用来枚举所有系统消息。这样设计主要是考虑通过区分枚举类型将不同类型的消息根据模块区分开便于维护和扩展,不同模块维护不同的消息队列,添加模块只需增加新的枚举类型即可。

    MessageCenter:

    消息管理器,对外公开了AddListener(订阅)、RemoveListener(注销)、Sendmessage(发送消息给监听者)等方法提供了基础的消息通信机制。

    MainThreadMessageHandler:

    内部维护一个委托队列,用来缓存派发至主线程的待执行方法,在主线程的Update周期中轮询来依次执行这些方法;对外公开了RegisterAction(派发主线程消息)方法。

     

    不足和反思:

    a.    在实现消息管理器时其实有两种方案:单例或者静态类,这部分的取舍很纠结,我当时是认为单例的生命周期和初始化顺序不容易控制,但是静态类不能继承也不面向对象,看网上相关资料推荐使用单例方案的较多,关于这个类型的设计还有不足。

    b.    关于消息类型的基类。为了图方便,目前消息管理模块所使用消息的基类为System.EventArgs,但是这个类型几乎没有提供任何功能,目前该框架需要在EventArgs的基础上封装一个消息类型的基类,提供配合消息管理器的相关功能。

    c.    发现消息管理器内部维护的字段类型是字典Dictionary<TMessage,EventHandler>,当时应该忽略了不同类型参数维护的不同静态字段,改为Queue<EventHandler>或者直接改为EventHandler应该都可以。

    d.    还有一个问题一直存在但是没处理,就是派发出的消息抛出异常时,函数调用栈比较长,如果在Unity编辑器双击错误会直接定位到消息管理器内部,只能查看异常内部的调用栈来找到出错代码费时费力。可以把消息管理器部分编译为动态链接库作为插件引入项目,这样报错时可以直接定位到抛出异常的代码。

     

    5.   场景管理

    场景管理模块主封装了场景管理器和一个处理场景的通用基类。

    该模块主要是为了提供唯一的场景入口和场景出口(包括游戏入口和游戏出口),并在入口处进行资源的加载和业务逻辑的初始化,在出口处进行资源的释放和相关业务逻辑的处理。这样做有利于控制正确的游戏流程顺序,避免初始化逻辑的分散。

    每个新场景都需要一个实例添加继承自Scene基类的组件,并重写场景入口和场景出口方法来进行场景管理,同时提供一个全局的App组件,统一在App类中实现游戏入口和游戏出口方法,场景管理的流程如下图所示。

    Scene:

    自定义场景类的基类,提供虚方法SceneEntrance(场景入口)和SceneExport(场景出口)配合场景管理器进行场景切换

    SceneManager:

    场景管理器,封装了UnityEngine.SceneManagement.SceneManager的部分方法,公开了SceneConvertAsync(异步切换场景)供业务层调用。

    App:

    存在于整个游戏生命周期的组件,在其GameEntrance(游戏入口)方法中进行全局框架和全局模块的初始化,在其OnApplicationQuit(游戏出口)方法中进行相关操作。也提供了部分游戏周期事件供外部没有继承自MonoBehaviour的类型订阅(如Pause, Update, FixUpdate),这些消息被SystemMessage所枚举。

     

    不足和反思:

    a.    场景切换的进度汇报显示的问题,目前只是进行简单的遮挡处理,当游戏场景较大时没有进度提醒会造成玩家误解。

    b.    关于“异步加载”的问题。在该框架中,场景和资源的加载是使用Unity的协程异步加载的,而网络和I/O部分则是使用多线程实现异步请求的,二者在同一个方法中使用会导致只能等待其中一个的结果,这十分棘手。因为一个方法要么返回IEnumertor,要么返回Task/Task<TResult>/void,也就是await和yield return 不能同时存在于一个方法中,毕竟这两种实现异步的机制不同。这个缺陷在切换场景时尤为放大,因为有时会需要在场景切换时进行网络请求和文件读写,目前没有想到较好的解决方案,只能在业务层尽可能避免同时使用两者。

     

    6.   数据解析与存取

    该模块主要向业务层提供文件存取的方法(包括异步和同步),并提供字符串的加密和解密服务,数据的序列化和反序列化通过调用第三方库Newtonsoft.Json实现。

    文件存取部分封装了System.IO命名空间下的文件流操作方法并以较友好的方式公开;加密和解密使用DES对称加密方案,封装了System.Security.Cryptography命名空间下的相关类型。业务层进行文件存储的流程一般是先将相关对象序列化为字符串,进行加密之后存储到本地可写路径;进行文件读取的流程是先从本地可读路径读取字节流转化为字符串,进行解密后再反序列化为相应对象供业务层使用。

    FileHelper:

    提供文件存取的相关功能并重载大量方法方便调用,包括Write(写)、Read(读)、WriteAsync(异步写)、ReadAsync(异步读)、SelectFile(选取文件)等方法。

    SecurityFactory:

    基于DES对称加密方案提供对称加密相关功能;基于MD5加密方案提供不可逆加密相关功能,包括Encrypt(加密)和Decrypt(解密)等方法。

     

    不足和反思:

    a.    对称加密的安全性仍没有得到保证。为了简化加密解密过程,在设计该模块时直接将密钥直接置于代码里了,由于托管语言的特性,游戏逻辑编译过后的托管库(Assembly-CSharp.dll)在不经过处理的情况下是可以反编译的,密钥很容易会被获取。考虑应该有如下几种解决方案:使用工具进行代码混淆提高破解难度;使用IL2CPP方案将托管语言编译为原生语言提高安全性;将密钥置于服务器在游戏运行时联网获取。


    展开全文
  • 基本信息mul函数mul函数,是表示矩阵M和向量V进行点乘,得到一个向量Z,这个向量Z就是对向量V进行矩阵变换后得到的值。 HLSL的mul函数接受mul(V, M)或mul(M, V),要注意通常HLSL要依DirectX计算(V * M)使用mul(V, M)...

    基本信息

    mul函数

    mul函数,是表示矩阵M和向量V进行点乘,得到一个向量Z,这个向量Z就是对向量V进行矩阵变换后得到的值。
    HLSL的mul函数接受mul(V, M)或mul(M, V),要注意通常HLSL要依DirectX计算(V * M)使用mul(V, M)的形式.
    特别需要小心的是,V如果是float3,前后行列不等,违反HLSL规范,但shader编译也不报错,直接当成float4(V, 0)处理,而不是当成float4(V, 1).即mul(float3, M)中的float3被当成向量,而不是顶点.
    Unity3d 中,若是OpenGL,用的应该是GLSL,mul方法是(M,V)。

    矩阵

    内置的矩阵(float4x4):

    名称 说明
    UNITY_MATRIX_MVP 当前模型视图投影矩阵
    UNITY_MATRIX_MV 当前模型视图矩阵
    UNITY_MATRIX_V 当前视图矩阵
    UNITY_MATRIX_P 当前的投影矩阵
    UNITY_MATRIX_VP 当前视图投影矩阵
    UNITY_MATRIX_T_MV 模型视图矩阵的转置
    UNITY_MATRIX_IT_MV 模型视图矩阵的逆转置
    _Object2World 当前模型矩阵
    _World2Object 当前世界矩阵的逆矩阵

    这里要特别说明一下UnityObjectToClipPos(v.vertex)) 方法,官方网站上说明,在写Instanced Shader时,通常情况下并不用在意顶点空间转换,因为所有内建的矩阵名字在Instanced Shader中都是被重定义过的,如果直接使用UNITY_MATRIX_MVP,会引入一个额外的矩阵乘法运算,所以推荐使用UnityObjectToClipPos / UnityObjectToViewPos函数,它们会把这一次额外的矩阵乘法优化为向量-矩阵乘法。

    相机

    名称 类型 数值
    _WorldSpaceCameraPos float3 世界空间相机的位置
    _ProjectionParams float4 x = 1.0(或如果当前使用翻转投影矩阵渲染则为-1.0),y是相机的近平面,z是相机的远平面,w是1 / FarPlane
    _ScreenParams float4 x是相机的渲染目标在像素里的宽度,y是相机的渲染目标在像素里的高度,z是1.0 + 1.0 /宽度和w是1.0 + 1.0 /高度
    _ZBufferParams float4 用于线性化Z缓冲区的值。x(1-far /near),y(far/near)、z(x /far)和w(y /far)
    unity_OrthoParams float4 x是正交的相机的宽度,y是正交的相机的高度,z是未使用的,为正交的相机时w为1.0,透视相机时w为0.0
    unity_CameraProjection float4x4 摄像机的投影矩阵
    unity_CameraInvProjection float4x4 摄像机的投影矩阵的逆矩阵
    unity_CameraWorldClipPlanes[6] float4 相机锥平面世界空间方程,按顺序为:左、右、底部、顶部、近、远

    光照

    名称 类型 数值
    _LightColor0(Lighting.cginc中声明) fixed4 光照颜色
    _worldspacelightpos0 float4 方向光:(世界空间方向,0)。其他光:(世界空间位置,1)
    _LightMatrix0(AutoLight.cginc声明) float4x4 world-to-light矩阵。用于样品cookie 和衰减纹理
    unity_4LightPosX0、unity_4LightPosY0 unity_4lightposz0 float4 (仅ForwardBase通道)前四个不重要的点光源的世界空间坐标
    unity_4lightatten0 float4 (仅ForwardBase通道)前四个不重要的点光源的衰减系数
    unity_lightcolor half4[4] (仅ForwardBase通过)前四个不重要的点光源的颜色数组

    在Shader的光照通道里的延迟着色和延迟光照(在unitydeferredlibrary.cginc):

    名称 类型 数值
    _LightColor float4 光照颜色
    _LightMatrix0 float4x4 world-to-light矩阵。用于样品cookie 和衰减纹理

    多光源下,最多8个光源在顶点通道,排序为从最亮的开始

    名称 类型 数值
    unity_LightColor half4[8] 光照颜色数组
    unity_LightPosition float4[8] 视图空间光源的位置。方向光源的坐标是(-方向,0);(位置,1)用于点/点指示灯,点光源,聚光灯的坐标是(位置,1)
    unity_LightAtten half4[8] 光源衰减的系数。X是cos(spotAngle/2)或非聚光灯为-1;Y为1/COS(spotangle / 4)或非聚光灯为-1;Z是衰减的二次方;W是正方形光源的范围
    unity_SpotDirection float4[8] 视图空间聚光灯的位置;(0,0,1,0)则非聚光灯。

    基本变换

    在Unity中,每个物体都有一个坐标系,就是自身坐标系,各个物体之间相互独立。
    所有的物体都处在一个统一的空间里,这个空间就是世界空间,也有一个世界坐标系。
    把一个3D物体渲染到2D的屏幕上的基本流程以及每个变换对应的矩阵

    1. 因为物体的顶点坐标是基于自身坐标系的,所以渲染时,最先的变换是 模型空间——>世界空间,对应矩阵:_Object2World
    2. 物体要渲染到相机平面上实际上,是相机的可视区域内有哪些物体,也就是物体处于相机坐标系的本地坐标(localPosition)处于哪个位置。这个变换是 世界空间——>相机空间,对应矩阵:UNITY_MATRIX_V
    3. 此刻获取到了物体处于相机空间的位置,要把相机空间的所有信息都渲染到2维图片上,此刻需要进行投影变换,透视相机投影变换的目的是为了把视锥体转换为立方体,转换后,视锥体近平面的右上角点变成立方体前平面的中心,把视锥体较小的部分放大,较大的部分缩小,形成最终的立方体。变换后的x坐标范围是[-1, 1],y坐标范围是[-1, 1],z坐标范围是[0, 1](OpenGL不同,z值范围是[-1, 1]),这个变换是 相机空间——>投影空间,对应矩阵:UNITY_MATRIX_P

      通过UNITY_MATRIX_MVP 这个矩阵,可以把物体的顶点位置从模型自身坐标系转换到投影空间。
      对投影矩阵感兴趣的,可以自己搜索一下,整个推导过程需要一定的数学基础,理解就行。

      切线空间矩阵

      法线贴图大部分都是蓝色的,是因为法线贴图里存储的值都是Tangent Space(切空间)下的。
      在切线空间中,垂直于表面的法线向量是(0,0,1)。该向量转成像素是(0,0,127),这个值是蓝色的,由于大部分的数值都跟这个相差不会很大,也就是说,像素之间的差别并不是非常的明显,所以大部分都是偏蓝色。
      切空间(TBN)是表示物体模型上的顶点坐标位置随着纹理坐标(u,v)的变化而变化,该顶点切线空间的X轴方向是连接该点与下一个点坐标u的方向,该顶点切线空间Y轴方向是连接改点与下一个坐标v的方向,Z轴则是改点的法线方向。简单一点来说,也可以这么理解,该顶点的X轴就是该顶点的uv坐标系下的u轴,Y轴就是该顶点的uv坐标系下的v轴。
      其中

      T=(dxdu,dydu,dzdu)B=N×TN=T×(dxdv,dydv,dzdv)

      TBN切线空间矩阵M是[T,B,N]
      [T,B,N]=TxTyTzBxByBzNxNyNz

      如果三向量互相垂直,实际上也基本如此,Unity中,没有考虑其它情况。
      它的逆矩阵就等于它的转置矩阵。
      [T,B,N]1=TxBxNxTyByNyTzBzNz

      切线空间矩阵[T,B,N]的作用是为了把空间向量转换为切线空间里的向量。在做光照计算时,光源实在世界空间中,由于需要得到光线向量和法向量的夹角,而法线向量是在切线空间中,所以都是把一个转换到另外一个空间中,进行计算。
      有的是把光线向量转换到切线空间中进行计算,所以用到的基本上都是矩阵[T,B,N]的逆矩阵
      [T,B,N]1

      用它来乘以光照方向,得到光照在切线空间的转换。
      有的是把法线转换到世界空间进行计算,直接用矩阵来进行计算。
    展开全文
  • 欢迎来到unity学习、unity培训、unity企业培训教育专区,这里有很多U3D资源、U3D培训视频、U3D教程、U3D常见问题、U3D项目源码,我们致力于打造业内unity3d培训、学习第一品牌。 本地客户端开发入门 本地客户端 ...
  • 这篇文章介绍如何使用EasyAR.unitypackage配置EasyAR 参考资料 1、EasyAR 初学者入门指南 http://forum.easyar.cn/portal.php?mod=view&amp;amp;amp;amp;amp;aid=2 2、EasyAR入门 ...
  • Shader效果常用于3D模型,但2D图也有不少常用的效果,例如:圆角头像,图片灰态,边缘着色等等,下面我就依次做一个介绍。 图片灰态 效果 核心思路 在片元着色器里面对每个图元进行采样,将图元的颜色x,y,z...
  • 课程目标:顶点转换像素着色Shader属性从顶点着色器传递数据到片段着色器查看编译的shader代码使用tiling 和offset属性从纹理取样 这是...在unity3d中创建一个新的场景。使用默认的相机跟平行光就可以了。接下来让...
  • 原文:Introduction To UFPS: Unity FPS Tutorial 作者:Anthony Uccello 用一支散弹枪轰杀大片凶恶的敌人或者在战场上小心翼翼地狙杀你的对手是一种爽到极点的游戏体验。以动作+射击为主的游戏被称作第一人称射击...
  • unity渲染优化

    2017-04-15 18:40:39
    首先,这个系列文章做个大致的介绍,题目“浅谈Unity”,因为公司和国内大部分3D手游开发还是以Unity3d为主,而Unity不开源的问题,也注定大部分用户是无法在架构和API的层面对它做改造和优化的,所以本文也不会涉太...
  • 一:什么是协同程序?...二:Unity3d中的碰撞器和触发器的区别? 碰撞器是触发器的载体,而触发器只是碰撞器身上的一个属性。当Is Trigger=false时,碰撞器根据物理引擎引发碰撞,产生碰撞的效果,可以调用OnCol
  • 这一节我们用下面的图片: 来实现动态的熊熊的烈火效果: 我们先来看一下全部的Shader代码,然后再进行分析 Shader "Custom/UVAnim" { Properties ... _Color("Base Color", Color) = (1,1,1,1) ...
  • 首先,这个系列文章做个大致的介绍,题目“浅谈Unity”,因为公司和国内大部分3D手游开发还是以Unity3d为主,而Unity不开源的问题,也注定大部分用户是无法在架构和API的层面对它做改造和优化的,所以本文也不会涉太...
  • EasyAR入门 EasyAR是好用免费的全平台AR(Augmented Reality,增强现实)引擎。 EasyAR支持使用平面目标的AR,支持1000个以上本地目标的流畅加载和识别,支持基于硬解码的视频(包括透明视频和流媒体)的播放,...
  • QQ 1285575001 Wechat M010527 技术交流 QQ群599020441 纪年科技aming 为什么要有规范 ...游戏开发相对其他软件开发难度大 ...要求策划、程序、美术等高度协同配合完成 ...前期松散 (需求不明确,跟进不到位...
  • OtherSettings Rendering ColorSpace 色彩空间 https://docs.unity3d.com/Manual/LinearLighting.html
1 2 3 4 5 ... 12
收藏数 228
精华内容 91