Unity Image(RawImage) 实现按轴心放大缩小,序列化存储轴心信息,实现编译器窗口保存轴心

news2024/11/15 17:16:20

工作时分配给我的要实现的功能,写的时候遇到挺多的坑的,在此记录一下

效果

放大缩小的效果
请添加图片描述

2.编译器扩展窗口记录请添加图片描述

实现点

1.Json序列化存储图片轴心位置, 放大倍率,放大所需要的事件
2.用了编译器扩展工具便于保存轴心信息

坑点

1.Image和RawImage的坐标都是以轴心位置计算的,更改轴心就需要重定位
2.确保画布的分辨率和测试的屏幕分辨率一致
3.计算轴心偏移和别忘记在乘以自己的放大倍率

工具的使用

1.打开WSC/保存图片轴心工具,在Image位置挂在需要保存轴心的图像,输出name作为key,并在Scene窗口中直接拖动轴心确定位置(不建议在Inspector中手动输入轴心修改,这样会让图像重定位)
请添加图片描述
请添加图片描述

2.程序调用

无论是简化key传入还是传入轴心对象的方式都需要传入要放大的Image

请添加图片描述

请添加图片描述

代码

using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;

namespace YBZ
{
    public class ControlImage : MonoBehaviour
    {   
        
        // 需要控制的图片
        [Header("2D的图像,如果是视屏改为RawImage进行推流即可")]
        public Image image;

        // Start is called before the first frame update
        void Start()
        {   
            if(image == null){
                
                if(!TryGetComponent<Image>(out image)){
                    gameObject.AddComponent<Image>();
                    image = GetComponent<Image>();
                }
            }
            rect = image.GetComponent<RectTransform>();

            // 先读取json中的文件
            Utility.Instance.ReadData();

            StartCoroutine(five());

        }

        IEnumerator five(){
            Utility.Instance.ImageZoom(image, Utility.Instance.dict["1"]);
            yield return new WaitForSeconds(1);

            Utility.Instance.ImageZoom(image, Utility.Instance.dict["2"]);
            yield return new WaitForSeconds(1.01f);

            Utility.Instance.ImageZoom(image, Utility.Instance.dict["3"]);
            yield return new WaitForSeconds(1.01f);
            
            Utility.Instance.ImageZoom(image, Utility.Instance.dict["4"]);
            yield return new WaitForSeconds(1.01f);
            
            Utility.Instance.ImageZoom(image, Utility.Instance.dict["5"]);
            yield return new WaitForSeconds(3.01f);

            Utility.Instance.ImageZoom(image, Utility.Instance.dict["6"]);
            yield return new WaitForSeconds(1.01f);

            Utility.Instance.ImageZoom(image, Utility.Instance.dict["7"]);
            yield return new WaitForSeconds(1.51f);

            Utility.Instance.ImageZoom(image, Utility.Instance.dict["8"]);
            yield return new WaitForSeconds(1.51f);

            Utility.Instance.ImageZoom(image, Utility.Instance.dict["9"]);
            yield return new WaitForSeconds(2.01f);

            Utility.Instance.ImageZoom(image, Utility.Instance.dict["10"]);
            yield return new WaitForSeconds(2.01f);

            Utility.Instance.ImageZoom(image, Utility.Instance.dict["11"]);
            yield return new WaitForSeconds(2.01f);

            Utility.Instance.ImageZoom(image, Utility.Instance.dict["12"]);
            yield return new WaitForSeconds(2.01f);

            Utility.Instance.ImageZoom(image, Utility.Instance.dict["RESET"]);
        }

        // 图片的放大接口
        public void ZoomEvent(Image image, string key){
            Utility.Instance.ZoomPivot(image, key);
        }

        
    }


    // 单例模式
    // 命名冲突时,请指定命名空间
    // 使用前一定要读取数据!
    public class Utility{

        public static Utility instance;

        public static Utility Instance { 
            get{
                if(instance == null) instance = new Utility();
                return instance;
            }
        }



        // 放大点位置的字典
        public Dictionary<String, ZoomPoint> dict = new Dictionary<string, ZoomPoint>();
        
        //json文件的位置,这个位置默认是在SteamingAssets下的
        private string jsonFileName ="/2DPivots.json";

        private string filePath =""; // "StreamingAssets/2Dpivots"
        public void SetFilePath(string value) => filePath = value;

        // 写入数据
        public void WriteDataTest(){
            Data data = new Data();
            data.zoomPoints = new List<ZoomPoint>();

            ZoomPoint point1 = new ZoomPoint
            {
                name = "1",
                pivot = new Vector2(0.75f,0.75f)
            };
            ZoomPoint point2 = new ZoomPoint
            {
                name = "2",
                pivot = new Vector2(0.5f,0.5f)
                
            };

            data.zoomPoints[0] = point1;
            data.zoomPoints[1] = point2;

            string js = JsonUtility.ToJson(data);

            // 获取项目路径
            string fileUrl;
            if(filePath == ""){
                fileUrl = Application.streamingAssetsPath + jsonFileName;
            }else{
                fileUrl = filePath;
            }
            
            using(StreamWriter sw  = new StreamWriter(fileUrl))
            {   
                sw.WriteLine(js); //保存数据
                sw.Close(); 
                sw.Dispose();
            }
        }  


        public void WriteData(ZoomPoint zp){
            Data data = ReadData();
            
            if(data != null && data.zoomPoints != null){
                foreach(var temp in data.zoomPoints){
                    if(temp.name == zp.name){
                        Debug.LogError("轴心名字重复 !(⊙_⊙)?");
                        return;
                    }
                }
            }
            
            data.zoomPoints.Add(zp);

            string js = JsonUtility.ToJson(data);

            // 获取项目路径
            string fileUrl;
            if(filePath == ""){
                fileUrl = Application.streamingAssetsPath + jsonFileName;
            }else{
                fileUrl = filePath;
            }

            using(StreamWriter sw  = new StreamWriter(fileUrl))
            {   
                sw.WriteLine(js); //保存数据
                sw.Close(); // 关闭文档
                sw.Dispose();
            }
            Debug.Log("保存成功(*^▽^*)");
        }

        /// <summary>
        /// 读取数据,在外界不需要接受Data只需要调用即可,返回的Data仅供我自己编写其他程序耦合使用,忽略返回值
        /// 请在调用dict 前一定要先调用ReadData函数
        /// </summary>
        public Data ReadData(){
            // 获取项目路径
            string fileUrl;
            if(filePath == ""){
                fileUrl = Application.streamingAssetsPath + jsonFileName;
            }else{
                fileUrl = filePath;
            }
            
            //读取文件
            string readDate;
            using (StreamReader sr = File.OpenText(fileUrl)){
                readDate = sr.ReadLine();
                sr.Close();
            }

            Data data = JsonUtility.FromJson<Data>(readDate);

            // 分配内存
            if(data == null ){
                data = new Data();
                data.zoomPoints = new List<ZoomPoint>();
                return data;
            }

            // 数据保存到字典里
            foreach(ZoomPoint zp  in data.zoomPoints){
                dict.TryAdd(zp.name, zp);
            }
            return data;
        }

        /// <summary>
        /// 按点放大轴心,无法捕获动画一定确保放大的动画之间不会冲突,简化了ImageZoom的调用
        /// </summary>
        /// <param name="zoomObj">被放大图像</param>
        /// <param name="key">轴心的名字</param>
        public void ZoomPivot(Image zoomObj, string key){
            ImageZoom(zoomObj, dict[key]);
        }

        /// <summary>
        /// 按倍率,轴心定位的方法放大图形,如果目标的轴心不在中心,会在设定完轴心后将目标移动到轴心,
        /// 需要注意的事,图片的坐标位置是以轴心位置为确定的,也因此放大缩小不改变图像的世界坐标
        /// </summary>
        /// <param name="zoomObj">需要方法的图像</param>
        /// <param name="x">轴心的x</param>
        /// <param name="y">轴心的y</param>
        /// <param name="zoomMagnification">放大倍率</param>
        /// 
        public void ImageZoom(Image zoomObj , ZoomPoint zp){
            float x = zp.pivot.x;
            float y = zp.pivot.y;
            float zoomMagnification = zp.zoomMagnification;
            float time = zp.time;

            // 当前和目标按图轴心位置 
            Vector3 currentPivotPostition = new Vector3(zoomObj.rectTransform.rect.width * zoomObj.rectTransform.pivot.x, zoomObj.rectTransform.rect.height * zoomObj.rectTransform.pivot.y) * zoomMagnification;
            Vector3 targetPivotPosition = new Vector3(zoomObj.rectTransform.rect.width * x, zoomObj.rectTransform.rect.height * y) * zoomMagnification;

            zoomObj.rectTransform.pivot = new Vector2(x, y); // 设定轴心
            zoomObj.transform.position += targetPivotPosition - currentPivotPostition; // 重定位

            Vector3 targetPostion = zoomObj.transform.position + currentPivotPostition - targetPivotPosition;

            //位移
            Translate(zoomObj, targetPostion, time);

            // 2023.7.30 经过测试,缩放仍然是更具初始轴心,能够达到理想效果不需要将LocalScale恢复为1
            // 为了结算范围失控的问题,需要将localScale恢复为1,再放大为需要的大小
            // zoomObj.rectTransform.localScale = new Vector3(1,1,1);
            Vector3 targetScale = new Vector3(zoomMagnification, zoomMagnification, zoomMagnification);
            zoomObj.rectTransform.DOScale(targetScale, time);

        }

        // 该方法主要是需要将目标点移动到坐标终点
        public void Translate(Image zoomObj, Vector3 target, float time){
            // 计算当前轴心和新轴心之间的位置
            Vector3 current = zoomObj.transform.position;
            // 移动
            zoomObj.transform.DOMove(target, time);
        }
    }
    

    // 数据存储接受类
    [Serializable]
    public class Data{
    	// 不能用字典直接存储,可以采用数据列表这样的连续空间
        // public ZoomPoint[] zoomPoints;
        // public Dictionary<string, ZoomPoint> zoomPoints;
        public List<ZoomPoint> zoomPoints;
    }


    // 可序列化
    [Serializable]
    public class ZoomPoint
    {
        // 点名称, 将作为Key被字典存储
        public string name;

        // 轴心X坐标
        public Vector2 pivot = Vector2.one / 2;

        // 放大倍率,小于1是为缩小倍率,小于0是取绝对值,不允许原点对称缩放,需要保证计算轴心逻辑正确
        // 默认设为1.5f 放大倍率
        public float zoomMagnification = 5f;

        // 改变的需要的时间,默认设为1f
        public float time = 1.0f;

        public override string ToString()
        {
            return $"name = {this.name}, pivot = ({pivot.ToString()}), zoomMagnification = {this.zoomMagnification}, time = {this.time}";
        }
    }

	
    // 实现编译器窗口扩展
    public class Editor :  ScriptableWizard 
    {
        //  文件路径
        public string filePath = Application.streamingAssetsPath + "/2DPivots.json";

        [SerializeField]
        public Image image;

        // 作为轴心点保存定位的Key
        new public string name;

        // 轴心
        public Vector2 pivot;

        // 放大倍率
        public float zoomMagnification = 5f;

        // 动画时间
        public float time = 1.0f;


        [MenuItem("WSC/保存图片轴心")]
        public static void CreateWizard() {
            ScriptableWizard.DisplayWizard<Editor>("Image zoom info save","退出", "保存当前轴心");
        }

        private int count = 1;
        void OnWizardCreate()
        {
            Utility.Instance.SetFilePath(filePath);
            Utility.Instance.ReadData();
            Debug.Log("轴心更新完毕(^-^)V");
        }

        void OnWizardOtherButton(){
            pivot = image.rectTransform.pivot;
            if (name == null) 
                name = (count++).ToString();

            ZoomPoint zp = new ZoomPoint
            {
                name = this.name,
                pivot = pivot,
                zoomMagnification = this.zoomMagnification,
                time = this.time
            };

            // reset image pivot
            Utility.Instance.WriteData(zp);
        }
    }
    

}


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

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

相关文章

【Linux】多路转接 -- select函数

文章目录 1. 认识select函数2. select函数原型3. socket就绪条件4. select工作流程5. select服务器6. select的优缺点 首先我们要了解一下&#xff0c;什么是多路转接&#xff1f; 多路转接也叫多路复用&#xff0c;是一种用于管理多个IO通道的技术。它能实现同时监听和处理多个…

C++ 访问控制——公有继承、私有继承、保护继承

派生类继承了基类的全部数据成员和除了构造函数和析构函数之外的全部函数成员&#xff0c;但是这些成员的访问属性在派生的过程中是可以调整的。从基类继承的成员&#xff0c;其访问属性由继承方式控制。 基类的成员有public&#xff08;公有&#xff09;、protected&#xff…

ArduPilot开源代码之Companion Computers简单分析

ArduPilot开源代码之Companion Computers简单分析 1. 源由2. 伴机系统2.1 APSync2.2 DroneKit2.3 FlytOS2.4 Maverick2.5 ROS2.6 Rpanion-server 3. 总结4. 参考资料 1. 源由 从稳定性&#xff0c;社区群体&#xff0c;以及开源方式的角度看&#xff0c;Ardupilot是不错的选择…

骑砍二 ATC MOD 使用教程与应用案例解析

骑砍二 ATC MOD 使用教程与应用案例解析 作者&#xff1a;blibli-财不外漏 / NEXUSMODS-PuepleKarmen 案例MOD依赖&#xff1a;ATC - Adonnay’s Troop Changer & AEW - Adonnay’s Exotic Weaponry & New Armor 文本编辑工具&#xff1a;VS Code&#xff08;推荐使用&…

【小沐学NLP】在线AI绘画网站(百度:文心一格)

文章目录 1、简介2、文心一格2.1 功能简介2.2 操作步骤2.3 使用费用2.4 若干示例2.4.1 女孩2.4.2 昙花2.4.3 山水画2.4.4 夜晚2.4.5 古诗2.4.6 二次元2.4.7 帅哥 结语 1、简介 当下&#xff0c;越来越多AI领域前沿技术争相落地&#xff0c;逐步释放出极大的产业价值&#xff0…

Amazon CloudFront 部署小指南(四)- CloudFront Function 基础与诊断

内容简介 本文适用于希望使用 Amazon CloudFront Functions 提升 Amazon CloudFront 边缘计算能力的用户&#xff0c;旨在帮助您更好的进行 CloudFront Functions 的开发、调试、测试、部署等工作。 首先我们会对 CloudFront Function 做个简单的介绍&#xff0c;然后分为七个步…

全志F1C200S嵌入式驱动开发(应用程序开发)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 我们在开发soc驱动的时候,很多情况下也要验证下当前的驱动功能是否正确。当然除了验证驱动功能之外,我们还要编写业务代码和流程代码。这中间就和各行各业有关了,有的是算法,有…

Redis BigKey案例

面试题&#xff1a; 阿里广告平台&#xff0c;海量数据里查询某一固定前缀的key小红书&#xff0c;你如何生产上限制keys*/flushdb/flushall等危险命令以防止误删误用&#xff1f;美团&#xff0c;MEMORY USAGE命令你用过吗&#xff1f;BigKey问题&#xff0c;多大算big&#…

GODOT游戏引擎简介,包含与unity性能对比测试,以及选型建议

GODOT&#xff0c;是一个免费开源的3D引擎。本文以unity作对比&#xff0c;简述两者区别和选型建议。由于是很久以前写的ppt&#xff0c;技术原因视频和部分章节丢失了。建议当做业务参考。 GODOT目前为止遇到3个比较重大的基于&#xff0c;第一个是oprea的合作奖&#xff0c;…

【redis】redis的认识和安装

目录 1.redis是什么2.Redis的特点3.安装redis4.设置远程连接4.1 开启隧道4.2 可视化客户端连接4.3 开启防火墙 5.redis常见数据类型5.1 redis的一些全局命令5.2 数据结构 6. redis的典型应用---缓存&#xff08;cache&#xff09;6.1 使用redis做缓存6.2 缓存穿透&#xff0c;缓…

【绪论0】

#pic_center R 1 R_1 R1​ R 2 R^2 R2 目录 知识框架No.0 引言No.1 操作系统的概念功能和定义一、操作系统的概念和定义1、电脑的演变 二、操作系统的功能和目标 No.2 操作系统的特征一、并发二、共享三、虚拟四、异步 No.3 操作系统的发展与分类一、手工操作阶段二、批处理阶段…

Windows11 家庭中文版关于本地组策略编辑器gpedit.msc找不到即打不开的解决办法(征诚小张售后实测有效)

Windows11 家庭中文版关于本地组策略编辑器gpedit.msc找不到即打不开的解决办法 根本原因&#xff1a;是因为Windows11家庭中文版的 版本系统没内置安装本地策略组编辑器 好了废话不多说 直接说解决办法 第一步 首先电脑上新建一个空文本文件 输入以下内容&#xff1a; echo o…

Android Studio安装AI编程助手Github Copilot

csdn原创谢绝转载 简介 文档链接 https://docs.github.com/en/copilot/getting-started-with-github-copilot 它是个很牛B的编程辅助工具&#xff0c;装它&#xff0c;快装它&#xff0e; 支持以下IDE: IntelliJ IDEA (Ultimate, Community, Educational)Android StudioAppC…

Qt应用开发(基础篇)——时间类 QDateTime、QDate、QTime

一、前言 时间类QDateTime、QDate、QTime、QTimeZone保存了Qt的时间、日期、时区信息&#xff0c;常用的时间类部件都会用到这些数据结构&#xff0c;常用概念有年、月、日、时、分、秒、毫秒和时区&#xff0c;时间和时区就关系到时间戳和UTC的概念。 UTC时间&#xff0c;又称…

FPGA初步学习之串口发送模块【单字节和字符串的发送】

串口相关简介 UART 在发送或接收过程中的一帧数据由4部分组成&#xff0c;起始位、数据位、奇偶校验位和停止位&#xff0c;如图所示。其中&#xff0c;起始位标志着一帧数据的开始&#xff0c;停止位标志着一帧数据的结束&#xff0c;数据位是一帧数据中的有效数据。 通常用…

【贪心算法】leetcode刷题

贪心算法无固定套路。 核心思想&#xff1a;先找局部最优&#xff0c;再扩展到全局最优。 455.分发饼干 两种思路&#xff1a; 1、从大到小。局部最优就是大饼干喂给胃口大的&#xff0c;充分利用饼干尺寸喂饱一个&#xff0c;全局最优就是喂饱尽可能多的小孩。先遍历的胃口&a…

Win11大小写切换图标关闭方法

大家使用Win11操作系统的时候经常会切换大小写键盘&#xff0c;有些游戏本在游戏过程中需要切换大小写&#xff0c;这个时候电脑的屏幕就会出现大小写切换的图标而影响游戏体验&#xff1b; 那么想要关闭Win11电脑上大小写切换图标&#xff0c;又不知道具体怎么操作&#xff0c…

VS Code search tab

Vs Code search 栏的应用 我发现&#xff0c;在vs code种&#xff0c;上面的搜索框的功能非常多。在最初使用vscode时候&#xff0c;以为这只是一个普通的搜索框。后来&#xff0c;发现它可以用于全局搜索文件&#xff0c;比如使用ctrlshiftp。 后来&#xff0c;我发现&#xf…

lifecycleScope Unresolved reference

描述 导入了lifecycle.lifecycleScope&#xff0c;但是在activity中使用lifecycleScope报错出现Unresolved reference找不到引用。 导包 import androidx.lifecycle.lifecycleScope使用 lifecycleScope.launch(Dispatchers.IO) {...}错误 方案 代码中的activity继承Activ…

SSM(Vue3+ElementPlus+Axios+SSM前后端分离)--功能实现【四】

文章目录 SSM--功能实现实现功能06-修改家居信息需求分析/图解思路分析代码实现注意事项和细节 实现功能07-删除家居信息需求分析/图解思路分析代码实现 实现功能08-分页显示列表需求分析/图解思路分析代码实现完成测试分页显示效果 SSM–功能实现 实现功能06-修改家居信息 需…