view Paper/chapter/4-WorkingInUnity.tex @ 32:f6120b4d214a

update Unity chaper
author riono <e165729@ie.u-ryukyu.ac.jp>
date Sat, 05 Feb 2022 01:44:53 +0900
parents 4cb0634a79ff
children 6a53740dd200
line wrap: on
line source

\chapter{Christie SharpのUnity上での動作}

\section{Unity API}
Unityでは、ゲーム開発を行うためのライブラリとしてUnity APIが公開されている。
ソースコード\ref{src:UnityExample}はUnity APIを使用したスクリプトの例である。
Spaceキーを押し続けている間、playerObjectは物理演算に従って、毎フレーム$\vec{A} = (10, 5, 10)$だけ力を加えられ移動する例である。
4行目では変数が初期化されていないまま使用されているように見えるが、Unityではpublic修飾子を使用するとUnityのエディタ上で事前に変数に代入することが可能になる。

\lstinputlisting[label=src:UnityExample, caption=Unity APIを使用した記述例]{src/Unity/UnityScriptExample.cs}

Unity APIではC\#にはないVector3やGameObject、Rigidbodyなどの特別な型があり、これらはUnityEngineをusingすることで使用可能である。
またMonoBehaviourを継承することによって、StartメソッドやUpdateメソッドなどのUnity上で決められた、あるタイミングで呼び出される関数を使用することが可能である。


Startメソッドはゲームの開始時、1フレーム目が始まる前に1回だけ実行され、主に初期化に使用されることが多い。
今回はplayerObjectに付与されている物理演算のComponentであるRigidbodyを取得している。


UpdateメソッドはStartメソットの実行後に毎フレーム呼び出される関数であり、ゲームのメインロジックで使用されることが多い。
15行目ではキー入力の有無を確認しており、Spaceキーの入力を監視している。
続く16行目では、Startメソッドにて取得したRigidbodyに対して、3次元ベクトルを力として与えている。
これを行うことによって、Spaceキーを押している間$\vec{A} = (10, 5, 10)$がplayerObjectに与えられ、playerObjectは物理演算に従って移動する。


UnityではStartメソッドやUpdateメソッドがある代わりに、C\#の一般的な実行メソッドであるMainメソッドによる処理の開始ができない。
そのため、Unityで処理を行うためには、MonoBehaviourにより提供されているメソッドを通して処理を実行する必要がある。


\section{StartCodeGearを使用しないChristie Sharpの実行}
% 例題など
% やったこと全部書いたほうがいい/できたこと
% 多重継承の話

Chrisite SharpではStartCGにMainメソッドを記述してChrisite Sharpを実行したが、Unityで動作させるためにはStartメソッドやUpdeteメソッドを使用す必要がある。
そのため、Chrisite Sharpの実行にStartCGを使用せずに実行できるよう変更を行った。

\lstinputlisting[label=src:CountUpScript, caption=Unit上でのChrisite Sharpの実行Script]{src/Unity/CountUpScript.cs}

ソースコード\ref{src:CountUpScript}はソースコード\ref{src:CSStartCGExample}をUnityで動作するように書き換えたものであり、StartCGを使用せずに、Unity APIであるStartメソッドよりChrisite Sharpを実行している。
StartCGは、CGMの初期化や処理の開始位置を明確化させるためのクラスであるため、Christie Sharpの実行に必須ではない。
また、C\#では多重継承が禁止されている。そのため、StartCGを継承しつつ、MonoBehaviourを継承してStartメソッドを使用する、と言うことはできない。
そのため可読性が下がってしまう可能性はあるが、CGMの初期化やSetupメソッドを直接Startメソッド内に記述できる。


\section{Unity上での動作確認}
% RemoteDG使ったやつ
% 2台で通信した場合
% LateUpdateの話
% UniRXのDispatcherとUnity APIの仕様の話

Christie Sharptの動作確認を行うため、以下の3点の確認を行った。
LocalDGMに関してはソースコード\ref{src:CountUpScript}で正しく動作することを確認している。

\begin{itemize}
  \item CodeGearを使用した、GameObjectの位置の操作
  \item 同一プロセスでの、複数DGMを使用したGameObjectの位置の同期
  \item PC2台を使用した、LAN上でのGameObjectの位置の同期
\end{itemize}


\lstinputlisting[label=src:TransformMoveTest, caption=GameObjectの位置の操作の準備を行うScript]{src/Unity/TransformMoveTest.cs}
\lstinputlisting[label=src:PositionAssignCodeGear, caption=GameObjectの位置の操作を行うCodeGear]{src/Unity/PositionAssignCodeGear.cs}

ソースコード\ref{src:TransformMoveTest}、\ref{src:PositionAssignCodeGear}は、CodeGearを使用した、GameObjectの位置の操作を行う処理である。
ソースコード\ref{src:TransformMoveTest}を付与したGameObjectの位置より、常にx軸に+3、z軸に+3離れた位置に他のGameObjectであるotherTransfromが移動をする。
19行目のLateUpdateメソッドはMonoBehaviourから継承している関数であり、全てのUpdateメソッドが実行された後、次のフレームに移行する直前に実行される関数である。

UnityでChrisite Sharpを使用する際に一つ問題が起きた。
Unity APIでサポートされている関数や型はMain Threadで動作時のみ処理が行われるという仕様がある。
これはUnityの根本的な考え方であり、Unityでは基本的にシングルスレッドでの動作を想定してる。
非同期処理を行うための機能として、CoroutineやInvoceなどの機能が提供されており、擬似的にMulti Threadのように処理を行うことが可能である。
しかしChrisite SharpではCodeGearをTaskで処理させている。
このためCGに直接Transformを操作しても処理が実行されない。


この問題を解決するために、UniRxを導入することで使用可能なMainThreadDispatcher.Postメソッドを使用した(ソースコード\ref{src:PositionAssignCodeGear} 6行目)。
UniRxとはUnity向けに作成された非同期処理の外部ライブラリである。
MainThreadDispatcher.Postを使用することで、Main ThreadでないThreadで行っている処理をMain Threadに移譲することが可能となる。
Postメソッドは第一引数にデリゲートの一種であるAction$<$T$>$を渡すため、ラムダ式での記述が可能である。


\lstinputlisting[label=src:RemoteMoveTest, caption=RemoteDGMでGameObjectの位置の操作の準備を行うScript]{src/Unity/RemoteMoveTest.cs}
\lstinputlisting[label=src:ObjectMoveCodeGear, caption=RemoteDGMでGameObjectの位置の操作を行うCodeGear]{src/Unity/ObjectMoveCodeGear.cs}
\lstinputlisting[label=src:Vector3Cmd, caption=GameObjectの位置データとして送信されるオブジェクトのクラス]{src/Unity/Vector3Cmd.cs}

ソースコード\ref{src:RemoteMoveTest}、\ref{src:ObjectMoveCodeGear}、\ref{src:Vector3Cmd}は同一プロセスにおいて、
RemoteDGMを使用しGameObjectの位置を操作するScriptである。

ソースコード\ref{src:RemoteMoveTest}のStartメソッドでは、playerCGMとenemyCGMの2つのDGMを用意し、
playerCGMの方では他方のenemyCGMをRemoteDGMとして指定を行っている。
また、enemyCGMではSetupを行い処理の待機状態にしている。
Updateメソッドでは、このScriptが付与されたGameObjectの位置を取得し、x軸に+3、z軸に+3離れた位置にotherTransformを移動させるようにし、
送信用クラスに変形を行っている。
21行目ではotherTransformを持っているenemyCGMではなく、playerCGMからPutを行っている。

MessagePack CSharpではUnityにも対応しており、Unity APIが提供している値型のVector3やQuaternionなどもSerialize/Deserialize可能である。
しかし、Christie SharpではDGでデータを管理する際に全ての型に対応できるようObject型にCastをしている。
このObject型のCastが原因によりVector3型を直接送信することができない。
そのため、ソースコード\ref{src:Vector3Cmd}のようにデータをプリミティブ型に分割してデータを送信している。

ソースコード\ref{src:ObjectMoveCodeGear}はソースコード\ref{src:PositionAssignCodeGear}と同様にMainThreadDispatcher.Postによって、
処理はMain Threadに移譲されて実行される。


\lstinputlisting[label=src:ObjectMoveServer, caption=GameObjectの位置をRemoteDGMに送信するScript]{src/Unity/ObjectMoveServer.cs}
\lstinputlisting[label=src:ObjectMoveListener, caption=GameObjectの位置を受信して位置を操作するScript]{src/Unity/ObjectMoveListener.cs}


ソースコード\ref{src:ObjectMoveServer}、\ref{src:ObjectMoveListener}は2台のPCを使用しLAN内でGameObjectの位置を通信して操作するScriptである。
CGや、送信するDGについては、ソースコード\ref{src:ObjectMoveCodeGear}、\ref{src:Vector3Cmd}を流用している。


ソースコード\ref{src:ObjectMoveServer}、\ref{src:ObjectMoveListener}それぞれの3行目ではhostNameを指定しており、通信先のLocal IPアドレスを入力することでSocket通信を行っている。
ソースコード\ref{src:ObjectMoveServer}は位置データの送信元であり、RemoteDGMとして指定したlistenerに向けて、自身のGameObjectの位置をPutしている。
ソースコード\ref{src:ObjectMoveListener}は何もデータをPutしておらず、LateUpdateメソッドでSetupを行うことでソースコード\ref{src:ObjectMoveServer}から受け取ったデータを基にGameObjectの位置を変更している。