工作时分配给我的要实现的功能,写的时候遇到挺多的坑的,在此记录一下
效果
放大缩小的效果
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);
}
}
}