Il tuo giocatore e il tuo troll non sono altro che insiemi di dati, quello che chiamiamo il Modello di dati che descrive il tuo mondo. Vita, inventario, capacità di attacco, persino la loro conoscenza del mondo: tutto consiste nel modello di dati.
Mantieni un singolo oggetto Modello principale che contiene tutti i dati che descrivono il tuo mondo. Conterrà informazioni generali sul mondo come difficoltà, parametri fisici, ecc. Conterrà anche un elenco / matrice di dati di entità specifiche come ho descritto sopra. Questo modello principale può consistere in molti oggetti secondari per descrivere il tuo mondo. In nessun punto del tuo modello dovresti avere delle funzioni che controllano la logica di gioco o la logica di visualizzazione; i getter sono l'unica eccezione e verrebbero utilizzati solo per consentire all'utente di ottenere dati dal modello più prontamente (se i membri pubblici non fanno già il trucco).
Quindi, creare funzioni in una o più classi "controller"; puoi scriverli tutti come funzioni di aiuto nella tua classe principale, anche se dopo un po 'potrebbe diventare un po' grande. Questi saranno chiamati ogni aggiornamento per agire sui dati delle entità per scopi diversi (movimento, attacco ecc.). Mantenere queste funzioni al di fuori di una classe di entità è più efficiente in termini di risorse e una volta che sai cosa descrive la tua entità, saprai automaticamente quali funzioni devono agire su di essa.
class Main
{
//...members variables...
var model:GameModel = new GameModel();
//...member functions...
function realTimeUpdate() //called x times per second, on a timer.
{
for each (var entity in model.entities)
{
//command processing
if (entity == player)
decideActionsFromPlayerInput(entity);
else //everyone else is your enemy!
decideActionsThroughDeviousAI(entity);
act(entity);
}
}
//OR
function turnBasedUpdate()
{
if (model.whoseTurn == "player")
{
decideActionsFromInput(model.player); //may be some movement or none at all
act(player);
}
else
{
var enemy;
for each (var entity in model.entities)
{
if (entity != model.player)
{
enemy = entity;
decideActions(enemy);
act(enemy);
}
}
}
}
//AND THEN... (common to both turn-based and real-time)
function decideActionsThroughDeviousAI(enemy)
{
if (distanceBetween(enemy, player) <= enemy.maximumAttackDistance)
storeAttackCommand(enemy, "kidney punch", model.player);
else
storeMoveCommand(player, getVectorFromTo(enemy, model.player));
}
function decideActionsFromPlayerInput(player)
{
//store commands to your player data based on keyboard input
if (KeyManager.isKeyDown("A"))
storeMoveCommand(player, getForwardVector(player));
if (KeyManager.isKeyDown("space"))
storeAttackCommand(player, "groin slam", currentlyHighlightedEnemy);
}
function storeAttackCommand(entity, attackType, target)
{
entity.target = target;
entity.currentAttack = attackType;
//OR
entity.attackQueue.add(attackType);
}
function storeMoveCommand(entity, motionVector)
{
entity.motionVector = motionVector;
}
function act(entity)
{
entity.position += entity.motionVector;
attack(entity.target, entity.currentAttack);
}
}
class GameModel
{
var entities:Array = []; //or List<Entity> or whatever!
var player:Entity; //will often also appear in the entity list, above
var difficultyLevel:int;
var globalMaxAttackDamage:int;
var whoseTurn:Boolean; //if turnbased
//etc.
}
Un'ultima nota è che è anche utile mantenere la logica di visualizzazione separata dalla logica di gioco. La logica di visualizzazione sarebbe "Dove posso disegnarlo sullo schermo e con quale colore?" vs. la logica di gioco è quella che ho delineato nello pseudcode sopra.
(Nota dello sviluppatore: durante l'utilizzo delle classi, segue vagamente un approccio di programmazione funzionale che considera tutti i metodi idealmente apolidi, consentendo un modello di dati pulito e un approccio di elaborazione che minimizza i bug causati dallo stato mantenuto. FP è l'MVC finale, poiché raggiunge gli MVC obiettivo della separazione esplicita delle preoccupazioni. Vedi questa domanda .)