Gör ett Megaman-inspirerat spel i Construct 2

Jag ska gå dig genom skapandet av ett Megaman-inspirerat skjutspel / plattformsspel. Vi kommer att fokusera mer på skytteaspekterna i spelet än plattformen. I denna handledning använder jag Construct 2 som verktyg för att göra spelet, men jag kommer att förklara logiken med pseudokod, så att du kan följa denna handledning i valfritt språk eller motor.

relaterade inlägg
  • Bygg ett spel med två spelare "Small Tactics" Board in Construct 2: Del 1
  • Skapa ett Bomberman-inspirerat spel i Construct 2: Spelaren och nivån
  • Gör ett match-3-spel i Construct 2: Grunderna
  • Vanliga missförhållanden av nykomlingar till Construct 2 Gamedev Community

Klicka på spelet för att ge det fokus, använd piltangenterna för att flytta och hoppa, och Z att skjuta.

För att fokusera på implementeringen av spelet, kommer jag inte att förklara varje Construct 2-funktion; Jag kommer att anta att du vet grunderna som att ladda en sprite, grundläggande kollision eller spela ljud. Med det sagt, låt oss börja göra spelet.


Förbered konstverket

Första sakerna först måste vi ha sprites för vårt spel. Lyckligtvis har opengameart oss täckt med sin underbara samling legala spelkonst. Vi behöver fyra uppsättningar sprites; en hjälte, en fiende och plattor för plattformar.

  • Hjälte (av Redshrike):
    http://opengameart.org/content/xeon-ultimate-smash-friends
  • Enemy (även av Redshrike):
    http://opengameart.org/content/fighting-robot-for-ultimate-smash-friends
  • Kakel (av robotenhet):
    http://opengameart.org/content/prototyping-2d-pixelart-tilesets

För att använda dem i Construct 2 skördar jag hjälten sprites i enskilda ramar med GIMP.


Grundläggande rörelse

Jag kommer att använda Construct 2s plattformsbeteende för resten av handledningen så att jag kan fokusera på skytte och AI-delen av spelet, vilket var huvudfokus för denna handledning.


Konstruera 2: s plattformsbeteende

Grundläggande rörelse förklaras

Om du arbetar på ett annat språk, eller vill implementera din egen grundläggande plattformsrörelse istället för att använda det inbyggda beteendet. Du behöver bara använda koden i det här avsnittet om du inte ska använda Construct 2s inbyggda beteende.

För att börja, måste vi överväga tre sätt som vår hjälte kan flytta; gå rätt, gå till vänster eller hoppa. Varje ram uppdaterar vi spelet simulering.

antal moveSpeed ​​= 50; funktionsuppdatering () moveHero (); 

För att uppdatera spelarens karaktär genomför vi grundläggande rörelse så här:

funktion moveHero () // spelare trycker på denna tangent ner om (keyDown (KEY_LEFT)) hero.x - = moveSpeed ​​* deltaTime; hero.animate ( "walkLeft");  om (keyDown (KEY_RIGHT)) hero.x + = moveSpeed ​​* deltaTime; hero.animate ( "walkRight");  om (keyDown (KEY_UP)) hero.jump (); hero.animate ( "hoppa");  // en nyckel som bara trycktes om (keyReleased (KEY_LEFT)) hero.animate ("standLeft");  om (keyReleased (KEY_RIGHT)) hero.animate ("standRight"); 

Jag använder en annan funktion för att hoppa eftersom hoppning inte bara handlar om att ändra y-värdet utan även beräkna tyngdkraften. Vi kommer också att ha en funktion som lyssnar på huruvida en nyckel har just släppts, för att returnera vår hjälteanimering till en stående animering.

Låt oss prata om hur man gör spelaren hoppa. Hjälten måste veta huruvida han för närvarande hoppar eller inte, och även om han för närvarande faller eller inte. Så vi kommer att förklara två nya variabler: isJumping och isFalling. Som standard är båda falska, vilket innebär att hjälten står på en plattform.

För att utföra ett hopp måste vi först kontrollera om båda värdena är falska eller inte, och gör sedan isJump sant.

Funktionshopp () if ! IsJumping &&! IsFalling) isJumping = True

För hjälten att kunna hoppa behöver vi en variabel som heter jumpPower och gravity. JumpPowers standardvärde är -20 och gravitationen är 1. Logiken är att lägga till värdet av hoppaffekten till hjälte Y-positionen och att lägga till tyngdkraften för att hoppa kraftens värde.

Vi gör det här varje ficka. Kanske är det inte den mest realistiska gravitationen fysiken finns, men spel behöver inte vara realistiska, de behöver bara vara trovärdiga, det är därför vissa spel har superhuman hopp och dubbelhopp. Koden nedan hör till uppdateringsfunktionen.

Om (isJumping || isFalling) hero.y + = hero.jumpPower; hero.jumpPower + = hjälte.gravity;  // så småningom hoppa kraften blir större än noll, och det betyder att hjälten faller om (hero.jumpPower> = 0) isJumping = False; isFalling = True;  // för att stoppa hösten, gör någonting när hjälten överlappar plattformen om (hero.isOverlapping (plattform1)) // platform1 är den plattform som vår hjälte kan gå på // återställer variabeln till standardvärdet isJumping = False; isFalling = False; hero.jumpPower = -20;  // och då är det fria fallet när spelare faller över kanten av en plattform om (! hero.isOverlapping (platform1) && hero.jumpPower < 0 && !isJumping)  // !hero.isOverlapping(platform1) checks whether or not our hero is standing on a platform // and if jumpPower is less than zero and the player is not currently jumping, then that means // he's falling // setting these two values like this will make the player fall. hero.jumpPower = 0; isFalling = true; 

Konstruktion 2: s inbyggda plattformsbeteende replikerar ovanstående exempelkod, som endast ges till hjälp de som arbetar på ett annat språk.


Genomföra fotograferingen

Nu kommer skjutdelen av spelet. I Megaman-serien finns tre typer av skott: vanliga skott, laddade skott och bossenergispel.

Normala skott är självförklarande. Laddade skott är skott som debiteras först innan de släpps. Dessa laddade skott finns i två typer: halv laddad och fulladdat. Dessa laddade attacker är starkare än vanliga skott, med den fulladdade blir den starkaste.

Boss energi skott är skott med makt som spelaren förvärvat efter att ha besegrat varje boss. Skadan är densamma som normalt men de har speciella egenskaper som vanliga skott inte har.

Nu när vi känner till varje skott typ, låt oss börja göra dem. Låt oss först se logiken bakom hur vi använder varje skott. Här antar vi att Z-knappen på tangentbordet används för att skjuta ett skott. Vi genomför två olika beteenden:

  • Vanliga skott: spelaren trycker på z och släpper den omedelbart (tryck på knappen). Kula kommer att skjutas en gång varje kran. Animationen ändras för att skjuta animering innan du omedelbart växlar till den stående animationen.
  • Laddade skott: Spelaren trycker på Z. Den första normala kula kommer att skjutas. Animationen ändras för att skjuta innan du omedelbart växlar till den stående animationen. Om Z fortsätter att tryckas kommer en laddningseffekt att läggas ovanpå spelanimationen (stående, gå). Om Z-knappen släpps på mindre än 5 sekunder sedan den första laddningen skottas en halvladdad kula. Om Z-knappen släpps efter 5 sekunder kommer en fullladdad kula att skjutas.
  • Boss energi skott: vår hjälte måste först utrusta den kula han förvärvade efter att ha besegrat en chef. Efter utrustningen trycker spelaren på en annan knapp för att skjuta denna kula. Detta kolluppförande varierar, och måste vara unikt kodade för varje kula.

Nu, låt oss börja koda. Eftersom vår hjälte kan skjuta åt vänster och höger måste vi veta vilken riktning han för närvarande står inför. Låt oss deklarera en ny variabel som kallas inför som lagrar ett strängvärde om hjälten är vänd mot vänster eller höger.

String vänd mot = "rätt"; // vilken riktning hjälten är närvarande inför funktion moveHero () // player trycker på denna tangent om (keyDown (KEY_LEFT)) hero.x - = moveSpeed ​​* deltaTime; hero.animate ( "walkLeft"); vänd = "vänster";  om (keyDown (KEY_RIGHT)) hero.x + = moveSpeed ​​* deltaTime; hero.animate ( "walkRight"); motsatt = "rätt";  // ... fortsättningen av moveHero () funktionen går här funktionsuppdatering () // ... uppdateringskoden som vi tidigare skrev går här ... // spelare tryck på den här tangenten en gång om (keyPressed (KEY_Z)) om == "rätt") hero.animate ("Shoot"); // vi lägger till skyttefunktion här senare annars om (mot = = "vänster") hero.animate ("shoot"); hero.mirrorSprite (); // den här funktionen vrider spritet horisontellt ​​om (keyReleased (KEY_Z)) if (vänd == "höger") hero.animate ("standRight");  annars om (inför == "vänster") hero.animate ("standLeft"); hero.mirrorSprite (); // vi behöver ringa det här igen eftersom spritet speglades // om vi inte speglar spritet igen, kommer standLeft att se ut som stående

Flytta händelser i Construct 2

Innan vi skjuter en kula måste vi titta på de egenskaper som kullen har:

  • Kraft: Kraftens attackkraft, skadan den kommer att hantera mot fienden
  • Hastighet: hur snabbt kullen går
  • Vinkel: Skjutvinkeln bestämmer i vilken riktning kula går.

Dessa egenskaper skiljer sig åt för varje punkt. I synnerhet kommer kraftegenskapen att vara annorlunda. Vinkelegenskapen är normalt endast en av två värden; oavsett om kulan är skjuten höger eller vänster, såvida det inte är en energiboll som kan skjutas i en unik vinkel.

Skottvariationer diskuteras senare så nu kommer jag bara att täcka grundläggande skott. Följande är kodstycket som skottar en kula.

// först skapar vi en funktion som skapar en ny kula Funktionsfotografering (strängvägToSprite, nummer bulletPower, number bulletSpeed, number bulletAngle) myBullet = new Bullet (pathToSprite); myBullet.power = bulletPower; // Bullet-klassen eller -objektet har två privata variabler som rör det enligt sin vinkel // Förklaring till dessa två linjer behöver mer matte, så jag väljer att inte förklara // Jag antar att din valfria motor har ett sätt att flytta en objekt enligt sin vinkel ySpeed ​​= Math.sin (bulletAngle) * bulletSpeed; xSpeed ​​= Math.cos (bulletAngle) * bulletSpeed;  // det här är Bullet Class-funktionen som kallas varje ram, detta flyttar kula enligt sin vinkelfunktion moveBullet () x + = xSpeed ​​* deltaTime; y + = ySpeed ​​* deltaTime;  // och det här är modifieringen av vår tidigare uppdatering () funktionsfunktionsuppdatering () // ... uppdateringskoden som vi tidigare skrev går här ... // spelare tryck på den här tangenten en gång om (keyPressed (KEY_Z)) if ( inför == "rätt") hero.animate ("Shoot"); hero.shoot ("path / to / sprite.png", 10, 400, 0);  annars om (motsatt == "vänster") hero.animate ("Shoot"); hero.mirrorSprite (); // den här funktionen vrider sprite horisontellt hjälte.shoot ("path / to / sprite.png", 10, 400, 180); // vinkeln är 180 så att kula går till vänster // ... fortsättningen av uppdateringskoden går här ...

Lägga till kula beteende för kula sprites
Skyttekoden där vi sköt ett nytt punktobjekt

Laddade skott

Vissa kulor kan vara kraftfullare än andra. För att skapa ett laddat skott behöver vi en variabel som heter laddad tid, vilket ökar varje sekund spelaren håller Z ner och kommer tillbaka till noll när kula är avfyrade. Ändringarna i uppdateringskoden är som följer:

// spelare släppte bara z-tangenten om (keyReleased (KEY_Z)) om (chargedTime> 0 && chargedTime <= 5)  if (facing == "right")  hero.animate("Shoot"); hero.shoot("path/to/halfChargedBullet.png", 20, 400, 0); chargedTime = 0;  else if (facing == "left")  hero.animate("Shoot"); hero.mirrorSprite(); // this function flips the sprite horizontally hero.shoot("path/to/halfChargedBullet.png", 20, 400, 180); chargedTime = 0;   else if (chargedTime > 5) om (motsatt == "rätt") hero.animate ("Shoot"); hero.shoot ("path / to / fullChargedBullet.png", 40, 400, 0); chargedTime = 0;  annars om (motsatt == "vänster") hero.animate ("Shoot"); hero.mirrorSprite (); // den här funktionen vrider sprite horisontellt hjälte.shoot ("path / to / fullChargedBullet.png", 40, 400, 180); chargedTime = 0;  om (motsatt == "rätt") hero.animate ("standRight");  annars om (inför == "vänster") hero.animate ("standLeft"); hero.mirrorSprite (); // vi behöver ringa detta igen eftersom spritet speglades // om vi inte speglar spritet igen, kommer standLeft att se ut som stående // spelare trycker på den här tangenten ner om (keyDown (KEY_Z)) // det här är den funktion som lägger till värdet på laddad tid varje sekund // detta keyDown-block av kod kommer att köras varje bildruta, vilket är mindre än en sekund // din valfria motor ska ha ett sätt att berätta om en sekund har passerat eller inte addChargedTime (); 

Vår hjältekaraktär ny rör sig åt vänster, höger och hoppar enligt våra inmatningar, och skjuter också kulor, antingen normala, halvladdade eller helt laddade.


Genomförande fienden

Vi har nu en kontrollerbar hjälte. Låt oss kalla det Xeon för enkelhetens skull. Han kan utföra några grundläggande rörelser som att gå, hoppa och skjuta. Toppen! Men vad bra är möjligheten att skjuta utan att någonting skjuter på, eller hur? Det är därför den här gången vi ska göra vår första fiende.

Låt oss utforma våra fiendens attribut innan vi börjar koda den.

  • Hälsa: Hur många hälsa vår fiende har bestämt hur många skott (och vilken typ) som behövs för att förstöra den.

  • Power: Enemys attack power, hur mycket skada handlar det om vår spelare.

  • ShotAngle: i vilken riktning fienden skottar kullen, kan den vara kvar eller rätt eller var som helst vi vill.

Det är ganska mycket vad vi behöver för vår fiende, nu låt oss göra fiendens klass / objekt.

Enemy-klassen / -objektet är ungefär detsamma som spelarklassen / -objektet, förutom att fienden inte lyssnar på spelarens inmatning. På grund av det måste vi ersätta de delar där hjälten lyssnar på spelarens inmatning, till fiendens AI / logik.


Enemy Attack AI

Till att börja med, låt oss hantera fiendens grundläggande skytte AI. Fienden kommer skjuta på spelaren när den ser spelaren.

För att avgöra om fienden "ser" spelaren måste vi definiera en variabel för fienden objekt som kallas mot vilket är en sträng som lagrar ett av två värden, "vänster" eller "höger".

Fienden behöver också en sorts synfält, varför vi ska göra en annan variabel som heter. Om spelaren är inom detta område betyder det att fienden "ser" spelaren. Pseudokoden är som följer:

funktion boolean checkSees () if (facing == "left" && hero.x> = enemy.x - range) return true;  om (motsatt == "rätt" && hero.x <= enemy.x + range)  return true;  return false; 

checkSees () funktionen

Kanske har du märkt någonting i denna pseudokod: den betraktar inte hjältens y-position, så fienden kommer fortfarande skjuta på hjälten även om de är på plattformar med olika höjder.

För tillfället är detta tillräckligt, för att göra en siktlinjealgoritm ligger utanför ramen för denna handledning. I ditt eget spel kanske du vill lägga till en Y-tolerans i den funktionen ovanför som kontrollerar om hjälte y-positionen ligger mellan två punkter som definierar fiendens höjder.


Att göra fienden skjuter

Pseudokoden för fiendens skott är som följer:

// kan vara i uppdatering () eller någon annanstans som utförs varje ramfunktionsuppdatering () if (checkSees ()) shoot ("path / to / bulletSprite.png", enemyPower, 400, shotAngle); 

Som du kan se är funktionen fiendsspel () lika med spelarens. Det tar spriteens väg, attackkraft, kulhastighet och skjutvinkel som parametrar.


Enemy Movement AI

När byter fienden från vänster till vänster mot höger? För vår hjälte använder vi spelarens inmatning för att ändra den riktning som vår hjälte står inför. För vår fiende har vi två alternativ: använd någon form av timer för att byta motsatt riktning några sekunder medan du har fienden att stanna stilla eller få fienden att gå till en viss plats och sedan byta motsatt riktning och sedan gå till en annan plats att vända dess riktning igen.

Denna andra metod kan användas som patrullering AI. Naturligtvis kan vi bara få fienden att gå i en riktning och aldrig vända tillbaka.

Pseudokoden för den första metoden är följande:

funktionen switchingAI () // elapsedTime () är en funktion som räknar hur många sekunder som har gått sedan dess värde återställs. // Jag antar att din valfria motor har denna typ av funktionalitet om (förflutitTime ()> 4.0) om == "left") facing = "right"; shotAngle = 0;  om (motsatt == "rätt") facing = "left"; shotAngle = 180;  enemy.mirrorSprite (); // också vända sprite horisontellt återställningstid (); // återställer tiden som räknas i förflutenTime ()

Byte av AI-funktioner

Enemy Patrolling AI

För att göra patruljering AI måste vi göra två osynliga föremål som är i slutet av båda sidorna av fiendens patrullrutt och få fienden att flytta på ett annat sätt om det kolliderar med dem.


Patruljering AI

Låt oss nu skriva vår pseudokod för fiendens patrullering AI:

funktion patrulleringAI () om (motsatt == "rätt") walkRight (); // samma som i spelareobjekt / klass om (collidesWith (rightPatrolBorder)) facing = "left"; enemy.mirrorSprite ();  om (motsatt == "vänster") walkLeft (); om (collidesWith (leftPatrolBorder)) facing = "right"; enemy.mirrorSprite (); 

Efter detta kommer patrullen att patrullera mellan två punkter som vi vill ha det till.

För att ställa in vilken AI fienden använder, kommer vi att lägga till en variabel med en strängtyp för vår fiende: fiende AI. Detta bestämmer vad AI ska använda varje ram, som så:

om (fiendeAI == "byta") switchingAI ();  annars om (fiendeAI == "patrullering") patrollingAI (); 

Naturligtvis kan du lägga till fler fienden AI-typ om du vill.


Shot Variation

Låt oss fortsätta hur vi kan göra skottvariationer för både spelaren och fienden. Vi gör skottvariationer genom att ändra två saker: skyttelvinkeln och antalet skottskott.

På det här sättet kan vi göra ett enkelt kula, eller ett treriktat kula. Innan vi gör detta kommer vi att göra en annan variabel till fiendens objekt / klass med namnet shotAI, vilket är en sträng. Vi använder det här i vår checkSees () kontrollerar om block, där fienden skottar. Ändringarna i det kodblocket kommer att vara så här:

// kan vara i uppdatering () eller någon annanstans som utförs varje ramfunktionsuppdatering () if (checkSees ()) if (shotAI == "simple") shoot ("path / to / bulletSprite.png" , 400, shotAngle);  om (shotAI == "threeBullets") shootThreeBullets (); 

Självklart är namnet på AI och vilken typ av skott fienden skulle skjuta upp till dig, det här är bara ett exempel.

Låt oss nu gräva djupare in i vad som finns inom shootThreeBullets () -funktionen.

Function shootThreeBullets () if (facing == "right") shoot ("path / to / bulletSprite.png", enemyPower, 400, 0); // denna kula går rakt till rätt skott ("path / to / bulletSprite.png", enemyPower, 400, 330); // detta går upp med 30 grader skjuta ("bana / till / bulletSprite.png", fiendePower, 400, 30); // detta går ner med 30 grader om (motsatt == "vänster") shoot ("path / to / bulletSprite.png", enemyPower, 400, 180); // denna kula går rakt till vänsterskottet ("path / to / bulletSprite.png", enemyPower, 400, 210); // detta går upp med 30 grader skott ("bana / till / bulletSprite.png", fiendePower, 400, 150); // detta går ner med 30 grader

Om du är osäker på varför 0 går till höger och 180 går till vänster beror det på att riktningen 0 grader går rakt till höger på skärmen, 90 grader går ner till skärmens undersida och så vidare tills den träffar 360 grader. När du väl vet vilket värde som går, kan du skapa din egen skottvariation.

Vi kan också göra en shotAI-variabel för spelaren, men jag föredrar att vi heter det valdaShot, eftersom vår spelare kommer att välja kula istället för programmerad från början. T

han logik i megaman är varje gång megaman besegrar en chef, får han den här chefsmakten som ett nytt skott. Jag ska försöka återskapa den logiken. För att göra detta behöver vi en array som innehåller spelarens skott, inklusive vanliga skott. Pseudokoden är så här:

var shotArr = ["normalShot", "boss1", "boss2"]; var shotIndex = 0; var selectedShot = "normalShot"; funktionsuppdatering () // det här är kvarterskoden i spelarens uppdateringsfunktion där spelaren skottar en kula // den här funktionen ändrar kula som spelaren skottar ändringsbullet (); // spelare tryck denna tangent en gång om (keyPressed (KEY_Z)) if (mot = = "right") hero.animate ("Shoot"); om (selectedShot == "normalShot") hero.shoot ("path / to / sprite.png", 10, 400, 0);  other if (selectedShot == "boss1") // lägg till koder för att skjuta den typ av skott som spelaren fick efter att ha besegrat chefen 1 funktionen ändraBullet () // ändrar skottetIndex baserat på knapptryckt om (tangenttryckt (KEY_E)) shotIndex + = 1;  om (keyPressed (KEY_Q)) shotIndex - = 1;  // fix shotIndex om det inte är i array längd om (shotIndex == shotArr.length) shotIndex = 0;  om (shotIndex < 0)  shotIndex = shotArr.length -- 1;  selectedShot = shotArr[shotIndex]; 

Vi måste hålla reda på två nya variabler:


Så här lägger du till en array i Construct 2

Vi kommer att skjuta nya element till shotArr när spelaren besegrar en chef.


Uppgradering av spelarens kulor

Precis som fiendens shootThreeBullet () kan du vara kreativ och skapa egna skottvariationer. Eftersom det här är hjälten kula, låt oss ge det något speciellt.

Låt oss göra en typ av skott för att vara effektiv mot en viss chef, så att den hanterar mer skada. För att göra detta skapar vi en variabel för bulletobjektet heter strongAgainst som är en annan strängtypsvariabel som innehåller namnet på chefen som den här kollan är effektiv mot. Vi kommer att lägga till dessa erbjudanden mer skada funktionalitet när vi diskuterar chefsdelen av spelet.


Hälsa och död

Det är här som alla skottvariationer vi gör verkligen börjar göra något. Här är vår hjälte skada och döda fienden, och tvärtom.

Till att börja med, låt oss göra en variabel för både hjälten och fiendens objekt, namnet hälsa som är ett int, och en annan variabel bara för hjälten som heter liv. Låt oss ta en titt på pseudokoden:

om (bullet.collidesWith (hero)) hero.health - = bullet.power; createExplosion (); // för nu har vi ingen explosionssprit, så det här kommer att fungera som en påminnelse // kontrollera om hjälten är död om (hjälte.health <= 0)  hero.lives -= 1; // decreases hero's total number of lives. destroyHero(); 

Vi kommer att göra samma pseudokod för att skada fiender, som så:

om (bullet.collidesWith (fiende)) enemy.health - = bullet.power; createExplosion ();  om (fiende.health <= 0)  destroyEnemy(); 

Hantera kollisioner mellan spelare och fiende

Health Bar GUI

Nu, om jag lämnar den då skulle det inte vara intressant. Så ska jag göra en rektangelsprite i det övre vänstra hörnet av skärmen som fungerar som vår hjälte hälsokar.

Den här hälsorbetslängden kommer att förändras beroende på vår hjältes nuvarande hälsa. Formeln för att ändra hälsobarens längd är följande:

// detta är i uppdateringsfunktionen healthBar.width = (hero.health / hero.maxHealth) * 100;

Vi behöver en mer variabel för vår hjälte som heter maxHealth; vår hjälte är fullt hälsovärde. För tillfället kan detta värde inte ändras men kanske i framtiden kan vi skapa ett objekt som ökar mängden hjälte maxHealth.


Skapa spelvärlden

Nu när vi har skapat vår hjälte, fiende och skottvariationer, måste vi göra flera nivåer och chefer.

Att ha flera nivåer innebär att någon gång i spelet kommer spelaren att nå en eller flera kontrollpunkter som byter spel från nivå 1-1 till nivå 1-2 till nivå 1-3 och så vidare tills de når chefen.

När spelaren dör någonstans på nivå 1-2 behöver han eller hon inte spela hela vägen tillbaka från början av nivå 1-1. Hur gör jag det här? Först ska vi göra nivån, jag kommer inte att förklara mycket om nivådesign, men här är exemplenivån i Construct 2.


provnivå design

Bilden längst upp till vänster är HUD-skiktet. Det kommer att rulla, efter hjälten när spelet spelas.


Dörrar och Checkpoints

En sprite du bör vara uppmärksam på är den gröna spriteen i övre högra delen av nivån. Det är kontrollpunkten på den här nivån när hjälten kolliderar med det, vi överför spelet till nivå 1-2.

För att hantera flera nivåer behöver vi tre variabler: currentLevel, levelName och nextLevel.

CurrentLevel-variabeln skapas i hjälteobjektet / klassen. Nivånamnet skapas i spelets scennivå (nivå) för varje nivå. NextLevel-variabeln skapas i det gröna spritobjektet.

Logiken är som följer: När hjälten kolliderar med den gröna spriten (jag kallar den greenDoor) kommer vi att ändra vår nivå till spelscenen i vilken nivånamn är densamma som nästaLevel-variabeln. När vi ändrar nivån kommer vi att ändra värdet på hjälteens nuvarande variabel till samma som spelplatsens nivånamn. Här är pseudokoden:

// detta är inspelningsuppdateringsfunktionen för spel om (hero.collidesWith (greenDoor)) changeLevelTo (greenDoor.nextLevel); 

Ändra nivåer när vi rör den gröna dörren

Initialisering av en ny nivå

Här är pseudokoden för att hantera när nästa nivå är laddad och redo att spela.

// detta är den funktion som utlöses när den nya nivån laddas funktion onStart () hero.currentLevel = scene.LevelName; hero.x = startPos.x; hero.y = startPos.y; 

Spelarens startpositioner

Nu när vi har ändrats till en ny nivå kommer jag att förklara det orange ordet bakom vår hjälte i nivådesignbilden ovan. Den orange sprite är ett objekt som jag kallar startPos. Den används för att markera startpositionen för varje nivå.

Vi hänvisar till det här objektet när hjälten just ändrade nivåer eller dog, så att vi vet var han ska gissa honom.

Här är pseudokoden för hantering när hjälten dör:

// funktionen som utlöses när hjälten förstörs Funktion onDestroyed () // återupplär hjälte om hjälten fortfarande har liv. Om (hero.lives> 0) var newHero = new Hero (); newHero.x = startPos.x; newHero.y = startPos.y; 

Nu kan vi ha flera nivåer och vi kan också respawn hjälten efter att han dör.

Du kan skapa så många nivåer som du vill, eller kanske skapa två GreenDoor-objekt på en nivå som en av dem går tillbaka till nivå 1-1 om spelaren löser ett pussel på fel sätt.


Boss Battles

Det är äntligen dags att genomföra chefen själv. Att göra en chef nivå är lika enkelt som att göra en annan nivå som kommer att koka en chef istället för vanliga fiender.

Den svåra delen är att skapa AI för chefen eftersom varje chef kommer att ha en unik AI. Så för att göra det lätt att förstå och duplicera för många chefer, ska jag göra chefen AI beroende av tiden efter att de har skottats. Vilket innebär att de ska göra A i x sekunder, sedan byta till B i y sekunder och sedan göra C för z sekunder innan de återvänder till A. Pseudokoden kommer att se något ut så här:

// den här koden är inne i chefsuppdateringsfunktionen, så den exekveras varje ram om (förflutenTime ()> 2.0) // detta om blocket exekveras i 3 sekunder, eftersom skillnaden i tid med if-blocket nedan // är tre sekunder. BossShot1 (); // skottvariation som ska utföras denna gång annars om (förflutitTime ()> 5.0) bossShot2 ();  annars om (förflutitTime ()> 6,0) bossShot3 ();  om (elapsedTime ()> 7.0) // återställ tiden så chefen kör den första åtgärden igen återställerTime (); 

Definitionen av chefsfotograferingsfunktionerna är nedan. Gärna ändra det för att passa vad du vill ha för en chefs kamp:

funktion bossShot1 () // en enkel rak skott bossEnemy.shoot ("path / to / bullet / sprite.png", bossPower, 400, shotAngle); // shotAngle är 180 funktion bossShot2 () // en tre riktning kulor sköt bossEnemy.shoot ("path / to / bullet / sprite.png", bossPower, 400, shotAngle); bossEnemy.shoot ("path / to / bullet / sprite.png", bossPower, 400, shotAngle + 30); bossEnemy.shoot ("path / to / bullet / sprite.png", bossPower, 400, shotAngle - 30);  funktion bossShot3 () // för den här ska jag göra ett cirkelslag, så kulorna kommer att bilda en cirkel för (var i = 0; i <= 9; i++)  bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, 36 * i); // change the shotAngle  

Det är upp till dig att lägga skottvariationer till chefs rutin. De variabler som används av chefsfiberobjektet är desamma som fiendens objekt, förutom att chefer inte använder fiendenAI och shotAI-variablerna, eftersom båda kommer att hanteras i if-blocket ovan beroende på den förflutna tiden.

Bossfiender har också en variabel som fiendens föremål inte gör. Det kallas belöningShot, vilket är en sträng. Denna variabel har namnet på en bossskott som spelaren kommer att få efter att ha besegrat chefen (de i skottArr-arrayvariabeln).

Detta kommer att göra det möjligt för spelaren att "lära" chefsangreppet som förklarats tidigare. För att lägga till denna skotttyp i arrayen av spelarspel måste vi lägga till följande kod efter att fienden dör:

funktion bossShot1 () // en enkel rak skott bossEnemy.shoot ("path / to / bullet / sprite.png", bossPower, 400, shotAngle); // shotAngle är 180 funktion bossShot2 () // en tre riktning kulor sköt bossEnemy.shoot ("path / to / bullet / sprite.png", bossPower, 400, shotAngle); bossEnemy.shoot ("path / to / bullet / sprite.png", bossPower, 400, shotAngle + 30); bossEnemy.shoot ("path / to / bullet / sprite.png", bossPower, 400, shotAngle - 30);  funktion bossShot3 () // för den här ska jag göra ett cirkelslag, så kulorna kommer att bilda en cirkel för (var i = 0; i <= 9; i++)  bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, 36 * i); // change the shotAngle  

För att implementera ytterligare kultyper för spelarna att njuta av, behöver du bara lägga till lämplig kod för varje belöning. Skapa din skapning, precis som vi gjorde tidigare.


Grattis!

Du har slutfört min handledning om hur du skapar ett Megaman-liknande Metroidvania-plattformsspel i Construct 2. Resten bygger bara upp ditt spel baserat på det du har lärt dig här för att göra det till ditt eget. Lägg till fler fientliga typer, fler nivåer, powerups och mer. Lycka till och ha kul.