web-dev-qa-db-ja.com

JavaScript MVCアプリケーションの設計(キャンバス)

JavascriptのアプローチのようなMVCを使用してキャンバスアプリケーションを構造化/構築する方法を理解するのが困難です。 UIはかなり流動的でアニメーション化され、ゲームはかなり単純化されていますが、トゥイーンとアニメーションに重点が置かれています。 MVCがどのように機能するかは原則的にわかりますが、実際には機能しません。私はこれからバガリーをグーグルで調べ、ひどいことを読んだので、始めたときと同じように混乱しています。

アプリケーション領域に関するいくつかの詳細:

  • マルチスクリーンゲームフレームワーク-複数のゲームがこのフレームワーク内に配置されます。一般的なUI「スクリーン」には、設定、情報、難易度の選択、メインメニューなどがあります。
  • 複数のインプットメソッド
  • 一部の画面のトップメニューバーなどの一般的なUI要素
  • さまざまなレンダリング方法(キャンバス/ DOM/webGL)の使用の可能性

現時点では、AppModel、AppController、AppViewがあります。ここから、各「画面」を追加してAppViewに接続することを計画していました。しかし、トップメニューバーのようなものはどうですか、それらは別のMVCトライアドである必要がありますか?コンポーネントを密結合せずにどこにどのように取り付けますか?

1つのMVCトライアドを別のトライアド内に配置することは受け入れられている慣行ですか?つまり、各「画面」をAppViewに追加できますか? 「トライアド」はMVC用語としても受け入れられますか?!

私の心はオプションの下で溶けています...私はここで基本的な何かを見逃しているように感じます。私はMVCアプローチを使用せずにすでに稼働しているソリューションを持っていますが、結局、密結合されたスープ-ロジックとビューを組み合わせて、現在組み合わせています。アイデアは、それを開いて、ビューを簡単に変更できるようにすることでした(たとえば、キャンバスビューをDOMベースのビューと交換するため)。

現在使用されているライブラリ:require.js、createJS、アンダースコア、GSAP、手動ロールMVC実装

特に物事の実際のデザインと、「スクリーン」を適切なM、V、またはCに分割することに関して、ポインタ、例などをいただければ幸いです。

...またはMVC以外のより適切な方法

[この質問を以前に見た場合、他の2つの誤ったstackexchangeコミュニティで質問したので...私の脳が機能しなくなった]

9
wigglyworm

MVCは非常に多くの場所で取り上げられているため、ここで繰り返し説明することはあまりありません。基本的に、オブジェクトグラフ、ヘルパー、ロジックをモデル層に含める必要があります。ビューは、ページの動的な部分を満たすために押し出される画面になります(そして、少量のロジックとヘルパーが含まれる場合があります)。また、コントローラーは、オブジェクトグラフ、ヘルパー、ロジックから得られたものに基づいて画面を提供する軽量の実装です。

モデル

これは、アプリケーションの中心となる場所です。サービス層、ロジック層、エンティティ層に階層化できます。これはあなたの例にとって何を意味しますか?

エンティティレイヤー

これには、ゲームのモデルと内部動作の定義が含まれているはずです。たとえば、マインスイーパのゲームがある場合、これは、ボードと四角形の定義が、内部状態をどのように変更するかと一緒にある場所です。

function Location(x,y){
 this.x = x;
 this.y = y;
}
function MineTile(x,y){
 this.flagged = false;
 this.hasMine = false;
 this.pristine = true;
 this.location = new Location(x,y);
}
MineTile.prototype.expose = function(){
 if( this.hasMine ) return false;
 this.pristine = false;
 return this.location;
};

したがって、MineTileは、それが表示されているか、調査されたかなどの内部状態を認識します(this.pristine)、地雷のあるタイルの場合(this.hasMinebutは、地雷が存在することになっていたかどうかを判断しません。それはロジック層までです。 (さらにOOPに進むために、MineTileは汎用タイルから継承できます)。

ロジックレイヤー

これは、アプリケーションがモードの変更、状態の維持などと対話する複雑な方法を収容する必要があります。したがって、現在のゲームの状態を維持するために、メディエーターパターンが実装されるのはここです。これは、たとえばゲームオーバー中に何が起こるかを決定するため、またはどのMineTilesが鉱山を持つかを設定するためのゲームロジックが存在する場所です。エンティティレイヤーを呼び出して、論理的に決定されたパラメーターに基づいてインスタンス化されたレベルを取得します。

var MineSweeperLogic = {
 construct: function(x,y,difficulty){
  var mineSet = [];
  var bombs = 7;
  if( difficulty === "expert" ) bombs = 15;
  for( var i = 0; i < x; i++ ){
   for( var j = 0; i j < y; j++ ){
    var mineTile = new MineTile(i,j);
    mineTile.hasMine = bombs-- > 0;
    mineSet.Push(mineTile);
   }
  }
  return mineSet;
 },
 mineAt: function(x,y,mineSet){
  for( var i = 0; i < mineSet.length; i++ )
   if( mineSet[i].x === x && mineSet[i].y === y ) return mineSet[i];
 }
};

サービス層

これは、コントローラーがアクセスできる場所です。ゲームを構築するためのロジックレイヤーにアクセスできます。完全にインスタンス化されたゲームまたは変更されたゲームの状態を取得するために、サービス層に高レベルの呼び出しを行うことができます。

function MineSweeper(x,y,difficulty){
 this.x = x;
 thix.y = y;
 this.difficulty = difficulty;
 this.mineSet = MineSweeperLogic.construct(x,y,difficulty);
}
MineSweeper.prototype.expose = function(x,y){
 return MineSweeperLogic.mineAt(x,y,this.mineSet).expose();
}

コントローラー

コントローラは軽量である必要があります。基本的に、これはクライアントとしてモデルに公開されるものです。多くのコントローラーがあるので、それらを構造化することが重要になります。コントローラー関数呼び出しは、JavaScriptイベントがUIイベントに基づいてヒットするものになります。これらは、サービスレイヤーで使用可能な動作を公開し、クライアントのビューを設定または変更する必要があります。

function MineSweeperController(ctx){
 var this.context = ctx;
}
MineSweeperController.prototype.Start = function(x,y,difficulty){
 this.game = new MineSweeper(x,y,difficulty);
 this.view = new MineSweeperGameView(this.context,this.game.x,this.game.y,this.game.mineSet);
 this.view.Update();
};
MineSweeperController.prototype.Select = function(x,y){
 var result = this.game.expose(x,y);
 if( result === false ) this.GameOver();
 this.view.Select(result);
};
MineSweeperController.prototype.GameOver = function(){
 this.view.Summary(this.game.FinalScore());
};

ビュー

ビューは、コントローラーの動作に関連して整理する必要があります。キャンバス処理を扱うため、これらはおそらくアプリケーションの最も集中的な部分になります。

function MineSweeperGameView(ctx,x,y,mineSet){
 this.x = x;
 this.y = y;
 this.mineSet = mineSet;
 this.context = ctx;
}
MineSweeperGameView.prototype.Update = function(){
 //todo: heavy canvas modification
 for(var mine in this.mineSet){}
 this.context.fill();
}

これで、この1つのゲームのMVCセットアップ全体ができました。あるいは、少なくとも、必要最低限​​の例では、ゲーム全体を書き出すことは過剰でした。

これがすべて完了したら、アプリケーションのグローバルスコープがどこかにある必要があります。これは、このシナリオのすべてのMVCスタックへのゲートウェイである現在のコントローラーの寿命を保持します。

var currentGame;
var context = document.getElementById("masterCanvas").getContext('2d');
startMineSweeper.click = function(){
 currentGame = new MineSweeperController(context);
 currentGame.Start(25,25,"expert");
};

MVCパターンの使用は非常に強力ですが、それらのすべてのニュアンスを守ることをあまり気にしないでください。最終的に、アプリケーションが成功したかどうかを決定するのはゲーム体験です:)

検討のため: 建築士がジョエル・スポルスキーによってあなたを怖がらせないでください

3
Travis J

これは、すでに間違っていることです。混乱した状態で、MVCが手元にないときにMVCを手動でローリングしました。

PureMVCを見てください。これは言語に依存しないため、実際にMVCを実行することに慣れるのに最適なプラットフォームです。

そのコードは小さく、わかりやすく、これにより、進行に応じてニーズに合わせてコードを微調整できます。

それを使って小さなシンプルなゲームを書き始めましょう。マインスイーパがいいでしょう。 Travis Jの発言の多くは、特にモデルに関しては良いことです。コントローラー(少なくともPureMvcでは)はステートレスであり、コントローラーが存在し、簡単な作業を行って消えることを覚えておく必要があることだけを付け加えておきます。彼らは知識のある人です。それらは関数のようなものです。 「グリッドを塗りつぶし、モデルを変更しました」、「モデルを更新し、ボタンを押しました」

ビュー(PureMVCのメディエーター)は最も馬鹿げており、モデルは少しだけスマートです。どちらも実装を抽象化するため、ユーザー(コントローラー)が直接UIやDBに触れることはありません。

UIのすべての要素(たとえば、winformsアプリなど)にはビューがあります(メディエーター-なぜこれがより優れた用語であるかがわかりますか?)。 UI要素全体で動作するマネージャ」。ここで重ねて考えます。

UIおよびDBイベントは自動的にコントローラーを呼び出すことができ(スマート命名スキームを使用している場合)、特定のコントローラーを段階的に廃止できます。メディエーターは、モデルデータ変更イベントを直接リッスンし、そのデータパッケージを配信できます。

これは一種のごまかしであり、モデルはそこに何があるかについて少し知っている必要があり、メディエーターはデータパッケージで何をすべきかを理解する必要がありますが、多くの場合、ありふれたコントローラーに悩まされることはありません。

モデル:ばかだが再利用可能。コントローラー:スマートですが再利用性は低くなります(アプリです)。メディエーター:ばかだが再利用可能。この場合の再利用性とは、別のアプリへの移植性を意味します。

2
Mark