De nombreux jeux Android ne sont même pas assez étendus pour justifier l'enregistrement / le chargement ou les options / préférences, sans parler des personnages personnalisés et autres simplement parce qu'ils sont joués à la main pendant 10 minutes dans le train de retour.
Ne commettez pas l'erreur que j'ai commise en me lançant dans quelque chose de très large pour un premier jeu Android. Faites un tas de petits jeux puis optez pour quelque chose de plus grand
Spécificités Android:
Structure:
Je recommanderais d'avoir presque tout le jeu dans une classe d'activité. Android fonctionne sur l'idée que chaque activité est comme une mini-application, semi-indépendante des autres activités de l'application. L'application est essentiellement une pile d'activités, avec l'utilisateur qui voit ce qui est le plus haut.
Lorsque vous en sautez une, elle est généralement détruite et sera recréée la prochaine fois que l'utilisateur commencera une nouvelle activité (intention startActivity). Cela rend difficile le maintien des états entre les activités et conduit à une architecture d'activité unique
Vous voudrez peut-être avoir une activité de type écran d'accueil avec un menu "Nouveau jeu / Charger un jeu / Options" et en faire l'activité de lancement. Cependant, vous finirez par avoir également un menu dans le jeu dans votre activité de jeu, qui aura la plupart des mêmes fonctions.
Ma propre application, j'ai fait une "MenuActivity" qui a les fonctions qui lancent un nouveau jeu, enregistrent, chargent, changent les options. Ensuite, ma HomeActivity étend cela, tout comme ma GameActivity.
Étant donné que tout est dans une classe d'activité, je recommanderais de faire une hiérarchie des classes d'activité et d'utiliser des lots d'étendues privées, protégées et par défaut. En procédant de cette façon, vous pouvez au moins diviser les choses en différents fichiers pour éviter d'avoir un fichier d'activité ingérable. Par exemple pour ma propre application:
GraphicsEngine extends MenuActivity
.
PhysicsEngine extends GraphicsEngine
.
GameLogicActivity extends PhysicsEngine
.
UIActivity extends GameLogicActivity
.
Étant donné que mon application est ouverte en 3D, beaucoup de choses que je fais ne fonctionnent pas entre les activités, donc tout est dans la même activité, alors peut-être que je suis partisan d'utiliser une architecture d'activité
-
Enfiler
Pour l'activité de jeu, vous avez deux fils (ou 3 si vous faites des trucs 3D opengl). Un thread est l'interface utilisateur / thread principal. Il s'agit du thread dans lequel l'activité démarre et s'exécute et vous est fourni par Android.
Ce thread d'interface utilisateur est le seul dans lequel vous pouvez mettre à jour des éléments d'interface utilisateur (vues, mises en page, etc.). C'est également celui sur lequel les écouteurs pour les entrées utilisateur s'exécuteront. Vous ne voyez aucune des mécaniques du thread d'interface utilisateur, juste qu'il s'exécute en boucle quelque part en arrière-plan.
Le deuxième fil, vous vous faites vous-même (je recommanderais d'utiliser AsyncTask , malgré toutes ses lacunes). Cela fait toutes les choses non-UI que vous faites dans une boucle de jeu normale, telles que la mise à jour du mouvement, le calcul des collisions, les calculs de combat, etc.
Vous créez ce thread / classe AsyncTask comme une classe interne de votre activité. De cette façon, vous pouvez avoir des objets de portée étendue ( Vector<Spaceship>
) accessibles par le thread d'interface utilisateur et le thread de boucle de jeu.
Étant donné que la logique du jeu se déroule dans le thread de boucle de jeu, c'est le seul thread qui devra réellement changer les valeurs des variables (mettre à jour la vitesse du tank, réduire les HP du joueur). Le thread d'interface utilisateur lit simplement les valeurs, il devrait donc y avoir un minimum de problèmes de concurrence.
Le plus difficile est d'obtenir que le thread d'interface utilisateur fasse des mises à jour à la demande du thread de boucle de jeu. Il y a plusieurs façons de faire ça. Si vous lisez la documentation AsyncTask, elle a la méthode publishProgress () / onProgressUpdate (). Cela ajoute en fait des trucs à la file d'attente du thread d'interface utilisateur pour faire la prochaine boucle.
Le gestionnaire fait exactement la même chose, où vous implémentez la méthode handleMessage () et cette méthode est en fait exécutée par la boucle suivante du thread d'interface utilisateur.
Enfin, toute entrée utilisateur, étant dans le thread d'interface utilisateur, vous pouvez mettre à jour les éléments d'interface utilisateur immédiatement à partir de lui (donc à l'intérieur de l'implémentation de tous les écouteurs onClick / onTouch). Si vous avez besoin de mettre à jour les objets du jeu, vous pouvez utiliser des choses comme la synchronisation ou implémenter votre propre file d'attente de mises à jour dans AsyncTask qui, comme le thread d'interface utilisateur, passe par la prochaine fois qu'il exécute la boucle de jeu.
Guide de filetage Android .
-
UI
En ce qui concerne la structure d'interface utilisateur réelle au sein de l'activité unique, je recommande d'avoir une disposition de cadre comme disposition de base. Les éléments enfants d'une disposition de cadre fonctionnent comme une file d'attente. Le premier élément est tiré en premier, le deuxième est dessiné en haut du premier, le troisième en haut du second.
De cette façon, vous pouvez avoir plusieurs fichiers de disposition XML et en gérant les enfants de la disposition du cadre, vous pouvez facilement permuter des ensembles de vues vers l'intérieur et vers l'extérieur.
Si vous avez toujours la SurfaceView en bas (premier enfant dans la disposition du cadre), vous pouvez utiliser toutes les vues / widgets Android habituels (boutons, vues de texte, vues de défilement), etc. en haut de la vue de surface, ce qui ne fait que la partie graphique du jeu.
Ainsi, par exemple, si quelque chose se charge, vous pouvez mettre en pause la boucle du jeu / simplement le faire sauter tout, ajouter un écran de «chargement» opaque en tant que dernier enfant à la disposition du cadre et l'utilisateur verra «chargement» apparaître sur son écran , tout à fait inconscient que d'autres vues sont derrière. De cette façon, vous n'avez pas à supprimer les vues qui prennent beaucoup de temps à configurer ou qui causent des complications à chaque fois qu'elles sont ajoutées / supprimées.
Je recommanderais également d'utiliser des lots View.setVisibility. Vous pouvez, par exemple, ajouter une mise en page complète de l'inventaire à la mise en page de votre cadre de base, puis y définir la visibilité (View.Visible) lorsque l'utilisateur clique pour afficher son inventaire et définir la visibilité (View.Gone) lorsqu'il la ferme à nouveau. De cette façon, vous ne gérez même pas les enfants de la disposition du cadre au lieu de simplement tout ajouter et de rendre les choses visibles / invisibles au fur et à mesure que l'utilisateur fait des choses différentes
Cela aide à enfiler à nouveau; lorsque l'utilisateur clique pour ouvrir son inventaire, onCLickListener est géré dans le thread d'interface utilisateur, l'inventaire y est rendu visible et la méthode updateInventory est appelée, toujours à partir du thread d'interface utilisateur, avec seulement des getters sur tous les objets de jeu appelés
Voici le diagramme que j'ai fait pour une question précédente sur l'idée de disposition d'activité / cadre unique:
Une grande partie de ce que je vois dans votre diagramme est liée à l'interface utilisateur, et cela semble assez raisonnable. Il n'y a qu'une petite partie de votre diagramme réservée à la logique réelle du jeu, et elle est très vague.
Je pense que vous essayez trop fort. Je vous suggère de commencer à écrire du code. Choisissez un problème particulier et commencez à le résoudre. Vous en apprendrez tellement qu'au moment où vous aurez une meilleure idée de votre espace problématique, vous aurez une perspective complètement différente (et plus utile).
la source