UGUI中所有元素都点击不到,重启unity 数组添加元素第一次运行可以用,停止后再次运行就不好

6421人阅读
[Unity引擎](86)
[游戏开发](79)
[算法相关](20)
各位朋友大家好,欢迎关注我的博客,我的博客地址是。最近因为受到工作上业务因素影响,所以博主在Unity引擎上的研究有所停滞。虽然目前的工作内容和Unity3D没有直接的关联,可是我觉得工程师应该有这样一种情怀,即工作和兴趣是完全不同的两个概念。编程对我而言,首先是一种兴趣,其次是一份工作。所以我宁愿在每天下班以后继续研究自己感兴趣的东西,而非为了取悦这个世界、为了加班而加班。最近广电总局让整个游戏行业都坐立不安了,因为其新发布的一系列规定,让中国的独立游戏开发者怨声载道。可是我们更应该看到积极的一面是,无数的小游戏公司会在最近数月内大量消失,或许对中国野蛮生长的游戏行业这是一次“形式”上的整顿,可对我们开发者来说,在这个过程中努力提升自我、巩固基础永远比追求时髦、流行的技术或者框架有意义的多,因为热闹的从来都是昙花一现般的璀璨,而永恒的永远都是历久弥新的真理。好了,闲言少叙,今天我们的话题是在uGUI中使用不规则精灵制作按钮。
从用户体验说起
我们都知道在现代应用程序设计中,用户体验(UX)和用户界面(UI)是两个非常重要的内容。为什么用户体验(UX)和用户界面(UI)会显得如此重要呢?这是因为从普通用户的角度来讲,用户界面(UI)是其接触到一个产品时最先看到的最直观的东西,而在这个过程中产生的直观感受就是用户体验(UX),所以说到底这是一个产品给用户的“第一印象”。
最近百度UE总监刘超在IXDC峰会上的演讲引起了大家的关注,抛开百度在人才选拔机制中存在的问题以及刘超本人在设计领域是否具备专业能力这两个问题,这件事情真正让大家吐槽的是什么呢?答案是用户体验。虽然IXDC并非国际级别的大型会议,但是我相信大家组织这样的活动,其本意是为了探讨交互、设计领域内的新方法和新思维,因为随着互联网行业的发展,交互和设计这个领域越来越被人们所关注,所以在这样一个场合下,当与会嘉宾都在试图向人们输出干货的时候,刘超以一个非常糟糕的“用户体验”来给大家讲什么是用户体验,这件事情起源自刘超的一个个人行为,结果牵一发而动全身,最终升级为百度继“魏则西事件”以后的又一次公关危机。
我到底想说什么呢?我说的本质上就是用户体验的问题,在这个事件中,刘超穿着上的不得体(短裤搭配拖鞋?)、PPT制作的粗制滥造(校招时所用修改)、演讲过程的敷衍糊弄(说相声、猜谜语)等因素,让刘超在与会者心目中的地位瞬间滑落到冰点,进而引发人们对百度在交互设计领域内的能力的怀疑,联想到百度最近这些年内出现的问题,这件事情难免会被人作为指责百度这家企业价值观问题,我想这是这个事情为什么会让大家如此关注的一个原因吧。
那么,我们说这些到底和今天的主题有什么关系呢?我想说这当然有关系啊,因为我们提出的这个问题就是一个用户体验的问题。我们知道游戏行业对美术资源高度依赖,不管是2D游戏还是3D游戏,一个项目组中前期主要的工作量其实都在美术这边,虽然不同的游戏引擎、GUI框架都为我们提供了标准的控件样式,然而在这样一个注重多样性的时代,默认样式、系统字体都会让人觉得这个产品缺乏新意,因此这种要求体现在游戏项目中就变成了,我们使用大量的图片资源来解决界面和字体的问题。
例如,我们通常使用BMFont来制作位图字体,这是为了同时满足字体的多样性和资源的容量这两个要求。再比如我们在使用cocos2d-x和Unity3D引擎开发游戏的时候,我们将大量的时间花费在了UI的制作上,这一切的一切从本质上来讲都是为了提升产品的童虎体验。这样我们就会遇到一个问题,UI中的按钮默认情况下都是规则的矩形,而实际上美术提供的素材常常是不规则的,因此如果继续使用以矩形为标准的这套机制,在实际使用中可能出现“用户点击在不该响应的区域结果程序响应了用户操作”这样的问题,为了解决这个问题,提升这一点点细微的用户体验,我们需要花费时间和精力来了解下面这些内容。
两种不同的方案
目前,关于这个问题如何,解决通过搜索引擎我们能找到两种不同的方案:
* 多边形碰撞器: 该方法是指给精灵(Sprite)添加一个多边形碰撞器(Rolygon Collider)组件,利用该组件来标记精灵的边界,这样通过比较鼠标位置和边界可以判断点击是否发生在精灵内部。这种方法的详细说明可以参考宣雨松的这篇文章:
* 精灵像素检测: 该方法是指通过读取精灵(Sprite)在某一点的像素值(RGBA),如果该点的像素值中的Alpha&0.5则表示该点处是透明的,即用户点击的位置在精灵边界以外,否则用户点击的位置在精灵边界内部。这种方法的详细说明可以参考
多边形碰撞器
多边形碰撞器这种方案从本质上来讲,其核心思路是验证某一点是否在任意多边形内部,因为在这里RolygonCollider2D组件的作用体现在:第一,它可以在编辑器下进行可视化编辑对用户友好;第二,它可以在帮助我们标记精灵边界的同时保留顶点信息。所以在这里RolygonCollider2D组件相当于为我们提供任意多边形的顶点信息,而接下来我们要做是将鼠标位置转化为屏幕坐标,这样我们就获得了某一点的坐标。整体思路看起来是没有问题的,但我个人以及网友都认为宣雨松这个算法存在问题,具体的理由如下:
1、uGUI中的元素采用的是以屏幕中心为原点(0,0)的平面直角坐标系,而普通屏幕坐标采用的是以左下角为原点(0,0)的平面直角坐标系,所以多边形顶点数组和鼠标位置不在一个坐标系内,使用AABBB这样的碰撞检测算法存在问题。
2、RolygonCollider2D中的points属性即多边形顶点数组存储的是相对于UI元素的相对坐标,在进行计算的时候应该统一转化为绝对坐标,这个过程在宣雨松的代码中有所涉及,但我认为对UI元素来讲,应该使用transform.GetComponent().position而非transform.position,因为transform.position最初是给3D物体使用的,而实际上这里是存在误差的。
3、我怀疑宣雨松提供的这个ContainsPoint方法的正确性,因为按照我的理解修改这个方法以后,发现界面响应的情况和实际情况是有所出入的,如下图所示,在整个区域内该方法都返回false。为了排除因为我的方法而对结果产生的影响,我使用宣雨松的代码进行了测试,结论是这个方法不管进行坐标系的转换与否,它在整个区域内的返回值都是false,因此我认为这个方法是错误的,虽然从理解算法的角度来看,它应该是根据线性差值来判断点在多边形中每条边的哪一侧的。
在评论中网友指出可以使用多边形碰撞器的OverlapPoint方法来判断一个点是否在多边形内部,可是经过我测试,这种方式和宣雨松提供的方法有着类似地问题,无论是否对坐标系进行转换,这个方法都返回false,响应区域与上图完全一致。
所以不管网络上有没有高质量的内容,一个核心的问题是你能否从中找到答案。如果你可以直接找到解决方案这可能是最好的结局;如果找不到直接的解决方案,却能够有所启发并独立解决问题,这是我们希望看到的结果。可是有时候人们并不这样想啊,人们想得到的是可以运行的代码而非解决问题的思路,因为可能人们并不想解决这个问题。
好了,经过知乎上相关我找到了这篇,文章中提到了判断一个点是否在任意多边形内部的两种方法,分别为Corssing Number和Winding Number。这两种方法在理论层面的相关细节请大家自行阅读这篇,我们这里选择的是前者,其基本思想是计算从该点引出的射线与多边形边界橡胶的次数,当其为奇数时表示该点在多边形内部,当其为偶数时表示在多边形外部。这里有一个有意思的事情是宣雨松选择的方法应该是著名的算法,可是为什么在这里会出现这样的问题呢?
孰是孰非,一切都交给实践来证明吧!下面是我根据中提供的算法改写的一段C#代码:
bool ContainsPoint2(Vector2[] polyPoints,Vector2 p)
//统计射线和多边形交叉次数
int cn = 0
//遍历多边形顶点数组中的每条边
for(int i=0
//正常情况下这一步骤可以忽略这里是为了统一坐标系
polyPoints [i].x += transform.GetComponent&RectTransform& ().position.x
polyPoints [i].y += transform.GetComponent&RectTransform& ().position.y
//从当前位置发射向上向下两条射线
if(((polyPoints [i].y &= p.y) && (polyPoints [i + 1].y & p.y))
|| ((polyPoints [i].y & p.y) && (polyPoints [i + 1].y &= p.y)))
//compute the actual edge-ray intersect x-coordinate
float vt = (float)(p.y - polyPoints [i].y) / (polyPoints [i + 1].y - polyPoints [i].y)
//p.x & intersect
if(p.x & polyPoints [i].x + vt * (polyPoints [i + 1].x - polyPoints [i].x))
//实际测试发现cn为0的情况即为宣雨松算法中存在的问题
//所以在这里进行屏蔽直接返回false这样就可以让透明区域不再响应
if(cn == 0)
return false
//返回true表示在多边形外部否则表示在多边形内部
return cn % 2 == 0
这段代码说实话我理解的不是很透彻,而且令人费解的是实际结论和算法结论完全相反,因为按照我现在这样的设计,当cn为偶数时返回为true,此时应该表示该点再多边形外部啊,可是事实上我测试这段代码的时候,它居然是可以正常工作的,即当该方法返回true的时候我的点击确实是在多边形内部,所以这是一段可以正常工作同时让我感到费解的代码,而且当我屏蔽了cn为0的这种情况以后,现在它已经可以完美的工作了
同样的,我们这里使用一张正五边形的精灵图片,然后编写下面的代码:
using UnityE
using System.C
using UnityEngine.UI;
using UnityEngine.EventS
public class UnregularButtonWithCollider : MonoBehaviour,IPointerClickHandler
多边形碰撞器
PolygonCollider2D polygonC
void Start()
polygonCollider = transform.GetComponent&PolygonCollider2D&();
public void OnPointerClick(PointerEventData eventData)
local.x = eventData.position.x - (float)Screen.width / 2.0f;
local.y = eventData.position.y - (float)Screen.height / 2.0f;
if(ContainsPoint(polygonCollider.points,local))
Debug.Log ("这是一个正五边形!");
判断指定点是否在给定的任意多边形内
bool ContainsPoint(Vector2[] polyPoints,Vector2 p)
int cn = 0;
for(int i=0; i&polyPoints.Length-1; i++)
polyPoints [i].x += transform.GetComponent&RectTransform& ().position.x;
polyPoints [i].y += transform.GetComponent&RectTransform& ().position.y;
if(((polyPoints [i].y &= p.y) && (polyPoints [i + 1].y & p.y))
|| ((polyPoints [i].y & p.y) && (polyPoints [i + 1].y &= p.y)))
float vt = (float)(p.y - polyPoints [i].y) / (polyPoints [i + 1].y - polyPoints [i].y);
if(p.x & polyPoints [i].x + vt * (polyPoints [i + 1].x - polyPoints [i].x))
if(cn == 0)
return false;
return cn % 2 == 0;
我们可以发现现在它可以正常工作啦!我们必须意识到的一点是,这个方法的空间复杂度为O(n-1),所以随着多边形顶点数目的增加,这个方法的执行效率会越来越低,如果对不规则精灵的边界没有十分苛刻的要求的话,我的建议是我们使用多边形碰撞器标记出一个相对模糊的边界即可,因为现在我们这个方法主要依靠数学计算,没有涉及到摄像机相关计算,所以宣雨松中有朋友指出他的方法仅仅适用于Canvas的模式为Screen-Space Camera这种情况,而我目前这个方法对除了World Space以外都是可以使用的,我最大的疑虑来自对鼠标位置进行转化的时候是否应该使用Screen.width和Screen.height,因为我担心可能会出现屏幕适配这种需求。
精灵像素检测
精灵像素检测这个方案的灵感来自Image组件,我们在MonoDevelop或者Visual Studio中通过”转到定义”这个功能可以获得Image组件的内部细节。我们发现uGUI在处理控件是否被点击的时候,主要是根据IsRaycastLocationValid这个方法的返回值来进行判断的,而这个方法用到的基本原理则是判断指定点对应像素的RGBA数值中的Alpha是否大于某个指定临界值。例如,我们知道半透明通常是指Alpha=0.5,而对一个.png格式的图片来说半透明甚至完全透明的区域理论上不应该被响应的,所以根据这个原理我们只需要设定一个透明度的临界值然后对当前鼠标位置对应的像素进行判断就可以了,因此这种方法叫做精灵像素检测。
下面我们来一起看这段uGUI的代码,这段代码通过MonoDevelop或者Visual Studio的”转到定义”功能可以找到,这里我做了简单的注释帮助大家理解代码:
public virtual bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
//当透明度&=1.0时,表示点击在可响应区域返回true
if(this.m_EventAlphaThreshold &= 1f){
return true
//当没有指定精灵时为什么要返回true?
Sprite overrideSprite = this.overrideSprite
if(overrideSprite == null){
return true
//坐标系转换
Vector2 local
RectTransformUtility.ScreenPointToLocalPointInRectangle(base.rectTransform, screenPoint, eventCamera, ref local)
Rect pixelAdjustedRect = base.GetPixelAdjustedRect ()
local.x += base.rectTransform.get_pivot ().x * pixelAdjustedRect.get_width ()
local.y += base.rectTransform.get_pivot ().y * pixelAdjustedRect.get_height ()
local = this.MapCoordinate(local, pixelAdjustedRect)
Rect textureRect = overrideSprite.get_textureRect ()
Vector2 vector = new Vector2(local.x / textureRect.get_width (), local.y / textureRect.get_height ())
//计算屏幕坐标对应的UV坐标
float num = Mathf.Lerp(textureRect.get_x (), textureRect.get_xMax (), vector.x) / (float)overrideSprite.get_texture().get_width()
float num2 = Mathf.Lerp(textureRect.get_y (), textureRect.get_yMax (), vector.y) / (float)overrideSprite.get_texture().get_height()
bool result
//核心方法:像素检测
result = (overrideSprite.get_texture().GetPixelBilinear(num, num2).a &= this.m_EventAlphaThreshold)
}catch(UnityException ex){
Debug.LogError("Using clickAlphaThreshold lower than 1 on Image whose sprite texture cannot be read. " + ex.Message + " Also make sure to disable sprite packing for this sprite.", this)
result = true
//返回结果
return result
从这段代码中我们可以看出,这个方法核心在第31行代码,即传入一个UV坐标返回一个RGBA数值并将其和临界值相比较。可是在此之前,我们看到在引入uGUI及其专属组件RectTransform以后,现在Unity中的坐标系转换变得更加复杂了,我个人看到这部分代码是相当凌乱的,或许我应该找时间补习下矩阵变换了吧。所以现在我们就有思路啦,我们有两种方式,第一种基于这个思路重新定制一个Image组件;第二种直接修改Image组件的eventAlphaThreshold属性。考虑到坐标系转换这里非常复杂,显然第二种方式更容易接受,为什么这里可以直接修改eventAlphaThreshold属性呢,因为它在Image组件内部和代码中的m_EventAlphaThreshold相关联,这就是这篇的完整解释啦!
好了,现在我们来一个简单的测试,我们这里准备一张圆形的精灵图片(如上图),然后编写下面的代码:
using UnityE
using System.C
using UnityEngine.UI;
using UnityEngine.EventS
public class UnregularButtonWithPixel : MonoBehaviour,IPointerClickHandler
透明度临界值
[Range(0.0f,0.5f)]
public float A
public void Start()
image = transform.GetComponent&Image&();
image.eventAlphaThreshold = A
public void OnPointerClick(PointerEventData eventData)
Debug.Log("这是一个圆形!");
这里我为了让大家在学(复)习(制)的时候更容易理解,我在Click事件的响应上,使用的是实现IPointerClickHandler接口这种方法,希望通过动态绑定这种方式添加事件响应的可以自己解决,我是不会为了满足你们的好(懒)奇(惰)而奉献出我的EventTriggerListener的代码的。好了,现在我们要做的就是为需要响应点击的不规则精灵附加该脚本,这样就可以解决不规则精灵响应的问题了。这种方法使用起来非常简单,需要注意的是:图片的类型必须是Advance且保证可读可写。因为我们在脚本中访问了像素,而简单伴随着的代价就是我们无法使用图集、该图片在内存中会复制一份,所以在项目性能上允许的情况下这种方法还是可以考虑使用的。
本文通过对网络上两种比较通用的不规则按钮制作方案进行对比和研究,解决了基于多边形碰撞器实现不规则按钮这个过程中存在的问题,剖析了基于精灵像素检测实现不规则按钮 这个过程的内部原理,从易用性角度来讲,后者要优于前者,而这种方法的缺陷主要来自于它对图片类型的限制以及允许像素可读写这两个方面,它必须是Advance类型,所以普通的Texture或者Sprite拥有的特性在这里它都无法享受,比如我们无法为其做颜色渐变这类Tween动画、无法使用精灵特有的图集特性等等,于此同时它必须允许像素可读写,因此在实际使用中它会在内存中复制一份,在执行效率上可能会受到影响。而从技术性角度来讲,我个人更推推崇前者,因为在这个过程中我们学到了新的知识,明白了如何利用一个算法来解决实际的问题,而且它不会限制我们对精灵的使用,所有精灵拥有的特性在这里我们都可以使用,无非是在寻找算法、解决问题的过程中我们耗费了大量精力,可是这是值得的啊,不是吗?这就是我们做这件事情的意义所在。从昨天开始研究这两个问题到今天写完整篇文章,整个人是非常疲惫的,欢迎大家继续关注我的博客,今天的内容就是这样啦,谢谢大家!
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1199223次
积分:13939
积分:13939
排名:第887名
原创:196篇
评论:1294条
人生到处知何似?应似飞鸿踏雪泥。
(1)(2)(3)(1)(3)(2)(5)(1)(4)(2)(5)(4)(1)(5)(4)(2)(1)(5)(4)(1)(7)(13)(1)(2)(1)(9)(8)(2)(5)(9)(23)(8)(13)(18)(7)(1)(1)(2)(1)(1)(1)(2)(1)(3)(1)Unity × EasyAR 实战教程-第5章:使用ImageTarget和“脱卡” - 简书
Unity × EasyAR 实战教程-第5章:使用ImageTarget和“脱卡”
本章要点:
使用ImageTarget预制件,调用3D模型(unitychan)
解决与ImageTarget预制件相关的“脱卡”问题
1. 使用ImageTarget预制件
ImageTarget预制件的加入,实际上等于是给现有的App添加了新的功能,所以,我们将会在一个新的场景中来实现这个功能。
1.1. 新建场景
新建场景的具体方法,请参考上一章的内容:。本章的要点是:
新建场景,命名为“02”
将场景“02”拖拽到Hierarchy面板中,Unload场景“00”和“01”
调整至Game视图
新建一个场景“02”并打开
1.2. 向场景中添加EasyAR的预制件
具体的添加方法,请参考第一章的内容:。本章的要点如下:
填写License Key,直接使用场景“01”中的Key即可
删除场景“02”中自带的Main Camera
将ImageTarget预制件添加到场景中(本章的主角)
向场景中添加EasyAR的预制件
1.3. 替换ImageTarget预制件自带的脚本
在Hierarchy面板中选择ImageTarget预制件,我们就可以在Inspector面板中看到,ImageTarget预制件自身携带了一个脚本:ImageTargetBehaviour(但是,这段脚本没有实际功能),这就是我们需要替换掉的脚本。用来替换的脚本是:EasyImageTargetBehaviour,这个脚本可以在自带的HelloAR演示场景中找到。为了方便起见,我在这里引用了EasyImageTargetBehaviour脚本的全部内容:
using UnityE
using UnityEngine.SceneM
namespace EasyAR
public class EasyImageTargetBehaviour : ImageTargetBehaviour
protected override void Awake()
base.Awake();
TargetFound += OnTargetF
TargetLost += OnTargetL
TargetLoad += OnTargetL
TargetUnload += OnTargetU
protected override void Start()
base.Start();
HideObjects(transform);
void HideObjects(Transform trans)
for (int i = 0; i & trans.childC ++i)
HideObjects(trans.GetChild(i));
if (transform != trans)
gameObject.SetActive (false);
void ShowObjects(Transform trans)
for (int i = 0; i & trans.childC ++i)
ShowObjects(trans.GetChild(i));
if (transform != trans)
gameObject.SetActive(true);
void OnTargetFound(ImageTargetBaseBehaviour behaviour)
ShowObjects(transform);
Debug.Log("Found: " + Target.Id);
void OnTargetLost(ImageTargetBaseBehaviour behaviour)
HideObjects(transform);
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);
上述EasyImageTargetBehaviour脚本实现的功能是:初始化阶段隐藏所有跟ImageTarget相关的模型,如果EasyAR识别到ImageTarget,则显示相关的模型,如果EasyAR没有识别到ImageTarget,则不显示相关的模型:
替换ImageTarget预制件自带的脚本
1.4. 导入ImageTarget所使用的图片
接下来我们需要向工程中导入一张图片,主要用途是作为ImageTarget的识别图。我选择的是下面这张图片,大家也可以选取自己喜欢的图片。但是,所选图片要尽量拥有复杂的图形和丰富的色彩,否则,EasyAR的SDK可能无法识别:
ImageTarget-UnityChan.jpg
然后,我们要在Assets文件夹下新建一个“StreamingAssets”文件夹,拼写和字母的大小写一定不能错。
关于该文件夹的详细说明,可以参考Unity官方手册:或者“Unity圣典”(中文):
我们把图片导入到两个不同的路径下,一个是之前的章节中也在使用的路径:Assets -& UnityChanAR -& Textures;另一个是本章中新出现的路径:Assets -& StreamingAssets:
导入至常规的Textures文件夹
导入至新建的StreamingAssets文件夹
1.5. 使用导入的图片
接下来我们分别来看看这两张图片怎么使用。首先,我们会把存放在Textures文件夹下的图片,添加到一个新建的材质(Material)上,稍后再将材质添加到ImageTarget预制件上。具体方法如下:
在Assets -& UnityChanAR文件夹中新建“Materials”文件夹
在该文件夹的空白处点击右键,选择Create -& Material
重命名材质,一般跟图片的名称一致即可
设置材质的Shader,选择Mobile -& Diffuse
点击Select,选取Texture文件夹中的图片
创建新的材质
接着,我们会使用存放在StreamingAssets文件夹中的图片,而且还要用到上一步骤中做好的材质(Material)。操作方法就是,先选中Hierarchy面板中的ImageTarget预制件,然后参考下图来设置它的Inspector面板:
使用StreamingAssets文件夹里的图片
完成以上两步操作之后,我们切换到Scene视图,应该可以看到如下画面:
场景中显示了ImageTarget
在本小节中,希望大家注意:如何设置EasyImageTargetBehaviour中的Path和Storage,不同的“Storage”对应了不同的存储路径,具体的解释请参考EasyAR官方文档中的说明:。
1.6. 添加模型——再次使用unitychan预制件
我们先找到unitychan预制件,然后拖拽到Hierarchy面板中,使其成为ImageTarget预制件的子对象。在EasyAR中,ImageTarget和3D模型被看作一个整体,当ImageTarget被识别之后,这个整体(包括子对象)就会被“激活”。添加好模型之后,我们可以切换到Game视图中查看模型的显示情况,如图:
添加unitychan预制件
在Game视图下可以看到,UnityChan现在没有面对我们,大家可以根据自己的想法设置UnityChan的位置和角度。我希望UnityChan面对着自己,所以对UnityChan的Transform组件进行了如下调整:
UnityChan的Transform组件
1.7. 布置UGUI界面
布置UGUI界面,并且实现各个按钮功能的具体方法,请参考:。注意:除了现在的场景外,我们还需要对场景“00”(主菜单)进行修改。完成后的效果如下:
场景“02”的UGUI
场景“00”的UGUI
1.8. 使用新的Animator(UnityChanActionCheck)
这里的操作很简单(如下图),但是,大家一定要自己体会:为什么可以这么简单地就实现?Animator中的各个控制条件是怎样的?为什么现有的脚本不用修改就可以使用?同时也可以参考:。
设置Animator组件中的Controller
如果顺利地完成了前面8个小节的工作,那我们现在就可以单独测试场景“02”了:
单独测试场景“02”
2. 实现“脱卡”功能
在单独测试场景“02”的时候,我们会发现一个现象:一旦摄像头捕捉不到ImageTarget的图片时,UnityChan的模型也会随之消失,问题是,我们还没有看到UnityChan所有的动作呢!
这个问题怎么破?其实答案就藏在第一章:。接下来,我们就从第一章的内容开始延伸,从而解决这个问题,实现AR应用中的“脱卡”。
首先,将ImageTarget下的unitychan预制件复制,然后粘体到Augmenter下,并重命名为“ unitychan-Augmenter”。复制粘贴是最好的选择,因为我们需要unitychan-Augmenter和ImageTarget下的unitychan具有相同的各种属性(从Animator到脚本):
unitychan-Augmenter
然后,修改一下unitychan-Augmenter的Transform组件。修改的方针是:尽量让两个UnityChan的模型在Game视图下重合在一起。
unitychan-Augmenter的Transform组件
在Game视图下的两个UnityChan的模型
做到这一步的时候,我们的思路应该逐渐清晰了:“脱卡(显示模型)”,其实就是一个控制“unitychan-Augmenter”什么时候显示的问题。场景中的主角是ImageTarget下的unitychan模型,当主角没有出场的时候,我们只要控制好替身——“unitychan-Augmenter”的出场时机就可以了。
关于“脱卡”功能的介绍,大家也可参考下面两篇大神的文章:
拜读上述大神的文章之后,我自己编了如下脚本,比大神们的脚本短一些,刚好够用,这里稍微解释一下:
ModelOfAugmenter变量,用来控制Augmenter下的模型,我们一会儿需要把unitychan-Augmenter赋给这个变量,相当于替身
ImageTarget变量,用来获取场景中的ImageTarget预制件的状态,一会儿需要把场景中的ImageTarget赋给这个变量,相当于一个时钟
HasFound变量,这是唯一的私有变量,我们不能在Unity编辑器内设定,当ImageTarget被识别过一次之后,这个变量就会一直为True,相当于一个时刻,在这个时刻,我们会让替身出现
using UnityE
using System.C
public class NoImageTargetShow : MonoBehaviour {
public GameObject ModelOfA
public GameObject ImageT
private bool HasFound =
// Use this for initialization
void Start () {
ModelOfAugmenter.SetActive (false);
// Update is called once per frame
void Update () {
if (ImageTarget.activeSelf == true) {
HasFound =
ModelOfAugmenter.SetActive (false);
if (ImageTarget.activeSelf == false && HasFound == true) {
ModelOfAugmenter.SetActive (true);
完成以上代码之后,我们需要在场景中加入一个GameObject来承载脚本。我把这个GameObject重命名为“NoImageTargetShow”,以表示它执行NoImageTargetShow脚本:
在场景中加入GameObject
然后,设置NoImageTargetShow对象的Inspector面板:
将NoImageTargetShow脚本添加到该对象上
将场景中的unitychan-Augmenter和ImageTarget对象拖拽到的相应的空白槽里,这样就可以给脚本中的公有变量赋值了
NoImageTargetShow对象的Inspector面板
10. 再次确认UGUI中的按钮
现在,我们已经基本上实现了“脱卡”功能。如果现在试运行程序的话,你就会发现:“脱卡”之后显示的UnityChan不能变换动作了,左右两边的按钮好像怎么点击都没有反应。
所以,再次确认UGUI中的按钮,这就是我们的最后一步。按钮为什么没有反应?去看看按钮的设置就明白了:
需要追加unitychan-Augmenter
按钮调用的游戏对象必须明确
本章是这个系列的最后一章,中间涉及到了第一、二、三章的内容,可以看作一次复习,同时也接触到了:在AR应用中如何使用ImageTarget,以及怎样实现“脱卡”。希望大家动手试一试,在实践的过程中,我们自然而然地就掌握这些技能了。
在下一个系列的教程中,我们会接触到Blender,并且真正地从零基础开始学习(本来这个系列也想做成零基础的,可能还是有点儿难度,抱歉啦各位)。同时,由于我的Blender水平还不够高,无法做到深入浅出地讲解,所以,下一系列的主体是翻译,我自己会补充图片或者说明,敬请期待。
其他章节:}

我要回帖

更多关于 unity list删除元素 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信