Så skriver du ditt första Android-spel i Java

Författare: John Stephens
Skapelsedatum: 1 Januari 2021
Uppdatera Datum: 19 Maj 2024
Anonim
Så skriver du ditt första Android-spel i Java - Appar
Så skriver du ditt första Android-spel i Java - Appar

Innehåll


Det finns många sätt att skapa ett spel för Android och ett viktigt sätt är att göra det från början i Android Studio med Java. Detta ger dig maximal kontroll över hur du vill att ditt spel ska se ut och bete sig och processen kommer att lära dig färdigheter du kan använda i en rad andra scenarier också - oavsett om du skapar en stänkskärm för en app eller bara vill lägg till några animationer. Med tanke på detta kommer den här tutorialen att visa dig hur du skapar ett enkelt 2D-spel med Android Studio och Java. Du hittar all kod och resurser på Github om du vill följa med.

Ställer in

För att skapa vårt spel kommer vi att behöva ta itu med några specifika begrepp: spelöglor, trådar och dukar. Till att börja med startar du Android Studio. Om du inte har det installerat, kolla in vår fullständiga introduktion till Android Studio, som går över installationsprocessen. Starta nu ett nytt projekt och se till att du väljer mallen 'Tom aktivitet'. Detta är ett spel, så naturligtvis behöver du inte element som FAB-knappen som komplicerar frågor.


Det första du vill göra är att ändra AppCompatActivity till Aktivitet. Det betyder att vi inte kommer att använda åtgärdsfältets funktioner.

På liknande sätt vill vi också göra vårt spel på full skärm. Lägg till följande kod i onCreate () innan samtalet till setContentView ():

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

Observera att om du skriver ut någon kod och den blir understrukad med rött, betyder det förmodligen att du måste importera en klass. Med andra ord måste du berätta för Android Studio att du vill använda vissa uttalanden och göra dem tillgängliga. Om du bara klickar någonstans på det understrukna ordet och sedan trycker på Alt + Enter, kommer det att göras automatiskt för dig!


Skapa din spelvy

Du kan vara van vid appar som använder ett XML-skript för att definiera layouten för vyer som knappar, bilder och etiketter. Detta är vad linjen setContentView gör för oss.

Men återigen, detta är ett spel som betyder att det inte behöver ha webbläsarfönster eller rullningsåtervinningsvyer. I stället för det vill vi visa en duk istället. I Android Studio är en duk precis samma som i konst: det är ett medium som vi kan dra på.

Så ändra den raden för att läsa så:

setContentView (nytt GameView (detta))

Du kommer att upptäcka att detta återigen är understruket rött. Men nu om du trycker på Alt + Enter har du inte möjlighet att importera klassen. Istället har du möjlighet att skapa en klass. Med andra ord är vi på väg att skapa vår egen klass som kommer att definiera vad som kommer att gå på duken. Det är detta som gör att vi kan dra till skärmen snarare än att bara visa färdiga vyer.

Så högerklicka på paketnamnet i hierarkin till vänster och välj Ny> Klass. Nu får du ett fönster för att skapa din klass och du kommer att ringa det GameView. Skriv under SuperClass: android.view.SurfaceView vilket innebär att klassen ärver metoder - dess funktioner - från SurfaceView.

I rutan Gränssnitt (er) skriver du android.view.SurfaceHolder.Callback. Som med alla klasser måste vi nu skapa vår konstruktör. Använd den här koden:

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

Varje gång vår klass kallas för att skapa ett nytt objekt (i detta fall vår yta), kommer den att driva konstruktören och den kommer att skapa en ny yta. Linjen "super" kallar superklassen och i vårt fall är det SurfaceView.

Genom att lägga till återuppringning kan vi fånga upp händelser.

Överskrid nu några metoder:

@Override public voidfaceChanged (SurfaceHolder holder, int format, int bredd, int höjd) {} ​​@Override public voidfaceCreated (SurfaceHolder holder) {} @Override public void ytan Förstört (SurfaceHolder holder) {}

Dessa tillåter oss i princip att åsidosätta (därav namnet) metoder i superklass (SurfaceView). Du bör nu inte ha fler röda understreck i din kod. Trevlig.

Du har precis skapat en ny klass och varje gång vi hänvisar till det kommer det att bygga duken för ditt spel att måla på. Klasser skapa föremål och vi behöver ett till.

Skapa trådar

Vår nya klass kommer att kallas MainThread. Och dess uppgift är att skapa en tråd. En tråd är väsentligen som en parallell gaffel med kod som kan köras samtidigt längs med huvudsakliga del av din kod. Du kan ha många trådar som körs samtidigt, vilket gör att saker kan ske samtidigt snarare än att följa en strikt sekvens. Detta är viktigt för ett spel, eftersom vi måste se till att det fortsätter att fungera smidigt, även när mycket pågår.

Skapa din nya klass precis som du gjorde tidigare och den här gången kommer den att förlängas Tråd. I konstruktören kommer vi bara att ringa super(). Kom ihåg att det är superklassen, som är tråd, och som kan göra allt tungt lyft för oss. Detta är som att skapa ett program för att tvätta diskarna som bara ringer tvättmaskin().

När den här klassen kallas kommer den att skapa en separat tråd som går som en utskjutning av det viktigaste. Och det är från här att vi vill skapa vår GameView. Det betyder att vi också behöver referera till GameView-klassen och vi använder också SurfaceHolder som innehåller duken. Så om duken är ytan är SurfaceHolder staffli. Och GameView är det som sätter allt ihop.

Det hela ska se ut så:

public class MainThread förlänger tråd {privat SurfaceHolderfaceHolder; privat GameView gameView; public MainThread (SurfaceHolderfaceHolder, GameView gameView) {super (); this.surfaceHolder =faceHolder; this.gameView = gameView; }}

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

Skapa spelslingan

Vi har nu de råvaror vi behöver för att göra vårt spel, men ingenting händer. Det är här spelslingan kommer in. I grund och botten är detta en kodslinga som går runt och runt och kontrollerar ingångar och variabler innan du tecknar skärmen. Vårt mål är att göra detta så konsekvent som möjligt, så att det inte finns stammare eller hicka i framerate, som jag ska utforska lite senare.

För närvarande är vi fortfarande i MainThread klass och vi kommer att åsidosätta en metod från superklass. Den här är springa.

Och det går lite så här:

@Override public void run () {medan (kör) {canvas = null; prova {canvas = this.surfaceHolder.lockCanvas (); synkroniserad (faceHolder) {this.gameView.update (); this.gameView.draw (canvas); }} catch (Undantag e) {} slutligen {if (canvas! = null) {prova {surfaceHolder.unlockCanvasAndPost (canvas); } catch (Undantag e) {e.printStackTrace (); }}}}}

Du kommer att se mycket betoning, så vi måste lägga till några fler variabler och referenser. Gå tillbaka till toppen och lägg till:

privat SurfaceHolder-ytaHoldare; privat GameView gameView; privat booleska körning; offentliga statiska kanfasdukar;

Kom ihåg att importera Canvas. Canvas är det vi faktiskt kommer att dra på. Vad gäller 'lockCanvas' är detta viktigt eftersom det är det som i huvudsak fryser duken så att vi kan dra på den. Det är viktigt eftersom annars kan du ha flera trådar som försöker dra på det på en gång. Vet bara att för att redigera duken måste du först låsa duken.

Uppdatering är en metod som vi kommer att skapa och det är här de roliga sakerna kommer att hända senare.

De Prova och fånga under tiden är helt enkelt krav på Java som visar att vi är villiga att försöka hantera undantag (fel) som kan uppstå om duken inte är klar etc.

Slutligen vill vi kunna starta vår tråd när vi behöver den. För att göra detta behöver vi en annan metod här som gör att vi kan sätta igång saker. Det är vad löpning variabel är för (Observera att en Boolean är en typ av variabel som bara är sant eller falskt). Lägg till den här metoden till MainThread klass:

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

Men vid denna tidpunkt bör en sak fortfarande lyfts fram och det är uppdatering. Det beror på att vi inte har skapat uppdateringsmetoden än. Så pop tillbaka in GameView och lägg nu till metod.

public void update () {}

Vi måste också Start tråden! Vi kommer att göra detta i vårt surfaceCreated metod:

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

Vi måste också stoppa tråden när ytan förstörs. Som ni kanske gissat, hanterar vi detta i surfaceDestroyed metod. Men eftersom det faktiskt kan ta flera försök att stoppa en tråd, kommer vi att sätta detta i en slinga och använda Prova och fånga om igen. Såhär:

@Override public void surfaceDestroyed (SurfaceHolder holder) {boolean retry = true; medan (försök på nytt) {försök {thread.setRunning (falskt); thread.join (); } catch (InterruptException e) {e.printStackTrace (); } försök igen = falsk; }}

Och slutligen, gå upp till konstruktören och se till att skapa den nya instansen av din tråd, annars får du det fruktade nollpekarundantaget! Och sedan kommer vi att göra GameView fokuserbar, vilket betyder att den kan hantera händelser.

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

Nu kan du till sist testa faktiskt den här saken! Det är rätt, klicka på Kör och det skall körs faktiskt utan några fel. Förbered dig på att blåsas bort!

Det är ... det är ... en tom skärm! All den koden. För en tom skärm. Men det här är en tom skärm av möjlighet. Du har fått din yta igång med en spelslinga för att hantera händelser. Nu är allt som är kvar att få saker att hända. Det spelar ingen roll om du inte följde allt i lektionen fram till denna punkt. Poängen är att du helt enkelt kan återvinna den här koden för att börja göra härliga spel!

Gör en grafik

Just nu har vi en tom skärm att rita på, allt vi behöver göra är att dra på den. Lyckligtvis är det den enkla delen. Allt du behöver göra är att åsidosätta dragmetoden i vår GameView klass och lägg sedan till några vackra bilder:

@Override public void draw (Canvas canvas) {super.draw (canvas); if (canvas! = null) {canvas.drawColor (Color.WHITE); Måla måla = ny måla (); paint.setColor (Color.rgb (250, 0, 0)); canvas.drawRect (100, 100, 200, 200, paint); }}

Kör detta och du bör nu ha en ganska röd fyrkant uppe till vänster på en annars vit skärm. Detta är verkligen en förbättring.

Du kan teoretiskt skapa stort sett hela ditt spel genom att hålla det inuti den här metoden (och åsidosätta) onTouchEvent att hantera input) men det skulle inte vara ett oerhört bra sätt att göra saker. Att placera ny färg inuti vår slinga kommer att sakta ner betydligt och även om vi lägger detta någon annanstans, läggs för mycket kod till dra metoden skulle bli ful och svår att följa.

I stället är det mycket mer meningsfullt att hantera spelobjekt med sina egna klasser. Vi kommer att börja med en som visar en karaktär och den här klassen kommer att kallas CharacterSprite. Gå vidare och gör det.

Denna klass kommer att dra en sprite på duken och kommer att se ut så

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

För att använda detta måste du först ladda bitmappen och sedan ringa klassen från GameView. Lägg till en referens till privata CharacterSprite characterSprite och sedan i surfaceCreated metod, lägg till raden:

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

Som ni kan se lagras bitmappen som vi laddar i resurser och kallas avdgreen (det var från ett tidigare spel). Nu behöver du bara överföra den bitmappen till den nya klassen i dra metod med:

characterSprite.draw (canvas);

Klicka nu på Kör så ska du se din grafik visas på din skärm! Det här är BeeBoo. Jag brukade rita honom i mina skolböcker.

Tänk om vi ville få den här lilla killen att flytta? Enkelt: vi skapar bara x- och y-variabler för hans positioner och ändrar sedan dessa värden i en uppdatering metod.

Så lägg till referenser till din CharacterSprite och rita sedan din bitmapp kl x, y. Skapa uppdateringsmetoden här och för nu ska vi bara prova:

y ++;

Varje gång spelslingan körs flyttar vi karaktären ner på skärmen. Kom ihåg, y koordinater mäts uppifrån så 0 är toppen av skärmen. Naturligtvis måste vi ringa uppdatering metod i CharacterSprite från uppdatering metod i GameView.

Tryck på play igen och nu ser du att din bild långsamt spårar ner på skärmen. Vi vinner inga spelutdelningar ännu men det är en början!

Okej, för att göra saker lite mer intressant, jag tappar bara en "bouncy ball" -kod här. Detta gör att vår grafik studsar runt skärmen utanför kanterna, som de gamla Windows skärmsläckarna. Du vet, de konstigt hypnotiska.

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; }}

Du måste också definiera dessa variabler:

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

Optimering

Det finns massor mer att fördjupa sig här, från att hantera spelarinmatning, att skala bilder, till att hantera att ha många tecken som rör sig runt skärmen på en gång. Just nu är karaktären studsande men om du tittar mycket noga så är det lätt stamande. Det är inte hemskt men det faktum att du kan se det med blotta ögat är något av ett varningstecken. Hastigheten varierar också mycket på emulatorn jämfört med en fysisk enhet. Föreställ dig nu vad som händer när du har det ton händer på skärmen på en gång!

Det finns några lösningar på detta problem. Det jag vill börja med är att skapa ett privat heltal i MainThread och ring det targetFPS. Detta har värdet 60.Jag kommer att försöka få mitt spel att köra med denna hastighet och under tiden kommer jag att kontrollera att det är det. För det vill jag också ha en privat dubbel som heter averageFPS.

Jag kommer också att uppdatera springa för att mäta hur lång tid varje spelslinga tar och sedan till paus den spelslingan tillfälligt om den ligger före targetFPS. Vi kommer sedan att beräkna hur lång tid det är nu tog och skriv ut det så att vi kan se det i loggen.

@Override public void run () {lång starttid; lång tidMillis; lång väntetid; lång total tid = 0; int frameCount = 0; lång targetTime = 1000 / targetFPS; medan (kör) {startTime = System.nanoTime (); duk = null; prova {canvas = this.surfaceHolder.lockCanvas (); synkroniserad (faceHolder) {this.gameView.update (); this.gameView.draw (canvas); }} catch (Undantag e) {} slutligen {if (canvas! = null) {prova {surfaceHolder.unlockCanvasAndPost (canvas); } catch (Undantag e) {e.printStackTrace (); }}} timeMillis = (System.nanoTime () - starttid) / 1000000; waitTime = targetTime - timeMillis; prova {this.sleep (waitTime); } catch (Undantag e) {} totalTime + = System.nanoTime () - startTime; frameCount ++; if (frameCount == targetFPS) {medelFPS = 1000 / ((totalTime / frameCount) / 1000000); ramCount = 0; total tid = 0; System.out.println (averageFPS); }}}

Nu försöker vårt spel låsa FPS till 60 och du bör upptäcka att det i allmänhet mäter en ganska stabil 58-62 FPS på en modern enhet. Men på emulatorn kan du få ett annat resultat.

Försök ändra 60 till 30 och se vad som händer. Spelet saknar ner och det skall läs nu 30 i din logcat.

Avslutande tankar

Det finns några andra saker vi kan göra för att optimera prestandan också. Det finns ett bra blogginlägg om ämnet här. Försök att avstå från att skapa nya instanser av målarfärg eller bitmappar inuti slingan och gör allt initierande utanför innan spelet börjar.

Om du planerar att skapa nästa hit Android-spel så finns det det säkert enklare och effektivare sätt att göra det idag. Men det finns definitivt fortfarande användningsfallsscenarier för att kunna dra på en duk och det är en mycket användbar färdighet att lägga till din repertoar. Jag hoppas att den här guiden har hjälpt något och önskar dig lycka till i dina kommande kodningsföretag!

NästaEn nybörjarguide till Java

Google Clock-appen har fått töd för Pandora och YouTube Muic i verion 6.1, vilket gör att användare kan vakna upp till ina favorit hit (h / t Gränen)....

Google har öppnat om varför några av ina tjänter gick ner i UA på öndag. Påverkade användare kunde tillfälligt inte få åtkomt till Google produkt...

Intressant