Natočte video ve stylu hry Mario. Super Mario: nové úrovně. Vezmeme si Koalina privilegia zpět

Popis flash hry

Chcete si vytvořit svou vlastní hru založenou na vesmíru oblíbené hry Mario? Pak začneme. Jedná se o plnohodnotný herní editor, který vám umožní vytvořit si vlastní sadu úrovní, příběh a také závěrečnou bitvu s libovolným bossem. Jinými slovy, vytvořte si zcela vlastní hru. Nejprve vymyslete zápletku. Mario se například znovu vydává za dobrodružstvím. Vybarvěte herní místa, jak chcete. Mohou to být lesy, pouště, tropické vesnice a pole. Hlavní je, že byly barevné a zajímavé. Poté vypracujte herní mapu. Přidejte další překážky a herní objekty, aby bylo hraní zajímavější. Nezapomeňte na své nepřátele. Také je třeba je umístit na mapu, aby hra nebyla tak snadná, čím vyšší úroveň, tím silnější nepřátelé. Určete, kolik bodů postava obdrží za zabití určitého monstra. Ještě trochu a hra bude připravena. Nyní přejdeme k tomu nejdůležitějšímu – šéfovi. Musí být velmi silný, aby hráč tvrdě pracoval na jeho poražení. Můžete jej vybavit zbraněmi nebo dalšími dovednostmi. Vedle něj umístěte několik předmětů, které lze použít v bitvě, jako jsou kameny nebo hořící pochodně. Tato hra bude velmi zajímavá pro mnoho fanoušků Maria!

Vítejte v Mario Makeru – hrajte online s bezplatným editorem super úrovní v ruštině! Naučte se, jak dokončit každou úroveň z první ruky, a sledujte, jak se Mariova postava během hraní mění. Malá rada před začátkem: hrajte na celé obrazovce, je to pohodlnější na ovládání.

Zde je jedinečné vydání hry Mario: dobrodružná hra pro jednoho s možností pokračovat v dobrodružství na nových mapách. Začněte výběrem jednoho ze dvou režimů: Nová hra nebo Editor.

Začněme hlavní funkcí Mario Makeru: schopností vytvářet vlastní mapy úrovní pomocí obrazovky editoru jako plátna.

Jak vytvářet úrovně v Mario Makeru

Rozhraní editoru je velmi pohodlné a vizuální. Hrací pole je označeno mřížkou, pod ní jsou tlačítka pro nástroje, kategorie objektů a výběr velikosti mapy.

Mapa malých úrovní je navržena tak, aby dokončila celou obrazovku bez posouvání, střední a velké mapy jsou pro složité a dlouhé úrovně.

Všechny herní předměty jsou umístěny v blocích. Chcete-li na hřiště umístit překážky, bonusy, nepřátele a další herní prvky, musíte:

  • označte místo pro blok myší (nebo klepněte, pokud hrajete na telefonu nebo tabletu);
  • klikněte na tlačítko požadovaného prvku;
  • použijte nástroj guma (průhledná klec), pokud chcete odstranit objekt;
  • Chcete-li úroveň dokončit, zaškrtněte políčko.

Nezapomeň na cihly nebo jiné základny pro předávání, jinak Mario spadne do propasti ještě předtím, než vůbec začne hrát! Uložte si mapu a pokud chcete začít hrát, přejděte z hlavní nabídky na tlačítko „Uložené úrovně“.

Jak hrát Mario

Pomozte hrdinovi dostat se na konec úrovně přeskakováním propastí a překážek, vyhýbáním se nepřátelům a sbíráním bonusů. Všechna vylepšení se získají zásahem do tajného bloku s otazníkem, který obvykle visí ve vzduchu a obsahuje:

  • další mince;
  • super houby, které promění obyčejného Maria v Super Maria;
  • ohnivé květiny dávají sílu ohně, zvyšují rychlost běhu a výšku skoku;
  • Ledové květy, jakmile se utrhnou, vám umožní zmrazit nepřátele.

Mario postavy

Hlavní postava má 4 stupně transformace:

  • klasický Mario je nejslabší formou postavy, může snadno přijít o život;
  • Super Mario je dvakrát větší než ten klasický, dokáže vzdorovat nepříteli bez ztráty života, ale dotek nepřítele se změní v malou podobu;
  • Fire or Ice Mario si hraje se superschopnostmi ohně a ledu;
  • nepřemožitelná postava obdrží dočasné kouzlo nezranitelnosti poté, co se dotkne superhvězdy.

Utržením ohnivé nebo ledové květiny Mario změní barvu a může útočit na nepřátele míčky. Ohnivé koule se odrážejí a dokážou na dálku porazit téměř všechny nepřátele. Led - válejte a zmrazte nepřítele.

Nyní víte, jak hrát Mario. Zbývá poslední tajemství: na konci každé úrovně je stožár, ze kterého postava sejme vlajku a dokončuje úroveň, vesele mává. Upozorňujeme, že čím vyšší je místo, kde narazíte na stožár, tím více bodů váš hrdina získá. Přeji dobrou hru!

Chcete-li začít, stáhněte si úvodní projekt pro tento výukový program. Rozbalte jej, otevřete v Xcode a spusťte. Na obrazovce emulátoru by se mělo objevit něco takového:

Přesně tak – jen nudná prázdná obrazovka! :] Vyplníme to úplně, jak projdeme tutoriálem
Všechny potřebné obrázky a zvuky již byly přidány do začínajícího projektu. Pojďme se podívat na obsah projektu:

  • Herní umění. Zahrnuje bezplatný herní umělecký balíček od Rayovy manželky Vicki.
  • Úroveň mapy. Speciálně pro vás jsem nakreslil mapu úrovní, počínaje první úrovní v SMB.
  • Skvělé zvukové efekty. Ostatně návod od raywenderlich.com! :]
  • Podtřída CCLayer. Třída s názvem GameLevelLayer, která implementuje b Ó většinu našeho fyzikálního enginu. I když teď je prázdný jako korek. (Ano, toto dítě jen čeká na naplnění!)
  • podtřída CCSprite. Třída s názvem Player, která obsahuje logiku koaly. Právě teď se naše koala pokouší odletět do dálky!

Základy fyzikálního motoru

Plošinovky fungují na bázi fyzikálních enginů a v tomto tutoriálu si napíšeme vlastní fyzikální engine.
Existují dva důvody, proč musíme napsat vlastní engine a nepoužívat stejný Box2D nebo Chipmink:
  1. Podrobné nastavení. Abyste mohli naplno zažít zen plošinovek, musíte se naučit, jak plně přizpůsobit svůj engine.
  2. Jednoduchost. Box2D a Chipmunk mají spoustu přizpůsobitelných funkcí, které vlastně nebudeme potřebovat. Navíc budou zdroje. A náš vlastní motor bude žrát přesně tolik, kolik mu dovolíme.
Fyzikální engine plní dva hlavní úkoly:
  1. Simuluje pohyb. Prvním úkolem fyzikálního enginu je simulovat protichůdné síly gravitace, pohybu, skákání a tření.
  2. Detekuje kolize. Druhým úkolem je odhalit kolize mezi hráčem a ostatními objekty v úrovni.
Například při skoku na naši koalu působí síla směřující vzhůru. Po nějaké době gravitační síla převýší sílu skoku, čímž získáme klasickou parabolickou změnu rychlosti.
Pomocí detekce kolize zastavíme naši koalu pokaždé, když bude chtít pod vlivem gravitace projít podlahou, a zjistíme, kdy naše koala šlápla na hroty (au!).
Podívejme se, jak to funguje v praxi.

Vytvoření fyzikálního enginu

Ve fyzikálním enginu, který vytvoříme, bude mít Koala své vlastní proměnné, které popisují její pohyby: rychlost, zrychlení a polohu. Pomocí těchto proměnných budeme v každém kroku našeho programu používat následující algoritmus:
  1. Je vybrána akce skoku nebo pohybu?
  2. Pokud ano, použijte na koalu sílu skoku nebo pohybu.
  3. Aplikujte také gravitaci na koalu.
  4. Vypočítejte výslednou rychlost koaly.
  5. Aplikujte výslednou rychlost na koalu a aktualizujte její polohu.
  6. Zkontrolujte, zda nedošlo ke kolizi koaly s jinými objekty.
  7. Pokud dojde ke srážce, pak buď přesuňte koalu do takové vzdálenosti od překážky, aby již ke srážkám nedocházelo; nebo způsobit poškození nebohé koaly.

Tyto kroky projdeme v každém kroku programu. V naší hře gravitace neustále tlačí koalu níž a níž skrz podlahu, ale detekce kolize ji pokaždé přivede zpět do bodu nad podlahou. Tuto funkci můžete také použít k určení, zda se koala dotýká země. Pokud ne, můžete hráči zabránit ve skoku, když je Koala ve stavu skákání nebo právě skočila z překážky.
Body 1-5 se vyskytují uvnitř objektu Koala. Všechny potřebné informace by měly být uloženy uvnitř tohoto objektu a je celkem logické umožnit Koale, aby sama aktualizovala své proměnné.
Když však dojde na 6. bod – detekci kolizí – musíme vzít v úvahu všechny vlastnosti úrovně, jako jsou: stěny, podlahy, nepřátelé a další nebezpečí. Detekce kolize bude prováděna v každém kroku programu pomocí GameLevelLayer – dovolte mi připomenout, že toto je podtřída CCLayer, která bude provádět většinu fyzikálních úkolů.
Pokud dovolíme koalě, aby sama aktualizovala svou pozici, nakonec se koala dotkne stěny nebo podlahy. A GameLevelLayer přivede Koalu zpět. A tak znovu a znovu – díky čemuž Koala vypadá, jako by vibrovala. (Moc kávy dnes ráno, Coalio?)
A tak nedovolíme, aby Koala aktualizovala svůj stav. Místo toho dáme Koale novou proměnnou požadovanouPosition, kterou Koala aktualizuje. GameLevelLayer zkontroluje, zda lze koalu přesunout na požadovanou pozici. Pokud ano, pak GameLevelLayer aktualizuje stav koaly.
Vše jasné? Pojďme se podívat, jak to vypadá v kódu!

Načítání TMXTiledMap

Předpokládám, že víte, jak fungují dlaždicové mapy. Pokud ne, doporučuji si o nich přečíst.
Pojďme se podívat na úroveň. Spusťte editor dlaždic dlaždic (stáhněte si, pokud jste tak dosud neučinili) a otevřete úroveň1.tmx ze složky vašeho projektu. Uvidíte následující:

Když se podíváte na postranní panel, uvidíte, že máme tři různé vrstvy:

  • nebezpečí: Tato vrstva obsahuje věci, na které si Koala musí dávat pozor, aby zůstala naživu.
  • stěny: Tato vrstva obsahuje buňky, kterými koala nemůže projít. V podstatě se jedná o podlahové buňky.
  • Pozadí: Tato vrstva obsahuje čistě estetické věci, jako jsou mraky nebo kopce.
Je čas kódovat! OTEVŘENO GameLevelLayer.m a přidejte následující po #import, ale před @implementation:

@interface GameLevelLayer() ( CCTMXTiledMap *map; ) @end
Přidali jsme místní mapu proměnných třídy CCTMXTiledMap pro práci s mapami sítě v naší třídě head.
Dále umístíme síťovou mapu na naši vrstvu přímo během inicializace vrstvy. K metodě přidáme následující init:

CCLayerColor *blueSky = [ initWithColor:ccc4(100, 100, 250, 255)]; ; map = [initWithTMXFile:@"level1.tmx"]; ;
Nejprve jsme přidali pozadí (CCLayerColor) v barvě modré oblohy. Další dva řádky kódu jednoduše načtou proměnnou mapy (CCTMXTiledMap) a přidají ji do vrstvy.

#import "Player.h"
Stále v GameLevelLayer.m Přidejme následující lokální proměnnou do sekce @interface:

Player = [initWithFile:@"koalio_stand.png"]; hráč.pozice = ccp(100, 50); ;
Tento kód načte objekt sprite Koala, udělí mu polohu a přidá jej do našeho objektu mapy.
Možná se ptáte, proč přidávat objekt koala na mapu, místo toho, abyste jej přidali přímo do vrstvy? Je to jednoduché. Chceme přímo ovládat, která vrstva bude před koalou a která za ní. Takže z koaly uděláme dítě mapy, ne hlavní vrstvu. Chceme, aby byla koala vpředu, a proto jí dáme Z-pořadí 15. Také když posouváme mapu, koala je stále ve stejné pozici vzhledem k mapě, nikoli k hlavní vrstvě.
Skvělé, zkusíme to! Spusťte svůj projekt a měli byste vidět následující:

Vypadá to jako hra, ale Coalio vzdoruje gravitaci! Je čas to přivést na zem – pomocí fyzikálního enginu:]

Gravitační situace Coalio


Chcete-li vytvořit fyzikální simulaci, můžete napsat složitou sadu logiky větvení, která by vzala v úvahu stav Koaly a aplikovala na ni síly na základě obdržených informací. Ale tento svět se okamžitě stane příliš složitým - a skutečná fyzika nefunguje tak složitě. V reálném světě gravitace jednoduše neustále stahuje předměty dolů. Takže přidáme konstantní gravitační sílu a aplikujeme ji na koalu v každém kroku programu.
Ostatní síly se také jen tak nezapínají a nevypínají. V reálném světě působí síla na předmět, dokud jiná síla není větší nebo rovna první.
Například síla skoku nevypne gravitaci; po nějakou dobu překonává gravitační sílu, dokud gravitace opět nepřitlačí koalu k zemi.
Tak se modeluje fyzika. Nerozhodujete se jen o tom, zda na koalu použijete gravitační sílu nebo ne. Gravitace vždy existuje.

Pojďme si hrát na boha


Logika našeho motoru říká, že pokud na objekt působí nějaká síla, bude se dále pohybovat, dokud jiná síla nepřekročí první. Když Coalio skočí z římsy, pokračuje v pohybu dolů s určitým zrychlením, dokud nenarazí na překážku v cestě. Když pohneme Coalio, nepřestane se pohybovat, dokud na něj nepřestaneme působit pohybovou silou; tření bude působit na Coalio, dokud se nezastaví.
Při sestavování fyzikálního enginu uvidíte, jak taková jednoduchá herní logika může pomoci vyřešit složité fyzikální problémy, jako je ledová podlaha nebo pád z útesu. Tento model chování umožňuje hře dynamicky se měnit.
Takový tah rytíře nám také umožní usnadnit implementaci, protože se nemusíme neustále ptát na stav našeho objektu - objekt se jednoduše bude řídit fyzikálními zákony z reálného světa.
Někdy si musíme hrát na boha! :]

Zákony planety Země: CGPody a síly

Pojďme si definovat následující pojmy:
  • Rychlost popisuje, jak rychle se objekt pohybuje určitým směrem.
  • Akcelerace popisuje, jak se rychlost a směr objektu mění v průběhu času.
  • Platnost je vliv, který způsobuje změnu rychlosti nebo směru.
Ve fyzikální simulaci síla aplikovaná na objekt zrychlí objekt na určitou rychlost a objekt se bude touto rychlostí pohybovat, dokud na cestě nenarazí na jinou sílu. Rychlost je veličina, která se mění od jednoho snímku k dalšímu, když se objeví nové síly.
Pomocí struktur CGPoint budeme reprezentovat tři věci: rychlost, sílu/zrychlení a polohu. Existují dva důvody, proč používat struktury CGPoint:
  1. Jsou 2D. Rychlost, síla/zrychlení a poloha jsou 2D veličiny pro 2D hru. Dá se říci, že gravitace působí pouze jedním směrem, ale co když v jednom bodě hry nutně potřebujeme změnit směr gravitace? Myslete na galaxii Super Mario!
  2. Je to pohodlné. Pomocí CGPoint můžeme využít různé funkce zabudované do Cocos2D. Použijeme zejména ccpAdd (sčítání), ccpSub (odčítání) a ccpMult (násobení proměnnou float). To vše usnadní čtení a ladění našeho kódu!
Náš objekt Koala bude mít proměnnou rychlost, která se bude měnit v důsledku výskytu různých sil, včetně gravitace, pohybu, skákání, tření.
V každém kroku hry sečteme všechny síly dohromady a výsledná hodnota se přičte k aktuální rychlosti Koaly. V důsledku toho získáme novou aktuální rychlost. Snížíme ji pomocí snímkové frekvence. Poté přesuneme koalu.
Začněme gravitací. Napíšeme běhovou smyčku, ve které budeme uplatňovat síly. Přidejte do metody init souboru GameLevelLayer.m následující kód těsně před uzavřením podmíněného bloku if:

;
Dále přidejte do třídy novou metodu:

- (void)update:(ccTime)dt ( ; )
Dále otevřete Hráč.h a změňte jej, aby vypadal takto:

#import #import "cocos2d.h" @interface Player: CCSprite @property (neatomické, přiřadit) CGPoint velocity; - (void)update:(ccTime)dt; @konec
Přidejte následující kód do Player.m:

Stiskněte mě

#import "Player.h" @implementation Player @synthesize velocity = _velocity; // 1 - (id)initWithFile:(NSString *)název souboru ( if (self = ) ( self.velocity = ccp(0.0, 0.0); ) return self; ) - (void)update:(ccTime)dt ( // 2 CGPot gravitace = ccp(0.0, -450.0) // 3 CGPot gravityStep = ccpMult(gravitace, dt) // 4 self.velocity = ccpAdd(self.velocity, gravityStep); = ccpAdd(self.position, stepVelocity ) @end);


Pojďme si projít výše uvedený kód krok za krokem
  1. Zde jsme přidali novou metodu init pro inicializaci objektu a nastavení proměnné rychlosti na nulu.
  2. Zde jsme určili hodnotu vektoru gravitace. Každou sekundu zrychlíme rychlost koaly o 450 pixelů.
  3. Zde jsme použili ccpMult ke snížení hodnoty vektoru gravitace, aby vyhovovala snímkové frekvenci. ccpMult přijme float a CGPoint a vrátí CGPoint.
  4. Zde, jakmile jsme vypočítali gravitaci pro aktuální krok, přičteme ji k aktuální rychlosti.
  5. Nakonec, jakmile spočítáme rychlost pro jeden krok, použijeme ccpAdd k aktualizaci pozice Koaly.
Gratulujeme! Jsme na dobré cestě k vytvoření našeho prvního fyzikálního enginu! Spusťte svůj projekt a uvidíte výsledky!

Jejda - Coalio propadne podlahou! Pojďme to napravit.

Nárazy v noci - detekce kolize

Detekce kolize je základem každého fyzikálního enginu. Existuje mnoho různých typů detekce kolizí, od jednoduchého použití obrazových snímků až po komplexní detekci kolizí 3D objektů. Naštěstí pro nás platforming nevyžaduje složité struktury.
K detekci kolizí Koaly s objekty použijeme TMXTileMap pro buňky, které Koalu bezprostředně obklopují. Dále pomocí několika funkcí zabudovaných do iOS zkontrolujeme, zda sprite Koala protíná sprite libovolné buňky.
Funkce CGRectIntersectsRect a CGRectIntersection takové kontroly velmi zjednodušují. CGRectIntersectsRect zkontroluje, zda se dva obdélníky protínají, a CGRectIntersection vrátí průsečíkový obdélník.
Nejprve musíme definovat rámec naší koaly. Každý načtený sprite má ohraničení, což je velikost textury, a lze k němu přistupovat pomocí parametru zvaného boundingBox.
Proč definovat hranici, když už je v boundingBoxu? Textura má většinou kolem sebe průhledné okraje, což se nám při detekci kolizí moc nechce brát v úvahu.
Někdy nemusíme vzít v úvahu ani pár pixelů kolem skutečného obrázku sprite (neprůhledný). Když Mario narazí do zdi, sotva se jí dotkne, nebo se mu nos lehce zaboří do bloku?
Zkusme to. Přidat do Hráč.h:

-(CGRect)collisionBoundingBox;
A přidat Player.m:

- (CGRect)collisionBoundingBox ( return CGRectInset(self.boundingBox, 2, 0); )
CGRectInset komprimuje CGRect o počet pixelů z druhého a třetího argumentu. V našem případě bude šířka našeho kolizního snímku o šest pixelů menší – tři pixely na každé straně.

Vzpírání

Je čas zvedat činky. („Hele, říkáš mi, že jsem tlustá?“ říká Coalio).
K detekci kolizí budeme v naší GameLevelLayer potřebovat řadu metod. Zejména:
  • Metoda, která vrací souřadnice osmi buněk obklopujících aktuální buňku Coalio.
  • Metoda, která určuje, které buňky jsou překážkou (a zda vůbec nějaké existují). Některé buňky nemají žádné fyzikální vlastnosti (oblaky) a Coalio s nimi nebude kolidovat.
  • Metoda, která řeší kolize v pořadí priority.
Vytvoříme dvě pomocné funkce, které výše popsané metody zjednoduší.
  • Metoda, která určuje polohu buňky Coalio.
  • Metoda, která přijímá souřadnice buňky a vrací obdélník buňky v souřadnicích Cocos2D.
Přidejte následující kód do GameLevelLayer.m:

- (CGPoint)tileCoordForPosition:(CGPoint)position (plovoucí x = podlaha(pozice.x / mapa.velikost.šířka); plovoucí úroveňVýškaVPixelech = mapa.Velikost mapy.výška * Velikost mapy.Velikost.výška; plovoucí y = podlaha((úroveňVýškaVPixelech - position.y) / map.tileSize.height); return ccp(x, y) - (CGRect)tileRectFromTileCoords:(CGPoint)tileCoords ( float levelHeightInPixels = map.mapSize.height * map.tileSize.height; CGPoint origin =); ccp(tileCoords.x * map.tileSize.width, levelHeightInPixels - ((tileCoords.y + 1) * map.tileSize.height)); return CGRectMake(origin.x, origin.y, map.tileSize.width, map. tileSize.height);
První metoda nám vrátí souřadnice buňky umístěné na souřadnicích pixelů, které předáme metodě. Abychom získali polohu buňky, jednoduše vydělíme souřadnice velikostí buněk.
Potřebujeme převrátit souřadnice výšky, protože systémové souřadnice Cocos2D/OpenGL začínají v levém dolním rohu a systémové souřadnice začínají v levém horním rohu. Standardy – není to cool?
Druhá metoda dělá opak. Vynásobí souřadnici buňky velikostí buněk a vrátí CGRect dané buňky. Opět musíme zvětšit výšku.
Proč potřebujeme přidat jedničku k souřadnici y výšky? Pamatujte, že souřadnice buňky začínají nulou, takže buňka 20 má skutečnou souřadnici 19. Pokud k výšce nepřičteme jedničku, bude bod 19 * tileHeight.

Jsem obklopen buňkami!

Nyní přejdeme k metodě, která určuje buňky obklopující koalu. V této metodě vytvoříme pole, které vrátíme. Toto pole bude obsahovat GID buňky, souřadnice buňky a informace CGRect této buňky.
Uspořádáme toto pole podle priority, ve které budeme detekovat kolize. Chceme například detekovat kolize shora, zleva, zprava, zespodu, než definujeme kolize diagonální. Také, když zjistíme kolizi koaly se spodní buňkou, nastavíme příznak dotyku země.
Přidejme tuto metodu k GameLevelLayer.m:

Stiskněte mě

- (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 – celý mrak kódu. Nebojte se, projdeme si to podrobně.
Ještě předtím si ale všimněte, že v naší mapě máme tři vrstvy.
Různé vrstvy nám umožňují definovat kolize pro každou vrstvu jinak.
  • Koala a nebezpečí. Pokud dojde ke srážce, zabijeme koalu (docela brutální, že?).
  • Koala a zdi. Pokud dojde ke srážce, nedovolíme Koale, aby se dále pohybovala tímto směrem. "Přestaň, klisno!"
  • Koala a pozadí. Pokud dojde ke kolizi, pak neděláme nic. Líný programátor je nejlepší programátor. Nebo co říkají lidé?
Samozřejmě existují různé způsoby, jak detekovat různé kolize s různými bloky, ale to, co máme - vrstvy na mapě - je docela efektivní.
Dobře, pojďme si projít kód krok za krokem.

1. Nejprve získáme souřadnice vstupní buňky (což budou souřadnice koaly).
2. Dále vytvoříme nové pole, které bude vracet informace o buňce.
3. Dále spustíme smyčku 9krát - protože máme 9 možných buněk pohybu, včetně buňky, ve které se koala již nachází. Následujících několik řádků definuje pozice devíti buněk a ukládá je do proměnné tilePos.

Poznámka: potřebujeme pouze informace o osmi buňkách, protože nikdy nebudeme muset detekovat srážky s buňkou, na které už koala je.
Vždy bychom měli využít této příležitosti a přesunout koalu do jedné z okolních buněk. Pokud je Coalio uvnitř pevné buňky, pak více než polovina skřítka Coalio odešla dovnitř. Neměl by se pohybovat tak rychle - alespoň ne v této hře!
Pro snazší obsluhu těchto osmi článků jednoduše přidejte na začátek článek Coalio a na konci jej odstraňte.

4. Ve čtvrté sekci voláme metodu tileGIDAt:. Tato metoda vrací GID buňky na konkrétní souřadnici. Pokud na přijatých souřadnicích není žádná buňka, metoda vrátí nulu. V následujícím textu budeme nulu používat ve významu „buňka nenalezena“.
5. Dále použijeme pomocnou metodu k výpočtu CGRect pro buňku na daných souřadnicích Cocos2D. Přijaté informace ukládáme do NSDictionary. Metoda vrací pole přijatého NSDictionary.
6. V šesté sekci odstraníme buňku Koala z pole a seřadíme buňky v pořadí podle priority.

Často v případě detekce kolizí s buňkou pod Koalou detekujeme i srážky s buňkami podél diagonály. Viz obrázek vpravo. Při detekci kolizí s buňkou pod Koalou, zvýrazněnou červeně, detekujeme také kolize s blokem #2, zvýrazněným modře.
Náš algoritmus detekce kolizí bude mít nějaké předpoklady. Tyto předpoklady platí spíše pro sousední než diagonální buňky. Pokusíme se tedy co nejvíce vyhnout práci s diagonálními buňkami.
A zde je obrázek, který nám názorně ukazuje pořadí buněk v poli před a po seřazení. Všimnete si, že jako první se zpracují horní, dolní, pravá a levá buňka. Znalost pořadí buněk vám usnadní určení, kdy se Koala dotýká země nebo létá v oblacích.

7. Smyčka v sekci sedm nám umožňuje sledovat buňky v reálném čase. Můžeme tak s jistotou vědět, že vše jde podle plánu.

Jsme téměř připraveni na další spuštění naší hry! Stále však existuje několik věcí, které je třeba udělat. Musíme přidat vrstvu stěn jako proměnnou do třídy GameLevelLayer, abychom ji mohli používat.

Uvnitř GameLevelLayer.m proveďte následující změny:

// Přidat do @interface CCTMXLayer *walls; // Přidá do metody init po přidání mapy do vrstev stěny = ; // add to update method;
Zahájení! Ale bohužel hra spadne. V konzoli vidíme něco takového:

Nejprve získáme informace o pozicích buněk a hodnotách GID (i když většinou nuly, protože nahoře je prázdné místo).
Nakonec vše spadne s chybou „TMXLayer: neplatná pozice“. K tomu dochází, když je pozice předána metodě tileGIDat:, která je mimo okraje mapy.
Této chybě se vyvarujeme o něco později – ale nejprve změníme stávající definici kolize.

Vezmeme si Koalina privilegia zpět

Do této chvíle Koala aktualizovala svou pozici sama. Ale teď jí toto privilegium bereme.

Pokud koala nezávisle aktualizuje svou pozici, nakonec začne poskakovat jako blázen! Ale to přece nechceme, že?
Koala tedy vyžaduje další proměnnou, requiredPosition, se kterou bude interagovat s GameLevelLayer.
Chceme, aby třída Koala vypočítala svou další pozici sama. Ale GameLevelLayer by měl přesunout Koalu na požadovanou pozici až poté, co zkontroluje její platnost. Totéž platí pro smyčku detekce kolizí – nechceme aktualizovat aktuální sprite, dokud nebudou všechny buňky zkontrolovány na kolize.
Musíme změnit pár věcí. Nejprve přidejte následující do Hráč.h

@vlastnost (neatomická, přiřadit) CGPod požadovanýPozice;
A syntetizovat to, co bylo přidáno Player.m:

@synthesize požadovanáPosition = _desiredPosition;
Nyní změňte metodu crashBoundingBox PROTI Player.m takže to vypadá takto:

- (CGRect)collisionBoundingBox ( CGRect kolizeBox = CGRectInset(self.boundingBox, 3, 0); CGPoint diff = ccpSub(self.desiredPosition, self.position); CGRect returnBoundingBox = CGRectOffset(collisionBox, diff.); return returnBoundingBox;
Tento kus kódu vypočítá rámec na základě požadované pozice, kterou GameLevelLayer použije k detekci kolizí.

Poznámka: Existuje mnoho různých způsobů, jak vypočítat kolizní snímky. Dalo by se napsat kód podobný tomu, co je již ve třídě CCNode, ale naše současná metoda je mnohem jednodušší, i když trochu nezřejmá.
Dále proveďte následující změny metody aktualizace tak, aby aktualizovala požadovanou pozici namísto aktuální pozice:

// Nahraďte "self.position = ccpAdd(self.position, stepVelocity);" to: self.desiredPosition = ccpAdd(self.position, stepVelocity);

Začněme odhalovat kolize!

Nastal čas pro vážné úspěchy. Dáme všechno dohromady. Přidejte následující metodu do GameLevelLayer.m:

Stiskněte mě

- (void)checkForAndResolveCollisions:(Player *)p ( NSArray *dlaždice = ; //1 pro (NSDictionary *dic v dlaždicích) ( 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); //5 int tileIndx = ; //6 if (tileIndx == 0) ( //Buňka přímo pod Koalou p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y + intersection.size.height); ) else if (tileIndx == 1) ( //Buňka přímo nad koalou p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y - intersection.size.height); ) else if (tileIndx == 2) ( //Buňka nalevo od koaly p.desiredPosition = ccp(p.desiredPosition.x + intersection.size.width, p.desiredPosition.y ) else if (tileIndx == 3) ( //Buňka napravo od koaly p .desiredPosition = ccp(p.desiredPosition.x - intersection.size.width, p.desiredPosition.y); ) else ( if (intersection.size.width > intersection.size.height) ( //7 //Buňka je diagonální, ale problém řešíme svisle plovoucí intersectionHeight; if (tileIndx > 5) ( intersectionHeight = intersection.size. výška; ) else ( intersectionHeight = -intersection.size.height; ) p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y + intersection.size.height ) else ( //Buňka je diagonální, ale my vyřešit problém vodorovně float resolutionWidth ; if (tileIndx == 6 || tileIndx == 4) ( resolutionWidth = intersection.size.width; ) else ( resolutionWidth = -intersection.size.width; ) p.desiredPosition = ccp(); p.požadovanáPozice.x , p. požadovanáPozice.y + rozlišeníŠířka ) ) ) ) p.pozice = p.požadovanáPozice; //7)


Skvělý! Podívejme se na kód, který jsme právě napsali.

1. Nejprve získáme sadu buněk obklopujících koalu. Dále projdeme každou buňkou z této sady. Pokaždé, když procházíme buňkou, kontrolujeme, zda v ní nejsou kolize. Pokud dojde ke kolizi, změníme požadovanou pozici koaly.
2. V každé smyčce smyčky nejprve získáme aktuální rámec koaly. Pokaždé, když je detekována kolize, proměnná requiredPosition změní svou hodnotu na takovou, že ke kolizi již nedochází.
3. Dalším krokem je získání GID, které jsme uložili do NSDictionary, které může být null. Pokud je GID nula, pak aktuální smyčka končí a přecházíme na další buňku.
4. Pokud je na nové pozici buňka, musíme ji získat CGRect. Může a nemusí dojít ke kolizi. Tento proces provedeme pomocí následujícího řádku kódu a uložíme jej do proměnné tileRect. Nyní, když máme Koalův CGRect a buňky, můžeme je otestovat na kolizi.
5. Chcete-li zkontrolovat kolizi buněk, spustíme CGRectIntersectsRect. Pokud dojde ke kolizi, dostaneme CGRect popisující průsečík CGRect pomocí funkce CGRectIntersection().

Přestaňme přemýšlet o dilematu...

Docela zajímavý případ. Musíme vymyslet, jak správně detekovat kolize.
Možná si myslíte, že nejlepší způsob, jak přesunout koalu, je oddálit ji od srážky. Některé fyzikální motory takto skutečně fungují, ale zkusíme lepší řešení.
Přemýšlejte o tom: gravitace neustále stahuje koalu dolů do buněk pod ní a tyto srážky se neustále dějí. Pokud si představíte koalu pohybující se vpřed, zároveň je koala stále stahována dolů gravitací. Pokud tento problém vyřešíme jednoduchou změnou pohybu v opačném směru, Koala se bude pohybovat nahoru a doleva - ale my potřebujeme něco jiného!
Naše koala se musí posunout o dostatečnou vzdálenost, aby stále zůstala nad těmito buňkami, ale pokračovala v pohybu vpřed stejným tempem.

Stejný problém nastane, pokud koala sklouzne po zdi. Pokud hráč zatlačí koalu proti zdi, požadovaná trajektorie koaly bude nasměrována diagonálně dolů a do zdi. Jednoduše obrácením směru přimějeme koalu, aby se pohybovala nahoru a pryč od zdi - opět, vůbec ne totéž! Pak chceme, aby koala zůstala mimo zeď, ale stále klesala stejným tempem!

Musíme se tedy rozhodnout, kdy zpracovat kolize vertikálně a kdy vodorovně, a zvládnout obě akce, které se vzájemně vylučují. Některé fyzikální motory neustále zpracovávají nejprve první událost a pak druhou; ale chceme se lépe rozhodnout na základě pozice buňky koaly. Takže například, když je buňka přímo pod koalou, chceme, aby detektor kolizí přivedl koalu zpět nahoru.
Co když je buňka diagonálně k pozici koaly? V tomto případě používáme křižovatky CGRect, abychom zjistili, jak bychom měli přesunout Koalu. Pokud je šířka tohoto obdélníku větší než výška, musí být koala vrácena svisle. Pokud je výška větší než šířka, pak by se koala měla pohybovat vodorovně.

Tento proces bude fungovat správně, pokud bude rychlost a snímková frekvence Koaly v určitých mezích. O něco později se naučíme vyhýbat se případům, kdy koala spadne příliš rychle a skočí dolů skrz celu.
Jakmile určíme, zda přesunout koalu vertikálně nebo horizontálně, použijeme velikost CGRect průsečíku k určení toho, jak moc se má přesunout koala. Podíváme se na šířku nebo výšku a použijeme tuto hodnotu jako vzdálenost posunutí koaly.
Proč kontrolovat buňky v určitém pořadí? Vždy byste měli nejprve pracovat na sousedních buňkách a poté na diagonálních. Koneckonců, pokud chcete zkontrolovat buňku vpravo dole od koaly, zda nedošlo ke kolizi, pak bude vektor posunutí nasměrován vertikálně.

Stále však existuje šance, že kolize CGRect bude vytažena nahoru, když se Koala sotva dotkne buňky.
Podívejte se na obrázek vpravo. Modrá oblast je roztažena nahoru, protože kolizní obdélník je pouze malou částí celkové kolize. Pokud jsme však již vyřešili problém s buňkou přímo pod koalou, pak již nemusíme detekovat srážky s buňkou níže vpravo od koaly. Takto obcházíme vzniklé problémy.

Zpět na kód!

Vraťme se k monstrózní metodě...

6. Šestá sekce nám umožňuje získat index aktuální buňky. K získání pozice buňky používáme index buňky. Budeme operovat přilehlé buňky jednotlivě, přesouvat koaly, odečítat nebo přičítat srážkovou délku nebo výšku. Docela jednoduché. Jakmile však dojde na diagonální buňky, použijeme algoritmus popsaný v předchozí části.
7. V sedmé části určíme, zda je naše kolizní oblast široká nebo protáhlá směrem nahoru? Pokud je široký, pracujeme svisle. Pokud je index buňky větší než 5, posuňte koalu nahoru. Pokud je oblast natažená nahoru, pracujeme vodorovně. Dodržujeme podobný princip řazení buněčných indexů. Na konci přiřadíme výslednou pozici koale.

Tato metoda je mozkem našeho systému detekce kolizí.

Uveďme všechny naše dosavadní znalosti do praxe! Změnit metodu Aktualizace(stále v GameLevelLayer:)

// Nahradit ";" na: ;
Blok můžete také smazat nebo okomentovat getSurroundingTilesAtPosition:forLayer:

/* pro (NSDictionary *d v gids) ( NSLog(@"%@", d); ) //8 */
Začněme! Překvapený výsledkem?

Paul zastaví Coalio, ale ten se v něm okamžitě utopí! Proč?
Uhodnete, co nám uniklo? Pamatujte - každý krok hry přidáváme gravitaci k rychlosti Koaly. To znamená, že Koala neustále zrychluje směrem dolů.
Sestupné trajektorii koaly neustále přidáváme rychlost, dokud nedosáhne velikosti buňky – projedeme celou buňkou v jednom kroku, což způsobuje problémy (nezapomeňte, že jsme o tom nedávno mluvili).
Jakmile zjistíme kolizi, musíme obnovit rychlost koaly ve směru buňky, do které se srazila! Koala se přestala pohybovat, takže je třeba počítat s rychlostí.
Pokud to neuděláme, skončíme s docela podivným herním chováním. Jak jsme poznamenali dříve, potřebujeme způsob, jak zjistit, zda se koala dotýká země, aby koala nemohla vyskočit ještě výš. Toto políčko hned zaškrtneme. Přidejte následující řádky do checkForAndResolveCollisions:

Stiskněte mě

- (void)checkForAndResolveCollisions:(Player *)p ( NSArray *dlaždice = ; //1 p.onGround = NE; //////Zde pro (NSDictionary *dic v dlaždicích) ( 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, tileRect). )) ( CGRect průnik = CGRectIntersection(pRect, tileRect); int tileIndx = ; if (tileIndx == 0) ( //buňka pod koalou p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y + průnik. size.height = ccp(p.velocity.x, 0,0); //////Here p.onGround = YES //////Here ) else if (tileIndx == 1) (); //buňka nad koalou p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y - intersection.size.height); p.velocity = ccp(p.velocity.x, 0.0) else if); (tileIndx == 2) ( //buňka vlevo p.desiredPosition = ccp(p.desiredPosition.x + intersection.size.width, p.desiredPosition.y); ) else if (tileIndx == 3) ( // buňka vpravo p.desiredPosition = ccp(p.desiredPosition.x - intersection.size.width, p.desiredPosition.y); ) else ( if (intersection.size.width > intersection.size.height) ( //dlaždice je diagonální, ale řeší kolize svisle p.velocity = ccp(p.velocity.x, 0,0); //////Zde float resolutionHeight; if (tileIndx > 5) ( resolutionHeight = intersection.size.height; p.onGround = YES; //////Hele ) else ( resolutionHeight = -intersection.size.height; ) p.desiredPosition = ccp( p.desiredPosition.x, p.desiredPosition.y + resolutionHeight else ( float resolutionWidth; if (tileIndx == 6 || tileIndx == 4) (solutionWidth = intersection.size.width; ) else ( resolutionWidth = -intersection . size.width ) p.požadovanáPozice = ccp(p.požadovanáPozice.x + rozlišeníŠířka, p.požadovanáPozice.y); //8)


Pokaždé, když je pod Koalou buňka (ať už sousedící nebo diagonální), nastavíme proměnnou p.onGround na YES a vynulujeme rychlost. Také, pokud je pod Koalou sousední buňka, vynulujeme její rychlost. To nám umožní správně reagovat na aktuální rychlost Koaly.
Proměnnou onGround nastavíme na začátku cyklu na NO. V tomto případě bude onGround nastaveno na ANO pouze tehdy, když zjistíme kolizi koaly s buňkou pod ní. Tuto funkci můžeme použít k určení, zda koala může v aktuálním okamžiku skočit nebo ne.
Přidejte následující kód do souboru záhlaví (a poté syntetizujte vše potřebné ve spustitelném souboru). Hráč.h:

@property (neatomická, přiřadit) BOOL onGround;
A dovnitř Player.m:

@synthesize onGround = _onGround;
Začněme! Funguje vše podle plánu? Ano! Ach, tento nádherný den! Hurá!

Co bude dál?

Gratulujeme! Úplně jste dokončili svůj fyzikální engine! Pokud jste se dostali k tomuto textu, můžete si vydechnout. To byla ta nejtěžší část – v druhé části tutoriálu nebude nic těžkého.
A zde jsou zdroje projektu, který jsme nyní dokončili.
Ve druhé části přimějeme naše Coalio běhat a skákat. Také učiníme bloky s hroty v podlaze nebezpečné pro naši koalu a vytvoříme obrazovky výher a proher.
Pokud chcete získat ještě více znalostí o fyzikálních motorech pro plošinovky, doporučuji vám navštívit následující zdroje:
Sonic the Hedgehog Wiki je skvělým vysvětlením toho, jak Sonic interaguje s pevnými buňkami.
Možná nejlepší průvodce vytvářením plošinovek z Higher-Order Fun.
výukový program Přidat značky