Comment écrire votre premier jeu Android en Java

Auteur: John Stephens
Date De Création: 1 Janvier 2021
Date De Mise À Jour: 19 Peut 2024
Anonim
Comment écrire votre premier jeu Android en Java - Applications
Comment écrire votre premier jeu Android en Java - Applications

Contenu


Il existe de nombreuses façons de créer un jeu pour Android et l'une des méthodes les plus importantes consiste à le faire à partir de zéro dans Android Studio avec Java. Cela vous donne le maximum de contrôle sur l'apparence et le comportement de votre jeu. Le processus vous apprendra également des compétences que vous pouvez utiliser dans de nombreux autres scénarios, que vous créiez un écran de démarrage pour une application ou que vous souhaitiez simplement ajouter des animations. Dans cet esprit, ce tutoriel va vous montrer comment créer un simple jeu en 2D avec Android Studio et Java. Vous pouvez trouver tout le code et toutes les ressources sur Github si vous voulez suivre.

Mise en place

Pour créer notre jeu, nous allons devoir traiter quelques concepts spécifiques: boucles de jeu, threads et toiles. Pour commencer, démarrez Android Studio. Si vous ne l’avez pas installé, consultez notre introduction complète à Android Studio, qui décrit le processus d’installation. Maintenant, démarrez un nouveau projet et assurez-vous de choisir le modèle "Activité vide". C’est un jeu, vous n’avez bien entendu pas besoin d’éléments comme le bouton FAB compliquer les choses.


La première chose à faire est de changer AppCompatActivity à Activité. Cela signifie que nous n’utilisons pas les fonctionnalités de la barre d’action.

De même, nous voulons également rendre notre jeu en plein écran. Ajoutez le code suivant à onCreate () avant l'appel à setContentView ():

getWindow (). setFlags (WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); this.requestWindowFeature (Window.FEATURE_NO_TITLE);

Notez que si vous écrivez du code et qu'il est souligné en rouge, cela signifie probablement que vous devez importer une classe. En d'autres termes, vous devez indiquer à Android Studio que vous souhaitez utiliser certaines instructions et les rendre disponibles. Si vous cliquez simplement n'importe où sur le mot souligné, puis appuyez sur Alt + Entrée, cela se fera automatiquement pour vous!


Créer votre vue de jeu

Vous êtes peut-être habitué aux applications qui utilisent un script XML pour définir la disposition des vues telles que des boutons, des images et des étiquettes. C'est ce que la ligne setContentView fait pour nous.

Mais là encore, il s’agit d’un jeu, ce qui signifie qu’il n’est pas nécessaire d’avoir une fenêtre de navigateur ou de faire défiler les vues du recycleur. Au lieu de cela, nous voulons montrer une toile. Dans Android Studio, une toile est identique à celle de l’art: c’est un support sur lequel on peut s’appuyer.

Alors changez cette ligne pour lire comme suit:

setContentView (nouveau GameView (this))

Vous constaterez que ceci est à nouveau souligné en rouge. Mais à présent si vous appuyez sur Alt + Entrée, vous ne pouvez pas importer la classe. Au lieu de cela, vous avez la possibilité de créer une classe. En d’autres termes, nous sommes sur le point de créer notre propre classe qui définira ce qui sera placé sur la toile. C’est ce qui nous permettra de dessiner à l’écran plutôt que de simplement montrer des vues toutes faites.

Alors faites un clic droit sur le nom du paquet dans votre hiérarchie à gauche et choisissez Nouveau> Classe. Vous allez maintenant avoir une fenêtre pour créer votre classe et vous allez l'appeler GameView. Sous SuperClass, écrivez: android.view.SurfaceView ce qui signifie que la classe héritera des méthodes - ses capacités - de SurfaceView.

Dans la zone Interface (s), vous écrivez android.view.SurfaceHolder.Callback. Comme dans toute classe, nous devons maintenant créer notre constructeur. Utilisez ce code:

fil de MainThread privé; GameView public (contexte de contexte) {super (contexte); getHolder (). addCallback (this); }

Chaque fois que notre classe est appelée pour créer un nouvel objet (dans ce cas notre surface), elle lance le constructeur et crée une nouvelle surface. La ligne «super» appelle la super-classe et dans notre cas, il s’agit de SurfaceView.

En ajoutant Callback, nous pouvons intercepter des événements.

Maintenant, remplacez certaines méthodes:

@Override public vide surfaceChanged (titulaire SurfaceHolder, format int, largeur int, hauteur hauteur) {} @Override public vide surfaceCreated (titulaire SurfaceHolder) {} @Override public vide surfaceDestroyed (titulaire SurfaceHolder) {}

Celles-ci nous permettent fondamentalement de remplacer (d'où le nom) les méthodes de la superclasse (SurfaceView). Vous devriez maintenant avoir plus de soulignements rouges dans votre code. Agréable.

Vous venez de créer une nouvelle classe et chaque fois que nous nous en référons, cela construira la toile sur laquelle votre jeu sera peint. Des classes créer objets et nous avons besoin d'un de plus.

Création de threads

Notre nouvelle classe va s'appeler MainThread. Et son travail sera de créer un fil. Un thread est essentiellement comme un fork parallèle de code pouvant fonctionner simultanément le long du principale une partie de votre code. Vous pouvez avoir beaucoup de threads en cours d'exécution en même temps, permettant ainsi aux choses de se produire simultanément plutôt que de respecter une séquence stricte. C'est important pour un jeu, car nous devons nous assurer qu'il continue à bien fonctionner, même lorsque beaucoup de choses se passent.

Créez votre nouvelle classe comme vous le faisiez auparavant et cette fois, elle sera étendue Fil. Dans le constructeur, nous allons simplement appeler super(). Rappelez-vous que c’est la super classe, qui est Thread, et qui peut faire tout le travail lourd pour nous. C'est comme créer un programme pour laver la vaisselle qui appelle simplement Machine à laver().

Lorsque cette classe est appelée, elle crée un thread séparé qui s’exécute en tant que ramification de l’essentiel. Et c’est de ici que nous voulons créer notre GameView. Cela signifie que nous devons également référencer la classe GameView et utiliser également SurfaceHolder, qui contient le canevas. Donc, si la toile est la surface, SurfaceHolder est le chevalet. Et GameView est ce qui met tout cela ensemble.

Le tout devrait ressembler à ceci:

Classe publique MainThread etend Thread {private SurfaceHolder surfaceHolder; GameView privé gameView; public MainThread (surfaceHolder surfaceHolder, GameView gameView) {super (); this.surfaceHolder = surfaceHolder; this.gameView = gameView; }}

Schweet. Nous avons maintenant un GameView et un fil!

Création de la boucle de jeu

Nous avons maintenant les matières premières nécessaires à la fabrication de notre jeu, mais rien ne se passe. C’est là que la boucle de jeu entre en jeu. En gros, c’est une boucle de code qui tourne en rond et qui vérifie les entrées et les variables avant de dessiner l’écran. Notre objectif est de rendre cela aussi cohérent que possible, de sorte qu’il n’y ait pas de bêtises ou de ratés dans le cadrage, ce que je vais explorer un peu plus tard.

Pour le moment, nous sommes toujours dans le MainThread classe et nous allons remplacer une méthode de la superclasse. Celui-ci est courir.

Et ça donne un petit quelque chose comme ça:

@Override public void run () {while (en cours d'exécution) {canvas = null; try {canvas = this.surfaceHolder.lockCanvas (); synchronized (surfaceHolder) {this.gameView.update (); this.gameView.draw (canvas); }} catch (exception e) {} finally {if (canvas! = null) {try {surfaceHolder.unlockCanvasAndPost (canvas); } catch (Exception e) {e.printStackTrace (); }}}}}

Vous verrez beaucoup de soulignements, nous devons donc ajouter plus de variables et de références. Retournez au sommet et ajoutez:

SurfaceHolder privé surfaceHolder; GameView privé gameView; course booléenne privée; toile de toile statique publique;

N'oubliez pas d'importer Canvas. La toile est la chose sur laquelle nous allons vraiment nous appuyer. Quant à ‘lockCanvas’, c’est important parce que c’est ce qui gèle la toile pour nous permettre d’en tirer. C’est important, car sinon, vous pourriez avoir plusieurs threads essayant de s’en servir en même temps. Sachez simplement que pour éditer la toile, vous devez d’abord fermer à clé La toile.

La mise à jour est une méthode que nous allons créer et c’est là que se dérouleront les activités les plus amusantes.

le essayer et capture en attendant, ce sont simplement des exigences de Java qui montrent que nous sommes prêts à essayer de gérer les exceptions (erreurs) qui pourraient survenir si le canevas n’est pas prêt, etc.

Enfin, nous voulons pouvoir démarrer notre fil lorsque nous en avons besoin. Pour ce faire, nous aurons besoin d’une autre méthode qui nous permette de mettre les choses en mouvement. C’est ce que le fonctionnement variable est pour (notez qu'un booléen est un type de variable qui n'est jamais que vrai ou faux). Ajouter cette méthode au MainThread classe:

public void setRunning (boolean isRunning) {running = isRunning; }

Mais à ce stade, une chose doit encore être soulignée et c’est mise à jour. En effet, nous n’avons pas encore créé la méthode de mise à jour. Alors revenez dans GameView et maintenant ajouter une méthode.

mise à jour publique void () {}

Nous devons aussi début le fil! Nous allons le faire dans notre surfaceCréée méthode:

@Override public void surfaceCreated (titulaire de SurfaceHolder) {thread.setRunning (true); thread.start (); }

Nous devons également arrêter le fil lorsque la surface est détruite. Comme vous l'avez peut-être deviné, nous nous en occupons dans le surfaceDestroyed méthode. Mais vu qu’il peut prendre plusieurs tentatives pour arrêter un fil, nous allons le mettre en boucle et utiliser essayer et capture encore. Ainsi:

@Override public void surfaceDestroyed (titulaire de SurfaceHolder) {boolean retry = true; while (réessayez) {try {thread.setRunning (false); thread.join (); } catch (InterruptedException e) {e.printStackTrace (); } retry = false; }}

Enfin, dirigez-vous vers le constructeur et assurez-vous de créer la nouvelle instance de votre thread, sinon vous obtiendrez l’exception redoutée du pointeur null! Nous allons ensuite rendre GameView accessible à la discussion, ce qui signifie qu’il peut gérer les événements.

thread = new MainThread (getHolder (), this); setFocusable (true);

Maintenant vous pouvez enfin effectivement tester cette chose! C’est vrai, cliquez sur Exécuter et devrait effectivement courir sans aucune erreur. Préparez-vous à être époustouflé!

C’est… c’est… un écran vide! Tout ce code. Pour un écran vide. Mais, ceci est un écran vide de opportunité. Votre surface est opérationnelle avec une boucle de jeu pour gérer les événements. Maintenant, il ne reste plus qu’à faire bouger les choses. Peu importe même si vous n’avez pas tout suivi dans le didacticiel jusqu’à maintenant. Le fait est que vous pouvez simplement recycler ce code pour commencer à faire des jeux glorieux!

Faire un graphique

Bien, nous avons maintenant un écran vide sur lequel dessiner, tout ce que nous avons à faire est de dessiner dessus. Heureusement, c’est la partie la plus simple. Tout ce que vous avez à faire est de remplacer la méthode de tirage dans notre GameView classe et ensuite ajouter quelques jolies images:

@Override public void draw (Canvas canvas) {super.draw (canvas); if (canvas! = null) {canvas.drawColor (Color.WHITE); Paint Paint = new Paint (); paint.setColor (Color.rgb (250, 0, 0)); canvas.drawRect (100, 100, 200, 200, peinture); }}

Exécutez ceci et vous devriez maintenant avoir un joli carré rouge en haut à gauche d'un écran autrement blanc. C'est certainement une amélioration.

Vous pouvez théoriquement créer à peu près tout votre jeu en le collant dans cette méthode (et en écrasant onTouchEvent gérer les entrées) mais ce ne serait pas une très bonne façon de faire les choses. Placer une nouvelle peinture dans notre boucle ralentira considérablement les choses et même si nous la placions ailleurs, ajouter trop de code à la dessiner méthode deviendrait laide et difficile à suivre.

Au lieu de cela, il est beaucoup plus logique de gérer des objets de jeu avec leurs propres classes. Nous allons commencer par un qui montre un personnage et cette classe sera appelée CaractèreSprite. Allez-y et faites ça.

Cette classe va dessiner un sprite sur la toile et ressemblera à

Classe publique CharacterSprite {image bitmap privée; public CharacterSprite (Bitmap bmp) {image = bmp; } public void draw (toile Canvas) {canvas.drawBitmap (image, 100, 100, null); }}

Maintenant, pour utiliser cela, vous devez d'abord charger le bitmap, puis appeler la classe de GameView. Ajouter une référence à private CharacterSprite characterSprite puis dans le surfaceCréée méthode, ajoutez la ligne:

characterSprite = new CharacterSprite (BitmapFactory.decodeResource (getResources (), R.drawable.avdgreen));

Comme vous pouvez le constater, le bitmap que nous chargeons est stocké dans des ressources et s’appelle avdgreen (c’était celui d’un jeu précédent). Maintenant, tout ce que vous avez à faire est de passer ce bitmap à la nouvelle classe du dessiner méthode avec:

characterSprite.draw (canvas);

Maintenant, cliquez sur Exécuter et vous devriez voir votre graphique apparaître sur votre écran! C'est BeeBoo. Je le dessinais dans mes manuels scolaires.

Et si on voulait faire bouger ce petit bonhomme? Simple: nous créons simplement des variables x et y pour ses positions, puis modifions ces valeurs dans un mise à jour méthode.

Alors ajoutez les références à votre CaractèreSprite puis dessinez votre bitmap à x, y. Créez la méthode de mise à jour ici et pour le moment, nous allons simplement essayer:

y ++;

Chaque fois que la boucle de jeu est exécutée, nous déplaçons le personnage sur l'écran. Rappelles toi, y les coordonnées sont mesurées à partir du haut afin 0 est le haut de l'écran. Bien sûr, nous devons appeler le mise à jour méthode en CaractèreSprite du mise à jour méthode en GameView.

Appuyez à nouveau sur Lecture et vous verrez maintenant que votre image trace lentement vers le bas de l'écran. Nous n’avons encore gagné aucun prix en jeu, mais c’est un début!

Ok, pour faire des choses légèrement plus intéressant, je vais juste laisser tomber un code de «balle rebondissante» ici. Cela fera rebondir notre graphique sur l’écran, comme sur les anciens écrans de veille Windows. Vous savez, ceux étrangement hypnotiques.

public void update () {x + = xVelocity; y + = yVitesse; if ((x & gt; screenWidth - image.getWidth ()) || (x <0)) {xVelocity = xVelocity * -1; } if ((y & gt; screenHeight - image.getHeight ()) || (y & lt; 0)) {yVelocity = yVelocity * -1; }}

Vous devrez également définir ces variables:

private int xVelocity = 10; privé int yVelocity = 5; private int screenWidth = Resources.getSystem (). getDisplayMetrics (). widthPixels; private int screenHeight = Resources.getSystem (). getDisplayMetrics (). heightPixels;

Optimisation

Il y a beaucoup Il faut explorer davantage ici, de la gestion de la saisie du joueur à la mise à l’échelle des images, en passant par la gestion de nombreux personnages se déplaçant tous en même temps sur l’écran. En ce moment, le personnage rebondit mais si vous regardez de très près, vous remarquerez un léger bégaiement. Ce n’est pas terrible, mais le fait que vous puissiez le voir à l’œil nu est un signe d’avertissement. La vitesse varie également beaucoup sur l'émulateur par rapport à un périphérique physique. Maintenant, imaginez ce qui se passe quand vous avez des tonnes passe à l'écran à la fois!

Il existe quelques solutions à ce problème. Ce que je veux faire pour commencer, est de créer un entier privé dans MainThread et appeler ça targetFPS. Cela aura la valeur de 60.Je vais essayer de faire mon jeu à cette vitesse et en attendant, je vais vérifier pour s'assurer que c'est. Pour cela, je veux aussi un double privé appelé FPS moyen.

Je vais aussi mettre à jour le courir méthode afin de mesurer combien de temps chaque boucle de jeu prend et ensuite pause cette boucle de jeu temporairement si elle est en avance sur la cibleFPS. Nous allons ensuite calculer combien de temps il à présent pris et ensuite imprimer que afin que nous puissions le voir dans le journal.

@Override public void run () {long startTime; long timeMillis; long waitTime; long totalTime = 0; int frameCount = 0; long targetTime = 1000 / targetFPS; while (en cours d'exécution) {startTime = System.nanoTime (); canvas = null; try {canvas = this.surfaceHolder.lockCanvas (); synchronized (surfaceHolder) {this.gameView.update (); this.gameView.draw (canvas); }} catch (exception e) {} finally {if (canvas! = null) {try {surfaceHolder.unlockCanvasAndPost (canvas); } catch (Exception e) {e.printStackTrace (); }}} timeMillis = (System.nanoTime () - startTime) / 1000000; waitTime = targetTime - timeMillis; try {this.sleep (waitTime); } catch (exception e) {} totalTime + = System.nanoTime () - startTime; frameCount ++; if (frameCount == targetFPS) {averageFPS = 1000 / ((totalTime / frameCount) / 1000000); frameCount = 0; totalTime = 0; System.out.println (moyenne FPS); }}}

Maintenant, notre jeu tente de verrouiller le nombre de FPS à 60 et vous devriez constater qu’il mesure généralement un FPS assez stable de 58 à 62 sur un appareil moderne. Sur l'émulateur, vous obtiendrez peut-être un résultat différent.

Essayez de changer 60 à 30 et voyez ce qui se passe. Le jeu ralentit et il devrait lisez maintenant 30 dans votre logcat.

Pensées de clôture

Nous pouvons également faire certaines choses pour optimiser les performances. Il y a un excellent article de blog sur le sujet ici. Essayez de vous abstenir de créer de nouvelles instances de Paint ou de bitmaps dans la boucle et effectuez toutes les initialisations. à l'extérieur avant que le jeu commence.

Si vous envisagez de créer le prochain jeu Android à succès, il existe certainement des moyens plus faciles et plus efficaces de s'y prendre ces jours-ci. Cependant, il existe toujours des scénarios d'utilisation permettant de dessiner sur une toile et c'est une compétence très utile à ajouter à votre répertoire. J'espère que ce guide vous a quelque peu aidé et vous souhaite bonne chance dans vos projets de codage à venir!

ProchainUn guide pour débutant en Java

Bien que Google Pixel 3 et Pixel 3 XL ne puient rivalier avec le Galaxy Note 9 en terme de pécification, ce téléphone améliorent l'apect même qui a rendu le Pixel 2 aui fo...

Le Pixel 3 et Pixel 3 XL de Google ont-il trop cher pour vou? i tel et le ca, vou voudrez peut-être vou permettre d’acheter un nouveau téléphone juqu’au printemp 2019 - c’et la rumeur e...

Notre Choix