IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> [UGUI进阶知识十八]跟随UI的屏幕出界限制判断[二] -> 正文阅读

[游戏开发][UGUI进阶知识十八]跟随UI的屏幕出界限制判断[二]

效果

效果如图所示
请添加图片描述

蓝色方块跟随红色方块左上角,当蓝色方块左边碰撞到屏幕的时候,其会变成在右边,然后回去到一定距离后又变成在左边。
上下则是出界的时候,蓝色方块不移动,等红色方块下移,并且蓝色方块与红色方块的垂直距离大于一定的时候,蓝色方块才跟随移动。
上方的界限是屏幕顶部,下方是某个自定义的距离。这些都可以根据情况修改参数

思路

这个的思路比一的难了一些,主要是之前没做过类似的,加上状态不好,一开始想法比较混乱。
做了之后看着代码分析就感觉容易了

具体思路是,

每次位移时计算出,新的位移点下,蓝色方块的顶部是否有超出屏幕顶部,底部是否有低于给定的底部。
如果没有则垂直方向上进行新的位移计算并重新赋值给蓝块。

这样的逻辑其实还缺了一部分,测试发现这样的逻辑会导致个问题
鼠标在屏幕上下边缘点击来更改等突然大距离的更改红块而导致的蓝块的竖直方向不改变。
所以在判断上下超界时,还要重新给蓝块赋值个上下的的边界位置值。

代码

参数部分代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 提取预览UI处理回弹问题的公共部分
/// </summary>
public partial class VerAOutBoundUI
{
    [Header("预览窗口位置偏移")]
    //这些偏移只适用于在3840*2160的机器上 其他分辨率的机器如1920*1080的机器要做对应的比例换算
    [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);
        
    }

        /// <summary>
        /// 根据屏幕分辨率重新调整偏移值
        /// 因为默认的偏移只适用于在3840*2160的机器上
        /// 其他分辨率的机器如1920*1080的机器要做对应的比例换算
        /// </summary>
         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;

/// <summary>
/// 在Marker距离屏幕右边一定距离往回走一定距离以后
/// 将预览画面自动往右靠 防止遮挡
/// 与VerA处理边界出境的方式联合使用
/// 即与RoamingPreviewCameraUIOutBoundBouncePart一起配合使用
/// </summary>
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
            
            //这里要特别注意 传入的是MarkerPosTransform.position 而不是  HangerRectTransform.position
            Vector3 worldPos = targetPosTransform.position;

            Vector3 currentSimulateLeftPos = new Vector3(worldPos.x + leftPosXDelta,
                worldPos.y, worldPos.z);

            // float halfWidth = RenderUIImageRectTransform.rect.width / 2f;

            //屏幕左边边缘的x坐标是0
            float screenLeftPosX = 0;
            float toAdjustScreenHorizontalPos = screenLeftPosX + halfWidth;

            // print("  currentSimulateRightPos " + currentSimulateLeftPos + 
            //      " halfWidth " + halfWidth + 
            //     " screenBoundPos " + horScreenBoundPos + 
            //      " toAdjustScreenPos " + toAdjustScreenHorizontalPos + 
            //      " worldPos " + worldPos
            //      + " currentSimulateRightPos.x < toAdjustScreenHorizontalPos " + 
            //      (currentSimulateLeftPos.x < toAdjustScreenHorizontalPos));
            

            if (currentSimulateLeftPos.x > toAdjustScreenHorizontalPos)
            {
                LeftAdjustPos();
            }
        }
    }

}

主体

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 处理关于边界出境的问题
/// 上下用贴边对齐和超距尾随的方式进行
/// 左右用回弹的方式进行
/// 这里对这种处理方式成为VerA
/// </summary>
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;
    
    /// <summary>
    /// 跟随物体相对被跟随物体的位置偏移
    /// </summary>
    private Vector2 currentPosOffset;



    private void Awake()
    {
        InitOutBoundParam();
        InitPosOffset();
    }


    void InitOutBoundParam()
    {
        float heightRatio = Utils.GetResolutionHeightRatio(rootCanvasRectTransform);
        float widthRatio = Utils.GetResolutionWidthRatio(rootCanvasRectTransform);

        
        //这里乘以 heightRatio 是因为出现了一种情况 
        // RenderUIImageRectTransform.rect.height和width
        // 在这里不管分辨率是2K还是4K都出现了数值一致的情况
        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;
        
        //这里乘以 heightRatio 是因为minPosThreshold本身是个固定值 本身只有4K才适用
        //在2K的时候要做相应的缩小
        minPosThreshold *= heightRatio;
        
        topPosYMinusHalfHeight = maxHeight - halfHeight;
        bottomPosYPlusHalfHeight = minPosThreshold + halfHeight;
    }


    private void Update()
    {
        RefreshPosJudgingOutBounds();
    }

    #region 垂直判断和操作部分
    
    /// <summary>
    /// 判断位置的y值是否比屏幕顶部的位置坐标大
    /// 即超过屏幕顶部
    /// </summary>
    /// <param name="yPos"></param>
    /// <returns></returns>
    bool JudgeYPosVerticalOverflow(float yPos)
    {
        int displau1Index = 0;

        var maxHeight = Utils.GetGameViewTopBoundYPos(displau1Index);

        if (yPos < maxHeight)
        {
            return false;
        }
        // Debug.Log("JudgeYPosVerticalOverflow yPos " + yPos + " maxHeight " + maxHeight);

        return true;
    }
    
    /// <summary>
    /// 判断位置的y值是否比要求的最小底部位置小
    /// 即超过屏幕底部
    /// </summary>
    /// <param name="yPos"></param>
    /// <returns></returns>
    bool JudgeYPosVerticalSink(float yPos)
    {
        if (yPos > minPosThreshold)
        {
            return false;
        }
        // Debug.Log("JudgeYPosVerticalSink yPos " + yPos + " minPosThreshold " + minPosThreshold);

        return true;
    }
    
    
    
    #endregion


    #region 水平判断和操作部分

    /// <summary>
    /// 判断相机画布是否水平方向超出屏幕
    /// 以及超出屏幕后设置标志位
    /// 要求预览图的pivot在中心
    /// </summary>
    void JudgeImageHorizontalOutOfScreen()
    {
        Vector3 worldPos = followerRectTransform.position;
        float halfWidth = followerRectTransform.rect.width / 2f;
        
        float rightPlaceXPos = worldPos.x + halfWidth;
        float leftPlaceXPos = worldPos.x - halfWidth;

        // Debug.Log(" topPlaceHeight " + topPlaceHeight);
        // Debug.Log(" halfWidth " + 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()
    {
        // print(" RefreshPosJudgingOutBounds not overflow ");

        HorizontalAdjust();

        VerticalAdjust();
    }

    private void VerticalAdjust()
    {
        //x值是一直都要更新的
        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;


        // Debug.Log( " MarkerPosTransform.position.y " + MarkerPosTransform.position.y + 
        //            " currentPosOffset.y " + currentPosOffset.y + 
        //            " halfHeight " + halfHeight);

        //这里更新y值
        //不做判断的话,会出现出界时候与上面两个判断隔一帧互相执行一次的情况
        //即上面判断出界了矫正,然后矫正后不走上面的判断走下面的,然后又出界,如此往复
        if (!JudgeYPosVerticalOverflow(topPosNew) &&
            !JudgeYPosVerticalSink(bottomPosNew))
        {
            followerRectTransform.position = new Vector3(followerRectTransform.position.x,
                targetPosTransform.position.y + currentPosOffset.y,
                followerRectTransform.position.z);
        }
        else if (JudgeYPosVerticalOverflow(topPosNew))
        {
            // Debug.LogError(" ------------------- JudgeYPosVerticalOverflow ");

            followerRectTransform.position = new Vector3(
                followerRectTransform.position.x,
                topPosYMinusHalfHeight,
                followerRectTransform.position.z);
        }
        else if (JudgeYPosVerticalSink(bottomPosNew))
        {
            // Debug.LogError(" ------------------- JudgeYPosVerticalSink ");

            followerRectTransform.position = new Vector3(followerRectTransform.position.x,
                bottomPosYPlusHalfHeight,
                followerRectTransform.position.z);
        }


        //在屏幕边境的时候 不能用currentPosOffset.y = maxHeight - halfHeight - MarkerUIPos.y
        //来更新currentPosOffset.y,然后再用currentPosOffset + MarkerPosTransform.position来更新HangerRectTransform.position
        //因为这个更新代码不管放在unity的哪种类型update方法都会造成预览窗口抖动
        // HangerRectTransform.position = MarkerPosTransform.position + new Vector3(
        //     currentPosOffset.x, 
        //     currentPosOffset.y,
        //     0);
    }

    private void HorizontalAdjust()
    {
        JudgeImageHorizontalOutOfScreen();
        ResetOffsetPos();
        JudgeLeftMoveCorrectByBounce();
    }
}

工程

工程链接

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-04-15 00:35:02  更:2022-04-15 00:36:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:50:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码