다른 Native 어플리케이션을 해봐서 그런지 Unity UI 그리기가 너무 번거롭네...
나는 Flutter와 Unity를 둘다 사용해본 경험이 있다.
Unity는 학생때 게임 개발자가 되기 위해서 공부했었고, Flutter는 현재 직장에서 크로스 플랫폼 어플을 개발하기 위해 사용중이다.
Unity는 현재도 취미 겸 부업으로 사용중인데 Flutter와 다른 Native 어플리케이션 개발을 진행하다보면 Unity의 단점이 여러가지 부각된다.
여기서 내가 가장 번거로워 하는 점은 바로 UI이다.
Flutter와 Unity에서 UI의 장단점을 알아보고 둘이 합치면 어떤 것이 나아졌는지 이번 글에 작성해보겠다.
Flutter는 Unity와 다르게 코드로 UI를 그린다.
코드로 UI를 그린다고?
React Native나 Android Compose를 사용해본 경험이 있다면 코드로 UI를 그린다는 말이 쉽게 이해가 될것이다.
하지만 순수 유니티 개발자라면 이 말이 매우 이상할 것이다.
Flutter에서 UI를 그리는 방법에 대해서 예시를 한번 봐보자
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
위 코드는 flutter에서 프로젝트를 처음 생성했을 때 생성되는 코드이다. 실행하면 아래와 같이 나온다.
Flutter는 기본적으로 Widget을 사용해 UI를 그린다.
Unity와는 다르게 순수 코드로만 작성해서 프로그래머가 수정하기 매우 편리하고 또한 기본적으로 UI 테마를 제공하기 때문에 깔끔한 UI를 기본적으로 사용할 수 있다.
하지만 많은 UI를 그릴 경우 코드가 많아질 수 있다는 단점이 존재한다.
Unity에서 UI를 구성하는 방법은 3가지이다.
유니티의 기본 UI 컴포넌트를 사용해 UI를 그리는 방식이다.
위 이미지처럼 Canvas하위에 있는 GameObejct에 Button 컴포넌트를 추가해서 UI를 그리는 방식이다.
이 방식은 Flutter에서 UI를 그리는 방식과는 달리 UI배치나 디자인을 Compenet에 의존해서 그리게 된다.
이러면 버튼의 바인딩 같은 경우 Unity 에디터 창과 코드창을 번갈아서 사용해야하는 번거로움이 있다.
UI Toolkit은 유니티에서도 UI를 그리는 것이 불편했는지 웹에서 UI를 그리는 방식과 비슷하게 Unity에서 UI를 그릴 수 있게 해주는 패키지이다.
웹의 CSS파일 처럼 유니티에서도 USS파일로 UI스타일을 따로 지정할 수 있으며 Android와 비슷하게 UXML로 UI를 그릴 수 있다.
비교 | Android | 웹 | Unity |
---|---|---|---|
UI | Xml | Html | UXml |
Style | Xml | Css | Uss |
Code | java | javascript | C# |
UI Toolkit은 이전에 UGUI로 수작업하는 시간을 매우 줄여주고 또한 코드로 UI를 작성할 수 있어 이전의 UGUI의 문제점인 Texture사이즈가 작을 경우 이미지가 깨지는 현상을 조금이나마 방지할 수 있다.
그리고 기존의 UGUI와 다르게 무조건 이미지를 사용해 렌더링 하는 방식이 아니여서 메모리 관리 측면에도 부담이 덜하며 Unity 내부의 Inspector를 직접 커스텀 할 수 있다.
하지만 UGUI만큼 손쉽게 커스텀하기 쉽지 않고 기존에 Unity만 개발한 개발자라면 손쉽게 접근이 어렵다는 단점이 있다.
이것은 유니티에서 OnGUI()
함수를 사용해 코드로 UI를 그릴 수 있는 시스템이다.
??? : 그러면 Unity에서도 코드로 UI를 그릴수 있는거 아닌가?
얼핏 보면 Flutter와 비슷하다 생각할 수 있는 데 이것은 Flutter처럼 디자인 적으로 예쁜 UI를 구현하기는 어렵다.
왜냐하면 이것은 개발자가 좀더 개발을 편리하게 하기 위해서 유니티에서 지원하는 것이기 때문이다.
실제로 UI를 보면 유저가 사용하기에는 디자인적으로 별로이다.
Flutter와 Unity, 둘 중 어떤 것이 UI를 더 그리기 편하다 했을 때 나는 Flutter가 훨씬 더 UI를 그리기 편하다고 생각한다.
Flutter의 Hot Reload기능으로 테스트를 진행하며 UI를 작성하는 시간이 Unity에서 UI를 그리는 시간 보다 무척이나 빠르기 때문이다.
하지만 게임을 만든다고 했을 때 Flutter로 3D게임을 만들기는 무척 까다롭다. Unity같은 경우 게임에 특화된 물리엔진을 가지고 있고 지원되는 기능들이 많아 Flutter보다 게임을 만들기 쉽다.
그러면 둘이 합칠수 없을까?
Unity와 Flutter를 합친다면 위 두 개의 장점을 모두 가진상태로 개발을 진행할 수 있을 것이다.
Flutter 패키지 중에는 Unity와 Flutter를 합칠 수 있게 도와주는 패키지가 있다.
바로 flutter_unity_widget이라는 패키지이다.
이 패키지는 Unity를 Flutter의 Widget으로 사용할 수 있게 해준다.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_unity_widget/flutter_unity_widget.dart';
void main() {
runApp(MaterialApp(
home: UnityDemoScreen()
));
}
class UnityDemoScreen extends StatefulWidget {
UnityDemoScreen({Key key}) : super(key: key);
_UnityDemoScreenState createState() => _UnityDemoScreenState();
}
class _UnityDemoScreenState extends State<UnityDemoScreen>{
static final GlobalKey<ScaffoldState> _scaffoldKey =
GlobalKey<ScaffoldState>();
UnityWidgetController _unityWidgetController;
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: SafeArea(
bottom: false,
child: WillPopScope(
onWillPop: () {
// Pop the category page if Android back button is pressed.
},
child: Container(
color: colorYellow,
child: UnityWidget(
onUnityCreated: onUnityCreated,
),
),
),
),
);
}
// Callback that connects the created controller to the unity controller
void onUnityCreated(controller) {
this._unityWidgetController = controller;
}
}
Flutter에서 Unity로 데이터를 전달할려면 UnityController의 postMessage함수를 사용해 전달할 수 있다.
예시
Flutter 코드
UnityWidgetController? _unityWidgetController;
void initState() {
super.initState();
}
void dispose() {
_unityWidgetController?.dispose();
super.dispose();
}
... some codes
void setRotationSpeed(String speed) {
_unityWidgetController?.postMessage(
'Cube',
'SetRotationSpeed',
speed,
);
}
Unity 코드
using System;
using FlutterUnityIntegration;
using UnityEngine;
using UnityEngine.EventSystems;
using System.Globalization;
public class Rotate : MonoBehaviour, IEventSystemHandler
{
[SerializeField]
Vector3 RotateAmount;
// Start is called before the first frame update
void Start()
{
RotateAmount = new Vector3(0, 0, 0);
}
// Update is called once per frame
void Update()
{
gameObject.transform.Rotate(RotateAmount * Time.deltaTime * 120);
for (int i = 0; i < Input.touchCount; ++i)
{
if (Input.GetTouch(i).phase.Equals(TouchPhase.Began))
{
var hit = new RaycastHit();
Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(i).position);
if (Physics.Raycast(ray, out hit))
{
// This method is used to send data to Flutter
UnityMessageManager.Instance.SendMessageToFlutter("The cube feels touched.");
}
}
}
}
// This method is called from Flutter
public void SetRotationSpeed(String message)
{
float value = float.Parse(message , CultureInfo.InvariantCulture.NumberFormat);
RotateAmount = new Vector3(value, value, value);
}
}
위 코드를 사용해 UnityController에서 특정 게임오브젝트의 컴포넌트의 스크립트를 실행할 수 있다.
Unity에서 Flutter로 데이터를 전달할려면
UnityMessageManager
의 SendMessageToFlutter
함수를 사용해 Flutter에 데이터를 전달할 수있다.
전달된 데이터는 UnityWidget
의 onUnityMessage
콜백으로 전달된다.
Flutter와 Unity를 둘 다 사용할 줄 안다면 Flutter로 UI를 구현하여 사용하는 것이 편리하다!
하지만 이것은 UI와 게임이 완전히 분리가 되어있을 경우에 해당되기에 Unity와 Flutter를 병합해서 사용하는 경우는 많이 있지 않을 것이라 생각된다.
개발자로서 성장하는 데 큰 도움이 된 글이었습니다. 감사합니다.