Come scrivere il tuo primo gioco Android in Java

Autore: John Stephens
Data Della Creazione: 1 Gennaio 2021
Data Di Aggiornamento: 19 Maggio 2024
Anonim
How to write your first Android game in Java
Video: How to write your first Android game in Java

Contenuto


Esistono molti modi per creare un gioco per Android e un modo importante è farlo da zero in Android Studio con Java. Questo ti dà il massimo controllo su come vuoi che il tuo gioco appaia e si comporti e il processo ti insegnerà le abilità che puoi usare anche in una serie di altri scenari, sia che tu stia creando una schermata iniziale per un'app o che tu voglia semplicemente aggiungi alcune animazioni. Con questo in mente, questo tutorial ti mostrerà come creare un semplice gioco 2D usando Android Studio e Java. Puoi trovare tutto il codice e le risorse su Github se vuoi seguirlo.

Impostare

Per creare il nostro gioco, avremo bisogno di affrontare alcuni concetti specifici: anelli di gioco, fili e tele. Per cominciare, avvia Android Studio. Se non lo hai installato, dai un'occhiata alla nostra introduzione completa ad Android Studio, che va oltre il processo di installazione. Ora avvia un nuovo progetto e assicurati di scegliere il modello "Svuota attività". Questo è un gioco, quindi ovviamente non hai bisogno di elementi come il pulsante FAB che complica le cose.


La prima cosa che vuoi fare è cambiare AppCompatActivity a Attività. Ciò significa che non utilizzeremo le funzioni della barra delle azioni.

Allo stesso modo, vogliamo anche rendere il nostro gioco a schermo intero. Aggiungi il seguente codice a onCreate () prima della chiamata a setContentView ():

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

Nota che se scrivi del codice e questo viene sottolineato in rosso, probabilmente significa che devi importare una classe. In altre parole, devi dire ad Android Studio che desideri utilizzare determinate dichiarazioni e renderle disponibili. Se fai semplicemente clic in un punto qualsiasi della parola sottolineata e poi premi Alt + Invio, ciò verrà fatto automaticamente per te!


Creare la vista di gioco

Puoi essere utilizzato per le app che utilizzano uno script XML per definire il layout di viste come pulsanti, immagini ed etichette. Questa è la linea setContentView sta facendo per noi.

Ma ancora una volta, questo è un gioco che significa che non ha bisogno di avere finestre del browser o scorrere le visualizzazioni dei riciclatori. Invece, vogliamo mostrare invece una tela. In Android Studio una tela è esattamente uguale all'arte: è un mezzo su cui possiamo attingere.

Quindi cambia quella riga per leggere in questo modo:

setContentView (nuovo GameView (questo))

Scoprirai che questo è ancora una volta sottolineato in rosso. Ma adesso se premi Alt + Invio, non hai la possibilità di importare la classe. Invece, hai la possibilità di creare una classe. In altre parole, stiamo per creare la nostra classe che definirà ciò che andrà sulla tela. Questo è ciò che ci consentirà di disegnare sullo schermo, piuttosto che mostrare semplicemente viste già pronte.

Quindi fai clic destro sul nome del pacchetto nella tua gerarchia a sinistra e scegli Nuovo> Classe. Ora ti verrà presentata una finestra per creare la tua classe e la chiamerai GameView. Sotto SuperClass, scrivi: android.view.SurfaceView il che significa che la classe erediterà i metodi - le sue capacità - da SurfaceView.

Nella casella Interfaccia (s), scriverai android.view.SurfaceHolder.Callback. Come con qualsiasi classe, ora dobbiamo creare il nostro costruttore. Usa questo codice:

thread MainThread privato; public GameView (Context context) {super (contesto); . GetHolder () addCallback (questo); }

Ogni volta che la nostra classe viene chiamata per creare un nuovo oggetto (in questo caso la nostra superficie), eseguirà il costruttore e creerà una nuova superficie. La linea "super" chiama la superclasse e, nel nostro caso, è SurfaceView.

Aggiungendo Callback, siamo in grado di intercettare gli eventi.

Ora sovrascrivi alcuni metodi:

@Override vuoto pubblico surfaceChanged (supporto SurfaceHolder, formato int, larghezza int, altezza int) {} @Override vuoto pubblico surfaceCreated (supporto SurfaceHolder) {} @Override vuoto vuoto superficieDestroyed (supporto SurfaceHolder) {}

Questi in sostanza ci consentono di sovrascrivere i metodi (da cui il nome) nella superclasse (SurfaceView). Ora non dovresti avere più sottolineature rosse nel tuo codice. Bello.

Hai appena creato una nuova classe e ogni volta che ci riferiamo a questo, costruirà la tela su cui dipingere il tuo gioco. Classi creare oggetti e ne abbiamo bisogno uno in più.

Creare discussioni

La nostra nuova classe verrà chiamata MainThread. E il suo compito sarà creare un thread. Un thread è essenzialmente come un fork di codice parallelo che può essere eseguito contemporaneamente a fianco di principale parte del tuo codice. Puoi avere molti thread in esecuzione tutti in una volta, permettendo così che le cose accadano simultaneamente piuttosto che aderire a una sequenza rigorosa. Questo è importante per un gioco, perché dobbiamo assicurarci che continui a funzionare senza intoppi, anche quando succede molto.

Crea la tua nuova classe proprio come facevi prima e questa volta si estenderà Filo. Nel costruttore stiamo per chiamare super(). Ricorda, questa è la super classe, che è Thread, e che può fare tutto il lavoro pesante per noi. È come creare un programma per lavare i piatti che chiama lavatrice().

Quando questa classe viene chiamata, creerà un thread separato che viene eseguito come derivazione della cosa principale. E viene da Qui che vogliamo creare il nostro GameView. Ciò significa che dobbiamo anche fare riferimento alla classe GameView e stiamo anche utilizzando SurfaceHolder che contiene l'area di disegno. Quindi se la tela è la superficie, SurfaceHolder è il cavalletto. E GameView è ciò che mette tutto insieme.

L'intero aspetto dovrebbe apparire così:

classe pubblica MainThread estende Thread {private SurfaceHolder surfaceHolder; GameView privato gameView; public MainThread (SurfaceHolder surfaceHolder, GameView gameView) {super (); this.surfaceHolder = surfaceHolder; this.gameView = gameView; }}

Schweet. Ora abbiamo un GameView e una discussione!

Creazione del loop di gioco

Ora abbiamo le materie prime di cui abbiamo bisogno per realizzare il nostro gioco, ma non sta succedendo nulla. È qui che entra in gioco il loop del gioco. Fondamentalmente, questo è un loop di codice che gira e rigira e controlla input e variabili prima di disegnare lo schermo. Il nostro obiettivo è di renderlo il più coerente possibile, in modo che non ci siano balbuzie o singhiozzi nel framerate, che esplorerò un po 'più tardi.

Per ora, siamo ancora nel MainThread classe e sostituiremo un metodo dalla superclasse. Questo è correre.

E va qualcosa del genere:

@Override public void run () {while (running) {canvas = null; try {canvas = this.surfaceHolder.lockCanvas (); synchronized (surfaceHolder) {this.gameView.update (); this.gameView.draw (tela); }} catch (Exception e) {} infine {if (canvas! = null) {try {surfaceHolder.unlockCanvasAndPost (canvas); } catch (Exception e) {e.printStackTrace (); }}}}}

Vedrai molte sottolineature, quindi dobbiamo aggiungere altre variabili e riferimenti. Torna all'inizio e aggiungi:

privato SurfaceHolder surfaceHolder; GameView privato gameView; corsa booleana privata; tela di canapa statica pubblica;

Ricorda di importare Canvas. La tela è la cosa su cui attingeremo davvero. Per quanto riguarda "lockCanvas", questo è importante perché è ciò che essenzialmente congela la tela per permetterci di disegnarci. Questo è importante perché, altrimenti, potresti avere più thread che tentano di attingere contemporaneamente. Sappi solo che per modificare la tela, devi prima serratura la tela.

L'aggiornamento è un metodo che stiamo per creare ed è qui che succederà qualcosa di divertente in seguito.

Il provare e catturare nel frattempo sono semplicemente requisiti di Java che dimostrano che siamo disposti a provare a gestire le eccezioni (errori) che potrebbero verificarsi se la tela non è pronta, ecc.

Infine, vogliamo essere in grado di iniziare il nostro thread quando ne abbiamo bisogno. Per fare questo, avremo bisogno di un altro metodo qui che ci permetta di mettere in moto le cose. Questo è ciò che il in esecuzione variabile è per (si noti che un valore booleano è un tipo di variabile che è sempre solo vero o falso). Aggiungi questo metodo a MainThread classe:

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

Ma a questo punto, una cosa dovrebbe ancora essere evidenziata e basta aggiornare. Questo perché non abbiamo ancora creato il metodo di aggiornamento. Quindi torna indietro GameView e ora aggiungi il metodo.

public void update () {}

Dobbiamo anche farlo inizio il filo! Lo faremo nel nostro surfaceCreated metodo:

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

Dobbiamo anche interrompere il filo quando la superficie viene distrutta. Come avrete intuito, gestiamo questo nel surfaceDestroyed metodo. Ma visto che in realtà possono essere necessari più tentativi per interrompere un thread, lo inseriremo in un ciclo e useremo provare e catturare ancora. Così:

@Override void pubblico surfaceDestroyed (supporto SurfaceHolder) {boolean retry = true; while (riprova) {try {thread.setRunning (false); Thread.join (); } catch (InterruptedException e) {e.printStackTrace (); } retry = false; }}

E infine, vai al costruttore e assicurati di creare la nuova istanza del tuo thread, altrimenti otterrai l'eccezione temuta puntatore null! E poi renderemo GameView focalizzabile, il che significa che può gestire eventi.

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

Ora puoi finalmente prova davvero questa cosa! Esatto, fai clic su Esegui e basta dovrebbero effettivamente eseguito senza errori. Preparati a essere spazzato via!

È ... è ... uno schermo vuoto! Tutto quel codice. Per uno schermo vuoto. Ma questa è una schermata vuota di opportunità. Hai la superficie aperta e funzionante con un loop di gioco per gestire gli eventi. Ora non resta che far accadere le cose. Non importa nemmeno se non hai seguito tutto nel tutorial fino a questo punto. Il punto è che puoi semplicemente riciclare questo codice per iniziare a creare giochi gloriosi!

Fare una grafica

Bene, ora abbiamo uno schermo vuoto su cui disegnare, tutto ciò che dobbiamo fare è disegnare su di esso. Fortunatamente, questa è la parte semplice. Tutto quello che devi fare è sostituire il metodo di disegno nel nostro GameView classe e quindi aggiungere alcune belle immagini:

@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, pittura); }}

Esegui questo e ora dovresti avere un bel quadrato rosso nella parte superiore sinistra di uno schermo altrimenti bianco. Questo è sicuramente un miglioramento.

Potresti teoricamente creare praticamente tutto il tuo gioco inserendolo in questo metodo (e scavalcandolo onTouchEvent per gestire l'input), ma non sarebbe un modo terribilmente buono per affrontare le cose. Inserire nuovi Paint all'interno del nostro loop rallenterà considerevolmente le cose e anche se lo inseriamo altrove, aggiungendo troppo codice a disegnare il metodo diventerebbe brutto e difficile da seguire.

Invece, ha molto più senso gestire oggetti di gioco con le proprie classi. Inizieremo con uno che mostra un personaggio e questa classe verrà chiamata CharacterSprite. Vai avanti e fallo.

Questa classe disegnerà uno sprite sulla tela e sembrerà così

public class CharacterSprite {immagine Bitmap privata; public CharacterSprite (Bitmap bmp) {image = bmp; } disegno vuoto pubblico (Canvas canvas) {canvas.drawBitmap (immagine, 100, 100, null); }}

Ora per usarlo, devi prima caricare la bitmap e quindi chiamare la classe da GameView. Aggiungi un riferimento a Personaggio privato Personaggio impresso Prite e poi nel surfaceCreated metodo, aggiungi la riga:

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

Come puoi vedere, la bitmap che stiamo caricando è memorizzata in risorse e si chiama avdgreen (era di un gioco precedente). Ora tutto ciò che devi fare è passare quella bitmap alla nuova classe in disegnare metodo con:

characterSprite.draw (tela);

Ora fai clic su Esegui e dovresti vedere la tua immagine apparire sullo schermo! Questo è BeeBoo. Lo disegnavo nei libri di testo della mia scuola.

E se volessimo far muovere questo piccoletto? Semplice: creiamo semplicemente variabili xey per le sue posizioni e poi cambiamo questi valori in un aggiornare metodo.

Quindi aggiungi i riferimenti al tuo CharacterSprite e quindi disegna la tua bitmap su x, y. Crea qui il metodo di aggiornamento e per ora proveremo solo:

y ++;

Ogni volta che il ciclo di gioco viene eseguito, spostiamo il personaggio sullo schermo. Ricorda, y le coordinate sono misurate dall'alto quindi 0 è la parte superiore dello schermo. Ovviamente dobbiamo chiamare il aggiornare metodo in CharacterSprite dal aggiornare metodo in GameView.

Premi di nuovo play e ora vedrai che l'immagine si sposta lentamente sullo schermo. Non stiamo ancora vincendo alcun premio di gioco, ma è un inizio!

Va bene, per fare le cose leggermente più interessante, ho intenzione di rilasciare un po 'di codice "palla rimbalzante" qui. Questo farà rimbalzare la nostra grafica attorno allo schermo dai bordi, come quei vecchi salvaschermi di Windows. Sai, quelli stranamente ipnotici.

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

Dovrai anche definire queste variabili:

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

Ottimizzazione

C'è abbondanza altro per approfondire qui, dalla gestione dell'input del giocatore, al ridimensionamento delle immagini, alla gestione di avere molti personaggi che si muovono sullo schermo contemporaneamente. In questo momento, il personaggio sta rimbalzando ma se guardi da vicino c'è una leggera balbuzie. Non è terribile, ma il fatto che tu possa vederlo ad occhio nudo è una specie di segnale di avvertimento. La velocità varia anche molto sull'emulatore rispetto a un dispositivo fisico. Ora immagina cosa succede quando hai tonnellate andando sullo schermo in una volta!

Esistono alcune soluzioni a questo problema. Quello che voglio fare per iniziare è creare un intero privato in MainThread e chiamalo così targetFPS. Questo avrà il valore di 60.Proverò a far funzionare il mio gioco a questa velocità e nel frattempo controllerò per assicurarmi che lo sia. Per questo, voglio anche un doppio privato chiamato averageFPS.

Ho anche intenzione di aggiornare il correre metodo per misurare quanto tempo impiega ogni ciclo di gioco e poi a pausa quel loop di gioco temporaneamente se è in anticipo rispetto al target FPS. Calcoleremo quindi per quanto tempo adesso preso e quindi stamparlo in modo che possiamo vederlo nel registro.

@Override public void run () {long startTime; molto tempo Millis; lunga waitTime; long totalTime = 0; int frameCount = 0; long targetTime = 1000 / targetFPS; while (in esecuzione) {startTime = System.nanoTime (); canvas = null; try {canvas = this.surfaceHolder.lockCanvas (); synchronized (surfaceHolder) {this.gameView.update (); this.gameView.draw (tela); }} catch (Exception e) {} infine {if (canvas! = null) {try {surfaceHolder.unlockCanvasAndPost (canvas); } catch (Exception e) {e.printStackTrace (); }}} timeMillis = (System.nanoTime () - startTime) / 1000000; waitTime = targetTime - timeMillis; provare {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 (averageFPS); }}}

Ora il nostro gioco sta tentando di bloccare l'FPS su 60 e dovresti trovare che in genere misura un FPS 58-62 abbastanza stabile su un dispositivo moderno. Sull'emulatore, però, potresti ottenere un risultato diverso.

Prova a cambiare da 60 a 30 e guarda cosa succede. Il gioco rallenta e lo fa dovrebbero ora leggi 30 nel tuo logcat.

Pensieri di chiusura

Ci sono alcune altre cose che possiamo fare anche per ottimizzare le prestazioni. C'è un ottimo post sul blog sull'argomento qui. Cerca di non creare mai nuove istanze di Paint o bitmap all'interno del ciclo e fai tutte le inizializzazioni al di fuori prima dell'inizio del gioco.

Se stai pensando di creare il prossimo gioco Android di successo, allora ci sono certamente modi più semplici ed efficienti per farlo al giorno d'oggi. Ma ci sono ancora scenari di casi d'uso per poter disegnare su una tela ed è un'abilità molto utile da aggiungere al tuo repertorio. Spero che questa guida ti abbia aiutato un po 'e ti auguro buona fortuna nelle tue prossime iniziative di programmazione!

Il prossimoUna guida per principianti a Java

PoitiviBuild premium Riconocimento facciale Launcher Android uniconegativiTroppo piccolo, potrebbe eere facile da perdere cara durata della batteria La fotocamera catta olo foto adeguate Mancanza di u...

Gli auricolari PaMu croll ollevati oltre $ 3 milioni u Indiegogo alla fine dell'anno coro, e per buoni motivi. Quete gemme combinano una tecnologia traordinaria con uno tile unico....

Nuovi Messaggi