Hvordan skrive ditt første Android-spill i Java

Forfatter: John Stephens
Opprettelsesdato: 1 Januar 2021
Oppdater Dato: 19 Kan 2024
Anonim
Hvordan skrive ditt første Android-spill i Java - Apps
Hvordan skrive ditt første Android-spill i Java - Apps

Innhold


Det er mange måter å lage et spill for Android, og en viktig måte er å gjøre det fra bunnen av i Android Studio med Java. Dette gir deg maksimal kontroll over hvordan du vil at spillet ditt skal se ut og oppføre seg, og prosessen vil lære deg ferdigheter du kan bruke i en rekke andre scenarier også - enten du lager en skvettskjerm for en app eller bare vil legg til noen animasjoner. Med det i tankene vil denne opplæringen vise deg hvordan du lager et enkelt 2D-spill ved å bruke Android Studio og Java. Du kan finne all koden og ressursene på Github hvis du vil følge med.

Setter opp

For å lage spillet vårt, trenger vi å håndtere noen få spesifikke konsepter: spillløkker, tråder og lerreter. Til å begynne med, start Android Studio. Hvis du ikke har det installert, kan du sjekke den fullstendige introduksjonen til Android Studio, som går over installasjonsprosessen. Nå starter et nytt prosjekt, og sørg for at du velger malen ‘Tom aktivitet’. Dette er et spill, så selvfølgelig trenger du ikke elementer som FAB-knappen som kompliserer saker.


Det første du vil gjøre er å endre AppCompatActivity til Aktivitet. Dette betyr at vi ikke bruker handlingslinjefunksjonene.

På samme måte ønsker vi også å lage spillet til full skjerm. Legg til følgende kode på onCreate () før samtalen til setContentView ():

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

Merk at hvis du skriver ut en kode og den blir understreket i rødt, betyr det sannsynligvis at du må importere en klasse. Med andre ord, du må fortelle Android Studio at du ønsker å bruke visse utsagn og gjøre dem tilgjengelige. Hvis du bare klikker hvor som helst på det understrekede ordet og deretter trykker på Alt + Enter, vil det gjøres automatisk for deg!


Oppretting av spillvisningen

Du kan være vant til apper som bruker et XML-skript for å definere utformingen av visninger som knapper, bilder og etiketter. Dette er hva linjen setContentView gjør for oss.

Men igjen, dette er et spill som betyr at det ikke trenger å ha nettleservinduer eller rulle gjenvinningsvisninger. I stedet for det, ønsker vi å vise et lerret i stedet. I Android Studio er et lerret akkurat det samme som i kunsten: det er et medium vi kan tegne på.

Så endre den linjen for å lese slik:

setContentView (nytt GameView (dette))

Du vil oppdage at dette nok en gang er understreket rødt. Men Hvis du trykker på Alt + Enter, har du ikke muligheten til å importere klassen. I stedet har du muligheten til skape en klasse. Med andre ord, vi er i ferd med å lage vår egen klasse som vil definere hva som skal gå på lerretet. Det er dette som lar oss tegne til skjermen, i stedet for bare å vise ferdige visninger.

Så høyreklikk på pakkenavnet i hierarkiet over til venstre og velg Ny> Klasse. Nå får du et vindu for å lage klassen din, og du vil kalle den GameView. Under SuperClass, skriv: android.view.SurfaceView noe som betyr at klassen vil arve metoder - dens evner - fra SurfaceView.

I grensesnitt (er) -boksen skriver du android.view.SurfaceHolder.Callback. Som med enhver klasse, må vi nå lage vår konstruktør. Bruk denne koden:

privat MainThread tråd; public GameView (Context context) {super (context); . GetHolder () addCallback (this); }

Hver gang klassen vår kalles for å lage et nytt objekt (i dette tilfellet overflaten vår), vil den kjøre konstruktøren og den vil skape en ny overflate. Linjen ‘super’ kaller superklassen og i vårt tilfelle er det SurfaceView.

Ved å legge til tilbakeringing, er vi i stand til å avskjære hendelser.

Nå overstyrer noen metoder:

@Override public voidfaceChanged (SurfaceHolder holder, int-format, int bredde, int høyde) {} @Override public voidfaceCreated (SurfaceHolder holder) {} @Override public void surfaceDestroyed (SurfaceHolder holder) {}

Disse lar oss i utgangspunktet overstyre (derav navnet) metodene i superklassen (SurfaceView). Du skal nå ikke ha flere røde understrekinger i koden. Hyggelig.

Du har nettopp opprettet en ny klasse, og hver gang vi viser til den, vil den bygge lerretet for spillet ditt å bli malt på. klasser skape gjenstander, og vi trenger en til.

Lage tråder

Den nye klassen vår skal hete MainThread. Og jobben vil være å lage en tråd. En tråd er egentlig som en parallell gaffel med kode som kan kjøres samtidig ved siden av hoved- del av koden din. Du kan ha mange tråder som kjører på en gang, og dermed lar ting skje samtidig, i stedet for å overholde en streng sekvens. Dette er viktig for et spill, fordi vi må sørge for at det fortsetter å gå jevnt, selv når det skjer mye.

Lag din nye klasse akkurat som du gjorde før, og denne gangen kommer den til å utvides Tråd. I konstruktøren skal vi bare ringe super(). Husk at det er superklassen, som er tråd, og som kan gjøre alt det tunge løftet for oss. Dette er som å lage et program for å vaske oppvasken som bare ringer vaskemaskin().

Når denne klassen blir kalt, kommer den til å lage en egen tråd som går som en forskyvning av hovedsaken. Og det er fra her at vi ønsker å opprette GameView. Det betyr at vi også må referere til GameView-klassen, og vi bruker også SurfaceHolder som inneholder lerretet. Så hvis lerretet er overflaten, er SurfaceHolder staffeliet. Og GameView er det som setter det hele sammen.

Det fulle skal se slik ut:

offentlig klasse MainThread utvider tråden {privat SurfaceHolderfaceHolder; private GameView gameView; offentlig MainThread (SurfaceHolderfaceHolder, GameView gameView) {super (); this.surfaceHolder = overflateHolder; this.gameView = gameView; }}

Schweet. Vi har nå en GameView og en tråd!

Opprette spillløkken

Vi har nå råvarene vi trenger for å lage spillet vårt, men ingenting skjer. Det er her spillsløyfen kommer inn. I utgangspunktet er dette en kodeslynge som går rundt og rundt og sjekker innganger og variabler før du tegner skjermen. Målet vårt er å gjøre dette så konsistent som mulig, slik at det ikke er raser eller hikke i frameraten, som jeg skal utforske litt senere.

Foreløpig er vi fremdeles i MainThread klasse, og vi kommer til å overstyre en metode fra superklassen. Denne er løpe.

Og det går litt i likhet med dette:

@Override public void run () {while (running) {canvas = null; prøv {canvas = this.surfaceHolder.lockCanvas (); synkronisert (faceHolder) {this.gameView.update (); this.gameView.draw (canvas); }} fangst (unntak e) {} endelig {hvis (lerret! = null) {prøv {overflateolder.unlockCanvasAndpost (lerret); } fangst (unntak e) {e.printStackTrace (); }}}}}

Du vil se mye understreking, så vi må legge til flere variabler og referanser. Gå tilbake til toppen og legg til:

privat SurfaceHolder overflate Holder; private GameView gameView; privat boolsk løping; offentlig statisk lerret lerret;

Husk å importere lerret. Lerret er det vi faktisk skal tegne på. Når det gjelder ‘lockCanvas’, er dette viktig fordi det er det som egentlig fryser lerretet slik at vi kan tegne på det. Det er viktig fordi ellers kan du ha flere tråder som prøver å tegne på den samtidig. Bare vet at for å redigere lerretet, må du først låse lerretet.

Oppdatering er en metode som vi skal lage, og det er her de morsomme tingene vil skje senere.

De prøve og å fange i mellomtiden er det ganske enkelt krav fra Java som viser at vi er villige til å prøve å håndtere unntak (feil) som kan oppstå hvis lerretet ikke er klart osv.

Til slutt ønsker vi å kunne starte tråden når vi trenger den. For å gjøre dette, trenger vi en annen metode her som lar oss sette ting i gang. Det er hva løping variabel er for (merk at en boolsk er en type variabel som bare er sann eller usann). Legg denne metoden til MainThread klasse:

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

Men på dette tidspunktet, bør en ting fremdeles fremheves, og det er det Oppdater. Dette er fordi vi ikke har opprettet oppdateringsmetoden ennå. Så pop tilbake til GameView og legg nå til metode.

offentlig ugyldig oppdatering () {}

Det må vi også start tråden! Vi kommer til å gjøre dette i vårt surfaceCreated metode:

@Override public voidfaceCreated (SurfaceHolder holder) {thread.setRunning (true); thread.start (); }

Vi må også stoppe tråden når overflaten ødelegges. Som du kanskje har gjettet, håndterer vi dette i surfaceDestroyed metode. Men siden det faktisk kan ta flere forsøk på å stoppe en tråd, kommer vi til å sette dette i en loop og bruke prøve og å fange en gang til. Som så:

@Override public void surfaceDestroyed (SurfaceHolder holder) {boolsk retry = true; mens (prøv på nytt) {prøv {thread.setRunning (falsk); thread.join (); } fangst (InterruptException e) {e.printStackTrace (); } prøv på nytt = falsk; }}

Og til slutt, ta turen opp til konstruktøren og sørg for å opprette den nye forekomsten av tråden, ellers får du det fryktede nullpekeren-unntaket! Og så skal vi gjøre GameView fokuserbar, noe som betyr at den kan håndtere hendelser.

tråd = ny MainThread (getHolder (), dette); setFocusable (true);

Nå kan du til slutt teste faktisk denne tingen! Det er riktig, klikk på Kjør og det bør kjører faktisk uten feil. Forbered deg på å bli blåst bort!

Det er ... det er ... en blank skjerm! All den koden. For en blank skjerm. Men dette er en blank skjerm av mulighet. Du har fått overflaten opp og kjører med en spillsløyfe for å håndtere hendelser. Nå er det bare igjen å få ting til å skje. Det betyr ikke en gang om du ikke fulgte alt i opplæringen frem til dette tidspunktet. Poenget er at du bare kan resirkulere denne koden for å begynne å lage herlige spill!

Å lage en grafikk

Akkurat, nå har vi en blank skjerm å tegne på, alt vi trenger å gjøre er å tegne på den. Heldigvis er det den enkle delen. Alt du trenger å gjøre er å overstyre trekkmetoden i vår GameView klasse og legg så til noen pene bilder:

@Override tegneserie fra tomrom (Canvas canvas) {super.draw (canvas); if (lerret! = null) {canvas.drawColor (Color.WHITE); Malingsmaling = ny Paint (); paint.setColor (farge.rgb (250, 0, 0)); lerret.drawRect (100, 100, 200, 200, maling); }}

Kjør dette, og du skal nå ha en ganske rød firkant øverst til venstre på en ellers hvit skjerm. Dette er absolutt en forbedring.

Du kan teoretisk lage ganske mye hele spillet ditt ved å holde det inne i denne metoden (og overstyre onTouchEvent å håndtere innspill) men det ville ikke være en veldig god måte å gjøre ting på. Å plassere ny maling i løkken vår vil bremse ting betraktelig, og selv om vi legger dette andre steder, legger vi for mye kode til tegne metoden ville bli stygg og vanskelig å følge.

I stedet er det mye mer fornuftig å håndtere spillobjekter med sine egne klasser. Vi kommer til å begynne med en som viser en karakter, og denne klassen vil bli kalt CharacterSprite. Gjør det videre.

Denne klassen skal tegne en sprite på lerretet og vil se slik ut

public class CharacterSprite {privat Bitmap-bilde; public CharacterSprite (Bitmap bmp) {image = bmp; } public void draw (Canvas canvas) {canvas.drawBitmap (bilde, 100, 100, null); }}

For å bruke dette, må du først laste inn bitmappen og deretter ringe klassen fra GameView. Legg til en referanse til private CharacterSprite characterSprite og deretter i surfaceCreated metoden, legg til linjen:

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

Som du kan se, er bitmappen vi laster inn lagret i ressurser og kalles avdgreen (det var fra et tidligere spill). Alt du trenger å gjøre er å overføre den bitmappen til den nye klassen i tegne metode med:

characterSprite.draw (canvas);

Nå klikk på Kjør, og du skal se grafikken din vises på skjermen! Dette er BeeBoo. Jeg pleide å tegne ham i skolebøkene mine.

Hva om vi ville få denne lille fyren til å flytte? Enkelt: vi lager bare x og y-variabler for hans posisjoner og endrer deretter disse verdiene i en Oppdater metode.

Så legg referansene til din CharacterSprite og tegne deretter bitmapet ditt kl x, y. Lag oppdateringsmetoden her, og foreløpig skal vi prøve:

y ++;

Hver gang spillsløyfen kjører, flytter vi tegnet nedover på skjermen. Huske, y koordinater måles fra toppen så 0 er toppen av skjermen. Selvfølgelig må vi kalle Oppdater metode i CharacterSprite fra Oppdater metode i GameView.

Trykk på play igjen, og nå vil du se at bildet ditt sakte sporer nedover på skjermen. Vi vinner ikke noen spillpriser ennå, men det er en start!

Ok, å lage ting litt mer interessant, jeg skal bare slippe noen "hoppball" -koder her. Dette vil gjøre at grafikken vår spretter rundt skjermen utenfor kantene, som de gamle Windows-skjermsparerne. Du vet, de merkelig hypnotiske.

offentlig ugyldig oppdatering () {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; }}

Du må også definere disse variablene:

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

optimalisering

Det er rikelig mer å fordype deg her, fra å håndtere spillerinndata, til skalering av bilder, til å administrere å ha mange tegn som alle beveger seg rundt på skjermen samtidig. Akkurat nå spretter karakteren, men hvis du ser veldig nøye er det lett stamming. Det er ikke forferdelig, men det faktum at du kan se det med det blotte øye er noe av et varselstegn. Hastigheten varierer også mye på emulatoren sammenlignet med et fysisk apparat. Forestill deg hva som skjer når du har det tonn skjer på skjermen på en gang!

Det er noen få løsninger på dette problemet. Det jeg vil gjøre for å begynne med, er å lage et privat heltall i MainThread og ring det targetFPS. Dette vil ha verdien 60.Jeg skal prøve å få spillet mitt til å løpe med denne hastigheten, og i mellomtiden skal jeg sjekke om det er. For det vil jeg også ha en privat dobbel som heter averageFPS.

Jeg skal også oppdatere løpe metode for å måle hvor lang tid hver spillsløyfe tar og deretter til pause den spillløkken midlertidig hvis den ligger foran targetFPS. Vi skal da beregne hvor lang tid det er tok og så skrev ut det slik at vi kan se det i loggen.

@Override public void run () {lang starttid; lang tidMillis; lang ventetid; lang totaltid = 0; int frameCount = 0; lang targetTime = 1000 / targetFPS; mens (kjører) {startTime = System.nanoTime (); lerret = null; prøv {canvas = this.surfaceHolder.lockCanvas (); synkronisert (faceHolder) {this.gameView.update (); this.gameView.draw (canvas); }} fangst (unntak e) {} endelig {hvis (lerret! = null) {prøv {overflateholder.unlockCanvasAndpost (lerret); } fangst (unntak e) {e.printStackTrace (); }}} timeMillis = (System.nanoTime () - starttid) / 1000000; waitTime = targetTime - timeMillis; prøv {this.sleep (waitTime); } fangst (unntak e) {} totalTime + = System.nanoTime () - startTime; frameCount ++; if (frameCount == targetFPS) {gjennomsnittFPS = 1000 / ((totalTime / frameCount) / 1000000); frameCount = 0; total tid = 0; System.out.println (averageFPS); }}}

Nå forsøker spillet vårt å låse FPS-en til 60, og du bør oppdage at det generelt måler en ganske jevn 58-62 FPS på en moderne enhet. På emulatoren kan du få et annet resultat.

Prøv å endre på 60 til 30 og se hva som skjer. Spillet bremser og det bør les nå 30 i din logcat.

Lukkende tanker

Det er noen andre ting vi kan gjøre for å optimalisere ytelsen også. Det er et flott blogginnlegg om emnet her. Forsøk å avstå fra å lage nye forekomster av maling eller bitmapper inne i løkken, og gjør alt initialiserende utenfor før spillet begynner.

Hvis du planlegger å lage det neste hit Android-spillet, er det det sikkert enklere og mer effektive måter å gjøre det på i disse dager. Men det er definitivt fortsatt bruk-case-scenarier for å kunne tegne på et lerret, og det er en veldig nyttig ferdighet å legge til repertoaret ditt. Jeg håper denne guiden har hjulpet noe og ønsker deg lykke til i dine kommende kodingssatsinger!

nesteEn nybegynnerguide for Java

amung Galaxy 10 Plu har forkjellige lagringkapaiteter: 128 GB, 512 GB og 1 TB. 128 GB og 512 GB varianter har 8 GB RAM, men 1 TB varianten er unik og tilbyr 12 GB RAM. Hvi du er villig til å beta...

amung Galaxy 10 og Galaxy 10 Plu inkluderer ikke en 6 GB RAM-modell, har amung bekreftet (via Android-politiet). Til tro for forkjellige påtander fra amung elv, tyder den ite informajonen på...

Vi Anbefaler Deg