Looge oma Mario maja. Super Mario: uued tasemed
Täpselt nii – lihtsalt igav tühi ekraan! :] Täidame selle täielikult õpetust läbides
Kõik vajalikud pildid ja helid on juba stardiprojekti lisatud. Vaatame projekti sisu üle:
- Mängukunst. Sisaldab Ray naise Vicki tasuta mängukunstipakki.
- Taseme kaart. Joonistasin spetsiaalselt teie jaoks tasemekaardi, alustades SMB esimesest tasemest.
- Suurepärased heliefektid. Lõppude lõpuks, õpetus saidilt raywenderlich.com! :]
- CCLayeri alamklass. Klass nimega GameLevelLayer, mis rakendab b O suurem osa meie füüsikamootoritest. Kuigi nüüd on see tühi kui kork. (Jah, see laps ootab, et ta saaks täis!)
- CCSprite alamklass. Klass nimega Player, mis sisaldab koala loogikat. Praegu üritab meie Koala kaugusesse lennata!
Füüsika mootori põhitõed
Platvormingud töötavad füüsikamootorite baasil ja selles õpetuses kirjutame oma füüsikamootori.On kaks põhjust, miks me peame kirjutama oma mootori, mitte kasutama sama Box2D-d või Chipminki:
- Üksikasjalikud seaded. Platvormimängijate zeni täielikuks kogemiseks peate õppima, kuidas oma mootorit täielikult kohandada.
- Lihtsus. Box2D-l ja Chipmunkil on palju kohandatavaid funktsioone, mida me tegelikult ei vaja. Pealegi on ressursse. Ja meie oma mootor sööb täpselt nii palju, kui me seda lubame.
- Simuleerib liikumist. Füüsikamootori esimene ülesanne on simuleerida vastandlikke gravitatsiooni-, liikumis-, hüppe- ja hõõrdejõude.
- Tuvastab kokkupõrkeid. Teine ülesanne on tuvastada kokkupõrkeid mängija ja teiste taseme objektide vahel.
Kokkupõrketuvastuse abil peatame oma Koala iga kord, kui ta tahab gravitatsiooni mõjul põrandast läbi minna, ja tuvastame, kui meie koala on astunud naelu (au!).
Vaatame, kuidas see praktikas toimib.
Füüsikamootori loomine
Loodavas füüsikamootoris on Koalal oma liikumist kirjeldavad muutujad: kiirus, kiirendus ja asend. Neid muutujaid kasutades kasutame oma programmi igas etapis järgmist algoritmi:- Kas hüppe või liigutus on valitud?
- Kui jah, siis kasutage koalale hüppe- või liikumisjõudu.
- Samuti rakendage koalale gravitatsiooni.
- Arvutage saadud koala kiirus.
- Rakendage saadud kiirus koalale ja värskendage selle asukohta.
- Kontrollige Koala kokkupõrkeid teiste objektidega.
- Kui toimub kokkupõrge, siis kas liigutage Koala takistusest nii kaugele, et kokkupõrkeid enam ei tekiks; või kahjustada vaest Koalat.
Me läbime need sammud programmi igas etapis. Meie mängus sunnib gravitatsioon Koala pidevalt läbi põranda madalamale ja madalamale, kuid kokkupõrketuvastus toob ta iga kord tagasi põrandast kõrgemale punktile. Seda funktsiooni saate kasutada ka selleks, et teha kindlaks, kas koaala puudutab maad. Kui ei, siis saate takistada mängijal hüppamist, kui Koala on hüppeseisundis või on just takistuselt alla hüpanud.
Punktid 1-5 esinevad Koala objekti sees. Kogu vajalik informatsioon tuleks selle objekti sees salvestada ja on üsna loogiline lubada Koalal oma muutujaid ise uuendada.
Kui aga rääkida 6. punktist – kokkupõrgete tuvastamisest – peame arvestama kõigi taseme iseärasustega, näiteks: seinad, põrandad, vaenlased ja muud ohud. Kokkupõrke tuvastamine toimub programmi igal etapil GameLevelLayeri abil – lubage mul teile meelde tuletada, et see on CCLayeri alamklass, mis täidab enamiku füüsikaülesannetest.
Kui lubame Koalal oma positsiooni ise värskendada, puudutab Koala lõpuks seina või põrandat. Ja GameLevelLayer toob Koala tagasi. Ja nii ikka ja jälle – mis muudab Koala mulje, nagu ta vibreeriks. (Liiga palju kohvi täna hommikul, Coalio?)
Seetõttu ei luba me Koalal oma olekut värskendada. Selle asemel anname Koalale uue muutuja wishPosition, mida Koala värskendab. GameLevelLayer kontrollib, kas koaala saab liigutada soovitud positsiooni. Kui jah, värskendab GameLevelLayer Koala olekut.
Kõik selge? Vaatame, kuidas see koodis välja näeb!
TMXTiledMapi laadimine
Eeldan, et olete Tile Mapsi tööpõhimõttega tuttav. Kui ei, siis soovitan nende kohta lugeda.Vaatame taset. Käivitage oma plaaditud kaardiredaktor (laadige alla, kui te pole seda varem teinud) ja avage level1.tmx oma projekti kaustast. Näete järgmist.
Kui vaatate külgriba, näete, et meil on kolm erinevat kihti:
- ohud: See kiht sisaldab asju, mida Koala peab elus püsimiseks jälgima.
- seinad: See kiht sisaldab rakke, mida Koala ei saa läbida. Põhimõtteliselt on need põrandaelemendid.
- taust: See kiht sisaldab puhtalt esteetilisi asju, nagu pilved või künkad.
@liides GameLevelLayer() ( CCTMXTiledMap *map; ) @end
Oleme lisanud kohaliku CCTMXTiledMap klassi muutujakaardi, et töötada meie peaklassi võrkkaartidega.
Järgmisena asetame võrgukaardi oma kihti kohe kihi lähtestamise ajal. Lisame meetodile järgmise selles:
CCLayerColor *blueSky = [ initWithColor:ccc4(100, 100, 250, 255)]; ; map = [initWithTMXFile:@"level1.tmx"]; ;
Esiteks lisasime taustavärvi (CCLayerColor) sinine taevas. Järgmised kaks koodirida laadivad lihtsalt kaardimuutuja (CCTMXTiledMap) ja lisavad selle kihti.
#import "Player.h"
Ikka veel sees GameLevelLayer.m Lisame jaotisesse @liides järgmine kohalik muutuja:
Player = [initWithFile:@"koalio_stand.png"]; mängija.positsioon = ccp(100, 50); ;
See kood laadib Koala sprite objekti, annab sellele asukoha ja lisab selle meie kaardiobjektile.
Miks lisada koaala objekt kaardile, võite küsida, selle asemel, et see lihtsalt kihti lisada? See on lihtne. Tahame otseselt kontrollida, milline kiht jääb Koala ette ja milline tema taha. Seega teeme Koalast kaardi lapse, mitte peamise kihi. Tahame, et koaala oleks ees, seega anname sellele Z-järgu 15. Samuti on kaarti kerides Koaala endiselt samas asendis, kaardi, mitte põhikihi suhtes.
Suurepärane, proovime järele! Käivitage oma projekt ja peaksite nägema järgmist.
Näeb välja nagu mäng, kuid Coalio trotsib gravitatsiooni! On aeg see maa peale tuua – kasutades füüsikamootorit:]
Coalio gravitatsiooni olukord
Füüsika simulatsiooni loomiseks saab kirjutada kompleksse hargnemisloogika komplekti, mis võtaks arvesse Koala olekut ja rakendaks talle saadud info põhjal jõude. Kuid see maailm muutub kohe liiga keeruliseks – ja päris füüsika ei tööta nii keeruliselt. Reaalses maailmas tõmbab gravitatsioon objekte pidevalt allapoole. Jah, lisame pidev jõud gravitatsiooni ja rakendage seda Koalale programmi igal sammul.
Ka teised jõud ei lülitu lihtsalt sisse ja välja. Reaalses maailmas mõjub jõud objektile seni, kuni teine jõud on suurem või võrdne esimesega.
Näiteks hüppe jõud ei lülita gravitatsiooni välja; see ületab mõnda aega gravitatsioonijõudu, kuni gravitatsioon taas koala maapinnale surub.
Nii modelleeritakse füüsikat. Sa ei otsusta lihtsalt, kas kasutada või mitte gravitatsioonijõud koalale. Gravitatsioon on alati olemas.
Mängime jumalat
Meie mootori loogika ütleb, et kui objektile mõjub jõud, jätkab see liikumist seni, kuni teine jõud ületab esimese. Kui Coalio hüppab servalt alla, jätkab see liikumist allapoole teatud kiirendusega, kuni kohtab oma teel takistust. Kui me liigutame Coaliot, ei lakka see liikumast enne, kui me lõpetame sellele liikumisjõu rakendamise; hõõrdumine mõjub Coaliole, kuni see peatub.
Füüsikamootorit ehitades näete, kuidas selline lihtne mänguloogika võib aidata lahendada keerulisi füüsikaprobleeme, nagu jääpõrand või kaljult alla kukkumine. See käitumismudel võimaldab mängul dünaamiliselt muutuda.
Samuti võimaldab selline rüütlikäik meil elluviimist lihtsamaks muuta, kuna me ei pea pidevalt küsima oma objekti olekut - objekt järgib lihtsalt reaalse maailma füüsikaseadusi.
Mõnikord peame jumalat mängima! :]
Planeedi Maa seadused: CGP-punktid ja jõud
Määratleme järgmised mõisted:- Kiirus kirjeldab, kui kiiresti objekt teatud suunas liigub.
- Kiirendus kirjeldab, kuidas objekti kiirus ja suund ajas muutuvad.
- Jõud on mõju, mis põhjustab kiiruse või suuna muutust.
CGPointi struktuuride abil esindame kolme asja: kiirust, jõudu/kiirendust ja asendit. CGPointi struktuuride kasutamiseks on kaks põhjust.
- Need on 2D. Kiirus, jõud/kiirendus ja asend on kõik 2D-mängu suurused. Võib öelda, et gravitatsioon toimib ainult ühes suunas, aga mis siis, kui ühel mänguhetkel on meil hädasti vaja gravitatsiooni suunda muuta? Mõelge Super Mario Galaxyle!
- See on mugav. CGPointi abil saame ära kasutada erinevaid Cocos2D sisseehitatud funktsioone. Eelkõige kasutame ccpAdd (liitmine), ccpSub (lahutamine) ja ccpMult (korrutamine ujuvmuutujaga). Kõik see muudab meie koodi palju lihtsamaks lugemiseks ja silumiseks!
Mängu igas etapis liidame kõik jõud kokku ja saadud väärtus lisatakse Koala praegusele kiirusele. Selle tulemusena saame uue praeguse kiiruse. Vähendame seda kaadrisageduse abil. Pärast seda kolime koala.
Alustame gravitatsioonist. Kirjutame jooksutsükli, milles rakendame jõude. Lisage faili init-meetodile GameLevelLayer.m järgmine kood vahetult enne tingimusliku ploki sulgemist:
;
Järgmine lisamine uus meetod klassile:
- (kehtetu)värskendus:(ccTime)dt ( ; )
Järgmisena avage Mängija.h ja muutke see selliseks:
#import
Lisage järgmine kood Player.m:
Vajutage mind
#import "Player.h" @rakendamine Player @synthesize speed = _velocity; // 1 - (id)initWithFile:(NSString *)failinimi ( if (self = ) ( self.velocity = ccp(0.0, 0.0); ) return self; ) - (void)update:(ccTime)dt ( // 2 CGP-punkti gravitatsioon = ccp(0,0, -450,0); // 3 CGP-punkti gravitatsiooniaste = ccpMult(gravity, dt); // 4 self.velocity = ccpAdd(self.velocity, gravityStep); CGP-punkti sammVelocity(self.ccpvelocity(self.ccpvelocity) dt); // 5 self.position = ccpAdd(self.position, stepVelocity); ) @end
Vaatame ülaltoodud koodi samm-sammult läbi
- Siin oleme lisanud uue init-meetodi objekti lähtestamiseks ja kiirusmuutuja nullimiseks.
- Siin oleme määranud gravitatsioonivektori väärtuse. Iga sekund kiirendame Koala kiirust 450 piksli võrra.
- Siin kasutasime ccpMulti, et vähendada gravitatsioonivektori väärtust, et see sobiks kaadrisagedusega. ccpMult võtab vastu float ja CGPpointi ning tagastab CGPpointi.
- Siin, kui oleme arvutanud praeguse sammu gravitatsiooni, lisame selle praegusele kiirusele.
- Lõpuks, kui oleme ühe sammu kiiruse välja arvutanud, kasutame Koala asukoha värskendamiseks ccpAdd.
Oeh – Coalio kukub läbi põranda! Teeme selle korda.
Mõjud öösel – kokkupõrke tuvastamine
Kokkupõrketuvastus on iga füüsikamootori alus. Seal on palju erinevat tüüpi kokkupõrke tuvastamine, alates lihtne kasutada pildiraamid, 3D-objektide keeruliste kokkupõrgeteni. Meie õnneks ei vaja platvormimine keerukaid struktuure.Koala kokkupõrgete tuvastamiseks objektidega kasutame Koalat vahetult ümbritsevate rakkude jaoks TMXTileMapi. Järgmisena kontrollime iOS-i sisseehitatud mitmete funktsioonide abil, kas Koala sprite lõikub mõne raku spraitiga.
Funktsioonid CGRectIntersectsRect ja CGRectIntersection muudavad sellised kontrollid väga lihtsaks. CGRectIntersectsRect kontrollib, kas kaks ristkülikut ristuvad, ja CGRectIntersection tagastab ristküliku ristküliku.
Esiteks peame määratlema oma koala raami. Igal laaditud spraitil on ääris, mis on tekstuuri suurus, ja sellele pääseb juurde parameetriga boundingBox.
Miks määrata piiri, kui see on juba boundingBoxis? Tekstuuril on tavaliselt ümber läbipaistvad servad, mida me kokkupõrgete tuvastamisel eriti arvestada ei taha.
Mõnikord ei pea me arvestama isegi paari piksliga tegeliku sprite-pildi ümber (mitte läbipaistev). Kui Mario vastu seina põrkab, kas ta seda napilt puudutab või vajub ta nina kergelt plokki?
Proovime. Lisa Mängija.h:
-(CGRect)collisionBoundingBox;
Ja lisada juurde Player.m:
- (CGRect)collisionBoundingBox (tagasi CGRectInset(self.boundingBox, 2, 0); )
CGRectInset tihendab CGRecti pikslite arvu järgi teisest ja kolmandast argumendist. Meie puhul on meie põrkekaadri laius kuus pikslit väiksem – kolm pikslit mõlemal küljel.
Jõutõstmine
On aeg tõsta raskusi. ("Hei, kas sa kutsud mind lihtsalt paksuks?" Ütleb Coalio).Kokkupõrgete tuvastamiseks vajame oma GameLevelLayeris mitmeid meetodeid. Eriti:
- Meetod, mis tagastab praegust Coalio lahtrit ümbritseva kaheksa lahtri koordinaadid.
- Meetod, mis määrab, millised rakud on takistuseks (ja kas neid üldiselt on). Mõnel rakul pole füüsikalised omadused(pilved) ja Coalio nendega kokku ei põrka.
- Meetod, mis käsitleb kokkupõrkeid prioriteetsuse järjekorras.
- Meetod, mis määrab Coalio raku asukoha.
- Meetod, mis võtab vastu lahtri koordinaadid ja tagastab lahtri ristküliku Cocos2D koordinaatidena.
- (CGPoint)tileCoordForPosition:(CGPoint)positsioon ( float x = floor(position.x / map.tileSize.width); float levelHeightInPixels = map.mapSize.height * map.tileSize.height; float y = floor((levelHeightInPixels - position.y) / map.tileSize.height); tagasta ccp(x, y); ) - (CGRect)tileRectFromTileCoords:(CGPoint)tileCoords ( float levelHeightInPixels = map.mapSize.height * map.tileSize.height; CGPpoint origin = ccp(tileCoords.x * map.tileSize.width, levelHeightInPixels - ((tileCoords.y + 1) * map.tileSize.height)); tagasta CGRectMake(origin.x, origin.y, map.tileSize.width, kaart. plaadi suurus.kõrgus);)
Esimene meetod tagastab meile pikslikoordinaatide juures asuva lahtri koordinaadid, mille me meetodile edastame. Lahtri asukoha saamiseks jagame lihtsalt koordinaadid lahtrite suurusega.
Peame kõrguse koordinaadid ümber pöörama, kuna süsteemi Cocos2D/OpenGL koordinaadid algavad vasakust alumisest nurgast ja süsteemi koordinaadid vasakust ülanurgast. Standardid – kas pole lahe?
Teine meetod toimib vastupidiselt. See korrutab lahtri koordinaadi lahtrite suurusega ja tagastab antud lahtri CGRect. Jällegi peame kõrgust suurendama.
Miks me peame kõrguse y-koordinaadile ühe lisama? Pidage meeles, et lahtri koordinaadid algavad nullist, seega on lahtri 20 tegelik koordinaat 19. Kui me kõrgusele üht ei lisa, on punkt 19 * tileHeight.
Mind ümbritsevad rakud!
Liigume nüüd edasi meetodi juurde, mis määrab Koala ümbritsevad rakud. Selle meetodi puhul loome massiivi, mille tagastame. See massiiv sisaldab lahtri GID-d, lahtri koordinaate ja selle lahtri CGRect teavet.Korraldame selle massiivi prioriteedi järjekorras, milles me kokkupõrkeid tuvastame. Näiteks tahame enne diagonaalsete kokkupõrkeid tuvastada ülalt, vasakult, paremalt, altpoolt. Samuti, kui tuvastame Koala kokkupõrke alumise lahtriga, seame maapinna puudutuse lipu.
Lisame selle meetodi GameLevelLayer.m:
Vajutage mind
- (NSArray *)getSurroundingTilesAtPosition:(CGPoint)position forLayer:(CCTMXLayer *)layer ( CGPoint plPos = ; //1 NSMutableArray *gids = ; //2 for (int i = 0; i< 9; i++) { //3 int c = i % 3; int r = (int)(i / 3); CGPoint tilePos = ccp(plPos.x + (c - 1), plPos.y + (r - 1)); int tgid = ; //4 CGRect tileRect = ; //5 NSDictionary *tileDict = , @"gid", , @"x", , @"y", ,@"tilePos", nil]; ; } ; atIndex:6]; ; ; ; //6 for (NSDictionary *d in gids) { NSLog(@"%@", d); } //7 return (NSArray *)gids; }
Pfft – terve koodipilv. Ärge muretsege, me käsitleme seda üksikasjalikult.
Kuid enne seda pange tähele, et meie kaardil on kolm kihti.
Kättesaadavus erinevad kihid võimaldab meil iga kihi jaoks kokkupõrkeid erinevalt defineerida.
- Koala ja ohud. Kui toimub kokkupõrge, siis tapame Koala (üsna jõhker, eks?).
- Koaala ja seinad. Kui kokkupõrge toimub, siis me ei luba Koalal selles suunas edasi liikuda. "Seisake, mära!"
- Koaala ja taustad. Kui toimub kokkupõrge, siis me ei tee midagi. Laisk programmeerija on parim programmeerija. Või mida inimesed ütlevad?
Olgu, vaatame koodi samm-sammult läbi.
1.
Esiteks saame sisendlahtri koordinaadid (mis on Koala koordinaadid).
2.
Järgmisena loome uue massiivi, mis tagastab teabe lahtri kohta.
3.
Järgmisena käivitame tsükli 9 korda - kuna meil on 9 võimalikku liikumisrakku, sealhulgas rakk, milles koaala juba on. Järgmised paar rida määratlevad üheksa lahtri asukohad ja salvestavad need muutujas tilePos.
Märge: vajame teavet ainult kaheksa raku kohta, kuna me ei pea kunagi tuvastama kokkupõrkeid rakuga, kus koaala juba on.
Peaksime alati sellest võimalusest kinni haarama ja viima Koala ühte kambrisse. Kui Coalio on tahke raku sees, siis üle poole Coalio spraidist on sinna sisse läinud. Ta ei tohiks nii kiiresti liikuda – vähemalt mitte selles mängus!
Nende kaheksa lahtriga töötamise hõlbustamiseks lisage algusesse Coalio lahter ja eemaldage see lõpus.
4. Neljandas jaotises nimetame tileGIDAt: meetodit. See meetod tagastab lahtri GID kindlal koordinaadil. Kui saadud koordinaatidel lahtrit pole, tagastab meetod nulli. Järgnevalt kasutame nulli, et tähendada "lahtrit ei leitud".
5. Järgmisena kasutame abimeetodit, et arvutada lahtri CGRect antud Cocos2D koordinaatidel. Saadud teabe salvestame NSDsõnastikusse. Meetod tagastab vastuvõetud NSDsõnastiku massiivi.
6. Kuuendas jaotises eemaldame massiivist Koala lahtri ja sorteerime lahtrid prioriteedi järjekorras.
Tihti avastame Koala all oleva rakuga kokkupõrgete tuvastamisel ka põrkeid piki diagonaali. Vaata pilti paremal. Kui tuvastame kokkupõrkeid punasega esiletõstetud Koala all oleva lahtriga, tuvastame ka kokkupõrkeid sinisega esile tõstetud plokiga nr 2.
Meie kokkupõrketuvastusalgoritm teeb mõned eeldused. Need eeldused kehtivad pigem külgnevate kui diagonaalsete lahtrite puhul. Seega püüame vältida diagonaalsete rakkudega tegelemist nii palju kui võimalik.
Ja siin on pilt, mis näitab meile selgelt massiivi lahtrite järjekorda enne ja pärast sorteerimist. Märkate, et kõigepealt töödeldakse ülemist, alumist, paremat ja vasakut lahtrit. Lahtrite järjekorda teades on teil lihtsam kindlaks teha, millal koaala puudutab maad või lendab pilvedes.
7. Seitsmendas jaotises olev silmus võimaldab meil rakke reaalajas jälgida. Nii saame kindlalt teada, et kõik läheb plaanipäraselt.
Oleme oma mängu järgmiseks käivitamiseks peaaegu valmis! Siiski on veel paar asja, mis tuleb ära teha. Peame lisama seinakihi muutujana klassi GameLevelLayer, et saaksime seda kasutada.
Sees GameLevelLayer.m tehke järgmised muudatused:
// Lisa @liidesesse CCTMXLayer *seinad; // Lisa init-meetodile pärast kaardi lisamist kihi seintele = ; // lisa värskendusmeetodile;
Käivitage! Kuid kahjuks jookseb mäng kokku. Näeme konsoolis midagi sellist:
Kõigepealt saame teavet lahtrite positsioonide ja GID väärtuste kohta (kuigi enamasti nullid, kuna peal on tühi ruum).
Lõpuks jookseb kõik kokku veaga "TMXLayer: kehtetu positsioon". See juhtub siis, kui asukoht edastatakse plaadileGIDat: meetodile, mis asub väljaspool kaardi servi.
Väldime seda viga veidi hiljem, kuid kõigepealt muudame olemasolevat kokkupõrke määratlust.
Koala õiguste tagasivõtmine
Kuni selle hetkeni oli Koala oma seisukohta üksinda värskendanud. Kuid nüüd võtame temalt selle privileegi ära.Kui koaala iseseisvalt oma positsiooni uuendab, hakkab ta lõpuks hullult ringi hüppama! Aga me ei taha seda, eks?
Seega vajab Koala täiendavat muutujat, wishPosition, millega ta suhtleb GameLevelLayeriga.
Tahame, et Koala klass arvutaks oma järgmise positsiooni ise. Kuid GameLevelLayer peaks Koala soovitud kohta viima alles pärast selle kehtivuse kontrollimist. Sama kehtib ka kokkupõrke tuvastamise ahela kohta – me ei taha tegelikku spraiti värskendada enne, kui kõik rakud on kokkupõrgete suhtes kontrollitud.
Peame mõnda asja muutma. Esmalt lisage järgmine Mängija.h
@omadus (mitteatomiline, määrake) CGPpoint soovitudPosition;
Ja sünteesida, mis on lisatud Player.m:
@synthesize wishPosition = _soovitavPosition;
Nüüd muutke meetodit kokkupõrgeBoundingBox V Player.m nii et see näeb välja selline:
- (CGRect)collisionBoundingBox ( CGRect collisionBox = CGRectInset(self.boundingBox, 3, 0); CGPpoint diff = ccpSub(self.desiredPosition, self.position); CGRect returnBoundingBox = CGRectOffset(collisionBox, diffyx); tagastada returnBoundingBox;)
See koodiosa arvutab kaadri soovitud positsiooni alusel, mida GameLevelLayer kasutab kokkupõrgete tuvastamiseks.
Märge: Seal on palju erinevatel viisidel kokkupõrkeraami arvutused. Võiksite kirjutada koodi, mis sarnaneb CNode klassis juba leiduva koodiga, kuid meie praegune meetod on palju lihtsam, ehkki pisut ebaselge.Järgmisena tehke värskendusmeetodis järgmised muudatused, et see värskendaks praeguse asukoha asemel soovitud positsiooni:
// Asenda "self.position = ccpAdd(self.position, stepVelocity);" to: self.desiredPosition = ccpAdd(self.position, stepVelocity);
Alustame kokkupõrgete tuvastamist!
Tõsiste saavutuste aeg on kätte jõudnud. Me paneme kõik kokku. Lisage järgmine meetod GameLevelLayer.m:Vajutage mind
- (void)checkForAndResolveCollisions:(Player *)p ( NSArray *tiles = ; //1 for (NSDictionary *dic in paanid) ( CGRect pRect = ; //2 int gid = [ intValue]; //3 if (gid) ( CGRect tileRect = CGRectMake([ floatValue], [ floatValue], map.tileSize.width, map.tileSize.height); //4 if (CGRectIntersectsRect(pRect, tileRect)) ( CGRect intersection = CGRectIntersection(pRect, tileRect); //5 int tileIndx = ; //6 if (tileIndx == 0) ( //lahter otse Koala all p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y + intersection.size.height); ) else if (tileIndx == 1) ( //lahter otse Koala kohal p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y - ristmik.size.height); ) else if (tileIndx == 2) ( //Koalast vasakul olev lahter p.desiredPosition = ccp(p.desiredPosition.x + intersection.size.width, p.desiredPosition.y); ) else if (tileIndx == 3) ( //lahter paremal Koala p.desiredPosition = ccp(p.desiredPosition.x - ristmik.size.width, p.desiredPosition.y); ) else ( if (ristmik.suurus.laius > ristmik.suurus.kõrgus) ( //7 //Lahter on diagonaalne, aga lahendame ülesande vertikaalselt float intersectionHeight; if (tileIndx > 5) ( intersectionHeight = intersection.size. kõrgus; ) else ( intersectionHeight = -intersection.size.height; ) p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y + intersection.size.height); ) else ( //Lahter on diagonaalne, aga lahendame ülesande horisontaalselt float resolutionWidth ; if (tileIndx == 6 || tileIndx == 4) ( resolutsioonWidth = ristmik.size.width; ) else ( resolutsioonWidth = -ristmik.size.width; ) p.desiredPosition = ccp( p.soovitavPosition.x , lk soovitudPosition.y + resolutsioonLaius); ) ) ) ) ) p.position = p.desiredPosition; //7)
Suurepärane! Vaatame äsja kirjutatud koodi.
1.
Esiteks saame Koalat ümbritsevate rakkude komplekti. Järgmisena vaatame läbi selle komplekti iga lahtri. Iga kord, kui me kambrist läbi läheme, kontrollime seda kokkupõrgete suhtes. Kui toimub kokkupõrge, muudame Koala soovitud positsiooni.
2.
Silmuse igas ahelas saame esmalt praeguse Koala raami. Iga kord, kui kokkupõrge tuvastatakse, muudab soovitud asukoha muutuja oma väärtust selliseks, et kokkupõrget enam ei esine.
3.
Järgmine samm on hankida NSDsõnastikusse salvestatud GID, mis võib olla null. Kui GID on null, siis jooksev tsükkel lõpeb ja liigume edasi järgmisesse lahtrisse.
4.
Kui uuel positsioonil on lahter, peame selle hankima CGRect. Kokkupõrge võib toimuda või mitte. Teostame selle protsessi järgmise koodirea abil ja salvestame selle muutuja tileRect. Nüüd, kui meil on Koala CGRect ja rakud, saame neid kokkupõrke suhtes testida.
5.
Lahtrite kokkupõrke kontrollimiseks käivitame CGRectIntersectsRect. Kui toimub kokkupõrge, saame CGRecti, mis kirjeldab ristmikku CGRect, kasutades funktsiooni CGRectIntersection().
Mõtiskleme dilemma üle...
Päris huvitav juhtum. Peame välja mõtlema, kuidas kokkupõrkeid õigesti tuvastada.Võib arvata, et Parim viis liiguta koalat – liiguta seda kokkupõrkelt vastassuunas. Mõned füüsikamootorid tegelikult töötavad sel viisil, kuid me proovime paremat lahendust.
Mõelge sellele: gravitatsioon tõmbab Koala pidevalt alla tema all olevatesse rakkudesse ja neid kokkupõrkeid juhtub kogu aeg. Kui kujutate ette, et Koala liigub edasi, siis samal ajal tõmbab Koalat endiselt alla gravitatsioon. Kui lahendame selle probleemi lihtsalt liikumist muutes tagakülg, siis liigub Koala üles ja vasakule – aga me vajame midagi muud!
Meie koaala peab liikuma piisavalt kaugele, et püsida nende rakkude kohal, kuid samas tempos edasi liikuda.
Sama probleem juhtub siis, kui Koala mööda seina alla libiseb. Kui mängija surub koala vastu seina, suunatakse soovitud koala trajektoor diagonaalselt alla ja seina sisse. Lihtsalt suunda muutes paneme Koala seinast üles ja eemale liikuma – jällegi, mitte samamoodi! Tahame siis, et Koaala jääks väljaspool müüri, kuid laskuks ikka samas tempos!
Seega peame otsustama, millal käsitleda kokkupõrkeid vertikaalselt ja millal horisontaalselt, ning käsitlema mõlemat toimingut, mis üksteist välistavad. Mõned füüsikamootorid töötlevad pidevalt kõigepealt esimest ja seejärel teist sündmust; kuid me tahame teha parema otsuse Koala rakukese positsiooni põhjal. Näiteks kui rakk asub otse Koala all, tahame, et kokkupõrkedetektor tooks Koala uuesti üles.
Mis siis, kui rakk on diagonaalselt Koala asukoha suhtes? Sel juhul kasutame CGRecti ristmikke, et välja selgitada, kuidas peaksime Koalat liigutama. Kui selle ristküliku laius rohkem kõrgust, siis tuleb Koala vertikaalselt tagastada. Kui kõrgus on suurem kui laius, peaks koala liikuma horisontaalselt.
See protsess töötab õigesti seni, kuni Koala kiirus ja kaadrisagedus on teatud piirides. Veidi hiljem õpime vältima juhtumeid, kui Koaala liiga kiiresti kukub ja läbi kambri alla hüppab.
Kui oleme otsustanud, kas liigutada koaala vertikaalselt või horisontaalselt, kasutame ristmiku CGRecti suurust, et määrata, kui palju koaala liigutada. Vaatame vastavalt laiust või kõrgust ja kasutame seda väärtust Koala nihkekaugusena.
Miks kontrollida lahtreid kindlas järjekorras? Kõigepealt peaksite alati töötama külgnevate lahtrite ja seejärel diagonaalsete lahtrite kallal. Lõppude lõpuks, kui soovite kontrollida Koala paremas alanurgas asuvat lahtrit kokkupõrke suhtes, suunatakse nihkevektor vertikaalselt.
Siiski on endiselt võimalus, et kokkupõrke CGRect tõmmatakse ülespoole, kui Koala vaevu kambrit puudutab.
Vaadake parempoolset pilti. Sinine ala on venitatud ülespoole, kuna kokkupõrke ristkülik moodustab vaid väikese osa üldisest kokkupõrkest. Kui aga oleme juba lahendanud probleemi vahetult Koala all oleva rakuga, siis ei pea me enam tuvastama kokkupõrkeid Koalast paremal asuva all oleva rakuga. Nii saame tekkivatest probleemidest mööda.
Tagasi koodi juurde!
Tuleme tagasi koletu meetodi juurde...6.
Kuues jaotis võimaldab meil saada praeguse lahtri indeksi. Lahtri asukoha leidmiseks kasutame lahtriindeksit. Me hakkame opereerima külgnevaid lahtreid eraldi, liigutades koalat, lahutades või lisades kokkupõrke pikkuse või kõrguse. Üsna lihtne. Kui aga tegemist on diagonaalsete lahtritega, rakendame eelmises jaotises kirjeldatud algoritmi.
7.
Seitsmendas jaotises teeme kindlaks, kas meie kokkupõrkeala on lai või piklik ülespoole? Kui see on lai, töötame vertikaalselt. Kui lahtri indeks on suurem kui 5, liigutage koaala üles. Kui ala on ülespoole venitatud, töötame horisontaalselt. Järgime lahtriindeksite järjestamisel sarnast põhimõtet. Lõpus määrame saadud positsiooni Koalale.
See meetod on meie kokkupõrketuvastussüsteemi aju.
Paneme kõik olemasolevad teadmised ellu! Muutke meetodit värskendada(ikka veel sees GameLevelLayer:)
// Asenda ";" kohta: ;
Samuti saate ploki kustutada või kommenteerida getSurroundingTilesAtPosition:forLayer:
/* jaoks (NSDictionary *d in gids) ( NSLog(@"%@", d); ) //8 */
Käivitame! Kas olete tulemusest üllatunud?
Paul peatab Coalio, kuid ta upub temasse kohe ära! Miks?
Kas oskate arvata, millest me ilma jäime? Pidage meeles – igal mängu sammul lisame Koala kiirusele gravitatsiooni. See tähendab, et koaala kiireneb pidevalt allapoole.
Me lisame Koala allakäigutrajektoorile pidevalt kiirust, kuni see muutub raku suuruseks – liigume ühe sammuga läbi terve raku, mis põhjustabki probleeme (pidage meeles, et me rääkisime sellest hiljuti).
Kui oleme avastanud kokkupõrke, peame lähtestama koala kiiruse selle raku suunas, millega ta kokku põrkas! Koaala on liikumise lõpetanud, seega tuleb kiirusega arvestada.
Kui me seda ei tee, jõuame mängus üsna veidra käitumiseni. Nagu me varem märkisime, vajame viisi, kuidas tuvastada, kas koaala puudutab maad, et koaala ei saaks veelgi kõrgemale hüpata. Märkame selle kasti kohe. Lisage järgmised read checkForAndResolveCollisions:
Vajutage mind
- (void)checkForAndResolveCollisions:(Player *)p ( NSArray *tiles = ; //1 p.onGround = EI; //////Siin (NSDictionary *dic plaatides) ( CGRect pRect = ; //3 int gid = [ intValue]; //4 if (gid) ( CGRect tileRect = CGRectMake([ floatValue], [ floatValue], map.tileSize.width, map.tileSize.height); //5 if (CGRectIntersectsRect(pRect, tileRect) )) ( CGRect ristmik = CGRectIntersection(pRect, tileRect); int tileIndx = ; if (tileIndx == 0) ( //lahter Koala all p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y + ristmik. suurus.kõrgus); p.velocity = ccp(p.velocity.x, 0,0); //////Siin p.onGround = JAH; //////Siin ) else if (tileIndx == 1) ( //koala kohal olev lahter p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y – ristmik.size.height); p.velocity = ccp(p.velocity.x, 0,0); ///// /Siin ) else if (tileIndx == 2) ( //lahter vasakul p.desiredPosition = ccp(p.desiredPosition.x + intersection.size.width, p.desiredPosition.y); ) else if (tileIndx == 3) ( //lahter paremal p.desiredPosition = ccp(p.desiredPosition.x - intersection.size.width, p.desiredPosition.y); ) else ( if (ristmik.suurus.laius > ristmik.suurus.kõrgus) ( //plaat on diagonaalne, kuid lahendab kokkupõrke vertikaalselt p.velocity = ccp(p.velocity.x, 0.0); //////Siin float resolutionHeight; if (tileIndx > 5) ( resolutsioonKõrgus = ristmik.size.height; p.onGround = JAH; //////Siin ) else ( resolutsioonKõrgus = -ristmik.suurus.kõrgus; ) p.desiredPosition = ccp( p.desiredPosition.x, p.desiredPosition.y + resolutsioonikõrgus); ) else ( float resolutionWidth; if (tileIndx == 6 || tileIndx == 4) ( resolutsioonWidth = ristmik.size.width; ) else ( resolutsioonLaius = -ristmik .size.width; ) p.desiredPosition = ccp(p.desiredPosition.x + resolutionWidth, p.desiredPosition.y); ) ) ) ) p.position = p.desiredPosition; //8)
Iga kord, kui Koala all on lahter (kas külgnev või diagonaalne), määrame muutuja p.onGround väärtuseks YES ja lähtestame kiiruse nulliks. Samuti, kui Koala all on külgnev lahter, lähtestame tema kiiruse nullile. See võimaldab meil Koala praegusele kiirusele õigesti reageerida.
Seadsime tsükli alguses muutuja onGround väärtuseks NO. Sel juhul määratakse onGround olekuks JAH ainult siis, kui tuvastame Koala põrkumise selle all oleva lahtriga. Seda funktsiooni saame kasutada selleks, et teha kindlaks, kas koaala suudab praegusel ajahetkel hüpata või mitte.
Lisage päisefaili järgmine kood (ja seejärel sünteesige käivitatavas failis kõik vajalik). Mängija.h:
@omadus (mitteatomiline, määramine) BOOL onGround;
Ja sisse Player.m:
@synthesize onGround = _onGround;
Käivitame! Kas kõik töötab nii nagu ette nähtud? Jah! Oh seda suurepärast päeva! Hurraa!
Mis järgmiseks?
Palju õnne! Olete oma füüsikamootoriga täiesti valmis! Kui oled selle tekstini jõudnud, võid kergendatult hingata. See oli raske osa – õpetuse teises osas pole midagi rasket.Ja siin on nüüdseks lõpetatud projekti allikad.
Teises osas paneme oma Coaliole jooksma ja hüppama. Samuti muudame oma Koalale ohtlikuks põrandas olevad naelplokid ning loome võidu- ja kaotusekraanid.
Kui soovite saada veelgi rohkem teadmisi platformerite füüsikamootorite kohta, soovitan teil külastada järgmisi ressursse:
Sonic the Hedgehog Wiki on suurepärane selgitus selle kohta, kuidas Sonic suhtleb tahkete rakkudega.
Võimalik, et parim juhend kõrgema järgu meelelahutuse platvormide loomiseks.
õpetus Lisa sildid
Tere tulemast Mario Makeri – mängige veebis tasuta supertaseme redigeerijaga vene keeles! Siit saate teada, kuidas läbida kõik tasemed enda kogemus ja jälgige, kuidas Mario tegelane mängu edenedes muutub. Väike nõuanne enne alustamist: mängi täisekraanil, seda on mugavam juhtida.
Siin on Mario mängu ainulaadne väljalase: seiklusmäng ühele, millel on võimalus jätkata seiklust uutel kaartidel. Alustage ühe kahest režiimist: Uus mäng või redaktor.
Alustame sellest peamine omadus Mario Makeri mängud: võimalus luua oma tasemekaarte, kasutades redigeerimisekraani lõuendina.
Kuidas Mario Makeris tasemeid teha
Redaktori liides on väga mugav ja visuaalne. Mänguväli on tähistatud ruudustikuga, selle all on nupud tööriistade, objektikategooriate ja kaardi suuruse valimiseks.
Väikese taseme kaart on mõeldud valmimiseks Täisekraan kerimine puudub, keskmine ja suur - keerukate ja pikkade tasemete jaoks.
Kõik mänguobjektid on paigutatud plokkidesse. Takistuste, boonuste, vaenlaste ja muude mänguelementide väljakule paigutamiseks peate:
- märkige hiirega ploki koht (või puudutage, kui mängite telefonis või tahvelarvutis);
- klõpsake soovitud elemendi nuppu;
- kasutage kustutuskummi (läbipaistev puur), kui soovite objekti eemaldada;
- Taseme lõpetamiseks märkige ruut.
Ärge unustage möödasõiduks telliseid ega muid aluseid, muidu kukub Mario kuristikku enne, kui mängima hakkab! Salvestage oma kaart ja kui soovite mängima hakata, minge peamenüüst, klõpsates nuppu "Salvestatud tasemed".
Kuidas Mariot mängida
Aidake kangelasel jõuda taseme lõpuni, hüpates üle kuristikest ja takistustest, vältides vaenlasi ja kogudes boonuseid. Kõik versiooniuuendused saadakse küsimärgiga salajasele plokile löömisel; need jäävad tavaliselt õhku ja sisaldavad:
- täiendavad mündid;
- superseened, mis muudavad tavalise Mario Super Marioks;
- tulelilled annavad tule jõu, suurendades jooksukiirust ja hüppekõrgust;
- Korjatud jäälilled võimaldavad teil vaenlasi külmutada.
Mario tegelased
Peategelasel on 4 transformatsiooniastet:
- klassikaline Mario on tegelase nõrgim vorm, võib kergesti elu kaotada;
- Super Mario on kaks korda suurem kui klassikaline, suudab vaenlasele vastu seista ilma elu kaotamata, kuid vaenlase puudutamine muutub väikeseks vormiks;
- Tuli või jää Mario mängib tule ja jää superjõududega;
- võitmatu tegelane saab pärast superstaari puudutamist ajutise haavamatuse võlu.
Tuld või jäälille valides muudab Mario värvi ja võib rünnata vaenlasi pallidega. Tulekerad põrkuvad ringi ja suudavad peaaegu kõik vaenlased eemalt lüüa. Jää – rulli ja külmuta vaenlane.
Nüüd teate, kuidas Mariot mängida. Jääb veel viimane saladus: iga taseme lõpus on lipuvarras, millelt tegelane lipu eemaldab ja rõõmsalt lehvitades taseme lõpetab. Pange tähele, et mida kõrgemale kohale lipumasti tabate, seda rohkem punkte teie kangelane saab. Head mängu jätku!
Flash-mängu kirjeldus
Kas soovite luua oma mängu, mis põhineb kõigi lemmikmängu Mario universumil? Alustame siis. See on täieõiguslik mänguredaktor, mis võimaldab teil luua oma tasemekomplekti, süžee ja ka viimase lahingu mis tahes ülemusega. Teisisõnu looge täielikult oma mäng. Esiteks mõtle välja süžee. Näiteks Mario läheb taas seiklema. Värvige mängukohad vastavalt soovile. Need võivad olla metsad, kõrbed, troopilised külad ja põllud. Peaasi, et need olid värvilised ja huvitavad. Seejärel koostage mängukaart. Mängimise huvitavamaks muutmiseks lisage rohkem takistusi ja mänguobjekte. Ärge unustage oma vaenlasi. Samuti tuleb need kaardile paigutada, et mäng poleks nii lihtne; mida kõrgem on tase, seda võimsamad on vaenlased. Määrake, kui palju punkte tegelane teatud koletise tapmise eest saab. Natuke veel ja mäng on valmis. Liigume nüüd edasi kõige tähtsama – ülemuse juurde. See peab olema väga võimas, et mängija pingutaks selle alistamiseks. Saate selle varustada relvade või lisaoskustega. Aseta selle kõrvale mitu eset, mida saab lahingus kasutada, näiteks kivid või põlevad tõrvikud. Seda mängu on paljudel Mario fännidel väga huvitav mängida!
Flash-mängu kirjeldus
Super Mario on paljude mängijate lemmikmäng. Lõppude lõpuks loodi see väga-väga kaua aega tagasi. See on juba läbitud suur hulk korda ning see on endiselt üks armastatumaid ja populaarsemaid mänge Dandy mängukonsoolil. Seejärel anti välja palju erinevaid Mario mänge. Kuid täna on teil võimalus mängida kultussarja jätku. Nüüd on Mariol uued tasemed, mis nõuavad kiiresti teie läbimist. Mängu sisenedes näed kallist ja tuttavat tegelast, kes on sind mängu naasmist juba pikka aega oodanud. Ka Mario maailmas jääb kõik samaks, erinevad olendid soovivad talle surma, kuid ta ei anna alla ja liigub edasi, kogudes samal ajal kuldmünte. Tasemetel on palju ohtlikke kohti, kus võite oma elu kaotada ja otsast alustada, nii et läbige need äärmise ettevaatusega. Uued tasemed on sama huvitavad kui vanad, sest need räägivad loo jätku. Pärast nende läbimist saate teada, mis juhtus tegelase ja tema sõpradega pärast mängu esimese osa sündmusi. Kas olete juba huvitatud? Siis jookse ja mängi! Ja aidake oma lemmiktegelasel vaenlase humanoidide armeega toime tulla.