【Unity NGUI游戏开发之三】TweenPosition位移动画(二):相对于UIAnchor不同分辨率下的完美适配位移动画

Unity中的UI我们采用的是NGUI,NGUI的界面位移动画,我们一般使用的是TweenPosition。

一种是简单的相对位移,不考虑分辨率适配问题,只需要简单的从位置A到位置B,已经在文中介绍了:

【Unity NGUI游戏开发之二】TweenPosition位移动画(一):不相对于Anchor的位移动画

另外一种是考虑到屏幕分辨率适配的位移动画,我们游戏中大多遇到的是这种情况。

eg.我们想让一个UI从屏幕外沿着屏幕的左边移动到屏幕的中央,TweenPositon播放动画,在960*640下正合适从屏幕外到屏幕内,那么在1136*640、2048*2536等各种分辨率下是否正常还是会穿帮?答案是肯定会穿帮的。

在这里我们对TweenPosition进行了扩展。屏幕的适配方案我们依旧采用NGUI默认的解决方案。

1. Animation and anchors

TweenPosition 组件不依赖锚点。有一个widget控件,使用UIAnchor的功能(新版本3.8, 3.9中UIAnchor已丢弃,已经继承到widget属性【Anchors】中), 使用NGUI的锚点功能把控件定位到屏幕左上角。改变分辨率一切效果正常。

当使用TweenPosition播放动画,从屏幕外移动到屏幕内,TweenPosition需要Vector3的起始位置和终止位置,设置如下:

当改变分辨率或宽高比的时,还按照坐标来设置的话,效果就出错了:

4:3

16:9

通过脚本来根据分辨率计算起始坐标和终止坐标,效果也不理想

NGUI中有一个组件TweenTransform,允许移动一个object从A移动到B,A、B只需要有transform即可。通过A、B对象来记录移动的目标点。

将要移动的对象的UIAnchor(Anchor属性)相对位置去掉,将锚点设置给A、B对象。否则这个这个对象会一直相对于这个锚点对齐,播放动画不会有任何效果。

更改分辨率看效果:

4:3 和 16:9

效果在不同分辨率下都正常了,接下来我们就是实际应用了,工程中有一堆这些效果,如果每一个都这么设置一个A、B对象,无疑是非常繁琐的,在这里将TweenTransform脚本进行扩展,实现一个自动化处理。

创建一个接口脚本 TweenPositonEx.cs,添加到任何想相对于屏幕或父节点做位移动画的控件上。脚本接口简单,只需要A、B两个控件。

/**
 	TransformPostion的扩展:
	 1.可以根据游戏屏幕分辨率播放从A到B动画 eg. UI从屏幕外左边移动到屏幕中英
	 2.TweenTransformExEditor.cs中对编辑器进行定制,实现了功能自动化处理,节省开发时间

	 Added by Teng.
 **/
using UnityEngine;
using System.Collections;

[RequireComponent(typeof(TweenTransform))]
public class TweenTransformEx : MonoBehaviour {
	public GameObject FromAnchor;
	public GameObject ToAnchor;
}

接下来是TweenPositonExEditor.cs,是TweenPositonEx.cs脚本的一个编辑器扩展,内部有些复杂,无需关心内容,也不需要添加到任何脚本行,只需要放到工程中即可。

/**
	对TweenTransformEx.cs类的属性的编辑器定制,实现了功能自动化处理,节省开发时间

	Added by Teng.
**/
using UnityEngine;
using UnityEditor;
using System.Collections;

[CustomEditor(typeof (TweenTransformEx))]
public class TweenTransformHelperEditor : Editor {

	private TweenTransformEx _tweener;

	private void Awake() {
		_tweener = (TweenTransformEx) target;
	}

	public override void OnInspectorGUI() {
		EditorGUILayout.BeginHorizontal();
		if (GUILayout.Button("Create from anchor")) {
			CreateAnchorFrom();
		}
		if (GUILayout.Button("Destroy")) {
			DestroyAnchor(_tweener.FromAnchor);
		}
		EditorGUILayout.EndHorizontal();
		_tweener.FromAnchor = (GameObject) EditorGUILayout.ObjectField(_tweener.FromAnchor, typeof (GameObject));

		EditorGUILayout.BeginHorizontal();
		if (GUILayout.Button("Create to anchor")) {
			CreateAnchorTo();
		}
		if (GUILayout.Button("Destroy")) {
			DestroyAnchor(_tweener.ToAnchor);
		}
		EditorGUILayout.EndHorizontal();
		_tweener.ToAnchor = (GameObject) EditorGUILayout.ObjectField(_tweener.ToAnchor, typeof (GameObject));

		CreateAndApplyTweener();
		UpdateUI();
	}

	private void CreateAndApplyTweener() {
		bool toAnchorNotEqualsNull = _tweener.ToAnchor;
		bool fromAnchorNotEqualsNull = _tweener.FromAnchor;

		if (!fromAnchorNotEqualsNull) {
			EditorGUILayout.HelpBox("From anchor not created!", MessageType.Warning);
		}
		else if (!toAnchorNotEqualsNull) {
			EditorGUILayout.HelpBox("To anchor not created!", MessageType.Warning);
		}
		else {
			if (GUILayout.Button("Apply to tween")) {
				var tweenComponent = _tweener.GetComponent<TweenTransform>() ?? _tweener.gameObject.AddComponent<TweenTransform>();
				tweenComponent.from = _tweener.FromAnchor.transform;
				tweenComponent.to = _tweener.ToAnchor.transform;
				tweenComponent.enabled = false;
			}
		}
	}

	private void UpdateUI() {
		if (GUI.changed) {
			EditorUtility.SetDirty(_tweener);
		}
	}

	private void DestroyAnchor(GameObject gameObj) {
		if (gameObj == null) {
			return;
		}
		DestroyImmediate(gameObj);
	}

	private void CreateAnchorTo() {
		var anchor = CreateAnchor("$anchorTo");
		_tweener.ToAnchor = anchor;
	}

	private void CreateAnchorFrom() {
		var anchor = CreateAnchor("$anchorFrom");
		_tweener.FromAnchor = anchor;
	}

	private GameObject CreateAnchor(string anchorName) {
		var anchorGameObj = new GameObject(anchorName);
		anchorGameObj.transform.parent = _tweener.transform;
		anchorGameObj.transform.localPosition = Vector3.zero;
		anchorGameObj.transform.localScale = Vector3.one;

		var widgetScript = anchorGameObj.AddComponent<UIWidget>();
		widgetScript.width = widgetScript.height = 100;
		return anchorGameObj;
	}
}

添加脚本到widget上,如下:

点击要播放动画的widget, 创建anchorTo和anchorFrom,点击【Apply to tween】,接下来只需要相对的调节anchorTo和anchorFrom的相对位置即可。

需要有以下几点注意:

1.要播放动画的widget控件不能有Anchor,最起码不能有OnUpdate的Anchor,否则会一直相对于Anchor设置位置,播放动画无任何效果,只需要调整A、B的Anchor即可。

2.要播放动画的widget控件在屏幕外的初始位置,可以设置OnStart的Anchor(仅执行一次),来确保肯定在屏幕外。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-10 15:37:24

【Unity NGUI游戏开发之三】TweenPosition位移动画(二):相对于UIAnchor不同分辨率下的完美适配位移动画的相关文章

【Unity NGUI游戏开发之五】多分辨率下完美分布式协同开发

NGUI多分辨率下完美分布式协同开发:不同分辨率下相对于屏幕坐标的Perfab数据不再丢失 NGUI多分辨率下完美分布式协同开发不同分辨率下相对于屏幕坐标的Perfab数据不再丢失 开发问题 原因分析 案例 完美过程 案例分析 实现过程 开发问题: NGUI分布式开发中,用git管理资源,团队成员每人负责一个perfab,所有现对于屏幕大小的相对位置的perfab因为引用了perfab外的数据,导致perfab的Anchor锚点数据丢失,最后的perfab集成后,必须重新设置,导致开发成本大幅度

【Unity NGUI游戏开发之二】TweenPosition位移动画(一):不相对于Anchor的位移动画

下面学些下NGUI的TweenPosition位移动画,一般的位移动画需求分为不想对于Anchor锚点的位移动画和相对于Anchor的位移动画(主要看是否考虑屏幕分辨率),下面介绍两种游戏中最常用的不相对于Anchor的TweenPosition用法: 用法1.NGUI的控件从PosA位置移动到PosB位置,播放动画 用法2.在游戏中需要动态创建带有TweenPosition组件动画的对象,对象创建.移动.到达指定位置.销毁的过程.eg.游戏中玩家吃金币,迟到金币后转换为分数,分数播放一个Twe

【Unity NGUI游戏开发之六】游戏背景采用UV纹理动画

开发背景 游戏中一些背景能采用UV动画,效果更佳.eg.星空.墙壁 因为gif的原因有卡顿,起始播放纹理动画的时候是不会有卡顿的. Unity的NGUI采用纹理动画 NGUI的UITexture允许使用一张纹理 有了这个,我们便可以扩展一个脚本来影响[UV Rect]参数了 /** 基于NGUI的UITexture的纹理动画 1.图片首尾相接的UITexture,可以播放UV纹理动画 2.可以根据定制UV动画方向.速度 3.图片属性: [Texture Type]:Texture [Wrap M

【Unity NGUI游戏开发之四】NGUI的DrawCall数量

看了很多关于NGUI drawCall的文章,见得比较多的一个观点是:一个 Atlas 对应一个Drawcall. 但其实NGUI内部有自己的一套对DrawCall的处理规则.相关的规则有: 1.Atlas图集数量有关 2.Atlas图集的调用顺序(绘制顺序)有关 3.和UIPanel的数量有关 一.减少NGUI 3的DrawCall数量 升级到NGUI3, DrawCall数由5个增长到了十七八个,想想应该不会是NGUI的问题吧.后来整理了一下,发现有两点: 1)对于同一Atlas,DrawC

?Unity 2D游戏开发教程之2D游戏的运行效果

Unity 2D游戏开发教程之2D游戏的运行效果 2D游戏的运行效果 本章前前后后使用了很多节的篇幅,到底实现了怎样的一个游戏运行效果呢?或者说,游戏中的精灵会不会如我们所想的那样运行呢?关于这些疑问,会在本节集中揭晓. (1)单击Unity上方,工具栏里的播放按钮,开始运行当前的游戏,默认精灵当前进入的是Idle动画状态,如图1-34所示. 图1-34  Idle状态 (2)当读者按下键盘上的左.右方向键,或者A.D键的时候,精灵会进入Walking动画状态,并且会向左或者向右移动,如图1-3

Unity 2D游戏开发教程之精灵的死亡和重生

Unity 2D游戏开发教程之精灵的死亡和重生 精灵的死亡和重生 目前为止,游戏项目里的精灵只有Idle和Walking这两种状态.也就是说,无论精灵在游戏里做什么,它都不会进入其它的状态,如死亡.于是我们发现游戏里的精灵,即使是跳入“万丈深渊”,也依然存活,显然这种游戏逻辑无法让人接受.因此,本节就来说明为精灵添加死亡和重生这两种状态的方法,并使用脚本实现这两种状态的逻辑.具体的实现步骤如下: (1)在Hierarchy视图里,新建一个Empty对象,并命名为Death Trigger,设置其

Unity 2D游戏开发教程之游戏中精灵的跳跃状态

Unity 2D游戏开发教程之游戏中精灵的跳跃状态 精灵的跳跃状态 为了让游戏中的精灵有更大的活动范围,上一节为游戏场景添加了多个地面,于是精灵可以从高的地面移动到低的地面处,如图2-14所示.但是却无法从低的地面移动到高的地面,因为当前的游戏精灵只能左右移动,即left和right.为了解决这个问题,本节就来为精灵添加跳跃状态.   图2-14  精灵从一个地面移动到另一个地面 (1)如果要为精灵添加跳跃状态,即jump,就不得不再引入其它状态: q   landing:用于表示精灵接触到地面

Unity 2D游戏开发教程之使用脚本实现游戏逻辑

Unity 2D游戏开发教程之使用脚本实现游戏逻辑 使用脚本实现游戏逻辑 通过上一节的操作,我们不仅创建了精灵的动画,还设置了动画的过渡条件,最终使得精灵得以按照我们的意愿,进入我们所指定的动画状态.但是这其中还有一些问题.例如,我们无法使用键盘控制精灵当前要进入的动画状态,而且精灵也只是在原地播放动画而已.但我们希望精灵在进入到PlayerWalkingAnimation状态时,位置应该发生改变. 要解决这些问题,就需要编写脚本.也就是说,要使用脚本来实现动画的播放控制,以及其它一些游戏的逻辑

Unity 2D游戏开发教程之摄像头追踪功能

Unity 2D游戏开发教程之摄像头追踪功能 上一章,我们创建了一个简单的2D游戏.此游戏中的精灵有3个状态:idle.left和right.这看起来确实很酷!但是仅有的3个状态却限制了精灵的能力,以及游戏逻辑的想象空间.看来有必要让精灵拥有更多的状态,而这就是本章要讲解的主要内容. 摄像头追踪功能 游戏里的精灵可以在游戏场景中任意移动,这没什么问题,可是这就导致了一个问题,就是精灵可能移动到我们的视野之外,或者说游戏视图之外.为了解决这个问题,很多游戏都采用了"摄像头追踪"的方法,使