前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >unity3d:小地图UV,UGUIshader毒圈挖孔,吃鸡跑毒缩圈

unity3d:小地图UV,UGUIshader毒圈挖孔,吃鸡跑毒缩圈

作者头像
立羽
发布2023-08-24 15:44:43
2250
发布2023-08-24 15:44:43
举报
文章被收录于专栏:Unity3d程序开发Unity3d程序开发

运行效果

场景中缩圈

在这里插入图片描述
在这里插入图片描述

小地图中挖孔

在这里插入图片描述
在这里插入图片描述

大地图中挖孔

在这里插入图片描述
在这里插入图片描述

小地图

方案1使用Mask

给了一个方形的mask组件,然后根据玩家位置计算出地图左下角的位置进行移动。这种实现方式虽然简单,但是会有两个问题: 1.Overdraw特别大,几乎很多时候会有整个屏幕的overdraw; 2.玩家在移动过程中,因为一直在持续移动图片的位置(做了适当的降频处理),所以会一直有UI的Mesh重建过程。

方案2使用RawImage,UV

小地图使用RawImage,设置显示大小为300*300,其中Texture,放入场景的顶视图

在这里插入图片描述
在这里插入图片描述

如何确定小地图的UV范围

1.先确定w的值为0.1,代表会从整个顶视图中取宽度占比0.1 2.再确定h的值,因为地图长宽不相等,按照 w 总宽 = h*总高,这样可以得到一个正方形显示

代码语言:javascript
复制
        public float m_mapWidth = 1680; //世界中场景的宽度,米
        public float m_mapHeight = 960; //世界中场景的高度,米
        m_xScale = 0.1f;
        m_yScale = m_xScale * m_mapWidth / m_mapHeight;

这样得到w = 0.1,h = 0.175

如何确定小地图的UV中X,Y

代码语言:javascript
复制
   public void SetMeInMini()
   {
       float posPlayerX = m_player.position.x / m_mapWidth;
       float posPlayerY = m_player.position.z / m_mapHeight;

       m_imgMap.uvRect = new Rect(posPlayerX - m_xScale / 2, posPlayerY - m_yScale / 2, m_xScale, m_yScale);
       Vector3 oriArrow = m_playerArrow.transform.eulerAngles;
       oriArrow.z = -m_player.eulerAngles.y;
       m_playerArrow.eulerAngles = oriArrow;
   }

假设地图的起点是从0点开始(实际项目时可以加偏移值,x,y,代表3d场景是从偏移点开始,并且增加3d场景的实际宽高)。这里为了简单实现,未加偏移值,与实际宽高

在这里插入图片描述
在这里插入图片描述

因为地图从0点开始 x方向,我占地图的百分比 float posPlayerX = m_player.position.x / m_mapWidth; y方向,我占地图的百分比float posPlayerY = m_player.position.z / m_mapHeight; m_imgMap.uvRect = new Rect(posPlayerX - m_xScale / 2, posPlayerY - m_yScale / 2, m_xScale, m_yScale); x方向百分比-m_xScale的一半,即为uv的x y方向百分比-m_yScale的一半,即为uv的y 这样显示出玩家的位置,一定是在小地图的中间,并加上箭头表示我的方向

在这里插入图片描述
在这里插入图片描述

如何确定地图上目标在小地图位置

把目标的世界坐标,转换成小地图的localPosition

代码语言:javascript
复制
    public Vector3 GetTarget2MiniMapPoint(Vector3 targetWorldPos,Vector3 posPlayer)
    {
        //世界坐标上与me的差,
        float x = (targetWorldPos.x - posPlayer.x) * m_meter2Pixel;
        float y = ( targetWorldPos.z  - posPlayer.z) * m_meter2Pixel;
        return new Vector3(x, y,0);
    }

小地图大小为300*300,因为是正方形,所以300像素单位最多代表的米为

代码语言:javascript
复制
    public float m_mapWidth = 1680; //世界中场景的宽度,米
    public float m_xScale = 0; //表示 uv 的取值w, 0-1,例如x = 0.1
    public float m_totalMeter = 0; //小地图,总像素长代表的米  
    m_totalMeter = m_mapWidth * m_xScale; 

那么每个像素单位可代表的米

代码语言:javascript
复制
m_viewWidth = m_imgMap.rectTransform.rect.width;
 m_meter2Pixel = m_viewWidth / m_totalMeter;

用目标的世界坐标x-我的世界坐标x,结果*m_meter2Pixel,则为目标在小地图localPosition

在这里插入图片描述
在这里插入图片描述

这里需要注意: 1.小地图的Pivot,min,max为0.5,才能让localPosition等于anchoredPosition,否则只能用anchoredPosition设置目标在小地图位置

在这里插入图片描述
在这里插入图片描述

2.目标点localPosition超过小地图的长宽,可以设置该点显示隐藏。或者使用RestMask2d

大地图

点击小地图,可展开大地图

如何确定我大地图的localPosition

在这里插入图片描述
在这里插入图片描述

世界坐标单位米与大地图上像素对应

代码语言:javascript
复制
float m_widthPixel = 1680; //大地图顶视图这张图的尺寸,单位像素
    float m_heightPixel = 960;
        public float m_widthScene = 1680; //场景中真实的最大宽,单位米
    public float m_heightScene = 960;
     public float m_scale = 0; //1米可对应多少像素。单位为  像素/米
     m_scale = m_widthPixel / m_widthScene;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

大地图采用真实像素大小,1680,960。虽然在1280*720的视图中有些边界显示不到,那是项目设计如此,边界不可达到

世界坐标转大地图localPosition

代码语言:javascript
复制
   public Vector2 PosWorld2Local(Vector2 pos)
   {
       Vector2 ret = Vector2.zero;
       float x = pos.x * m_scale;
       x -= m_widthPixel / 2;

       float y = pos.y * m_scale;
       y -= m_heightPixel / 2;

       ret = new Vector2(x, y);
       return ret;
   }

目标位置x * m_scale,可得到像素坐标x,-m_widthPixel / 2,地图bg的像素的一半,可得到在大地图中的localPosition

缩圈机制

1.小圆一定是全部包含在大圆内部。运动的是大圆,直到大圆与小圆圆心,半径重合 2.缩圈运动分两个阶段,第一阶段为向内切运动:大圆圆心不变,按照速度缩小大圆半径,直到大圆半径 = 圆心距离+小圆半径 3.第二阶段为先小圆运动:大圆圆心向着小圆圆心移动,同时大圆半径缩小,直到大圆半径= 小圆半径

第一阶段内切运动

在这里插入图片描述
在这里插入图片描述

小圆一开始在大圆内部,如果大圆半径R1> 小圆半径R2+圆心距离,说明还处在第一阶段向内切运动,否则转向第二阶段,向小圆运动

第二阶段向小圆运动

在这里插入图片描述
在这里插入图片描述

大圆的圆心P1向小圆圆心P2移动,每帧半径减少 float diffBigR = m_circleData.speed * Time.deltaTime 那么大圆圆心在x,y方向上变化量为 m_circleData.bigPos.x += diffBigR * m_circleData.cos; m_circleData.bigPos.y += diffBigR * m_circleData.sin;

缩圈数据

代码语言:javascript
复制
public class CircleData
{
    public float bigR; //大圆半径
    public Vector2 bigPos; //世界坐标,大圆圆心

    public float smallR; //小圆半径
    public Vector2 smallPos;//世界坐标,小圆圆心

    public float time = 10; //总共运动时间
    public float speed; //速度
    public float dis; //两圆心初始距离
    public float cos;  //大圆移动x上投影分量
    public float sin; //大圆移动y上投影分量

速度speed即为每帧大圆半径需要减少数值,用半径差/总时间

代码语言:javascript
复制
speed = (bigR - smallR) / time;

dis两圆心初始距离

代码语言:javascript
复制
dis = Vector2.Distance(bigPos, smallPos);

cos,sin的运动分量 内切运动运动结束后,每次大圆圆心,x方向+半径变化值*cos,即为新的大圆圆心x位置 有以下几种状态

代码语言:javascript
复制
if (bigPos.x == smallPos.x && bigPos.y != smallPos.y)
        {
            cos = 0;
            sin = 1;
        }
        else if (bigPos.x != smallPos.x && bigPos.y == smallPos.y)
        {
            cos = 1;
            sin = 0;
        }
        else if (bigPos.x == smallPos.x && bigPos.y == smallPos.y)
        {
            cos = 0;
            sin = 0;
        }
        else 
        {
            float a = smallPos.x - bigPos.x;
            float b = smallPos.y - bigPos.y;
            
            cos = a / dis;
            sin = b /dis;
        }

大圆运动

在update中执行 1.每帧大圆半径的变化量为m_circleData.speed * Time.deltaTime 2.大圆半径的减少为m_circleData.bigR -= diffBigR; 3.当是向内切阶段运动,则只会减少大圆半径 4.当是向小圆阶段运动,减少大圆半径的同时,大圆圆心运动 m_circleData.bigPos.x += diffBigR * m_circleData.cos; m_circleData.bigPos.y += diffBigR * m_circleData.sin; 5.直到大圆半径《=小圆半径,说明运动结束

代码语言:javascript
复制
float diffBigR = m_circleData.speed * Time.deltaTime;
            m_circleData.bigR -= diffBigR;
            if (m_circleData.bigR > m_circleData.smallR + m_circleData.dis)
            {
                Debug.Log("内切");
                UpdateTransCircle();
            }
            else if (m_circleData.bigR > m_circleData.smallR && m_circleData.bigR <= m_circleData.smallR + m_circleData.dis)
            {
                Debug.Log("小圆");
                m_circleData.bigPos.x += diffBigR * m_circleData.cos;
                m_circleData.bigPos.y += diffBigR * m_circleData.sin;
                UpdateTransCircle();
            }
            else if (m_circleData.bigR <= m_circleData.smallR)
            {
                m_isMove = false;
            }

毒圈在场景中表现 为一个去掉上顶,下底的圆柱体,每次移动改变它的坐标与缩放

代码语言:javascript
复制
    void UpdateTransCircle()
    {
        m_transCircle.position = new Vector3(m_circleData.bigPos.x, 0, m_circleData.bigPos.y);
        m_transCircle.localScale = new Vector3(m_circleData.bigR * 2, 1, m_circleData.bigR * 2);
    }
在这里插入图片描述
在这里插入图片描述

UGUI上毒圈挖孔

效果

小地图上显示

在这里插入图片描述
在这里插入图片描述

大地图上显示

在这里插入图片描述
在这里插入图片描述

其中白圈为小圆,即最终安全区 外围大圈会大圆不断缩小移动

小地图Mask

使用跟小地图同样像素大小的RawImage。

在这里插入图片描述
在这里插入图片描述

大地图Mask

在这里插入图片描述
在这里插入图片描述

可以使用不同像素大小,例如屏幕分辨率为1280720,大地图bg实际为1680960。但是他们的中心点是一致的,这样传递的localPosition在大地图上,和传递到Mask上是对等的

在这里插入图片描述
在这里插入图片描述

shader处理

代码语言:javascript
复制
v2f vert(appdata_t v)
				{
					v2f o;
					o.vertex = UnityObjectToClipPos(v.vertex);
					o.worldPosition = mul(unity_ObjectToWorld, v.vertex);
					o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
					o.color = v.color;
					return o;
				}

			
				fixed4 frag(v2f i) : SV_Target
				{
					//显示白圈
					float2 diffCurWithSmallllCenter = float2(i.worldPosition.x, i.worldPosition.y) - float2(_CenterSmall.x, _CenterSmall.y);
					float disCurWithSmallCenter = sqrt(diffCurWithSmallllCenter.x * diffCurWithSmallllCenter.x + diffCurWithSmallllCenter.y * diffCurWithSmallllCenter.y);
					if (abs(disCurWithSmallCenter - _SmallR) < _SmallWidth )
					{
						return float4(1, 1, 1, 1);
					}


				   float2 center = float2(i.worldPosition.x, i.worldPosition.y) - float2(_Center.x, _Center.y);
				   float disSquare = (center.x * center.x + center.y * center.y);

				   //比较距离大小,可以不用开平方
				   clip(disSquare - _BigRSquare);

				   fixed4 col = tex2D(_MainTex, i.texcoord) * i.color;
				   return col;
				}

显示小圆白圈

当前世界坐标与小圆圆心的坐标相差

代码语言:javascript
复制
float2 diffCurWithSmallllCenter = float2(i.worldPosition.x, i.worldPosition.y) - float2(_CenterSmall.x, _CenterSmall.y);

当前世界坐标与小圆圆心距离disCurWithSmallCenter,坐标开平方

代码语言:javascript
复制
float disCurWithSmallCenter = sqrt(diffCurWithSmallllCenter.x * diffCurWithSmallllCenter.x + diffCurWithSmallllCenter.y * diffCurWithSmallllCenter.y);

disCurWithSmallCenter - 小圆半径的绝对值,< _SmallWidth,直接返回白色

代码语言:javascript
复制
					if (abs(disCurWithSmallCenter - _SmallR) < _SmallWidth )
					{
						return float4(1, 1, 1, 1);
					}

因为小圆是要距离差的绝对值与圈宽_SmallWidth比较,所以不能使用平方比较

大圆挖孔

当前世界坐标与大圆圆心坐标差

代码语言:javascript
复制
float2 center = float2(i.worldPosition.x, i.worldPosition.y) - float2(_Center.x, _Center.y);

当前世界坐标与大圆圆心的距离的平方

代码语言:javascript
复制
 float disSquare = (center.x * center.x + center.y * center.y);

距离的平方<大圆半径的平方,则舍弃

代码语言:javascript
复制
//比较距离大小,可以不用开平方
clip(disSquare - _BigRSquare);

因为只要比较距离的平方的大小,这样省去一步开平方计算,提高点性能

C#中传递参数给Shader

代码语言:javascript
复制
    public void SetClip(Vector2 vec, float radius, Vector2 smallPos,float smallR)
    {
        Vector3 centerWrold = transform.TransformPoint(new Vector3(vec.x,vec.y,0));
        m_mat.SetVector("_Center", centerWrold);
        Vector3 pointInCircle = new Vector3(vec.x + radius, vec.y,0);
        Vector3 pointInCircleWorld = transform.TransformPoint(pointInCircle);
        Vector2 diffVecWorld = centerWrold - pointInCircleWorld;
        float bigRSquare = diffVecWorld.x * diffVecWorld.x + diffVecWorld.y * diffVecWorld.y;
        m_mat.SetFloat("_BigRSquare", bigRSquare);

        Vector3 smallPosWorld = transform.TransformPoint(new Vector3(smallPos.x, smallPos.y, 0));
        m_mat.SetVector("_CenterSmall", smallPosWorld);
        Vector3 pointInCircleSmall = new Vector3(smallPos.x + smallR, smallPos.y, 0);
        Vector3 pointInCircleWorldSamll = transform.TransformPoint(pointInCircleSmall);
        Vector2 diffVecWorldSmall = smallPosWorld - pointInCircleWorldSamll;
        float smallRSquare = diffVecWorldSmall.x * diffVecWorldSmall.x + diffVecWorldSmall.y * diffVecWorldSmall.y;
        float smallRWorld = Vector2.Distance(smallPosWorld, pointInCircleWorldSamll);
        m_mat.SetFloat("_SmallR", smallRWorld);
    }

传递圆心坐标值

不管大地图,小地图,传递的坐标为基于地图的localPosition,所以都要用Mask的transform转为世界坐标。mask即是挖孔

代码语言:javascript
复制
Vector3 centerWrold = transform.TransformPoint(new Vector3(vec.x,vec.y,0));

传递半径值

大圆

传入参数radius是指UI上的像素值,先用圆心x+半径,得到半径上一点,在UI上localPosition

代码语言:javascript
复制
Vector3 pointInCircle = new Vector3(vec.x + radius, vec.y,0);

再转为mask世界坐标上一点

代码语言:javascript
复制
Vector3 pointInCircleWorld = transform.TransformPoint(pointInCircle);

大圆只需要传递世界坐标下半径的平方,在shader做为世界坐标差平方,与大圆半径平方比较,进行clip。shader中减少一次开根运算

代码语言:javascript
复制
Vector2 diffVecWorld = centerWrold - pointInCircleWorld;
        float bigRSquare = diffVecWorld.x * diffVecWorld.x + diffVecWorld.y * diffVecWorld.y;
        m_mat.SetFloat("_BigRSquare", bigRSquare);
小圆

小圆因为要画一个白圈,具有一定的宽度,所以只能传递小圆半径 传入半径smallR,是UI上像素长 先用圆心x+半径,得到半径上一点,在UI上localPosition

代码语言:javascript
复制
Vector3 pointInCircleSmall = new Vector3(smallPos.x + smallR, smallPos.y, 0);

再转基于mask的世界坐标

代码语言:javascript
复制
Vector3 pointInCircleWorldSamll = transform.TransformPoint(pointInCircleSmall);

算出半径上一点,与圆心的世界坐标上差距,即为小圆世界坐标下半径长,传入shader

代码语言:javascript
复制
Vector3 pointInCircleWorldSamll = transform.TransformPoint(pointInCircleSmall);
        float smallRWorld = Vector2.Distance(smallPosWorld, pointInCircleWorldSamll);
        m_mat.SetFloat("_SmallR", smallRWorld);

小地图传递值

大圆的世界坐标圆心,转小地图的localPosition

代码语言:javascript
复制
Vector3 bigV3 = new Vector3(CircleMgr.instance.m_circleData.bigPos.x, 0, CircleMgr.instance.m_circleData.bigPos.y);
            Vector3 bigPos = GetTarget2MiniMapPoint(bigV3, m_player.position);

大圆的半径转成小地图的像素值

代码语言:javascript
复制
float bigR = CircleMgr.instance.m_circleData.bigR * m_meter2Pixel;

小圆同理,传递进入Mask

代码语言:javascript
复制
            Vector3 bigV3 = new Vector3(CircleMgr.instance.m_circleData.bigPos.x, 0, CircleMgr.instance.m_circleData.bigPos.y);
            Vector3 bigPos = GetTarget2MiniMapPoint(bigV3, m_player.position);
            float bigR = CircleMgr.instance.m_circleData.bigR * m_meter2Pixel;

            Vector3 smallV3 = new Vector3(CircleMgr.instance.m_circleData.smallPos.x, 0, CircleMgr.instance.m_circleData.smallPos.y);
            Vector3 smallPos = GetTarget2MiniMapPoint(smallV3, m_player.position);
            float smallR = CircleMgr.instance.m_circleData.smallR * m_meter2Pixel;

            m_circleClip.SetClip(bigPos, bigR, smallPos, smallR);

大地图传递值

大圆圆心世界坐标转地图上localPosition

代码语言:javascript
复制
Vector2 bigPos = PosWorld2Local(CircleMgr.instance.m_circleData.bigPos);

大圆半径世界坐标(米为单位)转地图上像素单位

代码语言:javascript
复制
float bigR = CircleMgr.instance.m_circleData.bigR* m_scale;

小圆同理,传递进入Mask

代码语言:javascript
复制
            Vector2 bigPos = PosWorld2Local(CircleMgr.instance.m_circleData.bigPos);
            Vector2 smallPos = PosWorld2Local(CircleMgr.instance.m_circleData.smallPos);
            float bigR = CircleMgr.instance.m_circleData.bigR* m_scale;
            float smallR = CircleMgr.instance.m_circleData.smallR* m_scale;

            m_circleClip.SetClip(bigPos,bigR, smallPos, smallR);
本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-08-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 运行效果
    • 场景中缩圈
      • 小地图中挖孔
        • 大地图中挖孔
        • 小地图
          • 方案1使用Mask
            • 方案2使用RawImage,UV
              • 如何确定小地图的UV范围
              • 如何确定小地图的UV中X,Y
              • 如何确定地图上目标在小地图位置
          • 大地图
            • 如何确定我大地图的localPosition
              • 世界坐标单位米与大地图上像素对应
                • 世界坐标转大地图localPosition
            • 缩圈机制
              • 第一阶段内切运动
                • 第二阶段向小圆运动
                  • 缩圈数据
                    • 大圆运动
                    • UGUI上毒圈挖孔
                      • 效果
                        • 小地图Mask
                          • 大地图Mask
                            • shader处理
                              • 显示小圆白圈
                              • 大圆挖孔
                            • C#中传递参数给Shader
                              • 传递圆心坐标值
                              • 传递半径值
                              • 小地图传递值
                              • 大地图传递值
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
                          http://www.vxiaotou.com