Я не согласен с тем, что, поскольку у вас есть основной класс Game, вся логика должна происходить в этом классе.
Чрезмерное упрощение, подражающее вашему примеру, просто чтобы подчеркнуть мою точку зрения:
mainloop:
moveEntities()
resolveCollisions() [objects may "disappear"/explode here]
drawEntities() [drawing before or after cleanEntitites() ain't an issue, a dead entity won't draw itself]
cleanDeadEntities()
Теперь у вас есть класс Bubble:
Bubble implements Drawable {
handle( Needle needle ) {
if ( needle collide with us ) {
exploded = true;
}
}
draw (...) {
if (!exploded) {
draw();
}
}
}
Так что, конечно, есть главный цикл, который заботится о передаче сообщений между сущностями, но логика, связанная со столкновением между Пузырьком и Иглой, определенно отсутствует в основном классе Игры.
Я почти уверен, что даже в вашем случае вся логика, связанная с движением, не происходит в основном классе.
Так что я не согласен с вашим утверждением, которое вы написали жирным шрифтом, что «вся логика происходит в основном классе».
Это просто не правильно.
Что касается хорошего дизайна: если вы можете легко представить другой «вид» вашей игры (например, мини-карту) и если вы можете легко закодировать «идеальное воспроизведение кадра за кадром», то ваш дизайн вероятно, не так уж и плохо (то есть: записав только входные данные и время, когда они произошли, вы сможете воссоздать игру точно так, как в нее играли. Именно так Age of Empires, Warcraft 3 и т. д. делают свое воспроизведение : записываются только пользовательские данные и время, когда они произошли (поэтому файлы воспроизведения, как правило, такие крошечные)).