Hoe je je eerste Android-game op Java schrijft

Schrijver: John Stephens
Datum Van Creatie: 1 Januari 2021
Updatedatum: 19 Kunnen 2024
Anonim
How To Create Your First Android Application with Java
Video: How To Create Your First Android Application with Java

Inhoud


Er zijn tal van manieren om een ​​game voor Android te maken en een belangrijke manier is om het helemaal opnieuw te doen in Android Studio met Java. Dit geeft je de maximale controle over hoe je wilt dat je game eruit ziet en zich gedraagt ​​en het proces leert je vaardigheden die je ook in een reeks andere scenario's kunt gebruiken - of je nu een splash-scherm voor een app maakt of gewoon wilt enkele animaties toevoegen. Met dat in gedachten laat deze tutorial je zien hoe je een eenvoudig 2D-spel maakt met Android Studio en Java. Je kunt alle code en bronnen vinden op Github als je wilt volgen.

Opzetten

Om onze game te maken, moeten we een paar specifieke concepten behandelen: gamelussen, threads en canvases. Start Android Studio om te beginnen. Als je het niet hebt geïnstalleerd, bekijk dan onze volledige inleiding tot Android Studio, die het installatieproces doorloopt. Start nu een nieuw project en zorg ervoor dat u de sjabloon ‘Lege activiteit’ kiest. Dit is een spel, dus je hebt natuurlijk geen elementen zoals de FAB-knop nodig die zaken compliceren.


Het eerste wat u wilt doen, is veranderen AppCompatActivity naar Werkzaamheid. Dit betekent dat we de functies van de actiebalk niet zullen gebruiken.

Op dezelfde manier willen we onze game ook op volledig scherm maken. Voeg de volgende code toe aan onCreate () vóór de aanroep van setContentView ():

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

Houd er rekening mee dat als u code schrijft en deze rood wordt onderstreept, dit waarschijnlijk betekent dat u een klasse moet importeren. Met andere woorden, u moet Android Studio vertellen dat u bepaalde verklaringen wilt gebruiken en deze beschikbaar stellen. Als u gewoon ergens op het onderstreepte woord klikt en vervolgens op Alt + Enter drukt, wordt dat automatisch voor u gedaan!


Je gameweergave maken

U bent misschien gewend aan apps die een XML-script gebruiken om de lay-out van weergaven zoals knoppen, afbeeldingen en labels te definiëren. Dit is wat de regel is setContentView doet het voor ons.

Maar nogmaals, dit is een spel, wat betekent dat het geen browservensters of scrollende weergaven voor recycling nodig heeft. In plaats daarvan willen we in plaats daarvan een canvas laten zien. In Android Studio is een canvas precies hetzelfde als in de kunst: het is een medium waarop we kunnen putten.

Dus verander die regel als volgt:

setContentView (nieuwe GameView (deze))

U zult zien dat dit nogmaals rood is onderstreept. Maar nu als u op Alt + Enter drukt, kunt u de klasse niet importeren. In plaats daarvan hebt u de optie om creëren een klas. Met andere woorden, we staan ​​op het punt onze eigen klas te maken die zal bepalen wat er op het doek gaat. Dit is wat ons in staat stelt om naar het scherm te tekenen, in plaats van alleen kant-en-klare weergaven te tonen.

Klik dus met de rechtermuisknop op de pakketnaam in uw hiërarchie links en kies Nieuw> Klasse. Je krijgt nu een venster om je klas te maken en je gaat het noemen GameView. Schrijf onder SuperClass: android.view.SurfaceView wat betekent dat de klasse methoden - de mogelijkheden ervan - van SurfaceView zal erven.

In het vak Interface (s) schrijft u android.view.SurfaceHolder.Callback. Zoals met elke klasse, moeten we nu onze constructor maken. Gebruik deze code:

privé MainThread-draad; public GameView (Context context) {super (context); . GetHolder () addCallback (deze); }

Elke keer dat onze klasse wordt opgeroepen om een ​​nieuw object te maken (in dit geval ons oppervlak), wordt de constructor uitgevoerd en wordt een nieuw oppervlak gemaakt. De lijn ‘super’ noemt de superklasse en in ons geval is dat de SurfaceView.

Door Callback toe te voegen, kunnen we gebeurtenissen onderscheppen.

Vervang nu enkele methoden:

@Override public void surfaceChanged (SurfaceHolder-houder, int formaat, int breedte, int hoogte) {} @Override public void surfaceCreated (SurfaceHolder-houder) {} @Override public void surfaceDestroyed (SurfaceHolder-houder) {}

Hiermee kunnen we in feite de methoden (vandaar de naam) in de superklasse (SurfaceView) negeren. U zou nu geen rode onderstreping meer in uw code moeten hebben. Leuk.

Je hebt zojuist een nieuwe klasse gemaakt en elke keer dat we daarnaar verwijzen, wordt het canvas waarop je spel kan worden geschilderd, gebouwd. Klassen creëren objecten en we hebben er nog één nodig.

Discussies maken

Onze nieuwe les gaat heten MainThread. En het is zijn taak om een ​​draad te creëren. Een thread is in wezen als een parallelle codevork die tegelijkertijd naast de code kan worden uitgevoerd hoofd onderdeel van uw code. Je kunt veel threads tegelijk laten lopen, waardoor dingen tegelijkertijd kunnen gebeuren in plaats van zich aan een strikte volgorde te houden. Dit is belangrijk voor een game, omdat we ervoor moeten zorgen dat deze soepel blijft werken, zelfs als er veel gebeurt.

Creëer je nieuwe klas zoals je eerder deed en deze keer gaat het uitbreiden Draad. In de constructor gaan we gewoon bellen super(). Vergeet niet dat dit de superklasse is, die Thread is en die al het zware werk voor ons kan doen. Dit is hetzelfde als het maken van een programma voor het afwassen van de afwas wasmachine().

Wanneer deze klasse wordt genoemd, wordt er een afzonderlijke thread gemaakt die wordt uitgevoerd als een uitloper van het belangrijkste. En het komt van hier dat we onze GameView willen maken. Dat betekent dat we ook moeten verwijzen naar de GameView-klasse en we gebruiken ook SurfaceHolder die het canvas bevat. Dus als het canvas het oppervlak is, is SurfaceHolder de ezel. En GameView brengt het allemaal samen.

Het volledige ding zou er zo uit moeten zien:

openbare klasse MainThread breidt draad {privé SurfaceHolder surfaceHolder uit; privé GameView gameView; openbare MainThread (SurfaceHolder surfaceHolder, GameView gameView) {super (); this.surfaceHolder = surfaceHolder; this.gameView = gameView; }}

Schweet. We hebben nu een GameView en een thread!

De gamelus maken

We hebben nu de grondstoffen die we nodig hebben om ons spel te maken, maar er gebeurt niets. Dit is waar de gamelus van pas komt. Kortom, dit is een codelus die rond en rond gaat en ingangen en variabelen controleert voordat het scherm wordt getekend. Ons doel is om dit zo consistent mogelijk te maken, zodat er geen haperen of hikken in de framerate zijn, die ik later zal onderzoeken.

Voor nu zijn we nog steeds in de MainThread klasse en we gaan een methode uit de superklasse overschrijven. Deze is rennen.

En het gaat ongeveer zo:

@Override public void run () {while (running) {canvas = null; probeer {canvas = this.surfaceHolder.lockCanvas (); gesynchroniseerd (surfaceHolder) {this.gameView.update (); this.gameView.draw (canvas); }} catch (uitzondering e) {} eindelijk {if (canvas! = null) {probeer {surfaceHolder.unlockCanvasAndPost (canvas); } catch (uitzondering e) {e.printStackTrace (); }}}}}

Je zult veel onderstreping zien, dus we moeten nog wat variabelen en verwijzingen toevoegen. Ga terug naar boven en voeg toe:

privé SurfaceHolder SurfaceHolder; privé GameView gameView; privé boolean running; openbare statische Canvas canvas;

Vergeet niet om Canvas te importeren. Canvas is iets waar we daadwerkelijk op zullen tekenen. Wat ‘lockCanvas’ betreft, dit is belangrijk omdat het in wezen het canvas bevriest zodat we er op kunnen tekenen. Dat is belangrijk omdat je anders meerdere threads tegelijk kunt proberen te gebruiken. Weet alleen dat je eerst het canvas moet bewerken slot het doek.

Update is een methode die we gaan maken en dit is waar de leuke dingen later zullen gebeuren.

De proberen en vangst ondertussen zijn eenvoudigweg vereisten van Java die laten zien dat we bereid zijn om uitzonderingen (fouten) te proberen die kunnen optreden als het canvas niet gereed is, enz.

Ten slotte willen we onze draad kunnen starten wanneer we die nodig hebben. Om dit te doen, hebben we hier een andere methode nodig waarmee we dingen in gang kunnen zetten. Dat is wat de running variabele is voor (merk op dat een Boolean een type variabele is die alleen ooit waar of onwaar is). Voeg deze methode toe aan de MainThread klasse:

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

Maar op dit punt moet nog één ding worden benadrukt en dat is het bijwerken. Dit komt omdat we de updatemethode nog niet hebben gemaakt. Dus kom terug in GameView en voeg nu de methode toe.

openbare ongeldige update () {}

We moeten ook begin de draad! We gaan dit doen in onze surfaceCreated methode:

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

We moeten ook de draad stoppen wanneer het oppervlak is vernietigd. Zoals je misschien al geraden hebt, behandelen we dit in de surfaceDestroyed methode. Maar aangezien het feitelijk meerdere pogingen kan kosten om een ​​thread te stoppen, gaan we dit in een lus zetten en gebruiken proberen en vangst nog een keer. Zoals zo:

@Override public void surfaceDestroyed (SurfaceHolder-houder) {boolean retry = true; terwijl (opnieuw) {probeer {thread.setRunning (false); thread.join (); } catch (InterruptedException e) {e.printStackTrace (); } opnieuw proberen = onwaar; }}

En tot slot, ga naar de constructor en zorg ervoor dat je de nieuwe instantie van je thread maakt, anders krijg je de gevreesde nulaanwijzeruitzondering! En dan gaan we GameView focusbaar maken, wat betekent dat het gebeurtenissen aankan.

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

Nu kan je Tenslotte test dit ding eigenlijk! Dat klopt, klik op uitvoeren en het Moeten eigenlijk zonder fouten worden uitgevoerd. Bereid je voor om weggeblazen te worden!

Het is ... het is ... een leeg scherm! Al die code. Voor een leeg scherm. Maar dit is een leeg scherm van kans. Je hebt je oppervlak op gang gebracht met een gamelus om evenementen af ​​te handelen. Het enige dat overblijft is dingen laten gebeuren. Het maakt zelfs niet uit of je tot nu toe niet alles in de tutorial hebt gevolgd. Het punt is, je kunt deze code eenvoudig recyclen om glorieuze spellen te maken!

Een grafiek maken

Nu hebben we een leeg scherm om op te tekenen, we hoeven er alleen maar op te tekenen. Gelukkig is dat het simpele gedeelte. Het enige dat u hoeft te doen, is de tekenmethode in onze overschrijven GameView klasse en voeg vervolgens een aantal mooie foto's toe:

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

Voer dit uit en je zou nu een mooi rood vierkant links bovenaan een anders wit scherm moeten hebben. Dit is zeker een verbetering.

Je zou theoretisch vrijwel je hele spel kunnen maken door het in deze methode te steken (en te negeren onTouchEvent om met input om te gaan), maar dat zou geen vreselijk goede manier zijn om dingen aan te pakken. Het plaatsen van nieuwe Paint in onze lus zal de zaken aanzienlijk vertragen en zelfs als we dit ergens anders plaatsen, wordt teveel code aan de toegevoegd trek methode zou lelijk en moeilijk te volgen worden.

In plaats daarvan is het veel logischer om spelobjecten met hun eigen klassen te hanteren. We gaan beginnen met een die een karakter toont en deze klasse zal worden genoemd CharacterSprite. Ga je gang en maak dat.

Deze klasse gaat een sprite op het canvas tekenen en zal er zo uitzien

public class CharacterSprite {privé Bitmap-afbeelding; public CharacterSprite (Bitmap bmp) {image = bmp; } openbare nietigmaking (canvascanvas) {canvas.drawBitmap (afbeelding, 100, 100, null); }}

Om dit te gebruiken, moet u eerst de bitmap laden en vervolgens de klasse van aanroepen GameView. Voeg een referentie toe aan privé CharacterSprite characterSprite en dan in de surfaceCreated methode, voeg de regel toe:

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

Zoals u kunt zien, wordt de bitmap die we laden opgeslagen in bronnen en deze wordt avdgreen genoemd (het was van een vorige game). Nu hoeft u alleen die bitmap door te geven aan de nieuwe klasse in de trek methode met:

characterSprite.draw (canvas);

Klik nu op uitvoeren en je afbeelding zou nu op je scherm moeten verschijnen! Dit is BeeBoo. Ik tekende hem altijd in mijn schoolboeken.

Wat als we deze kleine man wilden laten bewegen? Eenvoudig: we maken gewoon x- en y-variabelen voor zijn posities en veranderen deze waarden vervolgens in een bijwerken methode.

Dus voeg de referenties toe aan uw CharacterSprite en teken vervolgens uw bitmap naar x, y. Maak de updatemethode hier en voor nu gaan we het gewoon proberen:

y ++;

Elke keer dat de gamelus wordt uitgevoerd, verplaatsen we het personage naar beneden op het scherm. Onthouden, Y coördinaten worden gemeten vanaf de bovenkant dus 0 is de bovenkant van het scherm. Natuurlijk moeten we de bijwerken methode in CharacterSprite van de bijwerken methode in GameView.

Druk nogmaals op play en je ziet nu dat je afbeelding langzaam over het scherm loopt. We winnen nog geen game-awards, maar het is een begin!

Oké, om dingen te maken licht interessanter, ik ga hier gewoon een ‘bouncy ball’ -code plaatsen. Hierdoor stuitert onze afbeelding rond het scherm langs de randen, net als die oude Windows-screensavers. Je weet wel, de vreemd hypnotiserende.

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

U moet ook deze variabelen definiëren:

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

optimalisatie

Er bestaat overvloed meer om hier in te gaan, van het verwerken van spelerinvoer tot het schalen van afbeeldingen, tot het beheren van het hebben van veel karakters die allemaal tegelijk over het scherm bewegen. Op dit moment stuitert het personage, maar als je heel goed kijkt, stottert het licht. Het is niet verschrikkelijk, maar het feit dat je het met het blote oog kunt zien, is iets van een waarschuwingssignaal. De snelheid varieert ook veel op de emulator in vergelijking met een fysiek apparaat. Stel je nu voor wat er gebeurt als je dat hebt gedaan tons meteen op het scherm!

Er zijn een paar oplossingen voor dit probleem. Wat ik wil doen om mee te beginnen, is een privé-geheel getal in te maken MainThread en noem dat targetFPS. Dit heeft de waarde 60.Ik ga proberen mijn spel op deze snelheid te laten draaien en ondertussen zal ik controleren of het zo is. Daarvoor wil ik ook een privé dubbel genoemd averageFPS.

Ik ga ook de updaten rennen methode om te meten hoe lang elke gamelus duurt en vervolgens tot pauze die gamelus tijdelijk als deze voor de targetFPS ligt. We gaan dan berekenen hoe lang het duurt nu nam en print dat zodat we het in het logboek kunnen zien.

@Override public void run () {long startTime; lange tijd Millis; lange wachttijd; lange totalTime = 0; int frameCount = 0; lange targetTime = 1000 / targetFPS; terwijl (actief) {startTime = System.nanoTime (); canvas = null; probeer {canvas = this.surfaceHolder.lockCanvas (); gesynchroniseerd (surfaceHolder) {this.gameView.update (); this.gameView.draw (canvas); }} catch (uitzondering e) {} eindelijk {if (canvas! = null) {probeer {surfaceHolder.unlockCanvasAndPost (canvas); } catch (uitzondering e) {e.printStackTrace (); }}} timeMillis = (System.nanoTime () - startTime) / 1000000; waitTime = targetTime - timeMillis; probeer {this.sleep (waitTime); } catch (uitzondering e) {} totalTime + = System.nanoTime () - startTime; framecount ++; if (frameCount == targetFPS) {averageFPS = 1000 / ((totalTime / frameCount) / 1000000); frameCount = 0; totalTime = 0; System.out.println (averageFPS); }}}

Nu probeert onze game de FPS te vergrendelen op 60 en je zou moeten ontdekken dat het over het algemeen een redelijk stabiele 58-62 FPS meet op een modern apparaat. Op de emulator kunt u echter een ander resultaat krijgen.

Probeer die 60 te veranderen in 30 en kijk wat er gebeurt. Het spel vertraagt ​​en het Moeten lees nu 30 in uw logcat.

Gedachten sluiten

Er zijn nog enkele andere dingen die we kunnen doen om de prestaties te optimaliseren. Er is een geweldig blogbericht over dit onderwerp. Probeer nooit nieuwe instanties van Paint of bitmaps in de lus te maken en initialiseer alles buiten voordat het spel begint.

Als je van plan bent om de volgende populaire Android-game te maken, zijn die er zeker eenvoudiger en efficiëntere manieren om het tegenwoordig te doen. Maar er zijn zeker nog use-case scenario's om op een canvas te kunnen tekenen en het is een zeer nuttige vaardigheid om aan je repertoire toe te voegen. Ik hoop dat deze gids enigszins heeft geholpen en wens je veel succes met je komende coderingsactiviteiten!

volgendeEen beginnersgids voor Java

Al je op zoek bent naar een nieuwe carrière in 2019, zijn er altijd banen voor profeionele netwerker bechikbaar. Met een certificering voor de marktleider in bedrijfnetwerken, Cico, kunt u uw weg...

Welkom bij onze update-hub Clah of Clan! We verzamelen alle officiële balanwijzigingen en update voor Clah of Clan zodra deze worden uitgebracht door de Fine gigant upercell. Zelf na 6 jaar kunne...

Fascinerend