I denne opplæringen skal jeg forklare de viktigste trinnene og arbeidsflyten for å lage et enkelt romoverlevelsesspill, basert på tyngdekraftmekanikeren som er forklart i en tidligere opplæring. Dette spillet er skrevet i AS3 ved hjelp av FlashDevelop.
Bruk venstre og høyre piltast til å manøvrere skipet ditt, opp og ned piltastene for å øke eller redusere størrelsen på magnetfeltet det produserer, og mellomromstasten for å reversere polariteten. Samle de hvite krystallene for å øke drivstofftilførselen din - men unngå de røde, fordi de bruker den opp. Ikke slå en stein, eller det er spillet over!
I denne opplæringen vil vi ikke faktisk opprette hele spillet som vist ovenfor; Vi starter bare med det, ved å lage en veldig enkel versjon med primitiv grafikk og bare en type objekt. Men på slutten bør du ha lært nok til å kunne legge til de andre funksjonene selv!
Selve spillet er veldig enkelt i sin nåværende tilstand - ta en titt på denne kritikken for tips om hvordan du kan ta det fra en enkel demo til et helt spill!
Sett opp et nytt AS3-prosjekt i FlashDevelop, og sett dets dimensjoner til 550x600px.
pakke [SWF (width = "550", height = "600")] offentlig klasse Main utvider Sprite
Det er seks objekter i partikkel som du kan identifisere fra å spille spillet ovenfor:
Selvfølgelig kan du legge til i hvilket som helst annet objekt for å gjøre spillet mer interaktivt eller legge til en ny funksjon. For denne opplæringen vil vi bare gjøre
Energi
KlasseFra objektene vi identifiserte, jobber fire av dem faktisk på samme måte: ved å falle fra topp til bunn.
De er:
I denne opplæringen skal vi bare lage "energiforsyning" objekter, ut av de fire ovenfor. Så la oss begynne med å lage disse objektene og få dem til å falle ned, med en tilfeldig gyteposisjon og hastighet.
Begynn med å opprette en Energi
klasse:
pakke import flash.display.MovieClip; importere flash.events.Event; offentlig klasse Energi utvider MovieClip private var rSpeed: Number = 0; offentlig funksjon Energi (hastighet: tall) graphics.beginFill (0x321312); graphics.drawCircle (0, 0, 8); rSpeed = hastighet; // vi vil ringe dette hver ramme offentlig funksjon flytte (): void this.y + = rSpeed; // rotasjonshastighet er knyttet til bevegelseshastighet this.rotation + = rSpeed / 8;
GameScreen
KlasseDenne klassen vil etter hvert kontrollere de fleste aspekter av spillet vårt, inkludert spillerens bevegelse og spillsløyfen.
Lag klassen:
pakke offentlig klasse GameScreen utvider MovieClip offentlig funksjon GameScreen ()
Det er alt vi trenger for nå.
Vi lager nå en forekomst av GameScreen
innenfor Hoved
:
pakke import flash.display.Sprite; importere flash.events.Event; [SWF (bredde = "550", høyde = "600")] offentlig klasse Main utvider Sprite private var spill: GameScreen; offentlig funksjon Main (): void // Vis ikke et gult rektangel på skjermen ved oppstart stage.stageFocusRect = false; spill = ny GameScreen (); addChild (spill); // gi tastaturfokus til spillskjermen umiddelbart stage.focus = game;
Hvorfor bry seg? Vel, på denne måten blir det lettere å legge til ekstra skjermer senere hvis vi vil (som en forhåndsinnleder, tittelskjerm, et spill over skjerm ...).
For å unngå GameScreen
Klassen blir for mye av et rot, vi bruker separate klasser til å administrere hvert objekt.
Hver lederklasse vil inneholde alle funksjonene som er knyttet til, og samhandler med, en bestemt gjenstand. Her er Energy
klasse:
pakke import flash.display.MovieClip; offentlig klasse EnergyManager // denne Vector vil lagre alle forekomster av energiklassen privat var energyList: Vector.private var gameScreen: GameScreen; offentlig funksjon EnergyManager (gs: GameScreen) gameScreen = gs; energyList = ny vektor. ;
Merk at vi krever en referanse til GameScreen som skal sendes til konstruktøren, og vi lagrer denne referansen i en privat variabel. Vi oppretter også en Vector for å lagre referanser til alle energibjektene.
Så langt inneholder klassen ingen andre funksjoner; vi vil legge dem til senere.
Legg til underfunksjonen for å skape energi, dette er bare en funksjon; Vi vil ringe funksjonen senere fra GameScreen
Klasse:
offentlig funksjon createEnergy (tall: int): void var energi: Energy; for (var jeg: 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);
Vi lager en ny energiforsyning med en hastighet på 4, legg den til skjermlisten (via GameScreen), legg den til Vector of all energy objects som vi nettopp har opprettet, og sett posisjonen til et tilfeldig punkt innenfor bestemte grenser.
De Beregning.generatRandomValue (#, #)
er en statisk funksjon som vi ikke har skrevet ennå, så la oss gjøre det nå. Opprett en ny klasse som heter beregning
og legg til denne funksjonen:
offentlig statisk funksjon generateRandomValue (min: tall, maks: tall): tall var randomValue: Number = min + (Math.random () * (maks - min)); returnere randomValue;
Denne funksjonen vil generere et tilfeldig tall mellom de to verdiene som sendes til den. For mer informasjon om hvordan det fungerer, se denne raske tipsen. Siden dette er en statisk funksjon, trenger vi ikke å opprette en forekomst av beregning
for å ringe det.
Nå, hva er det addEnergyToScreen ()
funksjon? Vi har ikke definert det ennå, så la oss gjøre det nå. Legg til dette til GameScreen
:
offentlig funksjon addEnergyToScreen (energi: Energi): void addChild (energi);
Det legger bare til det forbigående forekomsten av energi til visningslisten. La oss også lage en tilsvarende funksjon for å fjerne et gitt energiobjekt fra skjermen:
offentlig funksjon removeEnergyFromScreen (energi: Energi): void if (energy.parent == this) removeChild (energy);
La oss stille en tidtaker som definerer intervallet for hver gyting. Denne koden går inn GameScreen
Konstruktørfunksjonen:
energyM = ny EnergyManager (dette); // husk å sende en referanse til spill skjermen var spawnTimer: Timer = ny Timer (3000, 0); spawnTimer.addEventListener (TimerEvent.TIMER, spawnEnergy); spawnTimer.start ();
Så, hvert tredje sekund, vil timeren ringe spawnEnergy ()
. La oss skrive den funksjonen nå:
privat funksjon spawnEnergy (e: TimerEvent): void energyM.createEnergy (4); // lag 4 energier
La oss bruke en annen, større sirkel for å representere spilleren. Du er velkommen til å importere et bilde som skal brukes i stedet:
offentlig funksjon Spiller () graphics.beginFill (0x7ebff1); graphics.drawCircle (0, 0, 20);
Legg til denne koden til GameScreen
for å legge til spilleren på skjermen:
// i variabelen definisjoner offentlig var spiller: Spiller;
// i konstruktørfunksjonsspiller = ny spiller; addChild (spiller); player.x = 275; player.y = 450;
Så langt skal vi ha noen strømforsyninger som faller noen sekunder, og spilleren som vises midt på skjermen:
Det er i utgangspunktet to måter å bruke bevegelse på:
ekte
. I hver rammeoppdatering er "flytte høyre" ekte
, vi øker objektets x-verdi. Bruk direkte oppdatering av hver ramme
- Når du trykker på høyre piltast, blir et objekt fortalt å bevege seg rett med en gang, ved å øke sin x-verdi. Den andre metoden fører ikke til jevn bevegelse når nøkkelen trykkes kontinuerlig, men den første metoden gjør - så vi skal bruke den første metoden.
Det er tre enkle trinn for å gjøre dette:
privat var moveRight: Boolean = false; privat var moveLeft: boolsk = false;
addEventListener (Event.ENTER_FRAME, oppdatering); addEventListener (KeyboardEvent.KEY_DOWN, KeyDownHandler); addEventListener (KeyboardEvent.KEY_UP, KeyUpHandler); privat funksjon KeyDownHandler (e: KeyboardEvent): void if (e.keyCode == Keyboard.RIGHT) moveRight = true; hvis (e.keyCode == Keyboard.LEFT) moveLeft = true; hvis (e.keyCode == Keyboard.SPACE) hvis (isGravityPushing == true) isGravityPushing = false; annet isGravityPushing = true; privat funksjon KeyUpHandler (e: KeyboardEvent): void if (e.keyCode == Keyboard.RIGHT) moveRight = false; hvis (e.keyCode == Keyboard.LEFT) moveLeft = false;
Ikke glem å først opprette en funksjonslytte fra enter-rammehendelsen, "oppdatering":
// ring denne funksjonen hver ramme privat funksjon oppdatering (e: Event): void if (moveRight == true) player.x + = 6; hvis (moveLeft == true) player.x - = 6;
Hold spilleren innenfor grensene på skjermen:
hvis (player.x> = 525) moveRight = false; hvis (player.x <= 20) moveLeft = false;
Slik ser alt ut, på plass:
pakke import flash.display.MovieClip; importere flash.events.Event; importer flash.events.TimerEvent; importere flash.ui.Keyboard; importere flash.utils.Timer; importer flash.events.KeyboardEvent; offentlig klasse GameScreen offentlig varespiller: Spiller; privat var energiM: EnergyManager; privat var moveRight: Boolean = false; privat var moveLeft: boolsk = false; privat var isGravityPushing: boolsk = sant; Privat var returnertPower: int = 0; privat var scoreText: Tekst; privat var totalScore: int = 0; privat var score: Tekst; offentlig funksjon GameScreen () scoreText = new Text ("Score:"); addChild (scoreText); energyM = ny EnergyManager; var spawnTimer: Timer = ny Timer (3000, 0); spawnTimer.addEventListener (TimerEvent.TIMER, spawnEnergy); spawnTimer.start (); spiller = ny spiller; addChild (spiller); player.x = 275; player.y = 450; addEventListener (Event.ENTER_FRAME, oppdatering); addEventListener (KeyboardEvent.KEY_DOWN, KeyDownHandler); addEventListener (KeyboardEvent.KEY_UP, KeyUpHandler); privat funksjon KeyDownHandler (e: KeyboardEvent): void if (e.keyCode == Keyboard.RIGHT) moveRight = true; hvis (e.keyCode == Keyboard.LEFT) moveLeft = true; hvis (e.keyCode == Keyboard.SPACE) hvis (isGravityPushing == true) isGravityPushing = false; annet hvis (isGravityPushing == false) isGravityPushing = true; privat funksjon KeyUpHandler (e: KeyboardEvent): void if (e.keyCode == Keyboard.RIGHT) moveRight = false; hvis (e.keyCode == Keyboard.LEFT) moveLeft = false; personlig funksjon oppdatering (e: Event): void if (player.x> = 525) moveRight = false; hvis (player.x <= 20) moveLeft = false; if (moveRight == true) player.x += 6; if (moveLeft == true) player.x -= 6;
I øyeblikket er energiforsyningene gytende, men ikke i bevegelse. Vi bruker GameScreen.update ()
funksjon for å få dem til å bevege seg, siden det kjører hver ramme.
Legg til denne koden til GameScreen.update ()
:
energyM.moveAll (); // vil gjøre hvert energibjekt bevege seg
Nå må vi selvfølgelig gjøre EnergyManager.moveAll ()
funksjon, så legg til dette til EnergyManager.as
:
offentlig funksjon moveAll (): void for (var i: int = 0; i < energyList.length; i++) var energyS:Energy = energyList[i]; energyS.move();
Vi må sjekke for sammenstøt mellom hvert energibjekt og spilleren. (Hvis du utvikler spillet videre, må du sjekke dette for asteroider og energiforbrukere, men ikke for stjerner.)
Det beste stedet å håndtere disse kontrollene er inne i Energy
, utløst hver ramme av GameScreen
.
En ting å vurdere: Kollisjonskontrollene vil være mellom to sirkler, så hitTestObject ()
er ikke ideell. I stedet bruker vi metoden som er forklart i denne opplæringen.
Vi kan skrive funksjonen som nedenfor:
offentlig funksjon checkCollision (p: Spiller): int // energi overført på grunn av kollisjon var energyTransfer: int = 0; for (var jeg: 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
er kort for energiforsyning.Du kan endre linje 51 til energyTransfer + = 1
, for å tillate spilleren å absorbere mer enn ett energibjekt samtidig. Det er opp til deg - prøv det og se hvordan det påvirker spillet.
Vi må sjekke for kollisjoner hver ramme, så vi bør ringe funksjonen vi nettopp skrev fra GameScreen.update ()
.
Først må vi opprette en heltallvariabel for å lagre energioverføringsverdien fra kollisjonsdetekteringsfunksjonen. Vi bruker denne verdien for å øke skipets energi og legge til spillerens poengsum.
Privat var returnertPower: int = 0;
returnedPower = energyM.checkCollision (spiller);
Før vi går inn i å skape spillmekaniker for skipets "Push" og "Pull" -funksjon, vil jeg gjerne introdusere fysikkbegrepet som mekanikeren bygger på.
Tanken er å tiltrekke objektet mot spilleren ved hjelp av a makt. Newtons universelle gravitasjonslove gir oss en stor (og enkel) matematisk formel som vi kan bruke til dette, hvor kraften selvsagt er gravitasjonskraften:
G er bare et tall, og vi kan sette det til hva vi liker. På samme måte kan vi sette massene av hvert objekt i spillet til alle verdier som vi liker. Gravity skjer over uendelige avstander, men i vårt spill har vi et avskjæringspunkt (betegnet av den hvite sirkelen i demoen fra starten av opplæringen).
De to viktigste tingene å merke seg om denne formelen er:
Før vi begynner å kodes spillmekanikkene for "Push" og "Pull" -funksjonen, la oss være klare på hva vi vil gjøre det:
I hovedsak ønsker vi at A (spilleren) skal utøve en bestemt kraft på B (en krystall), og flytte B mot A basert på den kraften.
Vi bør revidere noen få begreper:
Math.atan2 (B.y - A.y, B.x - A.x)
.B.x + = (Kraft * Math.cos (vinkel));
B.y + = (Force * Math.sin (vinkel));
For mer informasjon, se veiledningene Gravity in Action og Trigonometry for Flash Game Developers.
Basert på forrige forklaring kan vi komme med en oversikt over koden vår som tiltrekker hver krystall til skipet:
Eksempelkode:
offentlig funksjon gravityPull (p: Spiller): 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);
Her er en tidsplan som viser hvordan dette ser ut:
Legg merke til at energien beveger seg raskere jo nærmere den kommer til skipet, takket være r-kvadratet.
Vi kan implementere pushing-funksjonen bare ved å gjøre kraften negativ:
offentlig funksjon gravityPull (p: Spiller): 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);
Her beveger objektet seg sakte ettersom det kommer lenger unna spilleren, siden kraften blir svakere.
Selvfølgelig vil du trenge denne funksjonen for å bli kjørt hver ramme av GameScreen
- men før det må vi bruke en boolsk funksjon for å bytte mellom de to funksjonene:
privat var isGravityPushing: boolsk = sant; // slå plass skifter det
Vi skal bruke sant for 'Push' og false for 'Pull'.
Innsiden KeyDownHandler ()
:
hvis (e.keyCode == Keyboard.SPACE) hvis (isGravityPushing == true) isGravityPushing = false; annet hvis (isGravityPushing == false) isGravityPushing = true;
Etterpå må du sjekke den boolske hver ramme. Legg til dette til Oppdater()
:
hvis (isGravityPushing == true) energyM.gravityPull (spiller); hvis (isGravityPushing == false) energyM.gravityPush (spiller);
Du finner kanskje at bevegelsen ikke ser så fin ut. Dette kan skyldes at kraften ikke er helt ideell, eller på grunn av den r-kvadratiske termen.
Jeg vil gjerne endre formelen slik:
var f: tall = (0,8 * 50 * 10) / r;
Som du kan se, har jeg redusert verdien av "G" til 0,8, og forandret kraften til å bare avhenge avstanden mellom objektene, i stedet for avstanden kvadratet.
Prøv det og se om du liker endringen. Du kan alltid endre det uansett.
Vi må vise litt tekst på skjermen, for å vise poengsummen og skipets gjenværende kraft.
Til dette formål skal vi bygge en ny klasse, Tekst
:
pakke import flash.display.MovieClip; importer flash.text.TextField; importere flash.events.Event; importer flash.text.TextFormat; importer flash.text.TextFormatAlign; offentlig klasse Tekst utvider MovieClip public var _scoreText: TextField = nytt TextField (); offentlig funksjon Tekst (streng: String) var myScoreFormat: TextFormat = new TextFormat (); // Forandringsformat myScoreFormat.size = 24; myScoreFormat.align = TextFormatAlign.LEFT; myScoreFormat.color = (0x131313); _scoreText.defaultTextFormat = myScoreFormat; _scoreText.text = streng; addChild (_scoreText); offentlig funksjon oppdateringText (streng: streng) _scoreText.text = string;
Det er veldig enkelt; Det er i utgangspunktet en MovieClip med et tekstfelt inni.
For å gi spillet litt utfordring, vil vi få fartøyets kraft til å bli brukt opp sakte, slik at spilleren må samle energiobjekter for å lade opp.
For å gjøre skipets kraft til stede på selve skipet, kan vi bare legge til en forekomst av Tekst
til skipsobjektets visningsliste.
Erklære disse variablene i Skip
klasse:
offentlig var totalPower: Nummer = 100; // skipet starter med denne mye strøm private var powerText: Text;
Vi må holde mengden strøm (både lagret og vist) oppdatert hver ramme, så legg til denne nye funksjonen til Spiller
:
Først i konstruktøren:
// legge til et nytt tekstobjekt hvis det ikke allerede eksisterer hvis (! powerText) powerText = new Text (String (int (totalPower))); addChild (powerText); powerText.x - = 20; // Juster posisjon powerText.y - = 16;
Og så…
offentlig funksjon updatePower (): void // fps = 24, slik at dette reduserer strømmen med 1 / sek totalPower - = 1/24; powerText.updateText (String (mellomprodukt (totalPower)));
Strømmen vil redusere hver ramme med 1/24 av en enhet, noe som betyr at den vil senke med en full enhet hvert sekund.
Vi må gjøre dette løpe hver ramme, så legg denne linjen til GameScreen.update ()
:
player.updatePower ();
Når skipet kolliderer med et energibjekt, vil vi at det skal øke sin kraft.
I GameScreen.update ()
, legg til den uthevede linjen:
returnedPower = energyM.checkCollision (spiller); player.totalPower + = returnedPower;
Husk at du kan endre hvor mye strøm som er returnert i EnergyManager.checkCollision ()
funksjon.
Igjen, trenger vi tekstklassen. Denne gangen viser vi "Score" og deretter verdien.
Her trenger vi tre flere variabler:
Erklære disse i GameScreen
klasse:
privat var scoreText: Tekst; privat var totalScore: int = 0; privat var score: Tekst;
I konstruktøren legger du til denne koden:
scoreText = ny tekst ("Score:"); addChild (scoreText); score = ny tekst (streng (total score)); addChild (score); score.x = scoreText.x + 100; // Plassering den ved siden av "Score:" Tekst. score.y + = 2;
Nå, i Oppdater()
funksjon, legg til dette:
score.updateText (String (totalScore));
Det er det - vi har opprettet en grunnleggende versjon av spillet ovenfor!
Ta en titt (du må kanskje laste på siden):
Kanskje du også vil ha en bakgrunn med et innebygd bilde og stjerner. Legg dette til din Hoved
klasse:
[Embed (source = "/ ... /lib/SpaceBackground.jpg")] // Embed private var backgroundImage: Class; // Denne linjen må komme umiddelbart etter å legge inn privat var bgImage: Bitmap = ny backgroundImage (); privat var numOfStars: int = 70;
Opprett nå Stjerne
klasse:
pakkeverdier import flash.display.MovieClip; importere flash.events.Event; offentlig klasse Star utvider MovieClip privat var hastighet: nummer; offentlig funksjon Stjerne (alfa: Nummer, størrelse: Nummer, hastighet1: Nummer) graphics.beginFill (0xCCCCCC); graphics.drawCircle (0, 0, size); hastighet = speed1; // sørg for at du ringer dette hver ramme privat funksjon moveDown (): void this.y + = speed; hvis (this.y> = 600) this.y = 0;
I Hoved()
konstruktør, legg til dette for å lage stjernene:
for (var jeg: int = 0; i < numOfStars; i++) createStars();
Her er den faktiske createStars ()
funksjon:
privat funksjon createStars (): void var stjerne: Star = new Star (Math.random (), Calculations.getRandomValue (1, 2), Calculations.getRandomValue (2, 5)); // tilfeldig alfa, størrelse og hastighet addChild (stjerne); star.x = Calculations.getRandomValue (0, 550); star.y = Beregninger.getRandomValue (0, 600);
Med tilfeldig alfa, størrelse, posisjon og hastighet kan en pseudo-3D-bakgrunn genereres.
En rekkeviddeindikatorsirkel kan gjøres ved å bare opprette en annen sirkel og legge den til skipsens visningsliste, akkurat som hvordan du la til strømindikatorteksten. Pass på at sirkelen er sentrert på skipet, og har en radius lik fartøyets trykk / trekkområde.
Legg til gjennomsiktighet (alfa-verdi) i sirkelen med underkoden:
grafikk.beginFill (0xCCCCCC, 0,1);
Prøv å legge til ekstra kontroller som gjør at rekkevidden øker eller reduseres når piltastene opp og ned trykkes.
Jeg håper du likte denne opplæringen! Vennligst ikke la dine kommentarer.
Neste: Les denne kritikken for en guide til å ta Flux fra en enkel demo til et helt spill!