Sådan skriver du dit første Android-spil i Java

Forfatter: John Stephens
Oprettelsesdato: 1 Januar 2021
Opdateringsdato: 19 Kan 2024
Anonim
Sådan skriver du dit første Android-spil i Java - Apps
Sådan skriver du dit første Android-spil i Java - Apps

Indhold


Der er mange måder at oprette et spil til Android på, og en vigtig måde er at gøre det fra bunden af ​​i Android Studio med Java. Dette giver dig den maksimale kontrol over, hvordan du ønsker, at dit spil skal se ud og opføre sig, og processen lærer dig færdigheder, du kan bruge i en række andre scenarier også - uanset om du opretter en stænkskærm til en app, eller du bare vil tilføj nogle animationer. Med det i tankerne viser denne tutorial dig, hvordan man opretter et simpelt 2D-spil ved hjælp af Android Studio og Java. Du kan finde alle koder og ressourcer på Github, hvis du vil følge med.

Sætte op

For at skabe vores spil bliver vi nødt til at beskæftige os med et par specifikke koncepter: spilsløjfer, tråde og lærreder. Til at begynde med skal du starte Android Studio. Hvis du ikke har det installeret, så tjek vores fulde introduktion til Android Studio, der går over installationsprocessen. Start nu et nyt projekt, og sørg for at vælge skabelonen 'Tom aktivitet'. Dette er et spil, så selvfølgelig behøver du ikke elementer som FAB-knappen, der komplicerer sager.


Den første ting, du vil gøre, er at ændre AppCompatActivity til Aktivitet. Det betyder, at vi ikke bruger action bar-funktionerne.

På samme måde ønsker vi også at gøre vores spil på fuld skærm. Føj følgende kode til onCreate () inden opkaldet til setContentView ():

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

Bemærk, at hvis du skriver en eller anden kode, og den bliver understreget i rødt, betyder det sandsynligvis, at du er nødt til at importere en klasse. Med andre ord skal du fortælle Android Studio, at du ønsker at bruge visse udsagn og gøre dem tilgængelige. Hvis du bare klikker hvor som helst på det understregede ord og derefter rammer Alt + Enter, gøres det automatisk for dig!


Oprettelse af din spilvisning

Du er muligvis vant til apps, der bruger et XML-script til at definere layoutet af visninger som knapper, billeder og etiketter. Dette er hvad linjen setContentView gør for os.

Men igen, dette er et spil, hvilket betyder, at det ikke behøver at have browservinduer eller rullende recyclervisninger. I stedet for det, ønsker vi i stedet at vise et lærred. I Android Studio er et lærred nøjagtigt det samme som i kunsten: det er et medium, som vi kan trække på.

Så ændre den linje for at læse sådan:

setContentView (nyt GameView (dette))

Du finder ud af, at dette igen er understreget rødt. Men nu Hvis du trykker på Alt + Enter, har du ikke muligheden for at importere klassen. I stedet har du muligheden for at skab en klasse. Med andre ord er vi ved at lave vores egen klasse, der definerer, hvad der vil gå på lærredet. Dette er, hvad der giver os mulighed for at tegne til skærmen i stedet for bare at vise færdige synspunkter.

Så højreklik på pakkenavnet i dit hierarki over til venstre og vælg Ny> Klasse. Du får nu vist et vindue for at oprette din klasse, og du vil kalde det GameView. Skriv under SuperClass: android.view.SurfaceView hvilket betyder, at klassen vil arve metoder - dets muligheder - fra SurfaceView.

I feltet Interface (r) skal du skrive android.view.SurfaceHolder.Callback. Som med enhver klasse er vi nu nødt til at oprette vores konstruktør. Brug denne kode:

privat MainThread tråd; offentlig GameView (kontekstkontekst) {super (kontekst); . GetHolder () addCallback (dette); }

Hver gang vores klasse kaldes til at lave en ny genstand (i dette tilfælde vores overflade), kører den konstruktøren og den skaber en ny overflade. Linjen 'super' kalder superklassen, og i vores tilfælde er det SurfaceView.

Ved at tilføje tilbagekald er vi i stand til at aflytte begivenheder.

Tilsidesæt nu nogle metoder:

@Override public voidfaceChanged (SurfaceHolder holder, int format, int bredde, int højde) {} @Override public void surfaceCreated (SurfaceHolder holder) {} @Override public void overflade Ødelagt (SurfaceHolder holder) {}

Disse giver os grundlæggende mulighed for at tilsidesætte (dermed navnet) metoder i superklassen (SurfaceView). Du skal nu ikke have flere røde understregninger i din kode. Pæn.

Du har lige oprettet en ny klasse, og hver gang vi refererer til det, bygger det lærredet til dit spil at blive malet på. Klasser skab genstande, og vi har brug for et mere.

Oprettelse af tråde

Vores nye klasse bliver kaldt MainThread. Og dets job vil være at oprette en tråd. En tråd er i det væsentlige som en parallel gaffel med kode, der kan køre samtidigt langs med vigtigste del af din kode. Du kan have masser af tråde, der kører på én gang, og derved tillader ting at forekomme samtidig i stedet for at overholde en streng rækkefølge. Dette er vigtigt for et spil, fordi vi er nødt til at sørge for, at det fortsætter med at køre glat, selv når der foregår meget.

Opret din nye klasse, ligesom du gjorde før, og denne gang vil den forlænges Tråd. I konstruktøren ringer vi bare super(). Husk, det er superklassen, der er tråd, og som kan gøre alt det tunge løft for os. Dette er som at oprette et program til at vaske de retter, der bare ringer vaskemaskine().

Når denne klasse kaldes, vil den oprette en separat tråd, der kører som en forskydning af det vigtigste. Og det er fra her at vi vil oprette vores GameView. Det betyder, at vi også skal henvise til GameView-klassen, og vi bruger også SurfaceHolder, der indeholder lærredet. Så hvis lærredet er overfladen, er SurfaceHolder staffelien. Og GameView er det, der sætter det hele sammen.

Den fulde ting skal se sådan ud:

offentlig klasse MainThread udvider tråd {privat SurfaceHolder SurfaceHolder; privat GameView gameView; offentlig MainThread (SurfaceHolder surfaceHolder, GameView gameView) {super (); this.surfaceHolder = surfaceHolder; this.gameView = gameView; }}

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

Oprettelse af spilsløjfen

Vi har nu de råvarer, vi har brug for for at lave vores spil, men der sker ikke noget. Det er her spillet loop kommer ind. Grundlæggende er dette en loop af kode, der går rundt og rundt og kontrollerer input og variabler, før du tegner skærmen. Vores mål er at gøre dette så konsistent som muligt, så der ikke er stammere eller hikke i rammen, som jeg vil undersøge lidt senere.

For tiden er vi stadig i MainThread klasse, og vi vil tilsidesætte en metode fra superklassen. Denne er løb.

Og det går lidt i denne retning:

@Override offentligt ugyldigt løb () {mens (kører) {canvas = null; prøv {canvas = this.surfaceHolder.lockCanvas (); synkroniseret (surfaceHolder) {this.gameView.update (); this.gameView.draw (lærred); }} fangst (undtagelse e) {} endelig {hvis (lærred! = null) {prøv {surfaceHolder.unlockCanvasAndPost (lærred); } fangst (undtagelse e) {e.printStackTrace (); }}}}}

Du vil se en masse understregning, så vi er nødt til at tilføje nogle flere variabler og referencer. Gå tilbage til toppen og tilføj:

privat SurfaceHolder-overfladeHolder; privat GameView gameView; privat boolsk løb; statisk lærred lærred;

Husk at importere lærred. Lærred er det, vi faktisk vil tegne på. Hvad angår 'lockCanvas', er dette vigtigt, fordi det er det, der i bund og grund fryser lærredet, så vi kan trække på det. Det er vigtigt, for ellers kan du have flere tråde, der forsøger at trække på det på én gang. Bare ved, at du først skal redigere for at redigere lærredet låse lærredet.

Opdatering er en metode, som vi skal oprette, og det er her, de sjove ting vil ske senere.

Det prøve og fangst i mellemtiden er blot krav fra Java, der viser, at vi er villige til at prøve at håndtere undtagelser (fejl), der kan forekomme, hvis lærredet ikke er klar osv.

Endelig vil vi være i stand til at starte vores tråd, når vi har brug for den. For at gøre dette, har vi brug for en anden metode her, der giver os mulighed for at sætte ting i gang. Det er hvad kører variabel er til (bemærk, at en boolsk er en type variabel, der kun nogensinde er sand eller falsk). Føj denne metode til MainThread klasse:

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

Men på dette tidspunkt skal en ting stadig fremhæves, og det er det opdatering. Dette skyldes, at vi endnu ikke har oprettet opdateringsmetoden. Så pop tilbage ind GameView og tilføj nu metode.

offentlig ugyldig opdatering () {}

Vi er også nødt til det Start tråden! Vi vil gøre dette i vores surfaceCreated metode:

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

Vi er også nødt til at stoppe tråden, når overfladen ødelægges. Som du måske har gætt, håndterer vi dette i surfaceDestroyed metode. Men da det faktisk kan tage flere forsøg på at stoppe en tråd, vil vi sætte dette i en løkke og bruge prøve og fangst igen. Ligesom:

@Override offentligt tomrumsoverflade Ødelagt (SurfaceHolder holder) {boolsk retry = sand; mens (prøv igen) {prøv {thread.setRunning (falsk); thread.join (); } fangst (InterruptException e) {e.printStackTrace (); } prøv igen = falsk; }}

Og til sidst skal du gå op til konstruktøren og sørge for at oprette den nye forekomst af din tråd, ellers får du den frygtede null-pointer undtagelse! Og så vil vi gøre GameView fokuseret, hvilket betyder, at det kan håndtere begivenheder.

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

Nu kan du langt om længe test faktisk denne ting! Det er rigtigt, klik på Kør, og det skulle gerne kører faktisk uden fejl. Forbered dig på at blive sprængt væk!

Det er ... det er ... en tom skærm! Al den kode. For en blank skærm. Men dette er en tom skærm af mulighed. Du har din overflade op og kører med en spilsløjfe til at håndtere begivenheder. Nu er det eneste, der er tilbage, at få ting til at ske. Det betyder ikke engang, hvis du ikke fulgte alt i tutorial indtil dette tidspunkt. Pointen er, at du simpelthen kan genbruge denne kode for at begynde at fremstille herlige spil!

At lave en grafik

Lige nu har vi en blank skærm at tegne på, alt hvad vi skal gøre er at trække på det. Heldigvis er det den enkle del. Alt hvad du skal gøre er at tilsidesætte trækmetoden i vores GameView klasse og tilføj derefter nogle smukke billeder:

@Override offentligt ugyldigt træk (lærred lærred) {super.draw (lærred); if (lærred! = null) {canvas.drawColor (Color.WHITE); Maling maling = ny maling (); paint.setColor (farve.rgb (250, 0, 0)); canvas.drawRect (100, 100, 200, 200, maling); }}

Kør dette, og du skal nu have en smuk rød firkant øverst til venstre på en ellers hvid skærm. Dette er bestemt en forbedring.

Du kan teoretisk skabe stort set hele dit spil ved at sætte det inde i denne metode (og tilsidesætte onTouchEvent at håndtere input) men det ville ikke være en frygtelig god måde at gøre tingene på. At placere ny maling inden i vores løkke vil bremse tingene betydeligt, og selv hvis vi lægger dette andetsteds, tilføjer for meget kode til tegne metoden ville blive grim og vanskelig at følge.

I stedet er det meget mere fornuftigt at håndtere spilobjekter med deres egne klasser. Vi begynder med en, der viser en karakter, og denne klasse kaldes CharacterSprite. Gå videre og gør det.

Denne klasse vil tegne en sprite på lærredet og vil se sådan ud

public class CharacterSprite {privat Bitmap-billede; public CharacterSprite (Bitmap bmp) {image = bmp; } tegning af offentligt tomrum (lærred lærred) {canvas.drawBitmap (billede, 100, 100, null); }}

For at bruge dette, skal du først indlæse bitmappen og derefter ringe til klassen fra GameView. Tilføj en henvisning til private CharacterSprite characterSprite og derefter i surfaceCreated metode, tilføj linjen:

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

Som du kan se, gemmes den bitmap, vi indlæser, i ressourcer og kaldes avdgreen (det var fra et tidligere spil). Nu skal du bare videregive den bitmap til den nye klasse i tegne metode med:

characterSprite.draw (lærred);

Klik nu på Kør, så skal du se din grafik vises på din skærm! Dette er BeeBoo. Jeg plejede at tegne ham i mine skolebøger.

Hvad hvis vi ville få denne lille fyr til at flytte? Enkelt: vi opretter bare x- og y-variabler til hans positioner og ændrer derefter disse værdier i en opdatering metode.

Så tilføj referencerne til din CharacterSprite og derefter tegne din bitmap kl x, y. Opret opdateringsmetoden her, og nu skal vi bare prøve:

y ++;

Hver gang spillet loop kører, flytter vi karakteren ned på skærmen. Husk, y koordinater måles ovenfra 0 er toppen af ​​skærmen. Selvfølgelig er vi nødt til at kalde opdatering metode i CharacterSprite fra opdatering metode i GameView.

Tryk på afspil igen, og nu vil du se, at dit billede langsomt sporer ned på skærmen. Vi vinder ikke nogen spilpriser endnu, men det er en start!

Okay, at lave ting en anelse mere interessant, jeg vil bare slippe nogle 'hoppekugle' -kode her. Dette får vores grafik til at hoppe rundt på skærmen ud for kanterne, ligesom de gamle Windows-skærmskærme. Du ved, de underligt hypnotiske.

offentlig ugyldig opdatering () {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 skal også definere disse variabler:

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

Optimering

Der er masser mere at gå i dybden her, fra håndtering af spillerinput, til skalering af billeder, til at styre at have masser af tegn, der alle bevæger sig rundt på skærmen på én gang. Lige nu hopper karakteren, men hvis du ser meget nøje er der let stamming. Det er ikke forfærdeligt, men det faktum, at du kan se det med det blotte øje, er noget af et advarselsskilt. Hastigheden varierer også meget på emulatoren sammenlignet med en fysisk enhed. Forestil dig nu, hvad der sker, når du har det tons foregår på skærmen på en gang!

Der er nogle få løsninger på dette problem. Hvad jeg vil gøre for at starte med, er at oprette et privat heltal i MainThread og kalder det targetFPS. Dette har værdien 60.Jeg vil prøve at få mit spil til at køre med denne hastighed, og i mellemtiden skal jeg kontrollere, om det er tilfældet. Til det vil jeg også have en privat dobbeltopkald averageFPS.

Jeg vil også opdatere løb metode til at måle, hvor lang tid hver spilsløjfe tager og derefter til pause denne spilsløjfe midlertidigt, hvis det ligger foran targetFPS. Vi beregner derefter, hvor længe det er nu tog og udskrev det så vi kan se det i loggen.

@Override offentligt ugyldigt løb () {lang starttid; lang tidMillis; lang ventetid; lang totaltid = 0; int frameCount = 0; lang targetTime = 1000 / targetFPS; mens (kører) {startTime = System.nanoTime (); lærred = null; prøv {canvas = this.surfaceHolder.lockCanvas (); synkroniseret (surfaceHolder) {this.gameView.update (); this.gameView.draw (lærred); }} fangst (undtagelse e) {} endelig {hvis (lærred! = null) {prøv {surfaceHolder.unlockCanvasAndPost (lærred); } fangst (undtagelse e) {e.printStackTrace (); }}} timeMillis = (System.nanoTime () - starttid) / 1000000; waitTime = targetTime - timeMillis; prøv {this.sleep (ventetid); } fangst (undtagelse e) {} totalTime + = System.nanoTime () - startTime; frameCount ++; if (frameCount == targetFPS) {gennemsnitFPS = 1000 / ((totalTime / frameCount) / 1000000); frameCount = 0; total tid = 0; System.out.println (averageFPS); }}}

Nu forsøger vores spil at låse dets FPS til 60, og du skal opdage, at det generelt måler en forholdsvis stabil 58-62 FPS på en moderne enhed. På emulatoren kan du dog få et andet resultat.

Prøv at ændre denne 60 til 30 og se hvad der sker. Spillet bremser og det skulle gerne læs nu 30 i din logcat.

Lukende tanker

Der er nogle andre ting, vi også kan gøre for at optimere ydelsen. Der er et fantastisk blogindlæg om emnet her. Forsøg at afstå fra nogensinde at oprette nye tilfælde af maling eller bitmaps inde i løkken, og gør alt initialiserende uden for inden spillet begynder.

Hvis du planlægger at oprette det næste hit Android-spil, er der sikkert lettere og mere effektive måder at gøre det på i disse dage. Men der er afgjort stadig brug-case-scenarier for at være i stand til at tegne på et lærred, og det er en meget nyttig færdighed at tilføje dit repertoire. Jeg håber, at denne guide har hjulpet noget og ønsker dig held og lykke i dine kommende kodningsprojekter!

NæsteEn begyndervejledning til Java

Opdatering 23. april 2019 (17:50 ET):Kort efter, at gengivelen af ​​Galaxy View 2 dukkede op online, delte AT&T en algfremmende video af ginormou tablet på in YouTube-kanal. elvom oplyninger ...

En lækket amung Galaxy Watch Active 2-gengivele (et ovenfor) er kommet online, alt ammen for at bekræfte, hvordan det rygtede martwatch vil e ud. Den angiveligt officielle gengivele, offentl...

Interessant I Dag