Ti guiderò attraverso la creazione di un gioco sparatutto / platform di ispirazione Megaman. Saremo più concentrati sugli aspetti di ripresa del gameplay piuttosto che sulla piattaforma. In questo tutorial userò Construct 2 come strumento per creare il gioco, ma spiegherò la logica usando pseudocodice in modo da poter seguire questo tutorial in qualsiasi lingua o motore di tua scelta.
Post correlatiPer concentrarmi sull'implementazione del gameplay, non spiegherò tutte le funzionalità di Construct 2; Presumo che tu conosca le basi come caricare uno sprite, una collisione di base o suonare suoni. Detto questo, iniziamo a fare il gioco.
Per prima cosa, abbiamo bisogno di avere sprite per il nostro gioco. Per fortuna, opengameart ci ha coperti con la loro meravigliosa collezione di giochi legali. Abbiamo bisogno di quattro gruppi di sprite; un eroe, un nemico e tessere per piattaforme.
Per usarli in Costrutto 2, ritagliare gli sprite dell'eroe in singoli fotogrammi usando GIMP.
Userò il comportamento della piattaforma di Construct 2 per il resto del tutorial in modo che possa concentrarmi sulla parte di ripresa e AI del gioco, che era l'obiettivo principale di questo tutorial.
Se stai lavorando in un'altra lingua o vuoi implementare il tuo movimento di base per il platform invece di usare il comportamento integrato. È necessario utilizzare il codice solo in questa sezione se non si intende utilizzare il comportamento predefinito di Construct 2.
Per iniziare, dobbiamo considerare tre modi in cui il nostro eroe può muoversi; camminare a destra, camminare a sinistra o saltare. Ogni frame, aggiorniamo la simulazione del gioco.
numero moveSpeed = 50; function update () moveHero ();
Per aggiornare il personaggio del giocatore, implementiamo movimenti di base come questo:
function moveHero () // player sta premendo questo tasto down if (keyDown (KEY_LEFT)) hero.x - = moveSpeed * deltaTime; hero.animate ( "walkLeft"); if (keyDown (KEY_RIGHT)) hero.x + = moveSpeed * deltaTime; hero.animate ( "walkRight"); if (keyDown (KEY_UP)) hero.jump (); hero.animate ( "salto"); // una chiave che era appena stata chiusa se (keyReleased (KEY_LEFT)) hero.animate ("standLeft"); if (keyReleased (KEY_RIGHT)) hero.animate ("standRight");
Io uso un'altra funzione per fare il salto perché saltare non è solo questione di cambiare il valore y ma anche calcolare la gravità. Avremo anche una funzione che ascolta se è stata appena rilasciata una chiave, per restituire l'animazione dell'eroe a un'animazione permanente.
Parliamo di come far saltare il giocatore. L'eroe dovrà sapere se sta saltando o meno, e anche se sta cadendo o meno. Quindi dichiareremo due nuove variabili: isJumping e isFalling. Per impostazione predefinita, entrambi sono falsi, il che significa che l'eroe è in piedi su una piattaforma.
Per eseguire un salto, dobbiamo prima controllare se entrambi i valori sono falsi e quindi rendere vero isjump.
Funzione jump () if (! IsJumping &&! IsFalling) isJumping = True
Perché l'eroe possa saltare, abbiamo bisogno di una variabile chiamata jumpPower e gravità. Il valore predefinito di jumpPower è -20 e la gravità è 1. La logica è di aggiungere il valore della potenza di salto alla posizione Y dell'eroe e aggiungere gravità per saltare il valore della potenza.
Facciamo questo ogni segno di spunta. Forse questa non è la fisica gravitazionale più realistica che ci sia, ma i giochi non hanno bisogno di essere realistici, hanno solo bisogno di essere credibili, ecco perché alcuni giochi hanno un super salto umano e un doppio salto. Il codice sottostante appartiene alla funzione di aggiornamento.
Se (isJumping || isFalling) hero.y + = hero.jumpPower; hero.jumpPower + = hero.gravity; // alla fine il salto di potenza sarà maggiore di zero e ciò significa che l'eroe sta cadendo se (hero.jumpPower> = 0) isJumping = False; isFalling = True; // per fermare l'autunno, fai qualcosa quando l'eroe si sovrappone alla piattaforma se (hero.isOverlapping (platform1)) // platform1 è la piattaforma su cui il nostro eroe può calpestare // reimposta la variabile sul loro valore predefinito isJumping = False; isFalling = False; hero.jumpPower = -20; // e poi c'è la caduta libera, quando il giocatore cade oltre il bordo di una piattaforma se (! 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;
Il comportamento della piattaforma incorporata di Costrutto 2 replica il codice di esempio precedente, a cui viene fornito solo l'aiuto di coloro che lavorano in un'altra lingua.
Ora arriva la parte di ripresa del gioco. Nella serie Megaman ci sono tre tipi di colpi: colpi normali, colpi caricati e colpi di energia del boss.
I colpi normali sono auto esplicativi. I colpi caricati sono colpi che vengono caricati prima di essere rilasciati, questi colpi caricati sono di due tipi: semi caricati e completamente carichi. Questi attacchi caricati sono più forti dei colpi normali, con la carica completa diventano i più forti.
I colpi di energia del boss sono colpi con potenza che il giocatore ha acquisito dopo aver sconfitto ciascun boss. Il danno è lo stesso del normale ma hanno proprietà speciali che i colpi normali non hanno.
Ora che conosciamo il tipo di ogni sparo, iniziamo a farli. Per prima cosa vediamo la logica dietro a come usiamo ogni colpo. Qui assumiamo che il pulsante Z sulla tastiera sia usato per sparare un colpo. Implementeremo due comportamenti diversi:
Ora iniziamo a programmare. Poiché il nostro eroe può sparare a destra e sinistra, dobbiamo sapere in quale direzione si trova attualmente. Dichiariamo una nuova variabile chiamata facing che memorizza un valore stringa se l'eroe è rivolto a sinistra oa destra.
String facing = "right"; // quale direzione l'eroe sta attualmente affrontando function moveHero () // player sta premendo questo tasto down if (keyDown (KEY_LEFT)) hero.x - = moveSpeed * deltaTime; hero.animate ( "walkLeft"); facing = "left"; if (keyDown (KEY_RIGHT)) hero.x + = moveSpeed * deltaTime; hero.animate ( "walkRight"); facing = "right"; // ... la continuazione della funzione moveHero () va qui function update () // ... il codice di aggiornamento che abbiamo precedentemente scritto va qui ... // player premi questo tasto una volta se (keyPressed (KEY_Z)) if (facing == "right") hero.animate ("Spara"); // aggiungeremo qui la funzione di ripresa else if (facing == "left") hero.animate ("Spara"); hero.mirrorSprite (); // questa funzione ribalta lo sprite orizzontalmente if (keyReleased (KEY_Z)) if (facing == "right") hero.animate ("standRight"); else if (facing == "left") hero.animate ("standLeft"); hero.mirrorSprite (); // dobbiamo chiamarlo di nuovo perché lo sprite era speculare // se non specifichiamo di nuovo lo sprite, standLeft sarà simile a standRight
Prima di sparare a un proiettile, dobbiamo esaminare le proprietà del proiettile:
Queste proprietà saranno diverse per ogni punto. In particolare, le proprietà di potenza saranno diverse. La proprietà angle è normalmente solo uno dei due valori; se il proiettile è sparato a destra o a sinistra, a meno che non si tratti di un proiettile di energia del boss che può sparare con un'angolazione unica.
Le variazioni dei colpi saranno discusse più tardi, quindi ora coprirò solo i colpi base. Quello che segue è il pezzo di codice che spara un proiettile.
// prima, creiamo una funzione che crea un nuovo proiettile Funzione shoot (string pathToSprite, number bulletPower, number bulletSpeed, number bulletAngle) myBullet = new Bullet (pathToSprite); myBullet.power = bulletPower; // la classe o l'oggetto del proiettile ha due variabili private che lo spostano in base al suo angolo // più spiegazione a queste due linee richiede più matematica, quindi ho scelto di non spiegare // presumo che il tuo motore di scelta abbia un modo per spostare un oggetto in base al suo angolo ySpeed = Math.sin (bulletAngle) * bulletSpeed; xSpeed = Math.cos (bulletAngle) * bulletSpeed; // questa è la funzione della classe Bullet chiamata ogni frame, questo sposta il punto elenco in base alla sua funzione di angolo moveBullet () x + = xSpeed * deltaTime; y + = ySpeed * deltaTime; // e questa è la modifica alla nostra funzione update () funzione update precedente () // ... il codice di aggiornamento che abbiamo precedentemente scritto va qui ... // player premi questo tasto una volta se (keyPressed (KEY_Z)) if ( di fronte == "right") hero.animate ("Spara"); hero.shoot ("path / to / sprite.png", 10, 400, 0); else if (facing == "left") hero.animate ("Spara"); hero.mirrorSprite (); // questa funzione ribalta lo sprite orizzontalmente hero.shoot ("path / to / sprite.png", 10, 400, 180); // l'angolo è 180 in modo che il proiettile vada a sinistra // ... la continuazione del codice di aggiornamento va qui ...
Alcuni proiettili possono essere più potenti di altri. Per creare un tiro carico abbiamo bisogno di una variabile chiamata ChargeTime, che aumenterà ogni secondo che il giocatore tiene premuto Z, e tornerà a zero quando il proiettile è sparato. Le modifiche al codice di aggiornamento sono le seguenti:
// player ha appena rilasciato il tasto z if (keyReleased (KEY_Z)) if (chargeTime> 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) if (facing == "right") hero.animate ("Spara"); hero.shoot ("path / to / fullChargedBullet.png", 40, 400, 0); chargedTime = 0; else if (facing == "left") hero.animate ("Spara"); hero.mirrorSprite (); // questa funzione ribalta lo sprite orizzontalmente hero.shoot ("path / to / fullChargedBullet.png", 40, 400, 180); chargedTime = 0; if (facing == "right") hero.animate ("standRight"); else if (facing == "left") hero.animate ("standLeft"); hero.mirrorSprite (); // dobbiamo chiamarlo di nuovo perché lo sprite era speculare // se non specifichiamo nuovamente lo sprite, standLeft sarà simile a standRight // player sta premendo questo tasto giù se (keyDown (KEY_Z)) // questa è la funzione che aggiunge il valore di ChargeTime ogni secondo // questo blocco di codice keyDown verrà eseguito ogni frame, che è inferiore a un secondo // il tuo motore di scelta dovrebbe avere un modo per dire se un secondo è passato o no addChargedTime ();
Le nuove mosse del nostro eroe si spostano a sinistra, a destra e salgono secondo il nostro input, e sparano anche proiettili, sia normali, semi-caricati o completamente caricati.
Ora abbiamo un eroe controllabile. Chiamiamolo Xeon per semplicità. Può eseguire alcuni movimenti di base come camminare, saltare e sparare. È fantastico! Ma a che serve la capacità di scattare senza qualcosa da sparare, giusto? Ecco perché questa volta faremo il nostro primo nemico.
Progettiamo gli attributi del nostro nemico prima di iniziare a codificarlo.
Salute: Quanti salute il nostro nemico ha determinato quanti colpi (e che tipo) sono necessari per distruggerlo.
Potere: potere d'attacco del nemico, quanto danno infligge al nostro giocatore.
ShotAngle: in quale direzione il nemico spara al proiettile, può essere lasciato a destra, a destra o ovunque vogliamo.
Questo è praticamente ciò di cui abbiamo bisogno per il nostro nemico, ora facciamo la classe / oggetto nemico.
La classe / oggetto nemico è praticamente uguale alla classe / oggetto del giocatore, ad eccezione del nemico che non ascolta l'input del giocatore. Per questo motivo abbiamo bisogno di sostituire le parti in cui l'eroe ascolta l'input del giocatore, l'IA / logica nemica.
Per i principianti, gestiamo l'IA base di tiro del nemico. Il nemico sparerà al giocatore quando vedrà il giocatore.
Per determinare se il nemico "vede" il giocatore, dovremo definire una variabile per l'oggetto nemico chiamato fronte che è una stringa che memorizza uno dei due valori, "sinistra" o "destra".
Il nemico ha anche bisogno di un certo tipo di raggio visivo, motivo per cui creeremo un'altra variabile chiamata intervallo. Se il giocatore si trova all'interno di questo intervallo, significa che il nemico "vede" il giocatore. Lo pseudocodice è il seguente:
function boolean checkSees () if (facing == "left" && hero.x> = enemy.x - range) return true; if (facing == "right" && hero.x <= enemy.x + range) return true; return false;
funzione checkSees ()
Forse hai notato qualcosa in questo pseudocodice: non considera la posizione dell'eroe, quindi il nemico continuerà a sparare all'eroe anche se sono su piattaforme con altezze diverse.
Per ora questo è sufficiente, perché fare un algoritmo di linea di vista è al di fuori dello scopo di questo tutorial. Nel tuo gioco, potresti voler aggiungere una tolleranza Y in quella funzione in alto che controllerà se la posizione y dell'eroe si trova tra due punti che definiscono l'altezza del nemico.
Lo pseudocodice per le sparatorie nemiche è il seguente:
// può essere in update () o da qualche altra parte che viene eseguita ogni funzione di aggiornamento frame () if (checkSees ()) shoot ("path / to / bulletSprite.png", enemyPower, 400, shotAngle);
Come puoi vedere, la funzione shoot () del nemico è simile a quella del giocatore. Prende il percorso dello sprite, la potenza di attacco, la velocità del proiettile e l'angolo di tiro come parametri.
Quando il nemico passa dal lato sinistro a quello destro? Per il nostro eroe, utilizziamo l'input del giocatore per cambiare la direzione che il nostro eroe deve affrontare. Per il nostro nemico abbiamo due opzioni: usa un qualche tipo di timer per cambiare direzione di fronte ogni pochi secondi mentre fai in modo che il nemico si fermi o fai in modo che il nemico cammini in un determinato punto e poi cambi direzione e poi cammini in un altro punto per cambiare di nuovo la sua direzione di fronte.
Questo secondo metodo può essere utilizzato come AI di pattugliamento. Certo, possiamo solo far camminare il nemico in una direzione e non tornare indietro.
Lo pseudocodice per il primo metodo è il seguente:
function switchingAI () // elapsedTime () è una funzione che conta quanti secondi sono passati da quando il suo valore è resettato // Presumo che il tuo motore di scelta abbia questo tipo di funzionalità se (elapsedTime ()> 4.0) if (facing == "left") facing = "right"; shotAngle = 0; if (facing == "right") facing = "left"; shotAngle = 180; enemy.mirrorSprite (); // capovolge anche lo sprite orizzontalmente resetTime (); // ripristina il tempo che conta in elapsedTime ()
Per creare l'IA pattuglia, dobbiamo creare due oggetti invisibili che si trovano alla fine di entrambi i modi di pattugliamento del nemico, e fare in modo che il nemico si muova in un altro modo se si scontra con loro.
Ora scriviamo il nostro pseudocodice per l'IA pattuglia del nemico:
function patrollingAI () if (facing == "right") walkRight (); // uguale a quello in oggetto / classe giocatore se (collidesWith (rightPatrolBorder)) facing = "left"; enemy.mirrorSprite (); if (facing == "left") walkLeft (); if (collidesWith (leftPatrolBorder)) facing = "right"; enemy.mirrorSprite ();
Dopo questo, il nemico pattuglia tra due punti come lo vogliamo.
Per impostare quale IA utilizza il nemico, aggiungeremo un'altra variabile con un tipo di stringa per il nostro nemico: IA nemica. Questo determinerà quale AI usare ogni fotogramma, in questo modo:
if (enemyAI == "switching") switchingAI (); else if (enemyAI == "pattugliamento") patrollingAI ();
Naturalmente puoi aggiungere più tipi di IA nemici se vuoi.
Andiamo avanti su come possiamo fare le varianti di tiro sia per il giocatore che per il nemico. Stiamo facendo delle varianti di tiro cambiando due cose: l'angolo di tiro e il numero di proiettili.
In questo modo possiamo fare un semplice colpo di proiettile, o un colpo di proiettile a tre direzioni. Prima di fare questo, creeremo un'altra variabile per l'oggetto / classe nemico chiamato shotAI, che è una stringa. Lo useremo nei nostri checkSees () controllando se il blocco, dove spara il nemico. Le modifiche a quel blocco di codice saranno così:
// può essere in update () o da qualche altra parte che viene eseguito ogni funzione di aggiornamento frame () if (checkSees ()) if (shotAI == "simple") shoot ("percorso / a / bulletSprite.png", enemyPower , 400, shotAngle); if (shotAI == "threeBullets") shootThreeBullets ();
Naturalmente, il nome dell'IA e il tipo di tiro che il nemico dovrebbe sparare spetta a te, questo è solo un esempio.
Ora, approfondiamo cosa c'è dentro la funzione shootThreeBullets ().
Funzione shootThreeBullets () if (facing == "right") shoot ("percorso / a / bulletSprite.png", enemyPower, 400, 0); // questo proiettile va dritto alla ripresa giusta ("percorso / to / bulletSprite.png", enemyPower, 400, 330); // questo aumenta di 30 gradi ("path / to / bulletSprite.png", enemyPower, 400, 30); // questo scende di 30 gradi se (di fronte == "left") shoot ("path / to / bulletSprite.png", enemyPower, 400, 180); // questo proiettile va dritto a sinistra ("percorso / a / bulletSprite.png", enemyPower, 400, 210); // questo aumenta di 30 gradi ("path / to / bulletSprite.png", enemyPower, 400, 150); // questo va giù di 30 gradi
Se non sei sicuro del motivo per cui 0 va a destra e 180 a sinistra, è perché la direzione di 0 gradi va direttamente sul lato destro dello schermo, 90 gradi vanno verso il lato inferiore dello schermo, e così via fino a quando non colpisce 360 gradi. Una volta che sai quale valore va dove, puoi creare la tua variante di tiro.
Possiamo anche creare una variabile AI per il giocatore, ma preferisco chiamarla SelectedShot, perché il nostro giocatore sceglierà il proiettile anziché programmato dall'inizio. T
La logica in megaman è ogni volta che Megaman sconfigge un boss, ottiene il potere di quel boss come un nuovo colpo. Cercherò di ricreare quella logica. Per fare ciò abbiamo bisogno di un array che contenga i colpi del giocatore, inclusi i colpi normali. Lo pseudocodice è così:
var shotArr = ["normalShot", "boss1", "boss2"]; var shotIndex = 0; var selectedShot = "normalShot"; function update () // questo è il blocco di codice nella funzione di aggiornamento del giocatore dove il giocatore spara un proiettile // questa funzione cambia il proiettile sparato dal giocatore cambiaBullet (); // player premi questo tasto una volta se (keyPressed (KEY_Z)) if (facing == "right") hero.animate ("Spara"); if (selectedShot == "normalShot") hero.shoot ("path / to / sprite.png", 10, 400, 0); else if (selectedShot == "boss1") // aggiungi codici per sparare il tipo di colpo che il giocatore ha ricevuto dopo aver sconfitto il boss 1 function changeBullet () // cambia shotIndex in base al pulsante premuto if (keyPressed (KEY_E)) shotIndex + = 1; if (keyPressed (KEY_Q)) shotIndex - = 1; // corregge shotIndex se è fuori dalla lunghezza dell'array if (shotIndex == shotArr.length) shotIndex = 0; if (shotIndex < 0) shotIndex = shotArr.length -- 1; selectedShot = shotArr[shotIndex];
Dobbiamo tenere traccia di due nuove variabili:
Spingeremo nuovi elementi su shotArr quando il giocatore sconfiggerà un boss.
Proprio come lo shootThreeBullet () del nemico, puoi essere creativo e creare le tue varianti di tiro. Dato che questo è il proiettile dell'eroe, diamo qualcosa di speciale.
Facciamo in modo che un tipo di tiro sia efficace contro un boss specifico, in modo che infligga più danni. Per fare ciò, creeremo una variabile per l'oggetto bullet denominato strongAgainst che è un'altra variabile di tipo stringa che contiene il nome del boss con cui questo punto elenco è efficace. Aggiungeremo questa offerta più funzionalità di danno quando discuteremo la parte principale del gioco.
Questo è il punto in cui tutte le varianti di tiro che facciamo davvero iniziano ad avere importanza. Qui è dove il nostro eroe danneggia e uccide il nemico, e viceversa.
Per cominciare, creiamo una variabile per l'eroe e l'oggetto nemico, denominata salute che è un int e un'altra variabile solo per l'eroe chiamato vite. Diamo un'occhiata allo pseudocodice:
se (bullet.collidesWith (eroe)) hero.health = = bullet.power; createExplosion (); // per ora non abbiamo uno sprite dell'esplosione, quindi questo fungerà da promemoria // controlla se l'eroe è morto se (hero.health <= 0) hero.lives -= 1; // decreases hero's total number of lives. destroyHero();
Faremo lo stesso pseudocodice per danneggiare i nemici, in questo modo:
se (bullet.collidesWith (nemico)) enemy.health = = bullet.power; createExplosion (); if (enemy.health <= 0) destroyEnemy();
Ora, se lo lascio a quel punto, non sarebbe interessante. Quindi creerò uno sprite rettangolare nell'angolo in alto a sinistra dello schermo che funge da barra della salute del nostro eroe.
La durata di questa barra della salute cambierà a seconda della salute attuale del nostro eroe. La formula per cambiare la lunghezza della barra della salute è questa:
// questo è nella funzione di aggiornamento healthBar.width = (hero.health / hero.maxHealth) * 100;
Abbiamo bisogno di un'altra variabile per il nostro eroe chiamata maxHealth; valore di salute pieno del nostro eroe. Per ora questo valore non può essere cambiato ma forse in futuro possiamo creare un oggetto che aumenti la quantità di maxHealth dell'eroe.
Ora che abbiamo creato le nostre varianti di eroe, nemico e tiro, dobbiamo creare più livelli e boss.
Avere più livelli significa che a un certo punto del gioco il giocatore raggiungerà uno o più checkpoint che cambieranno la partita dal livello 1-1 al livello 1-2 al livello 1-3 e così via fino a raggiungere il boss.
Quando il giocatore muore da qualche parte nel livello 1-2, non ha bisogno di ripetere il gioco dall'inizio del livello 1-1. Come faccio a fare questo? Per prima cosa creeremo il livello, non spiegherò molto sulla progettazione dei livelli, ma ecco il livello di esempio in Construct 2.
L'immagine nell'angolo in alto a sinistra è il livello HUD. Scorrerà, seguendo l'eroe quando viene giocata la partita.
Uno sprite a cui dovresti prestare attenzione è lo sprite verde nella parte in alto a destra del livello. È il punto di controllo in questo livello quando l'eroe collide con esso trasferiremo il gioco al livello 1-2.
Per gestire più livelli sono necessarie tre variabili: currentLevel, levelName e nextLevel.
La variabile currentLevel viene creata nell'oggetto / classe hero. Il levelName viene creato nell'oggetto (livello) della scena di gioco per ogni livello. La variabile nextLevel viene creata nell'oggetto sprite verde.
La logica è la seguente: quando l'eroe si scontra con lo sprite verde (io lo chiamo greenDoor), cambieremo il nostro livello nella scena di gioco in cui levelName è uguale alla variabile nextLevel. Dopo aver cambiato il livello, cambieremo il valore della variabile currentLevel dell'eroe allo stesso livello di livello della scena di gioco. Ecco lo pseudocodice:
// questo è all'interno della funzione di aggiornamento del gioco se (hero.collidesWith (greenDoor)) changeLevelTo (greenDoor.nextLevel);
Ecco lo pseudocodice per affrontare quando il prossimo livello è caricato e pronto per giocare.
// questa è la funzione che viene attivata quando il nuovo livello viene caricato function onStart () hero.currentLevel = scene.LevelName; hero.x = startPos.x; hero.y = startPos.y;
Ora che siamo passati a un nuovo livello, spiegherò lo sprite arancione dietro il nostro eroe nell'immagine del livello superiore qui sopra. Quello sprite arancione è un oggetto che chiamo startPos. È usato per segnare la posizione di partenza di ogni livello.
Ci riferiamo a questo oggetto quando l'eroe ha appena cambiato livello o è morto, in modo da sapere dove generarlo.
Ecco lo pseudocodice per la gestione quando l'eroe muore:
// la funzione che viene attivata quando l'eroe viene distrutto. Funzione onDestroyed () // resuscita l'eroe se l'eroe ha ancora delle vite. If (hero.lives> 0) var newHero = new Hero (); newHero.x = startPos.x; newHero.y = startPos.y;
Ora possiamo avere più livelli e possiamo anche rigenerare l'eroe dopo che muore.
Puoi creare tutti i livelli che vuoi, o magari persino creare due oggetti greenDoor in un livello che uno di loro ritorna al livello 1-1 se il giocatore risolve un enigma in modo sbagliato.
È finalmente giunto il momento di implementare il boss stesso. Fare un livello boss è semplice come fare un altro livello che genererà un boss invece dei normali nemici.
La parte difficile è creare AI per il boss, perché ogni boss avrà un'IA unica. Quindi, per renderlo più facile da capire e duplicare per molti capi, ho intenzione di far dipendere l'intelligenza artificiale al boss dal momento in cui vengono generati. Il che significa che stanno per fare A per x secondi, quindi passare a B per y secondi, quindi fare C per z secondi prima di tornare a A. Lo pseudocodice avrà un aspetto simile al seguente:
// questo codice si trova all'interno della funzione di aggiornamento del boss, quindi viene eseguito ogni frame se (elapsedTime ()> 2.0) // questo se il blocco viene eseguito per 3 secondi, perché la differenza di tempo con il blocco if di seguito // è tre secondi. BossShot1 (); // variazione shot da eseguire questa volta else if (elapsedTime ()> 5.0) bossShot2 (); else if (elapsedTime ()> 6.0) bossShot3 (); if (elapsedTime ()> 7.0) // reimposta l'ora in modo che il boss esegua nuovamente la prima azione resetsTime ();
La definizione delle funzioni boss shot è sotto. Sentiti libero di cambiarlo per adattarlo a quello che vuoi per una lotta con il boss:
function bossShot1 () // un semplice colpo dritto bossEnemy.shoot ("path / to / bullet / sprite.png", bossPower, 400, shotAngle); // shotAngle is 180 function bossShot2 () // un proiettile a tre direzioni ha sparato a 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); function bossShot3 () // per questo, ho intenzione di fare un cerchio, quindi i proiettili formeranno un cerchio per (var i = 0; i <= 9; i++) bossEnemy.shoot("path/to/bullet/sprite.png", bossPower, 400, 36 * i); // change the shotAngle
Spetta a te aggiungere