Seu jogador e seu troll nada mais são que conjuntos de dados, o que chamamos de Modelo de dados que descreve seu mundo. Vida, inventário, recursos de ataque, até mesmo o conhecimento do mundo - tudo consiste no modelo de dados.
Mantenha um único objeto Modelo principal que mantenha todos os dados que descrevem seu mundo. Ele conterá informações gerais do mundo, como dificuldade, parâmetros físicos etc. Também conterá uma lista / matriz de dados de entidades específicas , como descrevi acima. Este modelo principal pode consistir em muitos subobjetos para descrever seu mundo. Em nenhum lugar do seu modelo você deve ter funções que controlem a lógica do jogo ou exibam a lógica; getters são a única exceção e seriam usados apenas para permitir que você obtenha dados do modelo mais rapidamente (se membros públicos ainda não fizerem o truque).
Em seguida, crie funções em uma ou mais classes "controladoras"; você pode escrevê-los todos como funções auxiliares na classe principal, embora isso possa ficar um pouco grande depois de um tempo. Eles serão chamados a cada atualização para atuar sobre os dados das entidades para diferentes finalidades (movimento, ataque etc.). Manter essas funções fora de uma classe de entidade é mais eficiente em termos de recursos e, depois de saber o que descreve sua entidade, você saberá automaticamente quais funções precisam agir sobre ela.
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.
}
Uma observação final é que também é útil manter sua lógica de exibição separada da lógica do jogo. A lógica de exibição seria: "Onde eu desenho isso na tela e em que cor?" vs. lógica do jogo, como descrevi no pseudocódigo acima.
(Nota do desenvolvedor: ao usar classes, isso segue livremente uma abordagem de programação funcional que considera todos os métodos como idealmente sem estado, permitindo um modelo de dados limpo e uma abordagem de processamento que minimiza os bugs causados pelo estado retido. FP é o MVC definitivo, pois atinge MVCs objetivo de separar as preocupações explicitamente. Veja esta pergunta .)