FairyGUI学习
分辨率自适应
知识点一 FairyGUI中的分辨率自适应
FairyGUI为手游开发提供了自动适应各种设备分辨率的UI适配策略
这意味着开发者只需要制作一套UI,就可以适配所有分辨率的设备
知识点二 分辨率自适应具体步骤
1.制作UI时使用一套设计分辨率(以最大的全屏UI界面的大小为标准)
比如:1136*640、1280*720、1920*1080等等如何确定设计分辨率
设计分辨率的确定主要按宽高比例设计 一般考虑市面上主流设备的分辨率比例
比如 16:9、19.5:9等等 目前iphone主流机型分辨率比例为 19.5:9(2436*1125) 建议采用该比例设计(选择较大比值)
采用该比例不是说就用2436*1125来做,因为分辨率越大,意味着图片越大,那么在使用时所占的内存就大
你可以根据自己游戏的实际情况来进行设计 比如 1950*900 也是19.5:9 但是分辨率明显就小了 内存也就小了
当这个UI真正在2436*1125的屏幕上显示时,FairyGUI会自动帮助我们把这个图片进行放大处理
只要差距不是太大,呈现出的画面是完全可以接受的2.在Unity中通过代码或者UIContentScaler组件设置 全局缩放方式和设计分辨率
适配模式
2-1 MatchWidthOrHeight 取宽和高比例较小的进行缩放
例如,设计分辨率是960x640,设备分辨率是1280×720
那么可以得到宽边的比例是1280/960=1.33,高边的比例是720/640=1.125
最后取较小的1.125作为全局缩放系数
这种缩放方式保证内容缩放后始终在屏幕内,可能会留边,但不会超出屏幕看不到
2-2 MatchWidth 固定取宽的比例进行缩放。高边可能会超出屏幕(竖屏游戏建议使用)
2-3 MatchHeight 固定取高的比例进行缩放。宽边可能会超出屏幕(横屏游戏建议使用)
GRoot.inst.SetContentScaleFactor(1365, 768, UIContentScaler.ScreenMatchMode.MatchHeight);
3.在FGUI中拼凑UI时,设定每个元件的关联系统对齐方式
4.对于全屏UI来说,通过代码将组件大小填满屏幕
如果屏幕大小会发生变化,需要给组件加上关联设置
UIPackage.AddPackage("UI/Teach");
理论上应该加载依赖包 但是我们知道它没有 我就省略了这的代码
TeachBinder.BindAll();RelationPanel panel = RelationPanel.CreateInstance();
把我们组件的尺寸设置为何我们屏幕的逻辑分辨率一致
panel.MakeFullScreen();GRoot.inst.AddChild(panel);
我们将面板组件和容器进行了宽高关联 容器变化 那么组件也会变化
我们需要在把它添加到了Groot之中之后 再去设置它
panel.AddRelation(GRoot.inst, RelationType.Size);
坐标系统
知识点一 坐标原点
FairyGUI是以屏幕左上角为原点的
/Unity的屏幕坐标是以左下角为原点如果想转化屏幕坐标和UI坐标
Vector3 screenPos = Input.mousePosition;screenPos.y = Screen.height - screenPos.y;
知识点二 UI对象和屏幕 坐标转换
TeachPanel panel =UIManager.Instance.ShowPanel<TeachPanel>("Teach");1.GObject里的x/y/position值都是局部坐标,也就是相对于父元件的偏移。
print(panel.m_graphTest.x);
print(panel.m_graphTest.y);
print(panel.m_graphTest.position);GObject obj = panel.m_svTest.GetChild("n1");
print(obj.x);
print(obj.y);
print(obj.position);2.获取任何一个元件在屏幕上的坐标 FairyGUI坐标系下(左上角原点)
Vector2 pos = obj.LocalToGlobal(Vector2.zero);
print("相对于FGUI屏幕坐标系的物理坐标" + pos);3.获取屏幕坐标在UI元件上的局部坐标
pos = obj.GlobalToLocal(pos);
print("通过FGUI坐标系下的屏幕坐标 转为 相对于某个对象内部的局部坐标" + pos);如果UI适配导致全局缩放,那么逻辑屏幕大小和物理屏幕大小不一致
逻辑屏幕坐标就是GRoot里的坐标
编辑器里定义和代码中处理的 指的都是逻辑屏幕坐标(UI坐标)4.物理屏幕坐标转换为逻辑屏幕坐标
print( "把物理坐标 转为 逻辑坐标" + GRoot.inst.GlobalToLocal(pos) );5.UI元件坐标与逻辑屏幕坐标之间的转换
pos = obj.LocalToRoot(Vector2.zero, GRoot.inst);
print("对象的相对屏幕的逻辑坐标" + pos);
pos = obj.RootToLocal(pos, GRoot.inst);
print("对象的相对屏幕的逻辑坐标 再转回 相对自己的坐标" + pos);6.A中相对坐标(10,10)相对B的位置
pos = obj.TransformPoint(Vector2.zero, panel.m_imgTest);
print("A中相对位置(0,0)相对B的位置" + pos);知识点三 世界坐标转换
1.世界坐标转UI坐标
世界——>屏幕——>UI
screenPos = Camera.main.WorldToScreenPoint(this.transform.position);
//Unity屏幕坐标 转 FairyGUI屏幕坐标(物理屏幕坐标)
screenPos.y = Screen.height - screenPos.y;
//转UI坐标
pos = GRoot.inst.GlobalToLocal(screenPos);
print("把世界坐标 转换成了 UI坐标" + pos);//2.UI坐标转世界坐标
pos = GRoot.inst.LocalToGlobal(pos);
//FairyGUI屏幕坐标 转 Unity屏幕坐标
pos.y = Screen.height - pos.y;
//一般情况下,还需要提供距离摄像机视野正前方distance长度的参数作为screenPos.z(如果需要,将screenPos改为Vector3类型)
Vector3 worldPos = Camera.main.ScreenToWorldPoint(pos);
#endregion
}// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
print("鼠标当前位置" + Input.mousePosition);
}
}
输入处理
知识点一 判断是否点在了UI上
TeachPanel panel = UIManager.Instance.ShowPanel<TeachPanel>("Teach");
注意:全屏界面,没有设置穿透时,空白区域也是接受输入的,只要是ture 那就一定是有UI遮挡
鼠标不管是点击还是悬浮 只要在UI上 就会返回trueif(Stage.isTouchOnUI){可以通过这种方式 得到鼠标或者手指指向的UI对象print(GRoot.inst.touchTarget.name);}else{}
知识点二 鼠标/触摸相关事件
1.鼠标(左中右键)/手指 按下
panel.m_btnTest.onTouchBegin.Add((data) =>{print("按下");如果想要获取鼠标哪个键按下0左键 1右键 2中键print(data.inputEvent.button);});
2.鼠标/手指 移动
移动 一定是先按下过后 才会响应的panel.m_btnTest.onTouchMove.Add((eventData) =>{print("移动");我们也可以通过参数中的变量获取到坐标这个坐标也是物理坐标 需要进行转换 才能变成我们的UI坐标print(eventData.inputEvent.x + "_" + eventData.inputEvent.y);});
3.鼠标/手指 抬起
panel.m_btnTest.onTouchEnd.Add(() =>{print("抬起");});
4.点击
点击事件 一定是按下后 没有太多的鼠标或手指位移 松手才会响应点击panel.m_btnTest.onClick.Add(() =>{print("点击");});
5.鼠标右键点击
panel.m_btnTest.onRightClick.Add(() =>{print("右键点击");});
6.鼠标或手指进入元件
panel.m_btnTest.onRollOver.Add(InObject);
7.鼠标或手指离开元件
panel.m_btnTest.onRollOut.Add((eventData) =>{print("鼠标离开对象" + (eventData.sender as GObject).name);});
知识点三 获取当前鼠标或者手指的位置鼠标位置或者最后一个手指的位置
Stage.inst.touchPosition;
获取指定手指位置,参数是手指ID 从0开始Stage.inst.GetTouchPosition(0);
得到的都是物理坐标
如果想要使用UI坐标需要进行一次转换GRoot.inst.GlobalToLocal
知识点四 多点触摸
当前按下手指数量int num = Stage.inst.touchCount;获得当前所有按下的手指idint[] touchIDs = Stage.inst.GetAllTouch(null);关闭多点触控Input.multiTouchEnabled = false;
知识点五 手势
长按LongPressGesture gesture = new LongPressGesture(panel.m_btnTest);gesture.onAction.Add(() =>{print("长按");});手指滑动 SwipeGestureSwipeGesture gesture2 = new SwipeGesture(panel.m_btnTest);gesture2.onAction.Add(() =>{print("手指滑动");});两指缩放 PinchGesturePinchGesture gesture3 = new PinchGesture(panel.m_btnTest);gesture3.onAction.Add(() =>{print("两指缩放");});两指旋转 RotationGestureRotationGesture gesture4 = new RotationGesture(panel.m_btnTest);gesture4.onAction.Add(() =>{print("两指旋转");});
知识点六 键盘输入
Stage.inst.onKeyDown.Add((data) =>{print(data.inputEvent.keyCode);});#endregion}private void InObject(EventContext eventData ){print("进入了某一个对象" + (eventData.sender as GObject).name);}// Update is called once per framevoid Update(){注意:全屏界面,没有设置穿透时,空白区域也是接受输入的,只要是ture 那就一定是有UI遮挡if (Stage.isTouchOnUI){print("鼠标或者手指在UI上");可以通过这种方式 得到鼠标或者手指指向的UI对象print(GRoot.inst.touchTarget.name);}else{print("鼠标或者手指不在UI上");}print(GRoot.inst.GlobalToLocal(Stage.inst.touchPosition));print(Stage.inst.touchPosition);print(Stage.inst.GetTouchPosition(0));} }
事件机制
知识点一 事件触发时的传递机制
举例说明
GRoot——>面板——>A——>B——>C
当C触发一个事件时,比如TouchBegin
该事件的触发会依次往上向父对象进行传递
C——>B——>A——>面板——>GRoot
它的事件触发的先后顺序也是如此
这种传递过程叫做冒泡传递机制 如果你想打断冒泡传递
可以通过 监听函数中的 EventContext参数中的
StopPropagation方法 进行打断TeachPanel panel = UIManager.Instance.ShowPanel<TeachPanel>("Teach");panel.m_btnTest.onTouchBegin.Add(() =>{print("按钮按下");});panel.onTouchBegin.Add((contextData) =>{print("面板按下");contextData.StopPropagation();});GRoot.inst.onTouchBegin.Add(() =>{print("GRoot按下");});
知识点二 事件监听的两种委托
无参委托 EventCallback0
有参委托 EventCallback1
panel.m_btnTest.onRollOut.Add(OutTest);
panel.m_btnTest.onRollOut.Remove(OutTest);
如果通过那么大表达式或者匿名函数添加 那么只能加没办法指定移除
panel.m_btnTest.onRollOut.Add(() =>
{});
知识点三 有参委托的EventContext参数
sender 获得事件的分发者initiator 获得事件的发起者(DisplayObject原生对象)
type 事件类型
inputEvent 如果事件是键盘/触摸/鼠标事件,通过访问inputEvent对象可以获得这类事件的相关数据
x y 鼠标或手指的位置(物理坐标,需要GlobalToLocal转换)
keyCode 按键代码
mouseWheelDelta 鼠标滚轮滚动值
touchId 当前事件相关的手指ID
isDoubleClick 是否双击data 事件的数据。根据事件不同,可以有不同的含义
StopPropagation 停止冒泡传递
CaptureTouch 在TouchBegin调用该方法,可以让TouchEnd在对象外部也能触发
UncaptureTouch 取消CaptureTouch发起的触摸事件捕获
private void OutTest(EventContext data){}
拖拽功能
知识点一 现有知识实现拖拽
利用OnTouchBegin、OnTouchEnd、OnTouchMove事件可以完成拖拽功能
知识点二 FGUI中的自由拖拽功能
获取对象设置 draggable属性为true 即可拖拽
注意:拖动只能在元件父组件内移动
TeachPanel panel = UIManager.Instance.ShowPanel<TeachPanel>("Teach");panel.m_btnTest.draggable = true;
事件相关
1开始拖panel.m_btnTest.onDragStart.Add(()=> {print("开始拖");});2拖动中panel.m_btnTest.onDragMove.Add(() => {print("拖动中");});3结束拖panel.m_btnTest.onDragEnd.Add(() => {print("结束拖动");});
知识点三 设置拖动范围
注意,范围是舞台上的坐标 不是元件的本地坐标
panel.m_btnTest.dragBounds = new Rect(500, 0, 1000, 200);
插入其他对象
知识点一 UI中插入3D对象或者粒子
1.创建3D对象GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);2.设置3D对象的相对坐标,缩放,旋转(注意:缩放比为100:1)obj.transform.localPosition = Vector3.zero;obj.transform.localScale = Vector3.one * 100;obj.transform.localEulerAngles = Vector3.zero;3.在UI中放置一个空白的图形获取它TeachPanel panel = UIManager.Instance.ShowPanel<TeachPanel>("Teach");4.创建一个GoWrapper对象,放入到图形中GoWrapper wrapper = new GoWrapper(obj);panel.m_pos3D.SetNativeObject(wrapper);
注意:这种方法的缺点是在UI摄像机下3D对象没有透视,如果想要有透视
可以使用RenderTexture的方式
知识点二 点击3D对象或者粒子
GoWrapper默认没有大小,所以不能处理点击事件
如果想要被点击,可以在3D对象显示区域添加一个透明度为0的图形作为点击区域或者一个空组件 通过他们来监听点击
panel.m_click3D.onClick.Add(()=> {print("点击3D对象");});
知识点三 通过UIPanel进行调试
通过上述代码创建3D物体不方便调试
我们可以利用UIPanel 勾选它的Set Native Children Order
然后放置3D物体或者粒子 把数据记录好 然后来改代码当中的数据
知识点四 更新对象状态
GoWrapper会在构造函数里查询你的GameObject里所有的Renderer并保存
//如果你的GameObject后续发生了改变,需要告知GoWrapper重新查询和保存,否则显示不正确
wrapper.CacheRenderers();
知识点五 更换显示的3D对象或者粒子
先把之前的删了 在关联新的
Destroy(obj);obj = GameObject.CreatePrimitive(PrimitiveType.Sphere);obj.transform.localPosition = Vector3.zero;obj.transform.localScale = Vector3.one * 100;obj.transform.localEulerAngles = Vector3.zero;wrapper.wrapTarget = obj;
知识点六 材质的复用
如果你想通过UI上的一些操作改变一个模型的材质
但是这个模型在场景中也使用了,如果在UI上改了场景上的也会被修改
这是因为他们使用的是共享材质
如果你不想UI上对模型的修改影响场景上的模型 可以使用复制材质
wrapper.SetWrapTarget(obj, true);
知识点七 UI中插入Canvas
1.设置Canvas的Render Mode为WorldSpace,Event Camera为Stage Camera。
2.删除Canvas Scaler组件(如果有)。
3.使用GoWrapper包装Canvas