对Unity的UIWidget的卡顿问题优化个人解决办法

本文适合已经会flutter,想在unity中尝试flutter的人群

虽然用UIWidget做的app UnityConnect就要停服了,UIWidget似乎也没什么人维护,Unityconnect github开源代码和flutter对比的话大部分基础函数都是一样的,对于在学习安卓flutter的同学可以尝试一下,很容易,而且我也不会unity中的控件布局,UiWidget的基础控件已经够用了。
个人觉得用这个库开发app还是不太现实,不如直接flutter。有3d交互但是对unity组件不熟练的可以试试。

UIWidget AnimatedBuilder动画的封装

如图是移动文字并淡出的动画
在这里插入图片描述
实现上述效果的代码示例

TestApp.cs

using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;

public class TestApp : UIWidgetsPanel
{
    protected override Widget createWidget()
    {
        return new WidgetsApp(
            home: new TestAppSt(),
            pageRouteBuilder: (RouteSettings settings, WidgetBuilder builder) =>
                new PageRouteBuilder(
                    settings: settings,
                    pageBuilder: (BuildContext context, Animation<float> animation,
                        Animation<float> secondaryAnimation) => builder(context)
                )
        );
    }

    class TestAppSt : StatefulWidget
    {
        public TestAppSt(
            Key key = null,
            AnimationController controller = null
        ) : base(key: key)
        {
            this.controller = controller;
        }

        public readonly AnimationController controller;

        public override State createState()
        {
            return new _CustomActivityIndicatorState();
        }
    }

    class _CustomActivityIndicatorState : State<TestAppSt>, TickerProvider
    {
        AnimationController _controller;

        public override void initState()
        {
            base.initState();

            if (this.widget.controller == null)
            {
                this._controller = new AnimationController(
                    duration: new TimeSpan(0, 0, 3),
                    vsync: this
                );
                this._controller.forward();
            }
            else
            {
                this._controller = this.widget.controller;
            }
            this._controller.addStatusListener((status =>
            {
                Debug.Log($"{status}");
            } ));
        }

        public Ticker createTicker(TickerCallback onTick)
        {
            return new Ticker(onTick: onTick, () => $"created by {this}");
        }

        public override void didUpdateWidget(StatefulWidget oldWidget)
        {
            base.didUpdateWidget(oldWidget: oldWidget);
            if (oldWidget is TestAppSt customActivityIndicator)
            {
            }
        }

        public override Widget build(BuildContext context)
        {
            int sideLength;
            return new AnimatedBuilder(
                animation: this._controller,
                builder: (cxt, widget) =>
                {
                    var value = _controller.value;
                    //Debug.Log(_controller?.value);
                    return new Stack(children: new List<Widget>
                    {
                        new Positioned(left:Screen.width/2-15,top: (value<0.2f?0:value-0.2f) * 100, child:
                            new Container(child: new Text("第一关", style: new TextStyle(color: new Color(0xFF0E3311).withOpacity(1-_controller.value))),
                                decoration: new BoxDecoration(
                                )
                            )
                        ),
                    });
                }
            );
        }

        public override void dispose()
        {
            if (this.widget.controller == null)
            {
                this._controller.dispose();
            }

            base.dispose();
        }
    }
}

话说这个csdn的代码块,dart不配拥有姓名…

这段代码在每次animatedbuilder的回调builder中都会大量创建ui控件,似乎会产生性能浪费,但是其实在体验上感受不出来。相反,builder执行完了反而出现了卡顿。
在这里插入图片描述
动画builder执行完了之后卡成了ppt,同样,在一般flutter写法的控件UIWidget中也会出现这样的3d世界卡顿问题,除非Destroy这个脚本。看来这个库有很大的问题。我在更换了unity的版本之后,性能有提升,但是依然卡顿。

为了尽可能达到像flutter那样易用的效果,需要进行一定的封装才能使用。基本上做UI是为了3D交互,消除卡顿必须要套一层AnimatedBuilder。(应该没人再用这个做纯app吧,unity Connect已经相当卡顿)。

3D世界解决办法

在github UIWidget官方issue中找到如下

在这里插入图片描述UIWidgets这个组件会自动调节刷新率,数值最低为1,为最流畅。原本是为了安卓手机开发设计的,因为这样可以节约电量。
对应github issue链接

也就是在OnUpdate函数中添加如下代码

OnDemandRendering.renderFrameInterval = 1;

封装需求分析

flutter中的两种控件stateful,stateless 控件,基于上述的结论,对于stateless和stateless,我只关心child控件,由于animatedBuilder是时刻刷新的(这很unity,就像update函数),我们在修改变量的时候不需要再调用setState方法(控件时时刻刻都在销毁重建,也就不存在flutter中的跳转)。

对于有动画的布局,我只关心动画的效果,启动的相关问题。

基于上述需求创建两个基本类

MyAnimateWidgetParent.cs(不可直接使用,默认在控件的最外层套一个AnimatedBuilder)

using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;


public class MyAnimateWidgetParent : StatefulWidget
{
    public delegate Widget WidgetBuilder(BuildContext context);

    public MyAnimateWidgetParent(
        Key key = null,
        WidgetBuilder builder = null
    ) : base(key: key)
    {
        this.controller = controller;
        this.builder = builder;
    }

    public WidgetBuilder builder;
    public readonly AnimationController controller;

    public override State createState()
    {
        return new _CustomActivityIndicatorState();
    }
}

class _CustomActivityIndicatorState : State<MyAnimateWidgetParent>, TickerProvider
{
    AnimationController _controller;

    public override void initState()
    {
        base.initState();

        if (this.widget.controller == null)
        {
            this._controller = new AnimationController(
                duration: new TimeSpan(0, 0, 1),
                vsync: this
            );
            this._controller.repeat();
        }
        else
        {
            this._controller = this.widget.controller;
        }

        //this._controller.addStatusListener((status => { Debug.Log($"{status}"); }));
    }

    public Ticker createTicker(TickerCallback onTick)
    {
        return new Ticker(onTick: onTick, () => $"created by {this}");
    }

    public override void didUpdateWidget(StatefulWidget oldWidget)
    {
        base.didUpdateWidget(oldWidget: oldWidget);
        if (oldWidget is MyAnimateWidgetParent customActivityIndicator)
        {
        }
    }

    public override Widget build(BuildContext context)
    {
        int sideLength;
        return new AnimatedBuilder(
            animation: this._controller,
            builder: (cxt, widget) => { return this.widget.builder(context); }
        );
    }

    public override void dispose()
    {
        if (this.widget.controller == null)
        {
            this._controller.dispose();
        }

        base.dispose();
    }
}

MyApp.cs(自己的ui继承此类)

using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;

public abstract class MyApp : UIWidgetsPanel
{
    //public delegate Widget build(BuildContext context);

    public abstract Widget onFlutterUpdate();

    protected override Widget createWidget()
    {
        
        Debug.Log("createwidget");
        return new WidgetsApp(
            home: new MyAnimateWidgetParent(builder:(context => onFlutterUpdate())),
            pageRouteBuilder: (RouteSettings settings, WidgetBuilder builder) =>
                new PageRouteBuilder(
                    settings: settings,
                    pageBuilder: (BuildContext context, Animation<float> animation,
                        Animation<float> secondaryAnimation) => builder(context)
                )
        );
    }

    class ExampleStfl : StatefulWidget
    {
        public ExampleStfl(
            Key key = null,
            AnimationController controller = null
        ) : base(key: key)
        {
            this.controller = controller;
        }

        public readonly AnimationController controller;

        public override State createState()
        {
            return new _CustomActivityIndicatorState();
        }
    }

    class _CustomActivityIndicatorState : State<ExampleStfl>, TickerProvider
    {
        AnimationController _controller;

        public override void initState()
        {
            base.initState();
            
            if (this.widget.controller == null)
            {
                this._controller = new AnimationController(
                    duration: new TimeSpan(0, 0, 1),
                    vsync: this
                );
                this._controller.repeat();
            }
            else
            {
                this._controller = this.widget.controller;
            }
            this._controller.addStatusListener((status =>
            {
                Debug.Log($"{status}");
            } ));
        }

        public Ticker createTicker(TickerCallback onTick)
        {
            return new Ticker(onTick: onTick, () => $"created by {this}");
        }

        public override void didUpdateWidget(StatefulWidget oldWidget)
        {
            base.didUpdateWidget(oldWidget: oldWidget);
            if (oldWidget is ExampleStfl customActivityIndicator)
            {
            }
        }

        public override Widget build(BuildContext context)
        {
            int sideLength;
            return new AnimatedBuilder(
                animation: this._controller,
                builder: (cxt, widget) =>
                {
                    var value = _controller.value;
                    //Debug.Log("t..."+Time.deltaTime);
                    Debug.Log(_controller?.value);
                    return new Stack(children: new List<Widget>
                    {
                        new Positioned(left:Screen.width/2-15,top: (value<0.2f?0:value-0.2f) * 100, child:
                            new Container(child: new Text("第一关", style: new TextStyle(color: new Color(0xFF0E3311).withOpacity(1-_controller.value))),
                                decoration: new BoxDecoration(
                                )
                            )
                        ),
                    });
                }
            );
        }

        public override void dispose()
        {
            if (this.widget.controller == null)
            {
                this._controller.dispose();
            }

            base.dispose();
        }
    }
}

示例代码:FirstUi.cs

using System.Collections;
using System.Collections.Generic;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Stack = Unity.UIWidgets.widgets.Stack;

public class FirstUi : MyApp
{
    private int counter = 0;
    public override Widget onFlutterUpdate()
    {
        return new Stack(children: new List<Widget>
        {
            new Positioned(top: Time.time, child: new GestureDetector(child:new Text($"{counter} - " + Time.time),onTap:(() =>
            {
                counter++;
            })))
        });
    }
}

在FirstUi.cs中可以看到,页面的布局相当简单,onFlutterUpdate()时刻都在调用,很符合unity的风格,至于页面跳转,setState可以通过保存Widget变量的方式。当然,如果喜欢flutter的那种页面切换方式也可以自己去实现。AnimatedController.value可以以unity的Time.deltaTime代替。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值