前言
游戏是竖屏类型,但是我想在电脑上运行也保持竖屏的显示方式。竖屏下其余部分将由纯色或图片来填充。
目的是为了用UI遮盖住区域外的游戏场景的内容。
设计思路
通过shader,对背景填充的Image的图片进行修改。从中抠出符合竖屏拉升后显示的游戏内容区域,能够动态调整保持游戏显示区域。

实现思路
一、确保竖屏显示UI
- Canvas Scaler 设置好长宽比;
- 设置屏幕匹配模式 ‘Match Width Or Height’
- 匹配拉到height位置,数值为1。
这样确保了UI都是保证高度适配屏幕,按长宽比保证显示区域。
二、UI的根节点
UI显示区域的最底层节点Root,用于保持UI显示的长宽比。
- 在Canvas下添加一个Root的空节点
- Root节点添加Aspect Ratio Fitter组件(锁定长宽比),选择 Height Control Width(按照高度匹配宽度),设置数值按照自己的需求,比如9/16=0.5625
- Root节点设置RectTransformd的锚点布局为全拉伸。同时将位置、顶部、底部等设置为0,
- Root节点添加 ‘Rect Mask 2D’ 组件。其作用是确保子节点的UI只显示在Root尺寸的区域内,区域外的不显示。类似于遮罩效果。
这样Root节点便能跟随屏幕变化始终保持最大高度的长宽比显示区域。
UI的搭建就都放在Root节点下即可。
二、背景UI
将背景UI设置的尽可能大
通过shader,以及C#脚本的配合,将Root节点的尺寸作为参照进行对背景UI进行抠图。
实现
一、组件配置
层级布局

Canvas Scaler 设置

Root节点配置

二、背景UI的配置
Shader脚本
负责处理图片的渲染
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| Shader "Unlit/UIBoundMask" { Properties { [PerRendererData] _MainTex ("Texture", 2D) = "white" {} _RectCenter ("Rect Center (x,y)", Vector) = (0.5, 0.5, 0, 0) _RectSize ("Rect Size (width, height)", Vector) = (0.5, 0.5, 0, 0) } SubShader { Tags { "RenderType"="Transparent" "Queue"="Transparent" } LOD 100
Blend SrcAlpha OneMinusSrcAlpha ZWrite Off Cull Off
Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag
#include "UnityCG.cginc"
struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float4 color : COLOR; };
struct v2f { float2 uv : TEXCOORD0; float4 pos : SV_POSITION; float4 color : COLOR; };
sampler2D _MainTex; float2 _RectCenter; float2 _RectSize;
v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.uv; o.color = v.color; return o; }
fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv) * i.color;
float2 uv = i.uv; float left = _RectCenter.x - _RectSize.x * 0.5; float right = _RectCenter.x + _RectSize.x * 0.5; float bottom = _RectCenter.y - _RectSize.y * 0.5; float top = _RectCenter.y + _RectSize.y * 0.5;
float isInsideX = step(left, uv.x) * step(uv.x, right); float isInsideY = step(bottom, uv.y) * step(uv.y, top); float isInside = isInsideX * isInsideY;
if (isInside > 0.5) { col.a = 0; }
return col; } ENDCG } } }
|
创建 UIBoundMask的材质

C#脚本
负责根据UI尺寸调整Shader渲染的图像,抠出合适的区域。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;
[RequireComponent(typeof(RectTransform))] public class UIBoundMask : MonoBehaviour { public Material hollowMaterial; public RectTransform targetRectTransform;
private RectTransform backgroundRectTransform;
void Start() { backgroundRectTransform = GetComponent<RectTransform>(); UpdateShaderProperties(); }
void Update() { }
void OnRectTransformDimensionsChange() { UpdateShaderProperties(); }
void UpdateShaderProperties() { if (hollowMaterial == null || targetRectTransform == null || backgroundRectTransform == null) return;
Rect targetRect = targetRectTransform.rect; Vector2 targetCenter = targetRect.center; Vector2 targetSize = targetRect.size;
Vector2 backgroundSize = backgroundRectTransform.rect.size;
Vector2 localCenter = new Vector2( (targetCenter.x - backgroundRectTransform.rect.xMin) / backgroundSize.x, (targetCenter.y - backgroundRectTransform.rect.yMin) / backgroundSize.y );
Vector2 uvSize = new Vector2( targetSize.x / backgroundSize.x, targetSize.y / backgroundSize.y );
hollowMaterial.SetVector("_RectCenter", new Vector4(localCenter.x, localCenter.y, 0, 0)); hollowMaterial.SetVector("_RectSize", new Vector4(uvSize.x, uvSize.y, 0, 0)); } }
|
背景UI配置
将Shader赋给一个材质,将材质挂载给背景UI的Image中
添加C#脚本,添加好脚本的挂点。

结束
只要运行以下游戏,脚本会修改材质的参数,从而实现抠图。
(由于修改的是材质资源,所以运行一次即可。)
如图:
(六边形、叹号标志都是场景sprite)
