效果如下:
以下为UI结构即具体设置:
PanelTest设置:
Center设置:
Scroll View设置:
Viewport设置:
Content设置:
ImageTempLate设置:
原理简图:
代码:
1 using Common; 2 using UnityEngine; 3 using UnityEngine.UI; 4 5 namespace Lugs 6 { 7 /// <summary> 8 /// 图形切割配置 9 /// </summary> 10 public class GridData 11 { 12 /// <summary> 13 /// 原始图片的文件 14 /// </summary> 15 public string SrcFileName = @"D: _lugsProjectsMapCutterSourceMap.jpg"; 16 /// <summary> 17 /// 切割后的图片输出目录 18 /// </summary> 19 public string OutDirectory = @"D: _lugsProjectsMapCutterRes"; 20 /// <summary> 21 /// 图片宽度 22 /// </summary> 23 public int ImageWidth = 3000; 24 /// <summary> 25 /// 图片高度 26 /// </summary> 27 public int ImageHeight = 1900; 28 /// <summary> 29 /// 格子宽度 30 /// </summary> 31 public int GridWidth = 256; 32 /// <summary> 33 /// 格子高度 34 /// </summary> 35 public int GridHeight = 256; 36 /// <summary> 37 /// 切片文件前缀 38 /// </summary> 39 public string titlePrefixName = "WorldMap_"; 40 /// <summary> 41 /// 切片文件扩展名 42 /// </summary> 43 public string expandedName = @".jpg"; 44 } 45 46 /// <summary> 47 /// UI显示的格子结构 48 /// </summary> 49 public class GridItem 50 { 51 public bool isUsed; 52 public int index; 53 public RawImage image; 54 55 GridData data; 56 public GridItem(RawImage itemTemplate, GridData data) 57 { 58 image = GameObject.Instantiate<RawImage>(itemTemplate, itemTemplate.transform.parent); 59 image.enabled = true; 60 image.gameObject.SetActive(true); 61 this.data = data; 62 } 63 64 public void Clear() 65 { 66 isUsed = false; 67 index = -1; 68 image.enabled = false; 69 } 70 71 public void SetInfo(int index, int xGridCount) 72 { 73 this.index = index; 74 isUsed = true; 75 image.enabled = true; 76 int x = index % xGridCount; 77 int y = index / xGridCount; 78 GameObject obj = image.gameObject; 79 obj.name = string.Format("[{0},{1}]", x, y); 80 int width = data.GridWidth; 81 int height = data.GridHeight; 82 obj.transform.localPosition = new Vector3(x * width, -y * height); 83 if ((x + 1) * width > data.ImageWidth) 84 { 85 width = data.ImageWidth - x * width; 86 } 87 88 if ((y + 1) * height > data.ImageHeight) 89 { 90 height = data.ImageHeight - y * height; 91 } 92 image.rectTransform.sizeDelta = new Vector2(width, height); 93 string imageName = "worldmap_" + (index + 1).ToString("D2"); 94 //Debug.Log("imageName = " + imageName); 95 ImageAssetItem item = new ImageAssetItem(UIManager.Str_WorldMapTxFolderName, imageName, false, true); 96 item.LoadImage(() => 97 { 98 image.texture = item.TexObject; 99 }); 100 } 101 } 102 }
UI代码:
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEngine.UI; 5 6 namespace Lugs 7 { 8 public sealed class PanelTest : MonoBehaviour 9 { 10 public RawImage RawImage_GridTemplate; 11 public ScrollRect scrollRect; 12 public Slider Slider_Scale; 13 /// <summary> 14 /// 视口的缩放系数,默认不缩放 15 /// </summary> 16 float mScreenRatio = 1.0f; 17 public float ScreenRatio 18 { 19 get 20 { 21 return mScreenRatio; 22 } 23 set 24 { 25 mScreenRatio = value; 26 Vector2 pos = scrollRect.normalizedPosition; 27 pos.x = Mathf.Clamp01(pos.x); 28 pos.y = Mathf.Clamp01(pos.y); 29 scrollRect.normalizedPosition = pos; 30 } 31 } 32 /// <summary> 33 /// 可视区域的可滑动的区域大小 34 /// </summary> 35 Vector2 scrollRectMoveArea; 36 /// <summary> 37 /// 可视区域大小 38 /// </summary> 39 Vector2 screenSize; 40 /// <summary> 41 /// 扩展的可视区域大小 42 /// </summary> 43 Vector2 screenExtendSize; 44 /// <summary> 45 /// 可视区域左下角在可滑动区域内的位置 46 /// </summary> 47 Vector2 screenLBPosAtMoveArea; 48 /// <summary> 49 /// 可视区域右上角在可滑动区域内的位置 50 /// </summary> 51 Vector2 screenRTPosAtMoveArea; 52 /// <summary> 53 /// 扩展的可视区域左下角在可滑动区域内的位置 54 /// </summary> 55 Vector2 screenExtendLBPosAtMoveArea; 56 /// <summary> 57 /// 扩展的可视区域右上角在可滑动区域内的位置 58 /// </summary> 59 Vector2 screenExtendRTPosAtMoveArea; 60 /// <summary> 61 /// 实际可视区域的扩展比例 62 /// </summary> 63 float screenExtendRatio; 64 /// <summary> 65 /// 地图的尺寸 66 /// </summary> 67 Vector2 mapSize; 68 /// <summary> 69 /// 铅直方向上的格子数量 70 /// </summary> 71 int mYGridCount; 72 /// <summary> 73 /// 水平方向上的格子数量 74 /// </summary> 75 int mXGridCount; 76 /// <summary> 77 /// 格子出现的左下位置 78 /// </summary> 79 Vector2 gridLBPos; 80 /// <summary> 81 /// 格子出现的右上位置 82 /// </summary> 83 Vector2 gridRTPos; 84 /// <summary> 85 /// 使用中的UI格子 86 /// </summary> 87 List<GridItem> useGridList = new List<GridItem>(); 88 /// <summary> 89 /// 未使用的UI格子 90 /// </summary> 91 List<GridItem> unUsedGridList = new List<GridItem>(); 92 /// <summary> 93 /// 格子的配置数据 94 /// </summary> 95 GridData mGridData = new GridData(); 96 void Start() 97 { 98 Init(); 99 OnScrollValueChanged(); 100 } 101 102 void Init() 103 { 104 RawImage_GridTemplate.gameObject.SetActive(false); 105 106 mXGridCount = Mathf.CeilToInt(mGridData.ImageWidth * 1.0f / mGridData.GridWidth); 107 mYGridCount = Mathf.CeilToInt(mGridData.ImageHeight * 1.0f / mGridData.GridHeight); 108 109 mapSize = new Vector2(mGridData.ImageWidth, mGridData.ImageHeight); 110 screenSize = UIManager.Instance.UIUGUIRootScene.sizeDelta; 111 scrollRect.content.sizeDelta = mapSize; 112 scrollRectMoveArea = mapSize - screenSize; 113 screenExtendRatio = 0.1f; 114 screenExtendSize = screenSize * screenExtendRatio; 115 scrollRect.onValueChanged.RemoveAllListeners(); 116 scrollRect.onValueChanged.AddListener(OnScrollValueChanged); 117 } 118 void OnScrollValueChanged(Vector2 vec2) 119 { 120 OnScrollValueChanged(); 121 } 122 123 void OnScrollValueChanged() 124 { 125 //ScrollRect当前在可是区域滑动的比例 126 float xPox = scrollRect.horizontalNormalizedPosition; 127 float yPos = scrollRect.verticalNormalizedPosition; 128 129 //视口在可移动区域内的位置 130 screenLBPosAtMoveArea.x = scrollRectMoveArea.x * xPox; 131 screenLBPosAtMoveArea.y = scrollRectMoveArea.y * yPos; 132 screenRTPosAtMoveArea = screenLBPosAtMoveArea + screenSize; 133 134 //扩展视口在可移动区域内的位置 135 screenExtendLBPosAtMoveArea = screenLBPosAtMoveArea - screenExtendSize; 136 screenExtendRTPosAtMoveArea = screenRTPosAtMoveArea + screenExtendSize; 137 138 //计算格子出现的位置:左下 右上 139 gridLBPos.x = Mathf.FloorToInt(screenExtendLBPosAtMoveArea.x / mGridData.GridWidth); 140 gridLBPos.y = Mathf.FloorToInt(screenExtendLBPosAtMoveArea.y / mGridData.GridHeight); 141 gridRTPos.x = Mathf.CeilToInt(screenExtendRTPosAtMoveArea.x / mGridData.GridWidth); 142 gridRTPos.y = Mathf.CeilToInt(screenExtendRTPosAtMoveArea.y / mGridData.GridHeight); 143 144 //分别计算左下格子和右上格子的索引 145 int iMin = (int)gridLBPos.x; 146 int iMax = (int)gridRTPos.x; 147 int jMin = (int)gridLBPos.y; 148 int jMax = (int)gridRTPos.y; 149 150 //刷新格子 151 RefreshGrid(iMin, iMax, jMin, jMax); 152 } 153 154 void RefreshGrid(int iMin, int iMax, int jMin, int jMax) 155 { 156 //先全部重置所有格子为 未使用 157 for (int i = 0; i < useGridList.Count; ++i) 158 { 159 GridItem item = useGridList[i]; 160 item.isUsed = false; 161 unUsedGridList.Add(item); 162 } 163 164 //整个扩展可视区域内需要的格子数量 165 int needCount = 0; 166 for (int i = iMin; i <= iMax; ++i) 167 { 168 for (int j = jMin; j <= jMax; ++j) 169 { 170 if (i < 0 || i >= mXGridCount || j < 0 || j >= mYGridCount) 171 { 172 continue; 173 } 174 175 ++needCount; 176 //格子在可移动区域的索引 177 int index = (mYGridCount - j - 1) * mXGridCount + i; 178 for (int k = 0; k < useGridList.Count; ++k) 179 { 180 GridItem item = useGridList[k]; 181 if (index == item.index) 182 { 183 unUsedGridList.Remove(item); 184 item.isUsed = true; 185 break; 186 } 187 } 188 } 189 } 190 191 //不够的话补够 192 for (int i = useGridList.Count; i < needCount; ++i) 193 { 194 GridItem item = GetItem(); 195 useGridList.Add(item); 196 } 197 198 //刷新格子的信息 199 bool isRightInfo = false; 200 GridItem firstFreeItem = null; 201 for (int i = iMin; i <= iMax; ++i) 202 { 203 for (int j = jMin; j <= jMax; ++j) 204 { 205 if (i < 0 || i >= mXGridCount || j < 0 || j >= mYGridCount) 206 { 207 continue; 208 } 209 210 isRightInfo = false; 211 firstFreeItem = null; 212 int index = (mYGridCount - j - 1) * mXGridCount + i; 213 for (int k = 0; k < useGridList.Count; ++k) 214 { 215 GridItem item = useGridList[k]; 216 217 if (null == firstFreeItem && !item.isUsed) 218 { 219 firstFreeItem = item; 220 } 221 222 if (item.isUsed && (index == item.index)) 223 { 224 isRightInfo = true; 225 break; 226 } 227 } 228 229 if (!isRightInfo) 230 { 231 if (null != firstFreeItem) 232 { 233 firstFreeItem.SetInfo(index, mXGridCount); 234 } 235 } 236 } 237 } 238 } 239 240 GridItem GetItem() 241 { 242 for (int i = unUsedGridList.Count - 1; i >= 0;) 243 { 244 GridItem tmpItem = unUsedGridList[i]; 245 tmpItem.Clear(); 246 unUsedGridList.Remove(tmpItem); 247 return tmpItem; 248 } 249 250 GridItem item = new GridItem(RawImage_GridTemplate, mGridData); 251 return item; 252 } 253 254 public void OnSliderValueChange() 255 { 256 float scale = Slider_Scale.value / Slider_Scale.maxValue; 257 scrollRect.viewport.localScale = new Vector3(scale, scale, 1); 258 Rect baseRect = (scrollRect.transform as RectTransform).rect; 259 scrollRect.viewport.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, baseRect.width / scale); 260 scrollRect.viewport.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, baseRect.height / scale); 261 ScreenRatio = 1 / scale; 262 } 263 } 264 }