Unity3d-UGUI特效之Image的Skew变形、倾斜效果

60 篇文章 3 订阅
16 篇文章 1 订阅

这次在项目中,做动画时,想要多UI的图片做倾斜动画,比如进场有一定的斜度,然后又变回原来的样子。于是在网上搜索有关Image变形或倾斜的做法,后面找到根据文档发现可以通过继承Image来做一些修改,关键是在Image渲染之后,我们拿到顶点坐标,对坐标做一定的偏移,就可以达到目的了。

先来看看效果:


看着效果还不错,看代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

[DisallowMultipleComponent]
[RequireComponent(typeof(RectTransform))]
[RequireComponent(typeof(CanvasRenderer))]
[AddComponentMenu("UI/UISkewImage (UI)", 99)]
[ExecuteInEditMode]
public class UISkewImage : Image {
	[SerializeField]
	private Vector3 offsetLeftButtom = Vector3.zero;
	public Vector3 OffsetLeftButtom{
		get{return offsetLeftButtom;}
		set{
			offsetLeftButtom = value;
			SetAllDirty();
		}
	}
	[SerializeField]
	private Vector3 offsetRightButtom = Vector3.zero;
	public Vector3 OffsetRightButtom{
		get{return offsetRightButtom;}
		set{
			offsetRightButtom = value;
			SetAllDirty();
		}
	}
	[SerializeField]
	private Vector3 offsetLeftTop = Vector3.zero;
	public Vector3 OffsetLeftTop{
		get{return offsetLeftTop;}
		set{
			offsetLeftTop = value;
			SetAllDirty();
		}
	}
	[SerializeField]
	private Vector3 offsetRightTop = Vector3.zero;
	public Vector3 OffsetRightTop{
		get{return offsetRightTop;}
		set{
			offsetRightTop = value;
			SetAllDirty();
		}
	}
	Vector3 GetOffsetVector(int i){
		if (i == 0){
			return offsetLeftButtom;
		}else if (i == 1){
			return offsetLeftTop;
		}else if (i == 2){
			return offsetRightTop;
		}else {
			return offsetRightButtom;
		}
	}
	protected override void OnPopulateMesh(VertexHelper toFill){
		base.OnPopulateMesh(toFill);
		int count = toFill.currentVertCount;
		for(int i=0;i<count;i++){
			UIVertex vertex = new UIVertex();
			toFill.PopulateUIVertex(ref vertex, i);
			// Debug.Log(i.ToString() + ": " + oldPosition[i].ToString());
			vertex.position += GetOffsetVector(i);
			toFill.SetUIVertex(vertex, i);
		}
	}
}

上面四个Offset分别对应四个顶点,如果要想丰富它,我觉得还是用一个插件好了,哈哈~数学不好,不知道怎么算顶点和三角面。

在上面代码中,虽然参数加了,但是自己定义的属性,没法显示在Inspector上面,因为我们直接继承了Image类,Image有一个ImageEditor的编辑器类,里面并没有把我们的参数显示出来,所以我们还需要写一个类,继承ImageEditor,然后在这个类里面,把我们自己定义的类显示在面板上,看截图:


这样就可以在编辑器里面直接操作了,编辑器类在这里:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
using UnityEditor.UI;

[CustomEditor(typeof(UISkewImage), true)]
[CanEditMultipleObjects]
public class UISkewImageEditor: ImageEditor {
	public override void OnInspectorGUI(){
		base.OnInspectorGUI();

		UISkewImage uiSkewImage = (UISkewImage)target;
		EditorGUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
		if (GUILayout.Button("重置", GUILayout.Width(40))){
			uiSkewImage.OffsetLeftTop = Vector3.zero;
		}
		uiSkewImage.OffsetLeftTop = EditorGUILayout.Vector3Field("左上", uiSkewImage.OffsetLeftTop, GUILayout.ExpandWidth(true));
		EditorGUILayout.EndHorizontal();

		EditorGUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
		if (GUILayout.Button("重置", GUILayout.Width(40))){
			uiSkewImage.OffsetRightTop = Vector3.zero;
		}
		uiSkewImage.OffsetRightTop = EditorGUILayout.Vector3Field("右上", uiSkewImage.OffsetRightTop, GUILayout.ExpandWidth(true));
		EditorGUILayout.EndHorizontal();

		EditorGUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
		if (GUILayout.Button("重置", GUILayout.Width(40))){
			uiSkewImage.OffsetLeftButtom = Vector3.zero;
		}
		uiSkewImage.OffsetLeftButtom = EditorGUILayout.Vector3Field("左下", uiSkewImage.OffsetLeftButtom, GUILayout.ExpandWidth(true));
		EditorGUILayout.EndHorizontal();

		EditorGUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
		if (GUILayout.Button("重置", GUILayout.Width(40))){
			uiSkewImage.OffsetRightButtom = Vector3.zero;
		}
		uiSkewImage.OffsetRightButtom = EditorGUILayout.Vector3Field("右下", uiSkewImage.OffsetRightButtom, GUILayout.ExpandWidth(true));
		EditorGUILayout.EndHorizontal();

		if (GUI.changed){
			EditorUtility.SetDirty(uiSkewImage);
		}
	}
}

到这里,基本上是可以用了,但是为了能够在任何时候都可以直接创建它,就和你在某个UI物体下面右键创建Image一样,我又写了一个编辑器类,可以直接创建SkewImage,这样用起来就方便啦。代码如下:

using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
using UnityEngine.EventSystems;

public class UICustomCreator {
	[MenuItem("GameObject/UI/UICustom/SkewImage", false, 0)]
	static void CreateUICustomSkewImageGuiObject(MenuCommand command){
		Canvas canvas = CreateCanvas();
		GameObject go = CreateUISkewImage(command, canvas);
		CreateEventSystem();
		Selection.activeGameObject = go;
	}
	// Create the UISkewImage Object
	static GameObject CreateUISkewImage(MenuCommand command, Canvas canvas){
		GameObject go = new GameObject("UISkewImage");
		RectTransform goRectTransform = go.AddComponent<RectTransform>();
		Undo.RegisterCreatedObjectUndo((Object)go, "Create " + go.name);
		// Check if object is being create with left or right click
		GameObject contextObject = command.context as GameObject;
		if (contextObject == null) {
			goRectTransform.sizeDelta = new Vector2(100f, 100f);
			GameObjectUtility.SetParentAndAlign(go, canvas.gameObject);
		}else {
			goRectTransform.sizeDelta = new Vector2(100f, 100f);
			GameObjectUtility.SetParentAndAlign(go, contextObject);
		}
		UISkewImage image = go.AddComponent<UISkewImage>();
		image.OffsetLeftTop = Vector3.zero;
		image.OffsetRightTop = Vector3.zero;
		image.OffsetLeftButtom = Vector3.zero;
		image.OffsetRightButtom = Vector3.zero;
		image.raycastTarget = true;
		return go;
	}
	// Check if there is a Canvas in the scene
	static Canvas CreateCanvas(){
		Canvas canvas = Object.FindObjectOfType<Canvas>();
		if (canvas == null)
		{
			// Create new Canvas since none exists in the scene.
			GameObject canvasObject = new GameObject("Canvas");
			canvas = canvasObject.AddComponent<Canvas>();
			canvas.renderMode = RenderMode.ScreenSpaceOverlay;
			canvas.gameObject.AddComponent<CanvasScaler>();
			// Add a Graphic Raycaster Component as well
			canvas.gameObject.AddComponent<GraphicRaycaster>();
			Undo.RegisterCreatedObjectUndo(canvasObject, "Create " + canvasObject.name);
		}
		return canvas;
	}
	// Check if an event system already exists in the scene
	static void CreateEventSystem(){
		if (!Object.FindObjectOfType<EventSystem>())
		{
			GameObject eventObject = new GameObject("EventSystem", typeof(EventSystem));
			eventObject.AddComponent<StandaloneInputModule>();
			#if UNITY_5_3_OR_NEWER
				// Nothing
			#else
				eventObject.AddComponent<TouchInputModule>();
			#endif
			Undo.RegisterCreatedObjectUndo(eventObject, "Create " + eventObject.name);
		}
	}
}

这样就可以咯,注意,Editor类一定要放在Assets/.../Editor/的子目录里面,中间省略是随意哪个目录,但是最终必须是Editor,否则是不会起作用的。

看效果:


看到了么,超级方便!

再来两个截图:



到这里就结束了吗?还没有,我后面做动画时,发现我改变自定义的值,却不能像其他已有参数一样,直接在Animation编辑器中显示这个参数,这就有点头疼了。因为这样没法在编辑器调整数值,还是一个个的手动在Animation面板中添加参数,然后还要在这里调整数值,实在是太麻烦了。于是去Unity官网提过的UI源码看他们源码是怎么做到的,这里有UGUI的源码,可以参考哦:Unity UGUI 源码

通过我对方尝试,终于成功了,只需要改改ImageEditor类就可以了,修改后的类如下:

[CustomEditor(typeof(UISkewImage), true)]
[CanEditMultipleObjects]
public class UISkewImageEditor: ImageEditor {
	SerializedProperty m_OffsetLeftTop;
	SerializedProperty m_OffsetRightTop;
	SerializedProperty m_OffsetLeftButtom;
	SerializedProperty m_OffsetRightButtom;
	GUIContent m_LTContent;
	GUIContent m_RTContent;
	GUIContent m_LBContent;
	GUIContent m_RBContent;
	protected override void OnEnable()
	{
		base.OnEnable();
		m_OffsetLeftTop = serializedObject.FindProperty("offsetLeftTop");
		m_OffsetRightTop = serializedObject.FindProperty("offsetRightTop");
		m_OffsetLeftButtom = serializedObject.FindProperty("offsetLeftButtom");
		m_OffsetRightButtom = serializedObject.FindProperty("offsetRightButtom");
		m_LTContent = new GUIContent("左上");
		m_RTContent = new GUIContent("右上");
		m_LBContent = new GUIContent("左下");
		m_RBContent = new GUIContent("右下");
	}
	public override void OnInspectorGUI(){
		base.OnInspectorGUI();
		serializedObject.Update();
		EditorGUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
		if (GUILayout.Button("重置", GUILayout.Width(40))){
			m_OffsetLeftTop.vector3Value = Vector3.zero;
		}
		EditorGUILayout.PropertyField(m_OffsetLeftTop, m_LTContent);
		EditorGUILayout.EndHorizontal();

		EditorGUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
		if (GUILayout.Button("重置", GUILayout.Width(40))){
			m_OffsetRightTop.vector3Value = Vector3.zero;
		}
		EditorGUILayout.PropertyField(m_OffsetRightTop, m_RTContent);
		EditorGUILayout.EndHorizontal();

		EditorGUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
		if (GUILayout.Button("重置", GUILayout.Width(40))){
			m_OffsetLeftButtom.vector3Value = Vector3.zero;
		}
		EditorGUILayout.PropertyField(m_OffsetLeftButtom, m_LBContent);
		EditorGUILayout.EndHorizontal();

		EditorGUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
		if (GUILayout.Button("重置", GUILayout.Width(40))){
			m_OffsetRightButtom.vector3Value = Vector3.zero;
		}
		EditorGUILayout.PropertyField(m_OffsetRightButtom, m_RBContent);
		EditorGUILayout.EndHorizontal();
		
		serializedObject.ApplyModifiedProperties();

		// UISkewImage uiSkewImage = (UISkewImage)target;
		// EditorGUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
		// if (GUILayout.Button("重置", GUILayout.Width(40))){
		// 	uiSkewImage.OffsetLeftTop = Vector3.zero;
		// }
		// uiSkewImage.OffsetLeftTop = EditorGUILayout.Vector3Field("左上", uiSkewImage.OffsetLeftTop, GUILayout.ExpandWidth(true));
		// EditorGUILayout.EndHorizontal();

		// EditorGUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
		// if (GUILayout.Button("重置", GUILayout.Width(40))){
		// 	uiSkewImage.OffsetRightTop = Vector3.zero;
		// }
		// uiSkewImage.OffsetRightTop = EditorGUILayout.Vector3Field("右上", uiSkewImage.OffsetRightTop, GUILayout.ExpandWidth(true));
		// EditorGUILayout.EndHorizontal();

		// EditorGUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
		// if (GUILayout.Button("重置", GUILayout.Width(40))){
		// 	uiSkewImage.OffsetLeftButtom = Vector3.zero;
		// }
		// uiSkewImage.OffsetLeftButtom = EditorGUILayout.Vector3Field("左下", uiSkewImage.OffsetLeftButtom, GUILayout.ExpandWidth(true));
		// EditorGUILayout.EndHorizontal();

		// EditorGUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
		// if (GUILayout.Button("重置", GUILayout.Width(40))){
		// 	uiSkewImage.OffsetRightButtom = Vector3.zero;
		// }
		// uiSkewImage.OffsetRightButtom = EditorGUILayout.Vector3Field("右下", uiSkewImage.OffsetRightButtom, GUILayout.ExpandWidth(true));
		// EditorGUILayout.EndHorizontal();

		// if (GUI.changed){
		// 	EditorUtility.SetDirty(uiSkewImage);
		// }
	}
}
主要是那个
serializedObject. Update();
serializedObject. ApplyModifiedProperties();
这样关键,会自动加入到动画系统里面去,看截图:


一开始是白色的,只要在Animation面板开启录制模式,我在这里修改任何一个参数,就会变红,就像Unity自身的参数一样!可以开心的调动画啦~哈哈

Unity-UGUI是Unity游戏引擎中的一个UI系统,可以用来创建和管理用户界面。它提供了丰富的功能和工具,使得开发者能够轻松地制作各种表格。 使用Unity-UGUI制作表格的步骤如下: 1. 创建Canvas对象:在Unity中,首先需要创建一个Canvas对象,作为UI渲染的容器。选择GameObject -> UI -> Canvas,即可创建一个Canvas对象。 2. 添加Table组件:选择Canvas对象,在Inspector面板中点击"Add Component"按钮,然后在搜索栏中输入"Table",选择适合的Table组件,点击添加。 3. 设置表格的行列数:在Table组件的Inspector面板中,设置表格所需的行数和列数。 4. 设置表格样式:可以在Inspector面板中设置表格的颜色、大小等属性,以满足具体需求。 5. 添加表格内容:可以通过代码或者拖拽方式,向表格中添加所需的文本或图片。可以通过操作表格的行列索引,将内容放置在特定的位置。 6. 设置表格的交互性:可以为表格中的每个单元格添加点击事件或其他交互效果,提升用户体验。 7. 调整表格布局:可以通过调整Canvas的大小、位置,或者改变组件之间的层次关系,来调整表格的布局。 8. 完善表格功能:可以根据具体需求,添加更多表格的功能,比如排序、过滤、搜索等。 9. 测试和优化:在表格制作完成后,可以进行测试,查看表格的显示效果和交互效果,并进行优化。 总之,使用Unity-UGUI制作表格,只需简单的操作和设置,就能够创建出各种样式、功能丰富的表格,满足游戏或应用程序的需求。
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苏小败在路上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值