昨今のUnity開発におけるMVP設計思想についてと、それの適用可否の話

こちらも合わせてどうぞ

yutakaseda3216.hatenablog.com

 

はじめに

こちらの記事を読んで、最近Unity開発でよく言われるようになった

MVPについてだいぶ浸透(あるいは導入)してきたのではないかと思った。

developers.cyberagent.co.jp

 

ただ、すべてのゲームがこの思想でカバーできるかというとそうでもないと思っている。

 

「そりゃ仕様によって最適解は変わるだろw」と思うかもしれないが

言いたいことはそういうことではなく

MVP設計というのは元々Web業界で浸透した設計であり

WebのフロントはユーザーがUIを操作するものであるということだ。

 

つまりこの設計思想をそのまま、あるいは少しのアレンジを加え適用できるのはユーザーがUIを操作することが基本となる

モバイル向けソーシャルゲームアプリということである。

 

それを意識せず、ユーザーがコントローラを持ち、キャラクターを操作するタイプのゲームなど(あくまで、など)で

 

「MVPっていうのがいいらしい」

「とりあえず導入しよう」

 

でどこまでがModelでどこまでがViewでPresenterをどこまで定義すれば良いのかが曖昧のまま設計が迷走する例をよく見た。

 

MVPの具体的な活用法に対しては先ほどの記事を読んでもらえばわかると思うので 

今回はModel-View-Presenterに対して、各レイヤーの解釈について特にUnityゲーム開発における持論を書こうと思う。

 

ある人にとっては間違っているかもしれないが、ある人にとっては正解への道標になるはずだ。

 

先に結論を書くと、Viewは積極的に拡大解釈しろという話だ。

 

あとUniRxとMVPの理解は前提で。

  

レイヤーの明確な責任

まずModel-View-Presenterにおける役割を明確にするのと同時に

ゲームを作るために必要なレイヤーを全て洗い出す。

 

MasterData

一応書く。

ゲーム中の経験値テーブルや、アイテムのId、価格などのデータ。

 

Model

ゲームを構成するマスターデータをもとに、ユーザーの現在所持しているアイテム数や、経験値などのいわゆるセーブ対象になるデータを管理しているもの。

ゲームを進行するごとに、書き換えられていく。

 

役割として

 

  1. データを保持する
  2. データの書き換えロジックを、メソッドとして外部に公開する
  3. データが書き換えられた時、通知を発行する

 

この役割さえ守っていれば、中身でどれだけインスタンスを生成しようと、どんなロジックを持とうと構わない。

逆に言うと、これ以外で役割を持ってしまってはいけない。

そしてModelは依存先を持たず、どんな状態でも自身をインスタンス化できるようにしなければならない。

 

View

プレイヤーが実際に見る画面と勘違いされやすい(名前的に)。

本質的な役割は

 

  1. ユーザーが実際に見る画面を構成し、表示する
  2. 画面を更新するメソッドを外部に公開する
  3. ユーザーの操作を通知する

 

最初に書いた通り、UIを操作するだけであるならこのUIが操作されたという通知を飛ばすだけで済む。

その通知の結果、Viewの更新を受け取るだけで済むからだ。

 

ただ実際にはそうはいかない。

なぜなら、キャラクターを操作できるゲームであるなら、キャラクターの位置が更新され、その結果アイテムを拾えたりする領域に入ったりするなど、複雑であるからだ。

 

そして愚直にMVPに当てはめるのであれば、コントローラ操作というViewが通知を発行し、キャラクターのモデルが位置の変更を通知で管理しなければいけなくなる。

 

はっきり言ってそんな設計は冗長すぎてクソだ。

なので後ほどViewの拡大解釈と自由設計について記述する。

 

ただ、上記3つの役割自体は変わらないので厳守しなければならない。

そして、Modelと同じく、Viewはどんな時でもインスタンス化可能でなければならない。

 

Presenter

具体的な機能の提供者である。役割として

 

  1. Viewからの通知を受け取り、Modelが外部公開しているメソッドを呼ぶ
  2. Modelのメソッドを呼んだ結果、Modelからの通知を受け取り、Viewが外部公開しているメソッドを呼ぶ

 

単体では機能しないViewとModelをつなげ、ゲームとして成り立つような機能をロジックとして提供する

となっているが、例として

 

  1. アイテムを拾える領域に入った通知がViewから飛んでくるので
  2. その通知が飛んできた後で他のViewからボタンが押された通知が飛んできたら
  3. Modelが公開しているアイテムを一個増やすメソッドを呼び
  4. アイテムが増えたという通知を受け取り
  5. Viewが公開している「アイテムを拾った演出」を出すメソッドを呼ぶ

 

という処理を行うといえば、わかりやすいだろうか。

Viewが作り込まれれば、あとはPresenterを量産するだけでゲームを拡張していくことが可能になる。

 

 

以上がレイヤーの責任の話。

 

 

Viewを積極的に拡大解釈していく

 

ここまでの話で、何が一体問題なのかというと

 

ゲームはユーザーの操作がバリエーション豊富すぎてViewの役割がブレやすい

 

というところだ。

 

例として、ゲームの操作キャラクターについての話をした。

ここで考えたい

 

キャラクターとはどこのレイヤーに属するのか?

 

ゲームの1機能としてキャラクターを見るのであれば

Model-View-Presenterにまたがって処理を書かなければいけない。

 

でも、ユーザーの操作だし、キャラだって要は画面の1要素なのだからView単体で記述しなければいけない。

 

どっちだろうとなる。

 

結論としては、どっちも正しい

なので

 

Viewとしての役割を厳守できれば、内部の処理はView内で完結されたMVPで記述されても良いのでは

と考えることも可能である。

 

そしてさらに拡大解釈するなら

 

Viewとしての役割さえ守れていれば、中身の設計はなんでもいい

 

だ。

 

例として、クラス図を書いてみた。

 

f:id:yutakaseda3216:20170222145935p:plain

 

ViewがCharacter全体の各コンポーネントで、ゲームに関連するHpなどをModelに移譲することで見通しが良くなる。

 

CharacterPresenterはCharacterの各コンポーネントの通知を使って、Modelを更新すれば良くなるが、既存のPresenterとは違う点がここで生まれる。

 

それは通知を発行したViewは自身で更新を行うということだ。

ダメージを受けた時に、ダメージモーションの遷移などをいちいちPresenterから発行されていては、Character自身が独立して動かなくなってしまう。

 

なのでPresenterの役割としては

 

Viewの通知を使い、Modelを更新した結果、必要であれば他のViewに対しての更新を行う

 

になる、ダメージの結果HPゲージを更新する処理を呼ぶとかね。

 

 

最後

 

蓋を開けてみればなんだそんなことかと思うかもしれないが

責任を明確にしないままMVPを導入するとカオスになるのを身を以て経験している。

 

うまくレイヤーを定義してやり、そのレイヤーに属するものは何なのかを考えよう。

そして、責任さえ守れていれば、レイヤーの中身は自由に解釈して構わない

 

そういう結論を今自分では出している。

 

 

推敲されておらず、勢いだけで書いた文なのでわかりづらいかもしれないが

 

 

許してね^^

 

 

何かあればTwitterまでどうぞ。

 

あとこんなこと書いてる暇あるならゲーム作れって言われそうなのでゲーム作ります・・・。

 

UniRxでObservable -> IEnumerator -> Observable変換した時のライフサイクル

コード

gist9ab1097554ad9f37a8931f82cfdcb467

 

破棄されない話

.SelectMany()にCoroutineを使用するとうまく破棄されない。

.ToYieldInstruction()を使ってObservable -> IEnumeratorに変換したCoroutineを実行中にコンポーネントを削除するとエラーを吐き出す。

 

.ToYieldInstruction()したCoroutineはどうやら源流のObservableに紐づけられないようなので

別途.AddTo()でライフサイクルを定義してあげる必要がある

 

CompositeDisposableなどを使用したライフサイクル管理をしている場合は

  1. 引数でライフサイクルの依存先を渡す
  2. Observable.FromCoroutine(_ => Coroutine()).AddTo(依存先)

でなんとかできるのではないでしょうか。

 

追記

 

作者様

neuecc (@neuecc) | Twitter

 

よりご回答いただきました、模範解答はこちらのようです。

gist.github.com

 

CancellationTokenと言う仕組みがあり、引数にそいつを渡してあげることでライフサイクルを定義できるようです。

シランカッタ・・・

 

使うときはちゃんとオペレータの実装も見ないといかんですね。

neuecc様有難うございました。

 

 

終わり

Monodevelop - UnityでVersionControlが消えた時の対処

 

はじめに

 

MacOSX 10.11.2

Unity 5.4.2f2

MonoDevelop-Unity 5.9.6

バージョン管理はGit

 

BlameやDiff、Changesをワンクリックで見れるので重宝してる、こんなやつ

f:id:yutakaseda3216:20170111112414p:plain

 

ただこいつはよく消える

 

消えた時

Monodevelop-Unity > Add-In Managerを開いてこれをenableにする

f:id:yutakaseda3216:20170111112604p:plain

 

これで復活

する時もあればしない時もある

しない時はSolutionWindowを開いて(View > Pads > Solutionで開ける)

スクリプトファイルを選択した時に出る歯車アイコンクリック、DiffとかBlameとかLogとか選択すると復活する

 

それでも復活しなかった時が来たら対処法調べて追記します。

【windows】何も考えずに最速でsshkeyを作ってgithubと連携する

Gitをインストールします

Git - Downloading Package

チェックボックスはこんな感じで

f:id:yutakaseda3216:20170105211036p:plain

f:id:yutakaseda3216:20170105211116p:plain

後は全部一番上にして、デフォルトのままでおk

 

インストール後

デスクトップにできたGitBashのアイコンをダブルクリックでBash開く

sshkeyを作る(下記をそのまま打ち込んでEnter)

ssh-keygen

ここに作りますか?って聞かれる。(ディレクトリ階層が表示されてる)

Enter押す

passphraseは?って聞かれる。何も考えないのでいらない

Enter押す

もういっかい入力してねって言われる。何もいれてないのでいらない

Enter押す

 

GitHubと連携

sshkeyは初期状態のままならUser/自分のアカウント名/.sshフォルダ内にある

隠しフォルダになってる場合があるので隠しフォルダ見えるようにしとこう

.sshフォルダの中にある、id_rsa.pubをテキストエディタで開くと謎の文字列が生成されているのですべてコピーする

 

f:id:yutakaseda3216:20170105212411p:plain

github開いて、右上の自分のアイコンからSettingsを選択するとこんな画面が出る。

その中のSSH and GPG keysを選択

 

New SSH Keyボタンを押してこんな感じで作成

f:id:yutakaseda3216:20170105212610p:plain

後はリポジトリクローンするときにsshでクローンして快適なgitライフを送ろう!

 

終わり

CUI慣れてからSourceTreeとかKrakenとかのGUIに移行しないとgitの内部的な動作ちんぷんかんぷんで終わるので気を付けような(もちろん内部知らなくていいよって人はKrakenをぜひオヌヌヌします)

コミケに参加して&SleepWalkerの今後

 

はじめに

PhantomIslandのブースまで、朝早くに来てくださった方、ご購入していただいた方

大変ありがとうございました。

動画を見ましたと言ってくれる方や、デジゲー博でプレイしていただいていた方など、多くの方に直接販売することができました。

私は、人に直接モノを売るというのが初めての経験でしたので、とてもうれしかったです。

30枚の用意でしたが、午前中早々に完売してしまい、ゲームを目当てにブースに来てくださった方に完売してしまいましたというのが心苦しかったです、数を用意できず申し訳ありません。

しかし海外の方が数名いらしていただいた際に、買えなかったですが今後に期待していますと言われ、とても身が引き締まる思いでした。

ありがとうございました。

 

コミケの活気はすごいですね!デジゲー博の時も思いましたが、素晴らしい場だと思います。

参加できて本当によかったです。

 

頒布したもの

今回頒布した体験版ですが

オープンワールドで自由に探索(本編)

・ボスとの戦闘モード

の二種類のモードをつけさせていただきました。

 

オープンワールドでは、様々な人と話をしたり、依頼をこなしたり、広大な世界を探索できます。

ボスとの戦闘は、一筋縄ではいかないものを用意させていただきました。

ご購入された方はぜひお楽しみください。

 

今後のSleepWalker

 

製品版としては、まだまだ未完成な部分も多く、システムすら乗っていないものもあります。

完成は来年の夏を予定していますが、その前に一度展示をしようと思っています。

BitSummitへの申し込みを予定していますが、予定は未定です。

 

また、今回の体験版で数多くの課題が出ました。

それを一つずつ潰していこうと思っています。

 

また。製品版へ追加するシステムはこのようになっています

・キャラクターの強化システムの作成

・敵キャラクターの追加

・必殺技追加

・攻撃技の追加

・新たに6ワールド追加、7つのダンジョンを作成

・アイテムの追加

・装備品(装飾品)の追加

 

キャラクターを育てる、世界をより仕上げていく、という部分をこれから開発していきます。

ご期待いただければ幸いでございます。

 

最後に

同人でオープンワールドゲームを開発しているということで、国内外で少しだけ注目していただけているようです。

少しプレッシャーもありますが、周りの開発者さんががんばっているところを見て、自分も頑張ろうという気持ちになれています。

ありがとうございます、これからもよろしくお願いいたします。

 

ゲームの完成をお楽しみに!

UnityでGitLFSを使用する際にTrackするべきファイル

 リポジトリが爆発したので背に腹は変えられないとLFSの導入を決意

Trackする画像や素材拡張子

*.png

*.psd

*.tga

*.jpg

*.fbx

*.anim

*.mp3

*.ogg

*.wav

 

git lfs track *.~するより、.gitattributesに直接記述した方が早い

 

追記(2017/03/10)

.animはアニメーション編集しない前提なので、編集する場合は.animを管理対象から外した方が良い

 

結果

導入前:リポジトリ9GB

導入後:リポジトリ1.2GB(!!!!) LFS 6GB

 

なんか差分の1GBくらいどっかいったけど、多分履歴をfilterしたりしたから?

バイナリ直で持たなくなったから減るとかあるのかな

 

うまく使ってBitBucket無料プランで乗り切っていきたい

 

あとSSH認証じゃないとファイル100件ごとにパスワード要求されるのでKeyは作っておこう

デジゲー博お疲れさまでした&SleepWalkerデモ版配布

 

どうも、かせです。

 

本日、デジゲー博にSleepWalkerのデモ版を出展させていただきました。

f:id:yutakaseda3216:20161113205901p:plain

会場から閉会まで絶え間なくプレイしていただき、デジゲー博の熱量の高さを思い知りました。

 

また、夏から開発しはじめ、初めて人前でプレイをしていただけたので、様々なフィードバックを得ることができました。

この場を借りて

デジゲー博準備会様

各出展者様

来場者様

に深く感謝の意を述べさせていただきたく思います。

 

また、ゲームライターコミュニティ様にて記事を書いていただきました。

【デジゲー博2016】SleepWalker – ゲームライターコミュニティ

ありがとうございました。

 

SleepWalkerについて

夏から開発をしている「オープンワールド3DアクションRPG」です。

世界の体験をコンセプトとして、開発をしています。

また、プレイ感覚としてリアルな感覚を追求するのではなく

より爽快、かつなめらかなアクションを目指しています。

 

今回のデモは、アクション部分のコンセプトモデルでした。

 

f:id:yutakaseda3216:20161113210515p:plain

 

f:id:yutakaseda3216:20161113210625p:plain

 

このデモ版の配布をさせていただきます。

 

Dropbox - sleepwalker.zip

 

操作方法

 

「移動」左スティック

「視点移動」右スティック

「会話」Bボタン

「ジャンプ」Aボタン

「飛行」ジャンプ中にAボタンを再度押す、押しっぱなしで持続

「弱攻撃」地上・空中でXボタン

「強攻撃」地上・空中でYボタン

「打ち上げ」地上でRトリガーをおしながらXボタン

「追撃」地上・空中でRトリガーをおしながらYボタン

「回避」Rトリガーボタン

 

打ち上げ、追撃はすべての行動をキャンセルすることができます。

かっこいいコンボができると気持ちいいです。

 

動作環境

 

そこそこのグラフィックスカードがあれば、ラップトップでも30FPSで動作すると思います。

また、ゲームパッドはXInput対応のもののみのサポートになります。

Xbox360コントローラ、XboxOneコントローラの動作は確認済みです。

ご意見などございましたら、気軽にTwitterの方でお声がけください。

twitter.com

 

今後について

冬コミに当選しました。

1日目・西"ひ"-13a

でSleepWalkerの

オープンワールドになってる(する)

ボス戦とか実装した(する)

体験版を頒布予定です。

 

少しでも魅力を感じていただけましたら、ぜひご来場くだされば幸いです。

よろしくお願い致します。

 

最後に

ゲームを面白くするために努力してまいりますので、ぜひSleepWalkerの完成をお楽しみに!