Mercurial > hg > Papers > 2022 > riono-master
view Slide/master-slide.html @ 62:74fb935dc5b5 default tip
update
author | riono <e165729@ie.u-ryukyu.ac.jp> |
---|---|
date | Wed, 02 Mar 2022 13:15:50 +0900 |
parents | c269b11bb810 |
children |
line wrap: on
line source
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8"> <title>継続を使用する並列分散フレームワークのUnity実装</title> <meta name="generator" content="Slide Show (S9) v4.1.0 on Ruby 2.6.3 (2019-04-16) [universal.x86_64-darwin19]"> <meta name="author" content="Ryo Yasuda, Shinji Kono" > <!-- style sheet links --> <link rel="stylesheet" href="s6/themes/projection.css" media="screen,projection"> <link rel="stylesheet" href="s6/themes/screen.css" media="screen"> <link rel="stylesheet" href="s6/themes/print.css" media="print"> <link rel="stylesheet" href="s6/themes/blank.css" media="screen,projection"> <!-- JS --> <script src="s6/js/jquery-1.11.3.min.js"></script> <script src="s6/js/jquery.slideshow.js"></script> <script src="s6/js/jquery.slideshow.counter.js"></script> <script src="s6/js/jquery.slideshow.controls.js"></script> <script src="s6/js/jquery.slideshow.footer.js"></script> <script src="s6/js/jquery.slideshow.autoplay.js"></script> <!-- prettify --> <link rel="stylesheet" href="scripts/prettify.css"> <script src="scripts/prettify.js"></script> <script> $(document).ready( function() { Slideshow.init(); $('code').each(function(_, el) { if (!el.classList.contains('noprettyprint')) { el.classList.add('prettyprint'); } }); prettyPrint(); } ); </script> <!-- Better Browser Banner for Microsoft Internet Explorer (IE) --> <!--[if IE]> <script src="s6/js/jquery.microsoft.js"></script> <![endif]--> </head> <body> <div class="layout"> <div id="header"></div> <div id="footer"> <div align="right"> <img src="s6/images/logo.svg" width="200px"> </div> </div> </div> <div class="presentation"> <div class='slide cover'> <table width="90%" height="90%" border="0" align="center"> <tr> <td> <div align="center"> <h1><font color="#808db5">継続を使用する並列分散フレームワークのUnity実装</font></h1> </div> </td> </tr> <tr> <td> <div align="left"> Ryo Yasuda, Shinji Kono 並列信頼研 <hr style="color:#ffcc00;background-color:#ffcc00;text-align:left;border:none;width:100%;height:0.2em;"> </div> </td> </tr> </table> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="並列分散フレームワークchristieとunity">並列分散フレームワークChristieとUnity</h2> <ul> <li>Unityとはゲーム開発でよく使用されているゲームエンジン</li> <li>ChristieとはDataGearのTake/Putを使用した分散フレームワーク</li> <li>UnityでServer抜きのネットワークゲーム開発を行いたい <ul> <li>Java版を接続するのは困難</li> <li>C#でChristieを実装した</li> </ul> </li> <li>Unityに並列分散処理をスムーズに導入できた</li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="christieによるunityネットワークゲーム実装の利点">ChristieによるUnityネットワークゲーム実装の利点</h2> <ul> <li>PUN2では企業ベースのServerが必要(開発がしづらい)</li> <li>WAN上なので低速 <ul> <li>ChristieにするとLAN上での高速通信が可能</li> <li>TopologyManagerを使うことによりLAN/WANで動的構成される</li> <li>スケーラビリティーもTopologyManagerで実現できる(分散ルーティング)</li> <li>Take/Putなので通信切断や遅延に対して対処しやすい</li> </ul> </li> <li>Unityはフレーム単位の処理なため並列処理が苦手 <ul> <li>Christieにより画面書き換えと非同期な並列処理や通信が可能</li> </ul> </li> <li>ChristieのMessagePack(データ通信ライブラリ)のバージョンの問題を解決</li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="christie-の計算モデル">Christie の計算モデル</h2> <ul> <li>DataGear(通信単位となるオブジェクト)が揃った時点でCodeGearが処理を開始</li> <li>DataGearは決まったkeyでDataGearManagerにQueueとして格納</li> <li>相手nodeのDataGearManagerのproxyで通信</li> </ul> <center><img src="../Paper/images/Remote_DataGearManager.pdf" alt="message" width="800" height="450" /></center> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="datagearのapi">DataGearのAPI</h2> <ul> <li>PutでQueueに追加</li> <li>TakeでQueueからの取り出し</li> <li>PeekでQueueから取り出さない読み出し <ul> <li>Takeされない限り同じDataGearを読み出し続ける</li> </ul> </li> <li>フレームごとにPeekでゲームオブジェクトの状態をUnity側で見れる</li> <li>通信停止/遅延でもUnityの動作に影響を与えない</li> <li>スケーラビリティやLAN/WAN構成はTopologyManagerのDGM porxyの管理</li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="javacのannotation">Java/C#のannotation</h2> <ul> <li>クラスやフィールドあるいはメソッドに付ける</li> <li>ユーザ定義可能なmeta計算属性</li> <li>MessagePackで通信可能なフィールドをannotationで指定する</li> <li>Christieで通信パターンを指定するのにannotationを用いる</li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="dgのannotation">DGのannotation</h2> <ul> <li>Take <ul> <li>先頭のDG を読み込み、そのDG を削除する</li> </ul> </li> <li>Peek <ul> <li>先頭のDG を読み込むがDG を削除しない</li> <li>操作をしない場合は同じデータを参照し続ける</li> </ul> </li> <li>TakeFrom <ul> <li>Take と同じ動作だが、remote 先のDGMを指定できる</li> </ul> </li> <li>PeekFrom <ul> <li>Peek と同じ動作だが、remote 先のDGMを指定できる</li> </ul> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="christieによる分散プログラミング">Christieによる分散プログラミング</h2> <ul> <li>Take/Put/Peekがデータベースのトランザクション</li> <li>ここではトランザクションはゲームプレイヤー間の合意されたイベント</li> <li>複数のプレイヤーが関与する分散プログラムをTake/Put/Peekでローカルに記述できる</li> <li>ローカルな記述を組み合わせて分散プログラムを記述する</li> <li>ネットワークの変化、故障、性能の多様性を吸収する</li> <li>Christieのデバッガー(モデル検査など)も使用可能</li> <li>TopologyManagerで負荷軽減やチート対策が可能</li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="ローカルnodeから見たchristie">ローカルnodeから見たChristie</h2> <ul> <li>型のあるDataGear とKey を持つタプル空間、DataGearManager として格納</li> <li>他のノードのDGM のproxyに書き込むことで通信</li> <li>CodeGear (クラスやスレッド)</li> <li>DataGear (変数データ)</li> <li>CodeGearManager (CG,DG,DGMを管理)</li> </ul> <center><img src="../Paper/images/GearsRelationships.pdf" alt="message" width="550" height="350" /></center> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="christie上でnetwork-topologyの形成">Christie上でNetwork Topologyの形成</h2> <ul> <li>参加を表明したノードにDataGearManager proxyを決まった名前で作成</li> <li>ノード同士の配線を指示</li> <li>分散プログラムの開始終了</li> <li>Treeなどの動的Topologyは自動的に接続される</li> </ul> <center><img src="../Paper/images/DynamicTopology.pdf" alt="message" width="550" height="350" /></center> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="christie-sharp-のコード例">Christie Sharp のコード例</h2> <ul> <li>Mainでport10001でChristie フレームワークを起動(CreateCGM)</li> <li>最初のCodeGearをCodeGearManagerに対して実行する <pre><code class="language-cs">public static void Main(string[] args) { StartCountUp start = StartCountUp(createCGM(10001)); } </code></pre> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="codegear-startcountup">CodeGear StartCountUp</h2> <ul> <li>CodeGearはCodeGearManagerを引数として持つ(baseでcgmに対してアクセスを許可)</li> <li>CodeGearManagerがRun(cgm)をThreadPoolを使って呼び出す <pre><code class="language-cs:StartCountUp.cs">public class StartCountUp : StartCodeGear { public StartCountUp(CodeGearManager cgm) : base(cgm) { } public override void Run(CodeGearManager cgm) { cgm.Setup(new CountUpper()); // 新しいCodeGearを生成 CountObject count = new CountObject(1); put("count", count); // DataGear countをkey countでPut } } </code></pre> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="codegear-countupper">CodeGear CountUpper</h2> </li> <li>key countのDataGearが来たらCountUpperのRunが実行される <pre><code class="language-cs:CountUpper.cs">public class CountUpper : CodeGear { [Take] public CountObject count; // key countをTakeするannotation public override void Run(CodeGearManager cgm) { Console.WriteLine(count.number); if (count.number < 10) { cgm.Setup(new CountUpper()); // 毎回新しいCodeGearを生成 count.number += 1; put("count", count); } else { cgm.GetLocalDGM().Finish(); // CodeGearManagerを終了 } } } </code></pre> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="datagearcountobject">DataGear CountObject</h2> </li> <li>MessagePackを使って通信されるDataGear</li> <li>Take/Put/Peekはこれに対して行われる <pre><code class="language-cs:CountObject.cs">[MessagePackObject] public class CountObject { public int number; public CountObject(int number) { // コンストラクタ this.number = number; } } </code></pre> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="unityの従来の通信ライブラリ">Unityの従来の通信ライブラリ</h2> <ul> <li>Unityはフレーム単位で処理を行う</li> <li>IEnumeratorでフレームを跨ぐ処理を記述する</li> <li>Send/Recieveなどでは通信を記述しづらい</li> <li>Photon Unity Network 2(PUN2) <ul> <li>Cloud ServerサービスPhoton Cloudがある</li> <li>Server側は開発できない</li> </ul> </li> <li>Mirror <ul> <li>OSSで開発が行われている</li> <li>メソッドをServerで実行し結果を反映</li> </ul> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="photon-unity-network2">Photon Unity Network2</h2> <pre><code class="language-cs">public class PhotonController : MonoBehaviourPunCallbacks { void Start() { PhotonNetwork.ConnectUsingSettings(); } void FixedUpdate() { if (photonView.IsMine) { // 自身であるか float x = Input.GetAxis("Horizontal"); float z = Input.GetAxis("Vertical"); CmdMoveSphere(x, z); } } void CmdMoveSphere(float x, float z) { Vector3 v = new Vector3(x, 0, z); GetComponent<Rigidbody>().AddForce(v); } public override void OnConnectedToMaster() { // Server接続時のcallback PhotonNetwork.JoinOrCreateRoom("Room", new RoomOptions(), TypedLobby.Default); } public override void OnJoinedRoom() { // Room接続時のcallback PhotonNetwork.Instantiate("Charactor", Vector3.zero, Quaternion.identity); } } </code></pre> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="mirror">Mirror</h2> <pre><code class="language-cs">public class PlayerController : NetworkBehaviour { [SyncVar] float speed = 1f; public override void OnStartServer() { // Server接続時のcallback speed = Random.Range(1f, 10f); } void FixedUpdate() { if (isLocalPlayer) { float x = Input.GetAxis("Horizontal"); float z = Input.GetAxis("Vertical"); CmdMoveSphere(x, z); } } [Command] // Server側で実行される void CmdMoveSphere(float x, float z) { Vector3 v = new Vector3(x + speed, 0, z + speed); GetComponent<Rigidbody>().AddForce(v); } } </code></pre> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="cへの書き換える利点">C#への書き換える利点</h2> <ul> <li>ChristieはJavaで実装されている <ul> <li>C#に書き換えを行うべきか</li> </ul> </li> <li>JavaをC#上で呼び出す方式 <ul> <li>annotationが使用できない</li> </ul> </li> <li>Unityはandroidの開発向けにjarファイルからJavaのメソッドをC#上で呼び出せる機能がある <ul> <li>stringを使用してリソースディレクトリから検索し、使用</li> <li>高速化が求められる並列分散プログラミングには不適</li> </ul> </li> <li>記述方法が似ているため移植が行いやすい</li> <li>ThreadPoolをC#で統一できる</li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="書き換えの方針">書き換えの方針</h2> <ul> <li>C#で記述するChristieをChristie Sharpとする</li> <li>Christie設計時の意図や、互換性を保つためChristieと同じ動作をさせる</li> <li>ChristieはJava9から開発されており、非推奨なコードやが含まれている</li> <li>C#に対応しつつ、処理動作の向上や最適化を行うために以下の変更を行った <ul> <li>MessagePackのバージョンアップ</li> <li>ThreadPoolからTaskへ変更</li> </ul> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="take-annotationの実装">Take annotationの実装</h2> <ul> <li>Christie ではDGを取得するためにannotation を使用している <ul> <li>C# ではannotation と同様の機能にattribute があり、Take をattribute で実装した</li> </ul> </li> <li>Take はフィールド変数に対して適用する</li> <li>Javaの場合 <pre><code class="language-java:Take.java">@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Take { } </code></pre> </li> <li>C#の場合 <pre><code class="language-cs:Take.cs">[AttributeUsage(AttributeTargets.Field)] public class Take : Attribute { } </code></pre> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="messagepackの相違点">MessagePackの相違点</h2> <ul> <li>Christie ではMessagePack を使用してデータをシリアライズし送受信している <ul> <li>インスタンス内のpublic 変数に対してシリアライズ可能</li> </ul> </li> <li>Java版はバージョンが古いため、現在はサポートされていないため最新版とは記述方法が異なる <pre><code class="language-java:MessagePackEx.java">public class MessagePackExample { @Message // 圧縮を行うクラスにつける public static class MyMessage { public String name; public double version; } public static void main(String[] args) throws Exception { MyMessage src = new MyMessage(); src.name = "msgpack"; src.version = 0.6; MessagePack msgpack = new MessagePack(); byte[] bytes = msgpack.write(src); // シリアライズ // Deserialize MyMessage dst = msgpack.read(bytes, MyMessage.class); // デシリアライズ } </code></pre> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="messagepackの相違点-1">MessagePackの相違点</h2> <ul> <li>C# のMessagePack は複数存在している <ul> <li>java 版と似たような書き方をするMessagePack-CSharp を選択した <pre><code class="language-cs">[MessagePackObject] // シリアライズしたいクラスにつける public class MyClass { [Key(0)] // keyを指定 public int Age { get; set; } [Key(1)] public string FirstName { get; set; } [Key(2)] public string LastName { get; set; } static void Main(string[] args) { var mc = new MyClass { Age = 99, FirstName = "hoge", LastName = "huga", }; byte[] bytes = MessagePackSerializer.Serialize(mc); // byte[] にシリアライズ MyClass mc2 = MessagePackSerializer.Deserialize<MyClass>(bytes); // デシリアライズ // [99,"hoge","huga"] var json = MessagePackSerializer.ConvertToJson(bytes); // Jsonとしても展開可能 } } </code></pre> </li> </ul> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="threadpoolからtaskへの書き換え">ThreadPoolからTaskへの書き換え</h2> <ul> <li>Christie ではThreadPool を使用していた <pre><code class="language-java:PriorityThreadPoolExecutors.java">public class PriorityThreadPoolExecutors { private static class PriorityThreadPoolExecutor extends ThreadPoolExecutor { private static final int DEFAULT_PRIORITY = 0; public PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, // コンストラクタ int keepAliveTime, TimeUnit unit) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, (BlockingQueue) new PriorityBlockingQueue<ComparableTask>(10, ComparableTask.comparatorByPriorityAndSequentialOrder())); } // 優先度によって処理順を入れ替え可能なQueueを指定 @Override public void execute(Runnable command) { super.execute(command); } } } </code></pre> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="threadpoolからtaskへの書き換え-1">ThreadPoolからTaskへの書き換え</h2> </li> <li>Christie Sharp ではThreadPoolより高機能なTask を用いて書き換えを行った</li> <li>Task は複雑な非同期処理を通常のコーディングと同じ感覚で直感的に記述</li> <li>裏でThreadPool が動作するため大きく動作は変わらない <pre><code class="language-cs:ThreadPoolExecuters.cs">public class ThreadPoolExecutors { public ThreadPoolExecutors(int nWorkerThreads, int nIOThreads) { // コンストラクタ ThreadPool.SetMinThreads(nWorkerThreads, nIOThreads); } public void Execute(CodeGearExecutor command) { Task.Factory.StartNew(() => command.Run()); // Taskを使用 } } </code></pre> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="christie-sharpのunityのコード例">Christie SharpのUnityのコード例</h2> <ul> <li>ゲームオブジェクトの座標をもう1つのゲームオブジェクトに追従させる例題</li> <li>StartやUpdateはMonoBehaviourを継承する</li> </ul> <pre><code class="language-cs:TransformMoveTest.cs">public class TransformMoveTest : MonoBehaviour { private CodeGearManager cgm; public Transform otherTransform; private Vector3 pos; void Start() { // ゲーム実行時の最初の1回呼び出し cgm = StartCodeGear.CreateCgm(10000); cgm.Setup(new PositionAssignCodeGear()); // 新しいCodeGearの生成 cgm.GetLocalDGM().Put("transform", transform); } public void Update() { // 毎フレーム呼び出し pos = otherTransform.position; Vector3 newPos = new Vector3(pos.x + 3, pos.y, pos.z + 3); cgm.GetLocalDGM().Put("pos", newPos); } private void LateUpdate() { // 次のフレーム直前に呼び出し cgm.Setup(new PositionAssignCodeGear()); } } </code></pre> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="christie-sharpのunityのコード例-1">Christie SharpのUnityのコード例</h2> <ul> <li>TransformなどのUnity AIPはMainThreadでのみ動作 <pre><code class="language-cs:PositionAssingCodeGear.cs">public class PositionAssignCodeGear : CodeGear { [Take] private Vector3 pos; [Peek] private Transform transform; public override void Run(CodeGearManager cgm) { MainThreadDispatcher.Post(_ => { // MainThreadへ処理を移譲 transform.position = pos; }, null); } } </code></pre> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="christie-sharpの利点">Christie Sharpの利点</h2> <ul> <li>Unityのフレーム同期処理とChristieの並列分散処理が共存 <ul> <li>CodeGear/DataGearを利用した待ち合わせ処理</li> <li>CodeGearの処理はTaskによってThreadPoolで行われる</li> <li>Christieを用いた非同期な並列処理が可能</li> <li>通信が切断した際にUnityの動作に影響を与えない</li> <li>通信先の参照データをPeekで待ち合わせないで取得する</li> <li>Take/PutによりトランザクションとしてDataGearをUpdate</li> <li>TopologyManagerによりDGMのproxyネットワークが構成される</li> <li>TopologyManagerにCookieの機能があるため、改良すれば切断から復帰可能</li> </ul> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="まとめ">まとめ</h2> <ul> <li>Christie をUnity で使用するためにC# に書き換えを行った</li> <li>書き換え方針としては、attribute やMessagePack などC# 独自の機能に対応しつつ元のソースコードと同じ機能になるように実装</li> <li>Unityで動作検証を行い、正しく動作することを確認した <ul> <li>Christie Sharpを利用したゲームの開発が可能</li> <li>ゲーム班などでネットワークゲームを容易にプログラムできる</li> </ul> </li> <li>TopologyManagerで自由にネットワーク環境を構築できる</li> <li>Take/Peek/Putを利用したゲームと相性の良いプログラミング</li> <li>Unity既存のライブラリとの比較を行った</li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h2 id="今後の課題">今後の課題</h2> <ul> <li>TopologyManagerの完成 <ul> <li>2nd keyを用いたTreeMapの通信</li> </ul> </li> <li>Christie Sharpの性能検証を行う <ul> <li>Aliceで動作していた水族館の例題</li> <li>Christie Sharpを用いた100人規模のゲーム開発</li> </ul> </li> </ul> </div> </div><!-- presentation --> </body> </html>