react 数据可视化库 react 可视化图形编辑器_react可视化编辑器


他山之石

在web前端领域,在UI制作方面有着悠久的历史,且一直都基于html+js+css技术,更利于技术的沉淀,是否能把web领域的优秀实践借鉴到游戏中呢?

上古时期流行的是dreamwaver的可视化编辑方式;而开源兴起后,作为最活跃的开源社区,相关技术百花齐放。react,angular,vue就是这些方案中的佼佼者。

其中react应该是最早探索非web领域使用的方案,比如react native正是这方面的探索,也验证了这种模式也适用于原生UI的制作。到了 React 16.2.0之后,更进一步的支持开发者定制自己的Host渲染器:于是有人将react适配到微信小程序,适配到three.js来做3D游戏,嵌入式系统等等,正所谓“Learn once, Write anywhere”。而本文正是利用该能力实现react在UE4上的移植。

如何实现自己的渲染器

首先,UE4下使用react你必须得有个js环境。试试这个:puerts。

其次,你得实现react的一些钩子。说白了也不高深,就是一些回调,react在UI控件的创建,更新,删除时会调用这些回调:

创建控件调用createInstance回调,告诉你要创建的是啥UI控件(Button,Text。。),属性(宽,高,颜色。。)是什么。

控件添加到UI树上调用appendChild回调,告诉你某UI控件,其父节点是什么。

控件属性的更新调用prepareUpdatecommitUpdate,告诉你原属性是啥,新属性是啥。

控件的删除调用removeChild回调。

。。。

细节可以参见源码。仅200多行代码,即完成了react和umg的对接。

React+UMG+Typescript=?

看个UI组件的实例:


class StatusBar extends React.Component<Props, State> {
    constructor(props: Props) {
      super(props);
  
      if ((props.initialPercent || 0) < 0) {
        throw new Error('initialPercent < 0');
      }
  
      this.state = {
        percent: props.initialPercent || 0.5
      };
    }

    get color(): LinearColor {
        return {R: 1 - this.state.percent , G: 0, B: this.state.percent};
    }
  
    onIncrement = () => this.setState({percent: this.state.percent + 0.01});
    onDecrement = () => this.setState({percent: this.state.percent - 0.01});
    
    render() {
        return (
            <HorizontalBox>
                {this.props.name}({this.state.percent.toFixed(2)})
                <ProgressBar Percent={this.state.percent} Slot={SlotOfProgressBar} FillColorAndOpacity={this.color}/>
                <Button OnClicked={this.onIncrement} >+</Button>
                <Button OnClicked={this.onDecrement} >-</Button>
            </HorizontalBox>
        );
    }
}


用过react的人会很熟悉,但不一定每个人都用过,说明下:

  • 我们用ProgressBar,Button,Text,HorizontalBox组件组成了一个更复杂的组件:StatusBar
  • 两个按钮,一个是+,一个是-,点击后会导致percent属性的变化
  • percent的变化,会导致ProgressBar的进度条和颜色发生变化。percent同时也会显示到屏幕上。

该组件的使用接着看代码:


class Hello extends React.Component<Props, State> {
    constructor(props: Props) {
      super(props);
    }
    render() {
        return (
            <CanvasPanel>
                <VerticalBox Slot={SlotOfVerticalBox}>
                    {this.props.names.map((name, idx) => <StatusBar name={name} key={idx}/>)}
                </VerticalBox>
            </CanvasPanel>
        );
    }
}

//渲染到屏幕
ReactUMG.render(<Hello names={["Health:", "Energy:"]}/>);


代码说明:

  • 又写了个叫Hello的组件;
  • 这组件会把N个StatusBar组件渲染到VerticalBox下,渲染多少个,取决于外部传入的names数组属性;
  • ReactUMG.render渲染一个Hello组件到屏幕,传入两个字符串元素的数组。

最终效果


react 数据可视化库 react 可视化图形编辑器_控件_02


优势

  • 组件化利于重用。
  • 相比蓝图的二进制格式,react的文本方式无论是对人,对版本管理都更友好些,更容易多人协作。
  • 别看那些标签长得像html,其实这是Typescript的JSX语法:UI标签的属性是能自动提示的,名字拼写错误,赋值了错误的类型都会编译错误。JSX语法没接触过可能需要一段时间去适应,但好几个新出的技术都采用了相类似的思路(Flutter的Widget,iOS13的SwiftUI,都是仿JSX的语法),这可能表示业界认为这种UI表达方式比较优秀。

劣势

  • 没有可视化编辑工具前,美术不见得能写jsx,而部分团队的UI制作是完全交给美术团队的。能提供一个蓝图到jsx双向转换的工具或者能解决这问题。

展望

这个尝试只是一个抛砖引玉,有兴趣的可以试试并一起完善。目前也只提供了UE4版本,如果在Unity也想使用,可以按照UE4的思路整一发,毕竟也没几行代码。