效果
效果如图所示
蓝色方块跟随红色方块左上角,当蓝色方块左边碰撞到屏幕的时候,其会变成在右边,然后回去到一定距离后又变成在左边。 上下则是出界的时候,蓝色方块不移动,等红色方块下移,并且蓝色方块与红色方块的垂直距离大于一定的时候,蓝色方块才跟随移动。 上方的界限是屏幕顶部,下方是某个自定义的距离。这些都可以根据情况修改参数
思路
这个的思路比一的难了一些,主要是之前没做过类似的,加上状态不好,一开始想法比较混乱。 做了之后看着代码分析就感觉容易了
具体思路是,
每次位移时计算出,新的位移点下,蓝色方块的顶部是否有超出屏幕顶部,底部是否有低于给定的底部。 如果没有则垂直方向上进行新的位移计算并重新赋值给蓝块。
这样的逻辑其实还缺了一部分,测试发现这样的逻辑会导致个问题 鼠标在屏幕上下边缘点击来更改等突然大距离的更改红块而导致的蓝块的竖直方向不改变。 所以在判断上下超界时,还要重新给蓝块赋值个上下的的边界位置值。
代码
参数部分代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public partial class VerAOutBoundUI
{
[Header("预览窗口位置偏移")]
[SerializeField] private float topPosYDelta = 300;
[SerializeField] private float bottomPosYDelta = -300;
[SerializeField] private float leftPosXDelta = -570;
[SerializeField] private float rightPosXDelta = 570;
private Vector2 topLeftPosOffset;
private Vector2 topRightPosOffset;
private Vector2 bottomRightPosOffset;
private Vector2 bottomLeftPosOffset;
[SerializeField] private Canvas _rootTransformCanvas;
public Canvas RootTransformCanvas => _rootTransformCanvas;
void InitPosOffset()
{
ResetOffsetPosByResolution();
topLeftPosOffset = new Vector2(leftPosXDelta, topPosYDelta);
topRightPosOffset = new Vector2(rightPosXDelta, topPosYDelta);
bottomRightPosOffset = new Vector2(rightPosXDelta, bottomPosYDelta);
bottomLeftPosOffset = new Vector2(leftPosXDelta, bottomPosYDelta);
}
void ResetOffsetPosByResolution()
{
#if UNITY_EDITOR
float ratioWidth = 0;
float ratioHeight = 0;
if (RootTransformCanvas != null )
{
int canvasDisplay = RootTransformCanvas.targetDisplay;
if (Display.displays[canvasDisplay] != null)
{
float editorWidth = Display.displays[canvasDisplay].renderingWidth;
float editorHeight = Display.displays[canvasDisplay].renderingHeight;
ratioWidth = (editorWidth * 1f) / (Const.standardResolutionWidth * 1f);
ratioHeight = (editorHeight * 1f) / (Const.standardResolutionHeight * 1f);
}
}
#else
float ratioWidth = (Screen.currentResolution.width * 1f) / (Const.standardResolutionWidth * 1f);
float ratioHeight = (Screen.currentResolution.height * 1f) / (Const.standardResolutionHeight * 1f);
#endif
leftPosXDelta *= ratioWidth;
topPosYDelta *= ratioHeight;
rightPosXDelta *= ratioWidth;
bottomPosYDelta *= ratioHeight;
}
}
左边回弹部分
using UnityEngine;
public partial class VerAOutBoundUI
{
void LeftAdjustPos()
{
if (currentHorizontalPosFlag == Const.HorizontalPosFlag.Right)
{
currentHorizontalPosFlag = Const.HorizontalPosFlag.Left;
ResetOffsetPos();
}
}
void JudgeLeftMoveCorrectByBounce()
{
if (currentHorizontalPosFlag == Const.HorizontalPosFlag.Left)
{
return;
}
if (targetPosTransform && followerRectTransform)
{
#if !UNITY_EDITOR
float horScreenBoundPos = Screen.currentResolution.width;
#else
float horScreenBoundPos = 0;
if (RootTransformCanvas)
{
int canvasDisplay = RootTransformCanvas.targetDisplay;
if (Display.displays[canvasDisplay] != null)
{
horScreenBoundPos = Display.displays[canvasDisplay].renderingWidth;
}
}
#endif
Vector3 worldPos = targetPosTransform.position;
Vector3 currentSimulateLeftPos = new Vector3(worldPos.x + leftPosXDelta,
worldPos.y, worldPos.z);
float screenLeftPosX = 0;
float toAdjustScreenHorizontalPos = screenLeftPosX + halfWidth;
if (currentSimulateLeftPos.x > toAdjustScreenHorizontalPos)
{
LeftAdjustPos();
}
}
}
}
主体
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public partial class VerAOutBoundUI : MonoBehaviour
{
private float halfHeight;
private float halfWidth;
[SerializeField]
private float minPosThreshold = 1000;
[SerializeField]
private RectTransform followerRectTransform;
[SerializeField]
private RectTransform rootCanvasRectTransform;
[SerializeField]
private RectTransform targetPosTransform;
private float topPosYMinusHalfHeight;
private float bottomPosYPlusHalfHeight;
private Const.HorizontalPosFlag currentHorizontalPosFlag;
private Const.VerticalPosFlag currentVerticalPosFlag;
private Vector2 currentPosOffset;
private void Awake()
{
InitOutBoundParam();
InitPosOffset();
}
void InitOutBoundParam()
{
float heightRatio = Utils.GetResolutionHeightRatio(rootCanvasRectTransform);
float widthRatio = Utils.GetResolutionWidthRatio(rootCanvasRectTransform);
halfHeight = followerRectTransform.rect.height / 2f * heightRatio;
halfWidth = followerRectTransform.rect.width / 2f * widthRatio;
int displau1Index = 0;
var maxHeight = Utils.GetGameViewTopBoundYPos(displau1Index);
currentVerticalPosFlag = Const.VerticalPosFlag.Top;
currentHorizontalPosFlag = Const.HorizontalPosFlag.Right;
minPosThreshold *= heightRatio;
topPosYMinusHalfHeight = maxHeight - halfHeight;
bottomPosYPlusHalfHeight = minPosThreshold + halfHeight;
}
private void Update()
{
RefreshPosJudgingOutBounds();
}
#region 垂直判断和操作部分
bool JudgeYPosVerticalOverflow(float yPos)
{
int displau1Index = 0;
var maxHeight = Utils.GetGameViewTopBoundYPos(displau1Index);
if (yPos < maxHeight)
{
return false;
}
return true;
}
bool JudgeYPosVerticalSink(float yPos)
{
if (yPos > minPosThreshold)
{
return false;
}
return true;
}
#endregion
#region 水平判断和操作部分
void JudgeImageHorizontalOutOfScreen()
{
Vector3 worldPos = followerRectTransform.position;
float halfWidth = followerRectTransform.rect.width / 2f;
float rightPlaceXPos = worldPos.x + halfWidth;
float leftPlaceXPos = worldPos.x - halfWidth;
int displau1Index = 0;
var maxWidth = Utils.GetGameViewRightBoundXPos(displau1Index);
if (rightPlaceXPos > maxWidth)
{
currentHorizontalPosFlag = Const.HorizontalPosFlag.Left;
}
else if (leftPlaceXPos < 0)
{
currentHorizontalPosFlag = Const.HorizontalPosFlag.Right;
}
}
#endregion
void ResetOffsetPos()
{
switch (currentVerticalPosFlag)
{
case Const.VerticalPosFlag.Top:
switch (currentHorizontalPosFlag)
{
case Const.HorizontalPosFlag.Left:
currentPosOffset = topLeftPosOffset;
break;
case Const.HorizontalPosFlag.Right:
currentPosOffset = topRightPosOffset;
break;
}
break;
case Const.VerticalPosFlag.Bottom:
switch (currentHorizontalPosFlag)
{
case Const.HorizontalPosFlag.Left:
currentPosOffset = bottomLeftPosOffset;
break;
case Const.HorizontalPosFlag.Right:
currentPosOffset = bottomRightPosOffset;
break;
}
break;
}
}
private void RefreshPosJudgingOutBounds()
{
HorizontalAdjust();
VerticalAdjust();
}
private void VerticalAdjust()
{
followerRectTransform.position = new Vector3(targetPosTransform.position.x + currentPosOffset.x,
followerRectTransform.position.y,
followerRectTransform.position.z);
float topPosNew = targetPosTransform.position.y + currentPosOffset.y + halfHeight;
float bottomPosNew = targetPosTransform.position.y + currentPosOffset.y - halfHeight;
if (!JudgeYPosVerticalOverflow(topPosNew) &&
!JudgeYPosVerticalSink(bottomPosNew))
{
followerRectTransform.position = new Vector3(followerRectTransform.position.x,
targetPosTransform.position.y + currentPosOffset.y,
followerRectTransform.position.z);
}
else if (JudgeYPosVerticalOverflow(topPosNew))
{
followerRectTransform.position = new Vector3(
followerRectTransform.position.x,
topPosYMinusHalfHeight,
followerRectTransform.position.z);
}
else if (JudgeYPosVerticalSink(bottomPosNew))
{
followerRectTransform.position = new Vector3(followerRectTransform.position.x,
bottomPosYPlusHalfHeight,
followerRectTransform.position.z);
}
}
private void HorizontalAdjust()
{
JudgeImageHorizontalOutOfScreen();
ResetOffsetPos();
JudgeLeftMoveCorrectByBounce();
}
}
工程
工程链接
|