您现在的位置是:首页 >学无止境 >Unity DOTS纯ECS的UI分辨率适配(写一个ECS的CanvasScaler)网站首页学无止境
Unity DOTS纯ECS的UI分辨率适配(写一个ECS的CanvasScaler)
UnityProjectTiny包是一个纯ECS写游戏代码的环境,目前已停止更新很久了,Entities包都更新到1.0去了,Tiny还是用的0.17的,不知后面还会不会更新。不过基本的游戏开发功能都有了,可以用来写些小Demo体验下纯ECS写代码的‘乐趣’…单单Entities包是无法使用纯ECS来写代码的,有很多GameObject组件,像Camera、Mesh、Material都是无法转成Entity。
Tiny包里带有UI模块,但是功能很有限,只有很基础的Button、Slider、Toggle、Image、Text等功能,跟UGUI的功能相比,差距极大,但是可以用。有很多限制,目前所有UI都还不支持旋转跟缩放。只支持一个Root Canvas,其他所有Root Canvas内的UI节点都不能挂Canvas了,挂了也不生效。Canvas的渲染模式只有ScreenSpaceOverlay能用,其他模式或是更不好用,或是还不支持。UI渲染的相机可以是一个单独的Camera,但是Tiny没提供UI在不同分辨率下的自适应功能!也就是Root Canvas上挂的CanvasScaler是不生效的。也就是如果UI是在1920*1080的分辨率下设计的大小,在小于这个分辨率下渲染会变大,在大于这个分辨率下渲染会变小。
不过问题不大,可以自己写一个类似CanvasScaler的组件。代码中的scaleFactor的计算扒了UGUI中的,只支持按宽高来缩放UI大小和计算位置,这种模式的UI在不同分辨率下也不会变形,像那种铺满屏幕的方式是会变形的,感觉没必要,就没加代码了。宽高比写死了0.5,可根据自己的需要进行更改。这里提供的代码只是做演示,可以写更完善一点。把代码丢到项目就中可以了,不用进行其他操作。
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Tiny.UI;
using Unity.Tiny;
namespace TinyUI {
internal struct CanvasScaler : IComponentData {
public float PreviousFactor;
public float ScaleFactor;
public int ScreenWidth;
public int ScreenHeight;
public CanvasScaler(int width, int height) {
PreviousFactor = ScaleFactor = 1;
ScreenWidth = width;
ScreenHeight = height;
}
}
[UpdateInGroup(typeof(TransformSystemGroup))]
[UpdateAfter(typeof(RectangleTransformSystem))]
[UpdateAfter(typeof(RectCanvas))]
[UpdateBefore(typeof(CanvasScaler))]
[UpdateBefore(typeof(UpdateRectTransformSystem))]
internal class AddScalerSystem : SystemBase
{
protected override void OnUpdate()
{
var di = GetSingleton<DisplayInfo>();
var ecb = new EntityCommandBuffer(Allocator.TempJob);
Entities.WithNone<CanvasScaler>().WithAll<RectCanvas>().ForEach((Entity entity) => {
ecb.AddComponent<CanvasScaler>(entity, new CanvasScaler(di.screenWidth, di.screenHeight));
}).WithoutBurst().Run();
ecb.Playback(EntityManager);
ecb.Dispose();
Enabled = false;
}
}
[UpdateInGroup(typeof(TransformSystemGroup))]
[UpdateAfter(typeof(RectangleTransformSystem))]
[UpdateAfter(typeof(RectCanvas))]
[UpdateBefore(typeof(UpdateRectTransformSystem))]
internal class CanvasScalerSystem : SystemBase
{
protected override void OnCreate()
{
base.OnCreate();
RequireSingletonForUpdate<CanvasScaler>();
}
protected override void OnUpdate()
{
var di = GetSingleton<DisplayInfo>();
int referenceWidth = di.screenWidth;
int referenceHeight = di.screenHeight;
int screenWidth = di.frameWidth;
int screenHeight = di.frameHeight;
Entities.ForEach((ref CanvasScaler canvasScaler) => {
if (canvasScaler.ScreenWidth != screenWidth || canvasScaler.ScreenHeight != screenHeight) {
float logWidth = math.log2((float)screenWidth / referenceWidth);
float logHeight = math.log2((float)screenHeight / referenceHeight);
float logWeightedAverage = math.lerp(logWidth, logHeight, 0.5f);
float scaleFactor = math.pow(2, logWeightedAverage);
canvasScaler.PreviousFactor = canvasScaler.ScaleFactor;
canvasScaler.ScaleFactor = scaleFactor;
canvasScaler.ScreenWidth = (int)screenWidth;
canvasScaler.ScreenHeight = (int)screenHeight;
}
}).WithoutBurst().Run();
}
}
[UpdateInGroup(typeof(TransformSystemGroup))]
[UpdateAfter(typeof(RectangleTransformSystem))]
[UpdateAfter(typeof(RectCanvas))]
[UpdateAfter(typeof(CanvasScalerSystem))]
internal class UpdateRectTransformSystem : SystemBase
{
protected override void OnCreate()
{
base.OnCreate();
RequireSingletonForUpdate<CanvasScaler>();
}
protected override void OnUpdate()
{
var cs = GetSingleton<CanvasScaler>();
if (cs.ScaleFactor != cs.PreviousFactor) {
Entities.WithNone<RectCanvas>().WithAll<RectParent>().ForEach((ref RectTransform rectTransform) => {
rectTransform.SizeDelta /= cs.PreviousFactor;
rectTransform.SizeDelta *= cs.ScaleFactor;
rectTransform.AnchoredPosition /= cs.PreviousFactor;
rectTransform.AnchoredPosition *= cs.ScaleFactor;
}).Run();
cs.PreviousFactor = cs.ScaleFactor;
SetSingleton<CanvasScaler>(cs);
}
}
}
}