项目中摘取出来的部分 效果如下,做了之后就感觉不难,但还是记录一下结果,指不定哪天就能用到, 有点类似于PC游戏上,跟随鼠标运动的UI在边界时候做的反应, 或者topdown游戏里面的跟随 目前直接更改位置有点生硬,可以加个过度的动画。
思路
有横向两个标志位左右,竖直两个标志位上下,组成四个不同的情况,各对应不同的偏移值。 当触碰到屏幕边距的时候,改变标志位,然后根据标志位重新刷新偏移值,蓝色方块根据红色方块和偏移值来确定自身位置
代码
using System;
using UnityEngine;
public partial class VerBOutBoundUI
{
private Const.HorizontalPosFlag currentHorizontalPosFlagVerB;
private Const.VerticalPosFlag currentVerticalPosFlagVerB;
[SerializeField] private RectTransform toAlignRecttransform;
[SerializeField] private RectTransform targetRecttransform;
private void Awake()
{
InitPosOffset();
}
private void Update()
{
JudgeOutScreenByBounce();
toAlignRecttransform.position = targetRecttransform.position + new Vector3(
currentPosOffset.x,
currentPosOffset.y,
0);
}
void JudgeOutScreenByBounce()
{
JudgeImageVerticalOutOfScreenByBounceVerB();
JudgeImageHorizontalOutOfScreenByBounceVerB();
ResetOffsetPosVerB();
}
void JudgeImageVerticalOutOfScreenByBounceVerB()
{
if (toAlignRecttransform != null)
{
Vector3 worldPos = toAlignRecttransform.position;
float halfHeight = toAlignRecttransform.rect.height / 2f;
float topPlaceYPos = worldPos.y + halfHeight;
float bottomPlaceYPos = worldPos.y - halfHeight;
#if !UNITY_EDITOR
float maxHeight = Screen.currentResolution.height;
#else
int canvasDisplay = RootTransformCanvas.targetDisplay;
float maxHeight = 0;
if (Display.displays[canvasDisplay] != null)
{
maxHeight = Display.displays[canvasDisplay].renderingHeight;
}
#endif
Vector3 screenPos = RectTransformUtility.WorldToScreenPoint(Camera.main, worldPos);
Debug.Log(" topPlaceHeight " + topPlaceYPos +
" bottomPlaceYPos " + bottomPlaceYPos +
" maxHeight " + maxHeight +
" worldPos " + worldPos +
" halfHeight " + halfHeight +
" screenPos " + screenPos);
if (topPlaceYPos > maxHeight)
{
currentVerticalPosFlagVerB = Const.VerticalPosFlag.Bottom;
}
else if (bottomPlaceYPos < 0)
{
currentVerticalPosFlagVerB = Const.VerticalPosFlag.Top;
}
}
}
void JudgeImageHorizontalOutOfScreenByBounceVerB()
{
Vector3 worldPos = toAlignRecttransform.position;
float halfWidth = toAlignRecttransform.rect.width / 2f;
float rightPlaceXPos = worldPos.x + halfWidth;
float leftPlaceXPos = worldPos.x - halfWidth;
#if !UNITY_EDITOR
float maxWidth = Screen.currentResolution.width;
#else
float maxHeight = 0;
int canvasDisplay = RootTransformCanvas.targetDisplay;
float maxWidth = 0;
if (Display.displays[canvasDisplay] != null)
{
maxWidth = Display.displays[canvasDisplay].renderingWidth;
}
#endif
if (rightPlaceXPos > maxWidth)
{
currentHorizontalPosFlagVerB = Const.HorizontalPosFlag.Left;
}
else if (leftPlaceXPos < 0)
{
currentHorizontalPosFlagVerB = Const.HorizontalPosFlag.Right;
}
}
void ResetOffsetPosVerB()
{
switch (currentVerticalPosFlagVerB)
{
case Const.VerticalPosFlag.Top:
switch (currentHorizontalPosFlagVerB)
{
case Const.HorizontalPosFlag.Left:
currentPosOffset = topLeftPosOffset;
break;
case Const.HorizontalPosFlag.Right:
currentPosOffset = topRightPosOffset;
break;
}
break;
case Const.VerticalPosFlag.Bottom:
switch (currentHorizontalPosFlagVerB)
{
case Const.HorizontalPosFlag.Left:
currentPosOffset = bottomLeftPosOffset;
break;
case Const.HorizontalPosFlag.Right:
currentPosOffset = bottomRightPosOffset;
break;
}
break;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public partial class VerBOutBoundUI : MonoBehaviour
{
[Header("预览窗口位置偏移")]
[SerializeField]
private float topPosYDelta = 300;
[SerializeField] private float bottomPosYDelta = -300;
[SerializeField] private float leftPosXDelta = -570;
[SerializeField] private float rightPosXDelta = 570;
[SerializeField] private Canvas RootTransformCanvas;
private Vector3 currentPosOffset;
private Vector2 topLeftPosOffset;
private Vector2 topRightPosOffset;
private Vector2 bottomRightPosOffset;
private Vector2 bottomLeftPosOffset;
void InitPosOffset()
{
topLeftPosOffset = new Vector2(leftPosXDelta, topPosYDelta);
topRightPosOffset = new Vector2(rightPosXDelta, topPosYDelta);
bottomRightPosOffset = new Vector2(rightPosXDelta, bottomPosYDelta);
bottomLeftPosOffset = new Vector2(leftPosXDelta, bottomPosYDelta);
ResetOffsetPosByResolution();
currentHorizontalPosFlagVerB = Const.HorizontalPosFlag.Right;
currentVerticalPosFlagVerB = Const.VerticalPosFlag.Top;
}
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
topLeftPosOffset.x *= ratioWidth;
topLeftPosOffset.y *= ratioHeight;
topRightPosOffset.x *= ratioWidth;
topRightPosOffset.y *= ratioHeight;
bottomRightPosOffset.x *= ratioWidth;
bottomRightPosOffset.y *= ratioHeight;
bottomLeftPosOffset.x *= ratioWidth;
bottomLeftPosOffset.y *= ratioHeight;
}
}
注意点
在Unity中, 当CanvasSacler的ScaleMode值为Constant Pixel Size并且其他属性如下
, 并且Canvas的RenderMode为Overlay类型的UI画布时,屏幕分辨率和UI的position和RectTransform的rect的宽高属性,才是在同一个单位下的,可以直接进行运算;
或者如下图所示,当运行的分辨率与CanvasSacler的参考分辨率相同的时候,也有这三个属性值是在同一个单位下的特性 我的工程是在这种情况下进行的,如果是在其他情况下,应该是要用RectTransformUtility进行一些世界坐标到屏幕坐标的相应转换。
示例工程
|