Javascript MVC application design (canvas)

9

Ho difficoltà a capire come strutturare / architettare un'applicazione su tela usando un approccio simile a MVC in Javascript. L'interfaccia utente sarà abbastanza fluida e animata, i giochi abbastanza semplici ma con una strong enfasi su tweening e animazione. Ho capito come funziona MVC in linea di principio ma non nella pratica. Ho cercato su google la sodomia, ho letto un sacco di cose e ora sono confuso com'ero quando ho iniziato.

Alcuni dettagli sull'area dell'applicazione:

  • framework per videogiochi multischermo: più giochi siederanno all'interno di questo framework le "schermate" comuni dell'interfaccia utente includono: impostazioni, informazioni, scegliere difficoltà, menu principale ecc.
  • più metodi di input
  • elementi dell'interfaccia utente comuni come la barra dei menu in alto su alcuni schermi
  • possibilità di utilizzare diversi metodi di rendering (canvas / DOM / webGL)

Al momento ho un AppModel, AppController e AppView. Da qui stavo progettando di aggiungere ciascuno degli "schermi" e collegarlo all'AppView. Ma per quanto riguarda le cose come la barra dei menu in alto, dovrebbero essere un'altra triade MVC? Dove e come dovrei collegarlo senza accoppiare saldamente i componenti?

È una pratica accettata avere una triade MVC all'interno di un'altra? Ad esempio, posso aggiungere ogni "schermo" all'AppView? La "triade" è anche un termine MVC accettato?!

La mia mente si sta sciogliendo sotto le opzioni ... Mi sento come se mi mancasse qualcosa di fondamentale qui. Ho già una soluzione pronta e funzionante senza l'utilizzo di un approccio MVC, ma ho finito con una zuppa strettamente accoppiata - logica e viste e attualmente combinata. L'idea era di aprirla e consentire un cambio di visualizzazione più semplice (ad esempio, scambiando una vista su tela con una vista basata su DOM).

Librerie correnti utilizzate: require.js, createJS, underscore, GSAP, implementazione MVC eseguita a mano

Qualunque suggerimento, esempio ecc., in particolare per quanto riguarda il design attuale della cosa e la divisione degli "schermi" in M, V o C, sarebbe apprezzato.

... o un metodo più appropriato diverso da MVC

[NB, se hai visto questa domanda prima è perché l'ho chiesta in altre 2 community errate di stackexchange ... il mio cervello ha smesso di funzionare]

    
posta wigglyworm 07.05.2014 - 16:52
fonte

2 risposte

3

MVC è stato coperto in così tanti posti quindi non ci dovrebbe essere molto da ripetere qui. In sostanza, si desidera che il grafico degli oggetti, gli helper e la logica siano contenuti nel livello del modello. Le viste saranno le schermate che vengono espulse per riempire la parte dinamica della pagina (e potrebbero contenere una quantità minima di logica e di aiutanti). E il controller, che è un'implementazione leggera per servire gli schermi in base a ciò che era disponibile dai grafici degli oggetti, dagli helper e dalla logica.

Modello

Questo dovrebbe essere dove si trova la carne dell'applicazione. Può essere suddiviso in livelli in un livello di servizio, un livello logico e un livello di entità. Cosa significa questo per il tuo esempio?

Livello entità

Questo dovrebbe contenere le definizioni dei modelli e dei comportamenti interni del tuo gioco. Ad esempio, se avessi un gioco per dragamine, questo sarebbe dove erano le definizioni di tavole e quadrati e come cambiano il loro stato interno.

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;
};

Quindi il MineTile conoscerà il suo stato interno, ad esempio se sta visualizzando o è stato esaminato ( this.pristine ), se era una delle tessere che ha una mina ( this.hasMine ) ma non determinerà se si suppone che abbia una miniera. Questo dipenderà dal livello logico. (Per andare ancora oltre in OOP, MineTile potrebbe ereditare da un Tile generico).

Livello logico

Questo dovrebbe ospitare i complessi modi in cui l'applicazione interagirà con il cambiamento delle modalità, il mantenimento dello stato, ecc. Quindi questo sarebbe dove sarebbe stato implementato un modello di mediatore per mantenere lo stato del gioco corrente. Questo sarebbe il luogo in cui la logica del gioco risiedeva per determinare cosa succede durante una partita, per esempio, o per impostare quali MineTiles avranno una mina. Effettua chiamate nel livello Entità per ottenere livelli istanziati in base a parametri determinati logicamente.

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];
 }
};

Livello di servizio

Questo sarà dove il controllore ha accesso. Avrà accesso al livello logico per la costruzione dei giochi. Una chiamata di alto livello può essere effettuata nel livello di servizio per recuperare un gioco completamente istanziato o uno stato di gioco modificato.

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();
}

Regolatore

I controller dovrebbero essere leggeri, essenzialmente questo è ciò che viene esposto come client al modello. Ci saranno molti controller, quindi strutturarli diventerà importante. Le chiamate alle funzioni del controller saranno ciò che le chiamate javascript colpiscono in base agli eventi dell'interfaccia utente. Questi dovrebbero esporre i comportamenti disponibili nel livello di servizio e quindi compilare o in questo caso modificare le visualizzazioni per il client.

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());
};

Visualizza

Le viste dovrebbero essere organizzate in relazione ai comportamenti del controllore. Saranno probabilmente la parte più intensiva della tua applicazione dal momento che si occupa di tele.

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();
}

Quindi ora hai l'intera configurazione MVC per questo gioco. O almeno, un semplice esempio, scrivere l'intero gioco sarebbe stato eccessivo.

Una volta che tutto è stato fatto, ci sarà bisogno di un ambito globale per l'applicazione da qualche parte. Ciò manterrà la durata del controller corrente, che è il gateway per tutto lo stack MVC in questo scenario.

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

L'uso di pattern MVC è molto potente, ma non preoccuparti troppo di aderire ad ogni sfumatura. Alla fine, è l'esperienza di gioco che determinerà se l'applicazione è un successo:)

Per considerazione: Non lasciare che gli astronauti dell'architettura ti spaventino di Joel Spolsky

    
risposta data 07.05.2014 - 20:26
fonte
2

Ecco cosa hai già sbagliato - hai fatto rotolare a mano un MVC mentre si trovava in uno stato di confusione e senza alcun MVC al sicuro.

Dai un'occhiata a PureMVC, è indipendente dal linguaggio e può essere una buona piattaforma per bagnarti i piedi con il MVC.

Il suo codice è piccolo e comprensibile, e questo ti permetterà di adattarlo alle tue esigenze mentre avanzi.

Inizia a scrivere un gioco semplice e semplice, il dragamine sarebbe bello. Molto di ciò che ha detto Travis J è buono, specialmente riguardo al Modello. Vorrei solo aggiungere che è necessario ricordare che i controller (almeno in PureMvc) sono senza stato, vengono all'esistenza, fanno il loro breve lavoro e vanno via. Sono quelli esperti. Sono come funzioni. "Riempi la griglia, perché il modello è cambiato", "Aggiorna il modello, perché è stato premuto un pulsante"

I Views (Mediatori in PureMVC) sono i più stupidi e il Modello è solo leggermente più intelligente. Entrambi astraggono l'implementazione, quindi tu (Controllers) non tocchi mai direttamente l'interfaccia utente o il DB.

Ogni elemento della tua interfaccia utente (ad esempio in un'app winforms ad esempio) ha una vista (Mediatore - capisci perché questo è un termine migliore ora?), ma i mediatori possono essere creati anche per meta-preoccupazioni come "Colore di controllo" o "Focus Manager" che operano attraverso elementi dell'interfaccia utente. Pensa a livelli qui.

Gli eventi UI e DB possono richiamare automaticamente i controllori (se si utilizza uno schema di denominazione intelligente) e alcuni controller possono essere eliminati gradualmente: è possibile creare un mediatore per ascoltare direttamente un evento di modifica dei dati del modello e consegnare il proprio pacchetto di dati .

Anche se è una specie di imbroglione e richiede al modello di sapere un po 'di ciò che è là fuori, e il mediatore di capire cosa fare con un pacchetto di dati, ma ti impedirà di essere sommerso da controllori banali in molti casi.

Modello: stupido ma riutilizzabile; Controller: intelligenti ma meno riutilizzabili (sono l'app); Mediatori: stupidi ma riutilizzabili. La riusabilità in questo caso significa portabile su un'altra app.

    
risposta data 28.07.2014 - 07:25
fonte

Leggi altre domande sui tag