项目组对小地图模块增加了一些进阶的功能,这里记录一下需求和制作过程
需求
简单来说就是如下图所示,当触控在地图外面的时候,实际相机的位置应该如图中的白块一样
思路
在一的基础上,将UI映射物的局部坐标限制住即可。因为实际地图本来是由UI映射物的局部坐标来重新映射的。
代码
时间关系直接给出代码吧,Test1只是阶段性的,Test2才是算实验最终版的
遇到的问题也在代码中注释了,另外这个demo只适用于大地图和UI映射物的pivot都是0.5,0.5的情况,其他情况要自行算出矩形的Rect的中心位置
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test1 : MonoBehaviour
{
[SerializeField]
private RectTransform mapRect;
[SerializeField]
private RectTransform smallMapRect;
[SerializeField]
private bool onlyMarginOffset;
[SerializeField]
private float offset;
[SerializeField]
private float currentOffset;
// Start is called before the first frame update
void Start()
{
if (onlyMarginOffset)
{
//尝试一 用对角线长度做偏移值 表现不是想要的
float halfDiagonal = Mathf.Sqrt(Mathf.Pow(smallMapRect.rect.width / 2f, 2) + Mathf.Pow(smallMapRect.rect.height / 2f, 2));
currentOffset = halfDiagonal;
}
else
{
//尝试二 用固定值做偏移值 表现不是想要的
currentOffset = offset;
}
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
bool isIn = RectTransformUtility.RectangleContainsScreenPoint(mapRect, Input.mousePosition);
print(" isIn " + isIn);
if (!isIn)
{
//要注意这一段与下一段的区别,要注意的是
//1. 坐标系,所有参与射线以及赋值的坐标需要在同一坐标系下,这样才有意义 要清楚Overlay的Canvas和Camera的Canvas的position的区别 目前的这个脚本只适用于Overlay
//2. 如果射线是从Collider内部发射出来的,那么碰撞的点只能是发射点,这个是Unity的Raycast的API的规则,
//规则是沿着射线起始点到终点的路径进行步进检测,一旦检测到某个点在某个Collider内了,就停止检测,并返回碰撞信息
//所以它是通过判断是否在内部做的不是精确到是否在边缘这种比较精确的做法
//所以传入Physics2D.Raycast的参数所形成的射线一定要是从collider外部往内部射的
// Vector3 dir = Input.mousePosition - mapRect.position;
// RaycastHit2D hit = Physics2D.Raycast(mapRect.position, dir);
Vector3 dir = mapRect.position - Input.mousePosition;
RaycastHit2D hit = Physics2D.Raycast(Input.mousePosition, dir);
if (hit.collider != null)
{
//尝试一和二 的代码
Vector3 dirVector = new Vector3(hit.point.x , hit.point .y, mapRect.position.z) - mapRect.position;
Vector3 normalizedDirVector = dirVector.normalized;
float newMagnitude = (dirVector.magnitude - currentOffset);
Vector3 newDirVector = normalizedDirVector * newMagnitude;
smallMapRect.position = mapRect.position + newDirVector;
}
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test2 : MonoBehaviour
{
[SerializeField]
private RectTransform mapRect;
[SerializeField]
private RectTransform smallPointRect;
private BoxCollider2D smallPointCollider2D;
private BoxCollider2D mapCollider2D;
private Vector2 lastHitMapBorderPos;
private bool rayCastNextFrame;
// Start is called before the first frame update
void Start()
{
smallPointCollider2D = smallPointRect.GetComponent<BoxCollider2D>();
mapCollider2D = mapRect.GetComponent<BoxCollider2D>();
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButton(0))
{
bool isIn = RectTransformUtility.RectangleContainsScreenPoint(mapRect, Input.mousePosition);
print(" isIn " + isIn);
if (!isIn)
{
//方案三
//对角线判断
Vector3 intoMapDir = mapRect.position - Input.mousePosition;
smallPointCollider2D.enabled = false;
mapCollider2D.enabled = true;
RaycastHit2D hit = Physics2D.Raycast(Input.mousePosition, intoMapDir);
//方案四
if (hit.collider != null)
{
smallPointRect.position = hit.point;
mapCollider2D.enabled = false;
smallPointCollider2D.enabled = true;
Vector3 outOfMapDir = Input.mousePosition - mapRect.position ;
lastHitMapBorderPos = hit.point;
RaycastHit2D hit2 = Physics2D.Raycast(mapRect.position, outOfMapDir);
if (hit2.collider != null )
{
float minusDeltaDistance = (hit2.point - hit.point).magnitude;
Vector2 normalizedDir = (hit.point - hit2.point).normalized;
Vector2 currentDirVector = new Vector3(hit.point.x, hit.point.y, mapRect.position.z) - mapRect.position;
float actualMagnitude = currentDirVector.magnitude - minusDeltaDistance;
Vector2 optimizedVector = actualMagnitude * normalizedDir;
Vector2 optimizedPos = mapRect.position + new Vector3(optimizedVector.x, optimizedVector.y, 0);
smallPointRect.position =
new Vector3(optimizedPos.x, optimizedPos.y, smallPointRect.position.z);
}
}
}
}
}
}
这里Test2的时候方案三是这样的,用矩形的中心点和矩形的四个角分成四个三角形,判断中心点360度被这四个三角形分开的角度, 根据鼠标到中心点的向量与图片这四个三角形某个线的夹角,判断出属于哪个三角形,然后根据三角形进行直接将大地图的边进行内推小映射物的宽度的长度实现将小映射物限制。但是感觉过于复杂,就采取了方案四
Demo