In deze tutorial leg ik de belangrijkste stappen en workflow uit voor het maken van een eenvoudig overlevingsspel over de ruimte, gebaseerd op de zwaartekrachtmechanica die is uitgelegd in een eerdere zelfstudie. Deze game is geschreven in AS3 met behulp van FlashDevelop.
Gebruik de linker en rechter pijltjestoetsen om je schip te manoeuvreren, de pijltoetsen omhoog en omlaag om het magnetisch veld dat het produceert te vergroten of verkleinen, en de spatiebalk om de polariteit om te keren. Verzamel de witte kristallen om je brandstoftoevoer te vergroten - maar vermijd de rode, want ze verbruiken het. Raak geen steen, of het is game-over!
In deze zelfstudie maken we niet de volledige game die hierboven wordt weergegeven; we beginnen er gewoon aan door een zeer eenvoudige versie te maken met primitieve grafische afbeeldingen en slechts één type object. Uiteindelijk zou u echter voldoende moeten hebben geleerd om de andere functies zelf toe te kunnen voegen!
De game zelf is heel eenvoudig in zijn huidige staat - bekijk deze kritiek voor tips over hoe je het kunt nemen van een eenvoudige demo tot een volledig spel!
Stel een nieuw AS3-project in in FlashDevelop en stel de afmetingen in op 550x600px.
package [SWF (width = "550", height = "600")] public class Main breidt uit
Er zijn zes deeltjes in deeltjes die je kunt herkennen aan het spelen van het spel hierboven:
Natuurlijk kun je een ander object toevoegen om het spel interactiever te maken of een nieuwe functie toe te voegen. Voor deze zelfstudie maken we gewoon
Energie
KlasseVan de objecten die we identificeerden, werken er vier op precies dezelfde manier: door van boven naar beneden te vallen.
Zij zijn:
In deze zelfstudie maken we alleen de 'energievoorziening'-objecten van de vier hierboven. Laten we beginnen met het maken van deze objecten en ze laten vallen, met een willekeurige spawning-positie en snelheid.
Begin met het maken van een Energie
klasse:
pakket import flash.display.MovieClip; import flash.events.Event; public class Energy breidt MovieClip uit private var rSpeed: Number = 0; openbare functie Energie (snelheid: getal) graphics.beginFill (0x321312); graphics.drawCircle (0, 0, 8); rSpeed = snelheid; // we noemen dit elk frame public function move (): void this.y + = rSpeed; // rotatiesnelheid is gekoppeld aan bewegingssnelheid this.rotation + = rSpeed / 8;
GameScreen
KlasseDeze klasse beheert uiteindelijk de meeste aspecten van ons spel, inclusief de bewegingen van de speler en de loop van het spel.
Maak de klas:
package public class GameScreen breidt MovieClip uit public function GameScreen ()
Dat is alles wat we nu nodig hebben.
We maken nu een exemplaar van GameScreen
binnen Hoofd
:
pakket import flash.display.Sprite; import flash.events.Event; [SWF (width = "550", height = "600")] public class Main breidt Sprite uit private var game: GameScreen; public function Main (): void // vertoon geen gele rechthoek op het scherm in opstartfase.stageFocusRect = false; game = nieuw GameScreen (); addChild (spel); // geef toetsenbordfocus onmiddellijk aan het spelscherm stage.focus = game;
Waarom zou je je drukmaken? Nou, op deze manier is het makkelijker om later extra schermen toe te voegen als we dat willen (zoals een preloader, een titelscherm, een game over het scherm ...).
Om het te vermijden GameScreen
klasse wordt een rotzooi, we gebruiken aparte klassen om elk object te beheren.
Elke managerklasse zal alle functies bevatten die betrekking hebben op en communiceren met een bepaald object. Hier is de EnergyManager
klasse:
pakket import flash.display.MovieClip; public class EnergyManager // deze vector slaat alle instanties van de energieklasse private var energyList op: Vector.privé var gameScreen: GameScreen; openbare functie EnergyManager (gs: GameScreen) gameScreen = gs; energyList = new Vector. ;
Merk op dat we een verwijzing naar het GameScreen moeten doorgeven aan de constructor en we slaan deze referentie op in een privévariabele. We hebben ook een Vector ingesteld om referenties op te slaan voor alle energieobjecten.
Tot dusver bevat de klasse geen andere functies; we zullen ze later toevoegen.
Voeg de onderstaande functie toe voor het maken van energie, dit is slechts een functie; we zullen de functie later vanuit roepen GameScreen
Klasse:
public function createEnergy (number: int): void var energy: Energy; for (var i: int = 0; i < number; i++) energy = new Energy(4); gameScreen.addEnergyToScreen(energy); energyList.push(energy); energy.x = Calculation.generateRandomValue(30, 520); energy.y = Calculation.generateRandomValue( -150, -20);
We creëren een nieuwe energievoorziening met een snelheid van 4, voegen deze toe aan de weergavelijst (via het GameScreen), voegen deze toe aan de Vector van alle energieobjecten die we zojuist hebben gemaakt, en stellen de positie in op een willekeurig punt binnen bepaalde grenzen.
De Calculation.generateRandomValue (#, #)
is een statische functie die we nog niet hebben geschreven, dus laten we dat nu doen. Maak een nieuwe klasse genaamd Berekening
en voeg deze functie toe:
public static function generateRandomValue (min: Number, max: Number): Number var randomValue: Number = min + (Math.random () * (max - min)); return randomValue;
Deze functie genereert een willekeurig getal tussen de twee waarden die eraan worden doorgegeven. Zie deze Snelle tip voor meer informatie over hoe het werkt. Aangezien dit een statische functie is, hoeven we geen instantie van te maken Berekening
om het te noemen.
Wat is dat nou? addEnergyToScreen ()
functie? We hebben dat nog niet gedefinieerd, dus laten we het nu doen. Voeg dit toe aan GameScreen
:
openbare functie addEnergyToScreen (energie: energie): void addChild (energie);
Het voegt gewoon het doorgegeven exemplaar van energie toe aan de weergavelijst. Laten we ook een overeenkomstige functie maken om een bepaald energieobject van het scherm te verwijderen:
openbare functie removeEnergyFromScreen (energie: energie): void if (energy.parent == this) removeChild (energie);
Laten we een timer instellen die het interval voor elke spawning definieert. Deze code gaat erin GameScreen
's constructorfunctie:
energyM = nieuwe EnergyManager (this); // vergeet niet om een verwijzing naar het spelscherm door te geven var spawnTimer: Timer = new Timer (3000, 0); spawnTimer.addEventListener (TimerEvent.TIMER, spawnEnergy); spawnTimer.start ();
Dus elke drie seconden belt de timer spawnEnergy ()
. Laten we die functie nu schrijven:
private function spawnEnergy (e: TimerEvent): void energyM.createEnergy (4); // creëer 4 energieën
Laten we een andere, grotere cirkel gebruiken om de speler te vertegenwoordigen. Voel je vrij om in plaats daarvan een afbeelding te importeren om te gebruiken:
publieke functie Player () graphics.beginFill (0x7ebff1); graphics.drawCircle (0, 0, 20);
Voeg deze code toe aan GameScreen
om de speler aan het scherm toe te voegen:
// in de variabele definities public var player: Player;
// in de constructorfunctiespeler = nieuwe speler; addChild (speler); player.x = 275; player.y = 450;
Tot nu toe moeten we enkele energievoorzieningen hebben die enkele seconden vallen, en de speler die in het midden van het scherm verschijnt:
Er zijn in principe twee manieren om beweging toe te passen:
waar
. In elke frame-update is "bewegingsrecht" waar
, we verhogen de x-waarde van het object. Gebruik directe update van elk frame
- wanneer op de rechterpijltoets wordt gedrukt, wordt aan een object verteld om meteen naar rechts te gaan door de x-waarde te verhogen. De tweede methode leidt niet tot vloeiende bewegingen als de toets continu wordt ingedrukt, maar de eerste methode wel - dus we zullen de eerste methode gebruiken.
Er zijn drie eenvoudige stappen om dit te doen:
private var moveRight: Boolean = false; private var moveLeft: Boolean = false;
addEventListener (Event.ENTER_FRAME, update); addEventListener (KeyboardEvent.KEY_DOWN, KeyDownHandler); addEventListener (KeyboardEvent.KEY_UP, KeyUpHandler); private function KeyDownHandler (e: KeyboardEvent): void if (e.keyCode == Keyboard.RIGHT) moveRight = true; if (e.keyCode == Keyboard.LEFT) moveLeft = true; if (e.keyCode == Keyboard.SPACE) if (isGravityPushing == true) isGravityPushing = false; else isGravityPushing = true; persoonlijke functie KeyUpHandler (e: KeyboardEvent): void if (e.keyCode == Keyboard.RIGHT) moveRight = false; if (e.keyCode == Keyboard.LEFT) moveLeft = false;
Vergeet niet om eerst een functie aan te maken luister vanuit de frame-gebeurtenis enter, "update":
// noem deze functie elke update van de persoonlijke framefunctie (e: Event): void if (moveRight == true) player.x + = 6; if (moveLeft == true) player.x - = 6;
Houd de speler binnen de grenzen van het scherm:
if (player.x> = 525) moveRight = false; if (player.x <= 20) moveLeft = false;
Hier ziet u hoe alles er uitziet, op zijn plaats:
pakket import flash.display.MovieClip; import flash.events.Event; import flash.events.TimerEvent; import flash.ui.Keyboard; import flash.utils.Timer; import flash.events.KeyboardEvent; public class GameScreen public var player: Player; private var energyM: EnergyManager; private var moveRight: Boolean = false; private var moveLeft: Boolean = false; private var isGravityPushing: Boolean = true; private var returnsPower: int = 0; privé var scoreTekst: Tekst; privé var totalScore: int = 0; privé var score: Tekst; openbare functie GameScreen () scoreText = nieuwe tekst ("Score:"); addChild (scoreText); energyM = nieuwe EnergyManager; var spawnTimer: Timer = new Timer (3000, 0); spawnTimer.addEventListener (TimerEvent.TIMER, spawnEnergy); spawnTimer.start (); speler = nieuwe speler; addChild (speler); player.x = 275; player.y = 450; addEventListener (Event.ENTER_FRAME, update); addEventListener (KeyboardEvent.KEY_DOWN, KeyDownHandler); addEventListener (KeyboardEvent.KEY_UP, KeyUpHandler); private function KeyDownHandler (e: KeyboardEvent): void if (e.keyCode == Keyboard.RIGHT) moveRight = true; if (e.keyCode == Keyboard.LEFT) moveLeft = true; if (e.keyCode == Keyboard.SPACE) if (isGravityPushing == true) isGravityPushing = false; else if (isGravityPushing == false) isGravityPushing = true; persoonlijke functie KeyUpHandler (e: KeyboardEvent): void if (e.keyCode == Keyboard.RIGHT) moveRight = false; if (e.keyCode == Keyboard.LEFT) moveLeft = false; persoonlijke functie-update (e: Event): void if (player.x> = 525) moveRight = false; if (player.x <= 20) moveLeft = false; if (moveRight == true) player.x += 6; if (moveLeft == true) player.x -= 6;
Op dit moment zijn de energievoorzieningen aan het paaien maar niet aan het bewegen. We zullen de gebruiken GameScreen.update ()
functie om ze te laten bewegen, omdat het elk frame uitvoert.
Voeg deze code toe aan GameScreen.update ()
:
energyM.moveAll (); // zal elk energieobject laten bewegen
Nu moeten we natuurlijk de EnergyManager.moveAll ()
functie, dus voeg dit toe aan EnergyManager.as
:
openbare functie moveAll (): void for (var i: int = 0; i < energyList.length; i++) var energyS:Energy = energyList[i]; energyS.move();
We zullen moeten controleren op botsingen tussen elk energieobject en de speler. (Als je het spel verder ontwikkelt, moet je dit controleren voor asteroïden en energieverbruikers, maar niet voor sterren.)
De beste plaats om met deze cheques om te gaan, is binnen de EnergyManager
, activeerde elk frame door de GameScreen
.
Eén ding om over na te denken: de botsingscontroles zullen tussen twee cirkels liggen, dus hitTestObject ()
is niet ideaal. In plaats daarvan gebruiken we de methode die in deze zelfstudie wordt uitgelegd.
We kunnen de functie als volgt schrijven:
publieke functie checkCollision (p: Player): int // energie overgedragen door botsing var energyTransfer: int = 0; for (var i: int = 0; i < energyList.length; i++) var energyS:Energy = energyList[i]; var newX:Number = p.x - energyS.x; var newY:Number = p.y - energyS.y; var distance:Number = Math.sqrt(newX * newX + newY * newY); if (distance <= 28) gameScreen.removeEnergyFromScreen(energyS); energyList.splice(i, 1); // for this simple game, we'll always transfer 1 unit // but you could alter this based on speed of collision // or any other factor energyTransfer = 1; return energyTransfer;
Energys
is een afkorting voor Energy Supply.Je zou Lijn 51 kunnen veranderen naar energyTransfer + = 1
, om de speler in staat te stellen meer dan één energieobject tegelijkertijd te absorberen. Het is aan jou - probeer het uit en zie hoe het de game beïnvloedt.
We moeten elk frame controleren op botsingen, dus we moeten de functie noemen die we zojuist hebben geschreven GameScreen.update ()
.
Eerst moeten we een integer-variabele maken om de energieoverdrachtswaarde op te slaan van de botsingsdetectiefunctie. We zullen deze waarde gebruiken om de energie van het schip te vergroten en toe te voegen aan de score van de speler.
private var returnsPower: int = 0;
returnPower = energyM.checkCollision (speler);
Voordat we de game-monteur gaan maken voor de 'Push'- en' Pull'-functie van het schip, wil ik het fysica-concept introduceren waarop de monteur is gebaseerd.
Het idee is om het object naar de speler te trekken door middel van een dwingen. Newtons wet van universele zwaartekracht geeft ons een grote (en eenvoudige) wiskundige formule die we hiervoor kunnen gebruiken, waarbij de kracht natuurlijk de zwaartekracht is:
G is slechts een cijfer en we kunnen het instellen op wat we maar willen. Op dezelfde manier kunnen we de massa van elk object in het spel instellen op elke gewenste waarde. Zwaartekracht vindt plaats over oneindige afstanden, maar in onze game hebben we een afkappunt (aangegeven door de witte cirkel in de demo vanaf het begin van de zelfstudie).
De twee belangrijkste dingen om op te merken over deze formule zijn:
Voordat we beginnen met het coderen van de game mechanics voor de 'Push' en 'Pull' functie, laten we duidelijk zijn wat we willen dat het doet:
In wezen willen we dat A (de speler) een bepaalde kracht uitoefent op B (een kristal) en B naar A beweegt op basis van die kracht.
We moeten een paar concepten herzien:
Math.atan2 (B.y - A.y, B.x - A.x)
.B.x + = (Forceer * Math.cos (hoek));
B.y + = (Forceer * Math.sin (hoek));
Zie de tutorials Gravity in Action en Trigonometry voor Flash Game Developers voor meer informatie.
Op basis van de vorige uitleg, kunnen we een overzicht maken van onze code die elk kristal naar het schip trekt:
Voorbeeldcode:
publieke functie gravityPull (p: Player): void for (var i: int = 0; i < energyList.length; i++) var energyS:Energy = energyList[i]; var nX:Number = (p.x - energyS.x); var nY:Number = (p.y - energyS.y); var angle:Number = Math.atan2(nY, nX); var r:Number = Math.sqrt(nX * nX + nY * nY); if (r <= 250) var f:Number = (4 * 50 * 10) / (r * r); energyS.x += f * Math.cos(angle); energyS.y += f * Math.sin(angle);
Hier is een timelapse die laat zien hoe dit eruit ziet:
Merk op dat de energie sneller beweegt, hoe dichter het bij het schip komt, dankzij de r-kwadratische term.
We kunnen de duwfunctie alleen implementeren door de kracht negatief te maken:
publieke functie gravityPull (p: Player): void for (var i: int = 0; i < energyList.length; i++) var energyS:Energy = energyList[i]; var nX:Number = (p.x - energyS.x); var nY:Number = (p.y - energyS.y); var angle:Number = Math.atan2(nY, nX); var r:Number = Math.sqrt(nX * nX + nY * nY); if (r <= 250) var f:Number = (4 * 50 * 10) / (r * r); energyS.x -= f * Math.cos(angle); energyS.y -= f * Math.sin(angle);
Hier beweegt het object langzamer naarmate het verder van de speler komt, omdat de kracht zwakker wordt.
Natuurlijk heb je deze functie nodig om elk frame te laten draaien GameScreen
- maar daarvoor moeten we een Booleaanse functie gebruiken om te schakelen tussen de twee functies:
private var isGravityPushing: Boolean = true; // als u op de spatiebalk drukt, schakelt het om
We gaan true gebruiken voor 'Push' en false voor 'Pull'.
Binnen KeyDownHandler ()
:
if (e.keyCode == Keyboard.SPACE) if (is GravityPushing == true) is GravityPushing = false; else if (isGravityPushing == false) isGravityPushing = true;
Daarna moet u de Boolean elk frame controleren. Voeg dit toe aan bijwerken()
:
if (is GravityPushing == true) energyM.gravityPull (speler); if (is GravityPushing == false) energyM.gravityPush (speler);
Misschien merk je dat de beweging er niet zo mooi uitziet. Dit zou kunnen zijn omdat de kracht niet helemaal ideaal is, of vanwege de r-kwadratische term.
Ik zou graag de formule willen aanpassen zoals:
var f: Number = (0,8 * 50 * 10) / r;
Zoals je kunt zien, heb ik de waarde van "G" teruggebracht tot 0,8, en de kracht veranderd in afhankelijkheid van de afstand tussen de objecten in plaats van de afstand in het kwadraat.
Probeer het en kijk of je geniet van de verandering. Je kunt het altijd veranderen zoals je wilt.
We zullen wat tekst op het scherm moeten tonen om de score en de resterende kracht van het schip te tonen.
Voor dit doel zullen we een nieuwe klas bouwen, Tekst
:
pakket import flash.display.MovieClip; import flash.text.TextField; import flash.events.Event; import flash.text.TextFormat; import flash.text.TextFormatAlign; Public Class Text breidt MovieClip uit public var _scoreText: TextField = new TextField (); public function Text (string: String) var myScoreFormat: TextFormat = new TextFormat (); // Formaat veranderbaar myScoreFormat.size = 24; myScoreFormat.align = TextFormatAlign.LEFT; myScoreFormat.color = (0x131313); _scoreText.defaultTextFormat = myScoreFormat; _scoreText.text = string; addChild (_scoreText); public function updateText (string: String) _scoreText.text = string;
Het is heel simpel; het is eigenlijk een MovieClip met een tekstveld erin.
Om het spel een uitdaging te geven, zullen we ervoor zorgen dat de kracht van het schip langzaam op is, zodat de speler energieobjecten moet verzamelen om op te laden.
Om de kracht van het schip op het schip zelf te laten lijken, kunnen we eenvoudig een instantie toevoegen van Tekst
naar de displaylijst van het schip.
Verklaar deze variabelen binnen de Schip
klasse:
public var totalPower: Number = 100; // schip begint met zoveel power private var powerText: Text;
We moeten de hoeveelheid energie (zowel opgeslagen als weergegeven) elk frame bijwerken, dus voeg deze nieuwe functie toe aan Speler
:
Ten eerste, in de constructor:
// voeg een nieuw tekstobject toe als dit nog niet bestaat als (! powerText) powerText = new Text (String (int (totalPower))); addChild (powerText); powerText.x - = 20; // Pas positie aan powerText.y - = 16;
En dan…
publieke functie updatePower (): void // fps = 24, dus dit maakt het vermogen kleiner met 1 / sec totalPower - = 1/24; powerText.updateText (String (int (totalPower)));
Het vermogen neemt elk frame af met 1/24 van een eenheid, wat betekent dat het elke seconde met één volledige eenheid zal afnemen.
We moeten dit laten uitvoeren in elk frame, dus voeg deze regel toe aan GameScreen.update ()
:
player.updatePower ();
Wanneer het schip botst met een energieobject, willen we dat het zijn kracht vergroot.
In GameScreen.update ()
, voeg de gemarkeerde regel toe:
returnPower = energyM.checkCollision (speler); player.totalPower + = retourPower;
Vergeet niet dat u kunt wijzigen hoeveel stroom wordt geretourneerd in de EnergyManager.checkCollision ()
functie.
Nogmaals, we hebben de tekstklasse nodig. Deze keer geven we 'Score' en vervolgens de waarde weer.
Hier hebben we nog drie variabelen nodig:
Verklaar deze binnen GameScreen
klasse:
privé var scoreTekst: Tekst; privé var totalScore: int = 0; privé var score: Tekst;
Voeg in de constructor deze code toe:
scoreText = nieuwe tekst ("Score:"); addChild (scoreText); score = nieuwe tekst (string (totalScore)); addChild (score); score.x = scoreText.x + 100; // Plaatsen naast de tekst "Score:". score.y + = 2;
Nu, in de bijwerken()
functie, voeg dit toe:
score.updateText (String (totaalscore));
Dat is het - we hebben een basisversie van het bovenstaande spel gemaakt!
Neem een kijkje (mogelijk moet je de pagina opnieuw laden):
Misschien wilt u ook een achtergrond met een ingesloten afbeelding en sterren. Voeg dit toe aan uw Hoofd
klasse:
[Embed (source = "/ ... /lib/SpaceBackground.jpg")] // embed private var backgroundImage: Class; // Deze regel moet onmiddellijk na het insluiten komen private var bgImage: Bitmap = new backgroundImage (); private var numOfStars: int = 70;
Maak nu het Ster
klasse:
pakketactiva import flash.display.MovieClip; import flash.events.Event; public class Star breidt MovieClip uit private var speed: Number; openbare functie Ster (alfa: Number, size: Number, speed1: Number) graphics.beginFill (0xCCCCCC); graphics.drawCircle (0, 0, grootte); snelheid = snelheid1; // zorg ervoor dat je dit elke private functie van frame roept moveDown (): void this.y + = speed; if (this.y> = 600) this.y = 0;
In de Hoofd()
constructor, voeg dit toe om de sterren te maken:
for (var i: int = 0; i < numOfStars; i++) createStars();
Dit is het echte createStars ()
functie:
persoonlijke functie createStars (): void var star: Star = new Star (Math.random (), Calculations.getRandomValue (1, 2), Calculations.getRandomValue (2, 5)); // willekeurige alfa, grootte en snelheid addChild (ster); star.x = Calculations.getRandomValue (0, 550); star.y = Calculations.getRandomValue (0, 600);
Met willekeurige alfa, grootte, positie en snelheid kan een pseudo-3D achtergrond worden gegenereerd.
U kunt een bereikindicatorcirkel maken door eenvoudig een nieuwe cirkel te maken en deze aan de weergavelijst van het schip toe te voegen, net zoals u de powerindicatietekst hebt toegevoegd. Zorg ervoor dat de cirkel gecentreerd is op het schip en een straal heeft gelijk aan het duw / trekbereik van het schip.
Voeg transparantie (alpha-waarde) toe aan de cirkel met de onderstaande code:
graphics.beginFill (0xCCCCCC, 0.1);
Probeer extra bedieningselementen toe te voegen die het bereik vergroten of verkleinen wanneer de pijltoetsen omhoog en omlaag worden ingedrukt.
Ik hoop dat je deze tutorial leuk vond! Laat alstublieft uw commentaar achter.
Volgende: Lees deze kritiek voor een gids om Flux mee te nemen van een eenvoudige demo tot een volledig spel!