Unity 中使用波浪动画创建 UI 图像

news2024/11/16 5:58:28

如何使用请添加图片描述

只需将此组件添加到画布中的空对象即可。强烈建议您将此对象放入其自己的画布/嵌套画布中,因为它会弄脏每一帧的画布并导致重新生成整个网格。

注意:不支持切片图像。

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Sprites;
using UnityEngine.UI;
#if UNITY_2017_4 || UNITY_2018_2_OR_NEWER
using UnityEngine.U2D;
#endif

#if UNITY_EDITOR
using UnityEditor;

// Custom Editor to order the variables in the Inspector similar to Image component
[CustomEditor( typeof( WavyImage ) )]
[CanEditMultipleObjects]
public class WavyImageEditor : Editor
{
	private SerializedProperty colorProp, spriteProp, preserveAspectProp, waveSpeedIgnoresTimeScaleProp;
	private GUIContent spriteLabel;

	private List<WavyImage> wavyImages;
	private bool inspectingAsset;

	internal static bool previewInEditor;

	private void OnEnable()
	{
		Object[] _targets = targets;
		wavyImages = new List<WavyImage>( _targets.Length );
		for( int i = 0; i < _targets.Length; i++ )
		{
			WavyImage wavyImage = _targets[i] as WavyImage;
			if( wavyImage )
			{
				wavyImages.Add( wavyImage );
				inspectingAsset |= AssetDatabase.Contains( wavyImage );
			}
		}

		colorProp = serializedObject.FindProperty( "m_Color" );
		spriteProp = serializedObject.FindProperty( "m_Sprite" );
		preserveAspectProp = serializedObject.FindProperty( "m_PreserveAspect" );
		waveSpeedIgnoresTimeScaleProp = serializedObject.FindProperty( "m_WaveSpeedIgnoresTimeScale" );

		spriteLabel = new GUIContent( "Source Image" );
	}

	private void OnDisable()
	{
		if( previewInEditor )
		{
			previewInEditor = false;
			RefreshWavyImages();
		}

		EditorApplication.update -= PreviewInEditor;
	}

	public override void OnInspectorGUI()
	{
		serializedObject.Update();

		EditorGUILayout.PropertyField( colorProp );
		EditorGUILayout.PropertyField( spriteProp, spriteLabel );
		if( spriteProp.hasMultipleDifferentValues || spriteProp.objectReferenceValue )
		{
			EditorGUI.indentLevel++;
			EditorGUILayout.PropertyField( preserveAspectProp );
			EditorGUI.indentLevel--;
		}

		DrawPropertiesExcluding( serializedObject, "m_Script", "m_Color", "m_Sprite", "m_PreserveAspect", "m_WaveSpeedIgnoresTimeScale", "m_OnCullStateChanged" );

		EditorGUI.indentLevel++;
		EditorGUILayout.PropertyField( waveSpeedIgnoresTimeScaleProp );
		EditorGUI.indentLevel--;

		serializedObject.ApplyModifiedProperties();

		if( !EditorApplication.isPlayingOrWillChangePlaymode && !inspectingAsset )
		{
			EditorGUILayout.Space();

			EditorGUI.BeginChangeCheck();
			previewInEditor = GUILayout.Toggle( previewInEditor, "Preview In Editor", GUI.skin.button );
			if( EditorGUI.EndChangeCheck() )
			{
				if( previewInEditor )
				{
					EditorApplication.update -= PreviewInEditor;
					EditorApplication.update += PreviewInEditor;
				}
				else
				{
					EditorApplication.update -= PreviewInEditor;
					RefreshWavyImages();
					EditorApplication.delayCall += RefreshWavyImages; // Preview is sometimes not reset immediately without this call
				}
			}
		}
	}

	private void PreviewInEditor()
	{
		if( EditorApplication.isPlayingOrWillChangePlaymode )
		{
			previewInEditor = false;
			EditorApplication.update -= PreviewInEditor;
		}
		else
			RefreshWavyImages();
	}

	private void RefreshWavyImages()
	{
		EditorApplication.QueuePlayerLoopUpdate();
	}
}
#endif

[RequireComponent( typeof( CanvasRenderer ) )]
[AddComponentMenu( "UI/Wavy Image", 12 )]
public class WavyImage : MaskableGraphic, ILayoutElement
{
	[SerializeField]
	private Sprite m_Sprite;
	public Sprite sprite
	{
		get { return m_Sprite; }
		set
		{
			if( m_Sprite )
			{
				if( m_Sprite != value )
				{
					m_Sprite = value;

					SetAllDirty();
					TrackImage();
				}
			}
			else if( value )
			{
				m_Sprite = value;

				SetAllDirty();
				TrackImage();
			}
		}
	}

	[SerializeField]
	private bool m_PreserveAspect;
	public bool preserveAspect
	{
		get { return m_PreserveAspect; }
		set { if( m_PreserveAspect != value ) { m_PreserveAspect = value; SetVerticesDirty(); } }
	}

	[Header( "Wave Animation" )]
	[Tooltip( "Number of rows and columns in the generated grid. The larger this value is, the smoother the wave animation will be but the calculations will also take more time." )]
	[Range( 0, 10 )]
	[SerializeField]
	private int m_WaveSegments = 3;
	public int waveSegments
	{
		get { return m_WaveSegments; }
		set { if( m_WaveSegments != value ) { m_WaveSegments = value; SetVerticesDirty(); } }
	}

	[Range( -0.5f, 0.5f )]
	[SerializeField]
	private float m_WaveStrength = 0.15f;
	public float waveStrength
	{
		get { return m_WaveStrength; }
		set { if( m_WaveStrength != value ) { m_WaveStrength = value; SetVerticesDirty(); } }
	}

	[Tooltip( "As this value increases, the wave animation will become more 'chaotic' because vertices will start moving in more random directions." )]
	[Range( 0f, 1f )]
	[SerializeField]
	private float m_WaveDiversity = 0.5f;
	private float waveDiversitySqrt; // The visual impact of waveDiversity's square root seems to be more uniform than waveDiversity itself
	public float waveDiversity
	{
		get { return m_WaveDiversity; }
		set { if( m_WaveDiversity != value ) { m_WaveDiversity = value; waveDiversitySqrt = Mathf.Sqrt( value ); SetVerticesDirty(); } }
	}

	[SerializeField]
	private float m_WaveSpeed = 0.5f;
	public float waveSpeed
	{
		get { return m_WaveSpeed; }
		set { if( m_WaveSpeed != value ) { m_WaveSpeed = value; SetVerticesDirty(); } }
	}

	[SerializeField]
	private bool m_WaveSpeedIgnoresTimeScale = true;
	public bool waveSpeedIgnoresTimeScale
	{
		get { return waveSpeedIgnoresTimeScale; }
		set { waveSpeedIgnoresTimeScale = value; }
	}

	private float waveAnimationTime;

	public override Texture mainTexture { get { return m_Sprite ? m_Sprite.texture : s_WhiteTexture; } }

	public float pixelsPerUnit
	{
		get
		{
			float spritePixelsPerUnit = 100;
			if( m_Sprite )
				spritePixelsPerUnit = m_Sprite.pixelsPerUnit;

			float referencePixelsPerUnit = 100;
			if( canvas )
				referencePixelsPerUnit = canvas.referencePixelsPerUnit;

			return spritePixelsPerUnit / referencePixelsPerUnit;
		}
	}

	public override Material material
	{
		get
		{
			if( m_Material != null )
				return m_Material;

			if( m_Sprite && m_Sprite.associatedAlphaSplitTexture != null )
			{
#if UNITY_EDITOR
				if( Application.isPlaying )
#endif
					return Image.defaultETC1GraphicMaterial;
			}

			return defaultMaterial;
		}
		set { base.material = value; }
	}

	protected override void Awake()
	{
		base.Awake();
		waveDiversitySqrt = Mathf.Sqrt( m_WaveDiversity );
	}

	protected override void OnEnable()
	{
		base.OnEnable();
		TrackImage();
	}

	protected override void OnDisable()
	{
		base.OnDisable();

		if( m_Tracked )
			UnTrackImage();
	}

	private void Update()
	{
		if( m_WaveSegments > 0 && m_WaveSpeed != 0f && m_WaveStrength != 0f )
			SetVerticesDirty();
	}

	protected override void OnPopulateMesh( VertexHelper vh )
	{
		vh.Clear();

		Color32 color32 = color;
		Rect rect = GetPixelAdjustedRect();

		Vector4 uv;
		float bottomLeftX, bottomLeftY, width, height;
		if( m_Sprite )
		{
			uv = DataUtility.GetOuterUV( m_Sprite );
			Vector4 padding = DataUtility.GetPadding( m_Sprite );
			Vector2 size = m_Sprite.rect.size;

			int spriteW = Mathf.RoundToInt( size.x );
			int spriteH = Mathf.RoundToInt( size.y );

			if( m_PreserveAspect && size.sqrMagnitude > 0f )
			{
				float spriteRatio = size.x / size.y;
				float rectRatio = rect.width / rect.height;

				if( spriteRatio > rectRatio )
				{
					float oldHeight = rect.height;
					rect.height = rect.width * ( 1f / spriteRatio );
					rect.y += ( oldHeight - rect.height ) * rectTransform.pivot.y;
				}
				else
				{
					float oldWidth = rect.width;
					rect.width = rect.height * spriteRatio;
					rect.x += ( oldWidth - rect.width ) * rectTransform.pivot.x;
				}
			}

			bottomLeftX = rect.x + rect.width * padding.x / spriteW;
			bottomLeftY = rect.y + rect.height * padding.y / spriteH;
			width = rect.width * ( spriteW - padding.z - padding.x ) / spriteW;
			height = rect.height * ( spriteH - padding.w - padding.y ) / spriteH;
		}
		else
		{
			uv = Vector4.zero;

			bottomLeftX = rect.x;
			bottomLeftY = rect.y;
			width = rect.width;
			height = rect.height;
		}

#if UNITY_EDITOR
		if( ( !Application.isPlaying && !WavyImageEditor.previewInEditor ) || m_WaveSegments <= 0 )
#else
		if( m_WaveSegments <= 0 )
#endif
		{
			// Generate normal Image
			vh.AddVert( new Vector3( bottomLeftX, bottomLeftY, 0f ), color32, new Vector2( uv.x, uv.y ) );
			vh.AddVert( new Vector3( bottomLeftX, bottomLeftY + height, 0f ), color32, new Vector2( uv.x, uv.w ) );
			vh.AddVert( new Vector3( bottomLeftX + width, bottomLeftY + height, 0f ), color32, new Vector2( uv.z, uv.w ) );
			vh.AddVert( new Vector3( bottomLeftX + width, bottomLeftY, 0f ), color32, new Vector2( uv.z, uv.y ) );

			vh.AddTriangle( 0, 1, 2 );
			vh.AddTriangle( 2, 3, 0 );
		}
		else
		{
			// Generate multiple small quads and move their vertices
			float invWaveSegments = 1f / m_WaveSegments;
			waveAnimationTime += ( m_WaveSpeedIgnoresTimeScale ? Time.unscaledDeltaTime : Time.deltaTime ) * m_WaveSpeed;

			float widthOffset = m_WaveStrength * width;
			float heightOffset = m_WaveStrength * height;

#if UNITY_EDITOR
			waveDiversitySqrt = Mathf.Sqrt( m_WaveDiversity );
#endif

			for( int i = 0; i <= m_WaveSegments; i++ )
			{
				for( int j = 0; j <= m_WaveSegments; j++ )
				{
					float normalizedX = j * invWaveSegments;
					float normalizedY = i * invWaveSegments;

					Vector2 _uv = new Vector2( uv.z * normalizedX + uv.x * ( 1f - normalizedX ), uv.w * normalizedY + uv.y * ( 1f - normalizedY ) );
					Vector3 _position = new Vector3( bottomLeftX + width * normalizedX, bottomLeftY + height * normalizedY, 0f );
					_position.x += ( Mathf.PerlinNoise( normalizedX * waveDiversitySqrt + waveAnimationTime, normalizedY * waveDiversitySqrt ) - 0.5f ) * widthOffset;
					_position.y += ( Mathf.PerlinNoise( normalizedX * waveDiversitySqrt, normalizedY * waveDiversitySqrt + waveAnimationTime ) - 0.5f ) * heightOffset;

					vh.AddVert( _position, color32, _uv );
				}
			}

			for( int i = 0; i < m_WaveSegments; i++ )
			{
				int firstTriangle = i * ( m_WaveSegments + 1 );
				for( int j = 0; j < m_WaveSegments; j++ )
				{
					int triangle = firstTriangle + j;
					int aboveTriangle = triangle + m_WaveSegments + 1;

					vh.AddTriangle( triangle, aboveTriangle, aboveTriangle + 1 );
					vh.AddTriangle( aboveTriangle + 1, triangle + 1, triangle );
				}
			}
		}
	}

	int ILayoutElement.layoutPriority { get { return 0; } }
	float ILayoutElement.minWidth { get { return 0f; } }
	float ILayoutElement.minHeight { get { return 0f; } }
	float ILayoutElement.flexibleWidth { get { return -1; } }
	float ILayoutElement.flexibleHeight { get { return -1; } }
	float ILayoutElement.preferredWidth { get { return m_Sprite ? ( m_Sprite.rect.size.x / pixelsPerUnit ) : 0f; } }
	float ILayoutElement.preferredHeight { get { return m_Sprite ? ( m_Sprite.rect.size.y / pixelsPerUnit ) : 0f; } }
	void ILayoutElement.CalculateLayoutInputHorizontal() { }
	void ILayoutElement.CalculateLayoutInputVertical() { }

	// Whether this is being tracked for Atlas Binding
	private bool m_Tracked = false;

#if UNITY_2017_4 || UNITY_2018_2_OR_NEWER
	private static List<WavyImage> m_TrackedTexturelessImages = new List<WavyImage>();
	private static bool s_Initialized;
#endif

	private void TrackImage()
	{
		if( m_Sprite != null && m_Sprite.texture == null )
		{
#if UNITY_2017_4 || UNITY_2018_2_OR_NEWER
			if( !s_Initialized )
			{
				SpriteAtlasManager.atlasRegistered += RebuildImage;
				s_Initialized = true;
			}

			m_TrackedTexturelessImages.Add( this );
#endif
			m_Tracked = true;
		}
	}

	private void UnTrackImage()
	{
#if UNITY_2017_4 || UNITY_2018_2_OR_NEWER
		m_TrackedTexturelessImages.Remove( this );
#endif
		m_Tracked = false;
	}

#if UNITY_2017_4 || UNITY_2018_2_OR_NEWER
	private static void RebuildImage( SpriteAtlas spriteAtlas )
	{
		for( int i = m_TrackedTexturelessImages.Count - 1; i >= 0; i-- )
		{
			WavyImage image = m_TrackedTexturelessImages[i];
			if( spriteAtlas.CanBindTo( image.sprite ) )
			{
				image.SetAllDirty();
				m_TrackedTexturelessImages.RemoveAt( i );
			}
		}
	}
#endif
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1130475.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法通关村第十一关白银挑战——位运算符的高频算法题

大家好&#xff0c;我是怒码少年小码。 今天讲讲几个位运算的经典算法。 位移的妙用 1. 位1的个数 LeetCode 191&#xff1a;编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中数字位数为 ‘1’ 的个…

html 常见兼容性问题

目录 前言: 用法: 代码: 1. 盒模型差异: 2. 表格布局问题: 3. 浏览器前缀问题: 4. 字体渲染问题: 理解: 讨论: 前言: 在Web开发中&#xff0c;兼容性问题是常见的挑战之一。不同的浏览器和设备可能以不同的方式解释和呈现HTML&#xff0c;导致网页在某些环境下出现问题…

fastjson对象序列化的问题

今天偶然遇到一个fastjson将字符串反序列化为一个对象的时候的问题&#xff0c;就是简单的通过com.alibaba.fastjson.JSON将对象转为字符串&#xff0c;然后再从字符串转换为原类型的对象。 涉及的代码也非常简单 package cn.edu.sgu.www.mhxysy.service.role.impl;import cn…

单片机设计基于STM32的空气净化器设计

**单片机设计介绍&#xff0c;1615[毕设课设]基于STM32的空气净化器设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图pcb设计图 五、 程序六、 文章目录 一 概要 此设计资料主要包含原理图、PCB、源程序、元器件清等资料&#xff0c; 二、功能设计 设计思路 …

uniapp--点击上传图片到oss再保存数据给后端接口

项目采用uniapp与uview2.0组件库 --1.0的也可以参考一下&#xff0c;大差不差 一、项目要求与样式图 点击上传n张图片到oss&#xff0c;然后点击提交给后端 二、思路 1、打开上传按钮&#xff0c;弹出框内出现上传图片和提交按钮 2、点击上传图片区域&#xff0c;打开本地图…

redis持久化之AOF(Append Only File)

1 : AOF 是什么 以日志的形式来记录每个写操作&#xff08;增量保存&#xff09;&#xff0c;将redis执行过的所有写指令记录下来&#xff08;读操作不记 录&#xff09;&#xff0c;只允追加文件但不可改写文件&#xff0c;redis启动之初会读取该文件重新构造数据&#xff0c;…

【Linux】IP协议

文章目录 &#x1f4d6; 前言1. 网络层2. IP协议格式3. IP报文分片和组装3.1 如何分片和组装&#xff1a;3.2 组装的衍生问题&#xff1a; 4. 网段划分&#xff08;重点&#xff09;4.1 子网掩码&#xff1a;4.2 IP地址的数量限制&#xff1a;4.3 私有IP地址和公网IP地址&#…

读高性能MySQL(第4版)笔记19_云端和合规性

1. 如何构建数据库环境 1.1. 托管MySQL 1.2. VM上构建 1.3. 天下没有免费的午餐&#xff0c;每一个选择都伴随着一系列的权衡 2. 托管MySQL 2.1. 服务商提供了一个可访问的数据库设置程序&#xff0c;而不需要用户深入了解MySQL的具体细节 2.2. 使用托管MySQL将缺乏很多的…

vueDay03——计算属性

一、一般场景 当我们需要对某个数据进行简单判断渲染的时候&#xff0c;我们通常会使用如下方法 <div>nginx当前状态&#xff1a;{{ openNginx true ? true : false}} </div> 但是这样就很影响观感&#xff0c;因为渲染出来的只有openNginx的值&#xff0c;而…

微信小程序投票管理系统:打造智能、便捷的投票体验

前言 随着社交网络的兴起和移动互联网的普及&#xff0c;人们对于参与和表达意见的需求越来越强烈。在这个背景下&#xff0c;微信小程序投票管理系统应运而生。它为用户提供了一个智能、便捷的投票平台&#xff0c;使用户可以轻松创建和参与各种类型的投票活动。本文将详细介…

【C++学习笔记】类和对象(上)

目录 1. 面向对象和面向过程的初步认识 2. 类的引入 3. 类的定义 3.1 类的两种定义方式 3.1.1声明和定义全部放在类体中 3.1.2.类声明放在.h文件中&#xff0c;成员函数定义放在.cpp文件中 4. 类的访问限定符及封装 4.1 访问限定符 4.2 面试题&#xff1a;C中struct…

百度文心一言4.0——使用及API测试

登录百度智能云&#xff1a;百度智能云 文心一言4.0使用 开通付费&#xff1a; 创建应用&#xff1a; 自行创建应用名称&#xff1a; 对话测试&#xff1a; API测试 ERNIE-Bot-4 API&#xff1a;ERNIE-Bot-4 打开链接查看自己的API Key&#xff0c;Secret Key。 可参…

Python手搓C4.5决策树+Azure Adult数据集分析

前言 课上的实验 由于不想被抄袭&#xff0c;所以暂时不放完整代码 Adult数据集可以在Azure官网上找到 Azure 开放数据集中的数据集 - Azure Open Datasets | Microsoft Learn 数据集预处理 删除难以处理的权重属性fnlwgt与意义重复属性educationNum去除重复行与空行删除…

从一个页面跳转到目标页面之后,对应的顶部路由高亮

需求&#xff1a;页面跳转到目标页面之后&#xff0c;对应的顶部路由高亮 上面的更多 跳转到 学情分析下面的学生分析 <template><div class"topBar" ref"topBar" v-loading.fullscreen.lock"fullscreenLoading"><div class&quo…

dc9靶机攻略

dc9 扫描 扫描结果如图 nmap 目录扫描 指纹扫描 渗透 访问首页 该处发现搜索框&#xff0c;正常搜名字可以直接返回该用户的信息&#xff0c;怀疑sql注入&#xff0c;使用单引号注入&#xff0c;发现没反应&#xff0c;再使用一下万能注入语句1 or 11 使用sqlmap sqlmap -…

什么是蓝桥杯?什么是蓝桥STEMA考试?

第十五届蓝桥大赛赛事安排? STEMA考试11月(考试时间11月26日) STEMA考试1月(2024年1月) STEMA考试3月(2024年3月) 第十五届蓝桥杯省赛(2024年4月待定) 第十五届蓝桥杯国赛(2024年5月待定) 注:以上时间具体以组委会官方发布为准。 01.蓝桥杯 蓝桥杯全国软件和…

【每日一题】掷骰子等于目标和的方法数

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;动态规划 写在最后 Tag 【动态规划】【数组】 题目来源 1155. 掷骰子等于目标和的方法数 题目解读 你手里有 n 个一样的骰子&#xff0c;每个骰子都有 k 个面&#xff0c;分别标号 1 到 n。给定三个整数 n&#xff0…

部署基于efk+logstash+kafka构建日志收集平台并对nginx日志进行分析

文章目录 1.1 安装zookeeper集群1.2 安装kafka集群1.3 部署filebeat服务1.4 部署logstash1.5 部署es和kibana服务1.6 配置kibana ui界面1.7 对nginx进行日志分析 Filebeat采集日志kafka topic存起来日志->logstash去kafka获取日志&#xff0c;进行格式转换->elasticsearc…

【计算机网络笔记】网络应用进程通信

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

【RocketMQ系列十四】RocketMQ中消息堆积如何处理

您好&#xff0c;我是码农飞哥&#xff08;wei158556&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精…