Wie man ein Spiel wie Fruit Ninja mit Box2D und Cocos2D macht – Teil 1

Dies ist ein Beitrag von iOS Tutorial Teammitglied Allen Tan, einem iOS Entwickler und Mitbegründer von White Widget. Sie finden ihn auch auf Google+ und Twitter.

In diesem Tutorial erfahren Sie, wie Sie ein Sprite-Schneidspiel für das iPhone erstellen, das Fruit Ninja von Halfbrick Studios ähnelt, indem Sie die leistungsstarken Bibliotheken Cocos2D und Box2D sowie einige vorgefertigte Tools verwenden.

Wenn Sie in den meisten Slicing-Spielen eine Schnittlinie durch ein Sprite zeichnen, konvertiert das Spiel das Sprite-Bild normalerweise in zwei vorgezeichnete Sprite-Bilder, wobei das Slice immer in der Mitte liegt, unabhängig davon, wo Sie tatsächlich schneiden.

Aber dieses Tutorial wird eine noch coolere Technik demonstrieren. Unsere Früchte können mehrmals geschnitten werden, und sie werden dynamisch basierend auf den genauen Schnittlinien geteilt!

Wie Sie sich vorstellen können, ist dies eine fortgeschrittene Technik, daher ist dieses Tutorial für fortgeschrittene Cocos2D- und Box2D-Entwickler gedacht. Wenn Sie neu in Cocos2D oder Box2D sind, sollten Sie (zumindest) die Tutorials Intro to Cocos2D und Intro to Box2D durchgehen, bevor Sie mit diesem Tutorial fortfahren.

Diese Tutorial-Serie ist in drei Teile gegliedert:

  • In diesem ersten Teil der Serie legen Sie den Grundstein für das Spiel und lernen, wie Sie strukturierte Polygone erstellen.
  • Der zweite Teil zeigt Ihnen, wie Sie & diese strukturierten Polygone in Scheiben schneiden.
  • Der dritte Teil zeigt Ihnen, wie Sie dies in ein komplettes Spiel verwandeln können, indem Sie Gameplay und Effekte hinzufügen.

Ich möchte mich besonders bei Rick Smorawski bedanken, der die Grundlagen für das Projekt gelegt hat, auf dem dieses Tutorial basiert. Er war verantwortlich für die Portierung dieser Flash-basierten Slicing-Demo in Cocos2D sowie für die Portierung von CCBlade und PRKit nach Cocos2D 2.0.

Lesen Sie weiter, um das Video zu sehen, was Sie machen werden, und um einige coole neue Techniken zu lernen!

Spieldemo

Hier ist ein Demo-Video, das Ihnen zeigt, was Sie in dieser Tutorial-Serie machen werden:

Wie ich bereits erwähnt habe, werden Sie sehen, dass der Fruchtschneideeffekt wirklich dynamisch ist. Die Frucht wird dynamisch geschnitten, je nachdem, wo Sie schneiden, und da Sie Objekte mehrmals schneiden können, können Sie die Dinge wirklich zerhacken!

Sie können sehen, dass Sie auch einen coolen Slicing-Trail-Effekt, einige Partikelsysteme, Gameplay-Logik und Sounds implementieren, um die Dinge aufzupeppen.

Es gibt viel zu berichten – also fangen wir an!

Erste Schritte: Projekteinrichtung

Sie werden Cocos2D 2 verwenden.X in diesem Projekt, so gehen Sie vor und laden Sie es, wenn Sie es nicht bereits haben. Beachten Sie, dass Sie Cocos2D 1 verwenden können.X statt 2.X. Mit 1.Sie können jedoch die Teile zum Konvertieren von PRKit und CCBlade in Cocos2D 2 überspringen.Achten Sie jedoch auf die anderen kleinen Änderungen, die Sie an diesen Klassen vornehmen.

Doppelklicken Sie nach dem Herunterladen auf den Tar, um die Archivierung aufzuheben, und installieren Sie die Vorlagen mit den folgenden Befehlen im Terminal:

cd ~/Downloads/cocos2d-iphone-2.0-beta./install-templates.sh -f -u

Starten Sie Xcode und erstellen Sie ein neues Projekt mit iOS \ cocos2d v2.x \ cocos2d iOS mit Box2d Vorlage und nennen Sie es CutCutCut.

Ihr neues Projekt sollte ungefähr so aussehen:

Projektstart

Das Wichtigste zuerst – Sie sollten die Vorlage ein wenig bereinigen, um zu einem guten Ausgangspunkt zu gelangen.

Öffne HelloWorldLayer.h und entfernen Sie die folgende Zeile:

CCTexture2D *spriteTexture_;// weak ref

Wechseln zu HelloWorldLayer.mm und nehmen Sie die folgenden Änderungen vor

// Remove this line from the top#import "PhysicsSprite.h"// Replace the init method with this-(id) init{ if( (self=)) { // enable events self.isTouchEnabled = YES; self.isAccelerometerEnabled = YES; CGSize s = .winSize; // init physics ; ; } return self;}// Remove these two methods-(void) createMenu { //all content}-(void) addNewSpriteAtPosition:(CGPoint)p methods { //all content}// Remove this line from ccTouchesEnded;

Zu diesem Zeitpunkt haben Sie alle Verweise auf PhysicsSprite aus HelloWorldLayer entfernt, die Dateien jedoch noch nicht aus dem Projekt entfernt. Später müssen Sie eine Methode kopieren, die PhysicsSprite.mm enthält woanders, so lassen Sie es für jetzt setzen.

Drücken Sie Befehl + R, um Ihr Projekt zu kompilieren und auszuführen, und Sie sollten einen leeren Bildschirm mit einem grünen Rand sehen:

 Clean Slate

Der verbleibende Vorlagencode hat Box2D debug drawing eingerichtet, das Ränder um die Box2D-Körper auf dem Bildschirm zeichnet. Sehen Sie die dünnen grünen Linien auf dem Bildschirm? Dies sind die Wände, die von der Standardmethode initPhysics generiert werden, die mit der Vorlage geliefert wurde.

Sehen Sie sich den verbleibenden Vorlagencode an, um sicherzustellen, dass Sie verstehen, was bisher vor sich geht – er initialisiert eine Box2D-Welt, richtet den Boden ein (grüne Ränder), richtet eine Zeichnung ein usw. Dies ist ein ziemlich guter „fast leerer“ Ausgangspunkt für Box2D, auf den wir von hier aus aufbauen können.

Resource Kit

Laden Sie als Nächstes die Ressourcen für dieses Projekt herunter und entpacken Sie die Datei.

Fügen Sie noch nicht alles zum Projekt hinzu; Einige der Dateien sind tatsächlich optional. Halten Sie den Ordner jedoch griffbereit – während Sie das Tutorial durchgehen, werde ich Sie von Zeit zu Zeit bitten, einige dieser Dateien in das Projekt aufzunehmen.

Hier ist, was Sie im Inneren finden:

  • Ein Hintergrundbild und ein Haufen Obstkunst von Vicki und andere verschiedene Bilder im Bilderordner
  • Der mit Gomix erstellte Hintergrundsound-Mix.es im Sounds-Ordner
  • Soundeffekte, die mit bfxr erstellt oder von freesound heruntergeladen wurden im Sounds-Ordner
  • Alle mit Particle Designer erstellten Partikelsysteme im Particle-Ordner
  • Eine von PhysicsEditor generierte PLIST-Datei mit Vertex-Informationen für die Früchte & Bombenklassen im Misc-Ordner
  • Früchte & Bombenklassen im Klassenordner
  • Die Versionen von PRKit und CCBlade, die Sie im Tutorial im Klassenordner verwenden
  • Eine Attributionsliste für Ressourcen, die unter der Attributionslizenz im Misc stehen ordner

Texturierte Polygone mit PRKit zeichnen

Unser Ziel ist es, Sprites in mehrere Teile zu schneiden. Ein typisches CCSprite enthält eine Textur und einen Begrenzungsrahmen, unabhängig von der Form des Bildes. Dies ist für unser Spiel nicht geeignet, da die Kenntnis der tatsächlichen Formen in den Bildern ein entscheidender Schritt zum Erstellen von Sprites ist, die geschnitten, geschnitten und geteilt werden können.

Sie müssen texturierte Polygone erstellen, die:

  • Erstellen einer Entsprechung zwischen einem Polygon / einer Form und einem Bild (Texturzuordnung)
  • Zeigen Sie nur die Teile des Bildes an, die sich innerhalb der Grenzen des Polygons befinden (Texturfüllung)

Weder Cocos2D noch Box2D verfügen über eine integrierte Klasse, die die gewünschten benutzerdefinierten Funktionen verarbeitet, und normalerweise würde dies eine Triangulation in Verbindung mit benutzerdefiniertem OpenGL-Zeichencode erfordern.

Klingt hart, oder?

Glücklicherweise wurden alle komplizierten Berechnungen und Zeichnungscodes, die dazu erforderlich sind, bereits von den guten Leuten von Precognitive Research geschrieben. Sie haben eine Ergänzung zur Cocos2D-Bibliothek namens PRKit erstellt, die Texturzuordnung und -füllung übernimmt.

Um mit texturierten Polygonen zu beginnen, laden Sie PRKit herunter, extrahieren Sie es und ziehen Sie den PRKit-Ordner in Ihr Projekt. Stellen Sie sicher, dass „Elemente in den Ordner der Zielgruppe kopieren“ aktiviert und „Gruppen für alle hinzugefügten Ordner erstellen“ ausgewählt ist.

Beachten Sie, dass PRKit von Precognitive Research gepflegt wird, sodass es möglicherweise rechtzeitig aktualisiert wird. Um Verwirrung zu vermeiden, enthält unser Resource Kit auch die genaue Version von PRKit, die ich bei der Erstellung dieses Tutorials verwendet habe.

Ihr Projekt sollte nun diese Dateien enthalten:

 PRKit Ja!

Kompilieren und ausführen, und Sie werden auf einige Fehler stoßen:

 PRKit muss konvertiert werden

Die Fehler werden angezeigt, da PRKit für Cocos2D 1 erstellt wurde.X, das OpenGL ES 1.1 verwendet, während Sie Cocos2D 2 verwenden.X, das OpenGL ES 2 verwendet.0, und es gibt signifikante Unterschiede zwischen den beiden.

Um dies zu beheben, öffnen Sie PRFilledPolygon.m und nehmen Sie diese Änderungen vor:

// Add inside the initWithPoints: andTexture: usingTriangulator: methodself.shaderProgram = programForKey:kCCShader_PositionTexture];// Replace the calculateTextureCoordinates method with this-(void) calculateTextureCoordinates { for (int j = 0; j < areaTrianglePointCount; j++) { textureCoordinates = ccpMult(areaTrianglePoints,1.0f/texture.pixelsWide*CC_CONTENT_SCALE_FACTOR()); textureCoordinates.y = 1 - textureCoordinates.y; }}// Replace the draw method with this-(void) draw{ CC_NODE_DRAW_SETUP(); ccGLBindTexture2D( self.texture.name ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); ccGLBlendFunc( blendFunc.src, blendFunc.dst); ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position | kCCVertexAttribFlag_TexCoords ); glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, areaTrianglePoints); glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinates); glDrawArrays(GL_TRIANGLES, 0, areaTrianglePointCount);}

Lassen Sie uns diese Änderungen Stück für Stück durchgehen.

Erstens ist in Cocos2D an jeden CCNode ein OpenGL ES 2.0-Shader-Programm angehängt. Um das PRFilledPolygon zu zeichnen, müssen Sie den integrierten Shader „Position / Textur“ verwenden, den Sie in der init-Methode zuweisen.

Als nächstes müssen Sie die korrekten Texturkoordinaten für jeden Punkt im Polygon einrichten. Dazu müssen Sie zwei Änderungen an der calculateTextureCoordinates-Methode vornehmen:

  • Skalierung: Da diese Klasse ihre eigenen Berechnungen für die Koordinaten ihrer Textur durchführt, verarbeitet sie das Retina-Display nicht automatisch. Um dies zu beheben, multiplizieren Sie einfach die Textur.pixelsWide mit CC_CONTENT_SCALE_FACTOR – ein praktischer Multiplikatorwert, der von Cocos2D zum Konvertieren von Werten zwischen regulären und Retina-Äquivalenten bereitgestellt wird.
  • Flip Y: Aus irgendeinem Grund zeichnet PRFIlledPolygon Texturen verkehrt herum, sodass Sie hier einfach den y-Wert umdrehen.

Zuletzt wird der Zeichencode auf OpenGL ES 2 aktualisiert.0, ähnlich wie sich CCSprite drawing von Cocos2D 1 geändert hat.X zu Cocos2D 2.X:

  • Rufen Sie zunächst CC_NODE_DRAW_SETUP() auf, um den Knoten für das Zeichnen vorzubereiten.
  • Aufrufe von glDisableClientState() und glEnableClientState() sind veraltet und werden verworfen.
  • Die Befehle glVertexPointer() und glTexCoordPointer() werden beide durch glVertexAttribPointer() ersetzt, das jetzt die Scheitelpunktposition oder die Texturkoordinate als erste Option akzeptiert.
  • Das Setup von glTexEnvf(), das für die Wiederholung des Sprites verantwortlich war, falls das Polygon größer als die Textur war, wird durch Aufrufe von glTexParameteri() ersetzt.

Wenn Sie durch irgendetwas davon verwirrt sind, sollten Sie sich unsere OpenGL ES 2.0 für iPhone und benutzerdefinierte Cocos2D 2 ansehen.X Shaders Tutorials für weitere Hintergrundinformationen. Aber Sie müssen sich nicht zu viele Sorgen machen, denn an diesem Punkt portieren wir die Klasse nur so, dass sie in Cocos2D 2 funktioniert.X :]

Kompilieren und ausführen, und alle PRKit-Fehler sollten verschwinden!

Es ist Zeit, PRKit in Aktion zu setzen. Sie werden die PRFilledPolygon-Klasse von PRKit unterklassifizieren, um eine Basis-PolygonSprite-Klasse zu erstellen, die unsere Früchte zeichnet.

Das PolygonSprite baut auf PRFilledPolygon auf, indem es dem Sprite einen Box2D-Body hinzufügt, und es enthält auch andere benutzerdefinierte Variablen und Methoden für die Früchte in unserer Spielimplementierung.

Kommen wir zur Sache. Drücken Sie Befehl + N und erstellen Sie eine neue Datei mit iOS \ cocos2d v2.x \CCNode Klasse Vorlage. Machen Sie es zu einer Unterklasse von PRFilledPolygon und nennen Sie es PolygonSprite.m.

Wechseln Sie zu PolygonSprite.h und nehmen Sie die folgenden Änderungen vor:

// Add to top of file#import "Box2D.h"#import "PRFilledPolygon.h"#define PTM_RATIO 32// Add inside @interfaceb2Body *_body;BOOL _original;b2Vec2 _centroid;// Add after the @interface@property(nonatomic,assign)b2Body *body;@property(nonatomic,readwrite)BOOL original;@property(nonatomic,readwrite)b2Vec2 centroid;// Add before the @end-(id)initWithTexture:(CCTexture2D*)texture body:(b2Body*)body original:(BOOL)original;-(id)initWithFile:(NSString*)filename body:(b2Body*)body original:(BOOL)original;+(id)spriteWithFile:(NSString*)filename body:(b2Body*)body original:(BOOL)original;+(id)spriteWithTexture:(CCTexture2D*)texture body:(b2Body*)body original:(BOOL)original;-(id)initWithWorld:(b2World*)world;+(id)spriteWithWorld:(b2World*)world;-(b2Body*)createBodyForWorld:(b2World*)world position:(b2Vec2)position rotation:(float)rotation vertices:(b2Vec2*)vertices vertexCount:(int32)count density:(float)density friction:(float)friction restitution:(float)restitution;-(void)activateCollisions;-(void)deactivateCollisions;

Der obige Code deklariert die Basisvariablen und Methoden, die Sie zum Erstellen eines PolygonSprite benötigen. Dies sind:

  • body: Dies ist der Box2D-Body, der an unser Sprite angehängt ist. Es wird für die Physiksimulation benötigt.
  • original: Vollständige und geschnittene Sprites verwenden dieselbe PolygonSprite-Klasse. Wenn dies JA ist, bedeutet dies, dass es sich um das ungeschnittene oder ursprüngliche Objekt handelt, das Sie erstellt haben.
  • Schwerpunkt: Die Mitte des Polygons innerhalb des Bildes ist nicht immer die gleiche wie die Mitte des Bildes, daher ist es nützlich, diesen Wert zu speichern.
  • Eigenschaften: Stellen Sie alle Variablen mithilfe von Eigenschaften bereit, damit andere Klassen frei darauf zugreifen können.
  • init / spriteWith*: Unsere wichtigsten Init-Methoden folgen derselben Namenskonvention wie Cocos2D.
  • andere Methoden: Dies sind Methoden, die & erstellen, die sich mit dem angehängten Box2D-Body und seinen Eigenschaften befassen.
  • PTM_RATIO: Pixel zu Meter verhältnis. Box2D benötigt diesen Umrechnungswert, da es sich um Meter anstelle von Pixeln handelt.

Wechseln Sie schnell zu PolygonSprite.m und benennen Sie es um PolygonSprite.mm . Alle Klassen, die Objective-C- (Cocos2D) und C ++ – (Box2D) Code mischen, müssen eine Erweiterung „.mm“ haben, um den Compiler über die gemischte Syntax zu informieren.

Nehmen Sie als nächstes die folgenden Änderungen an PolygonSprite.mm:

// Add inside the @implementation@synthesize body = _body;@synthesize original = _original;@synthesize centroid = _centroid;+(id)spriteWithFile:(NSString *)filename body:(b2Body *)body original:(BOOL)original{ return initWithFile:filename body:body original:original] autorelease];}+(id)spriteWithTexture:(CCTexture2D *)texture body:(b2Body *)body original:(BOOL)original{ return initWithTexture:texture body:body original:original] autorelease];}+(id)spriteWithWorld:(b2World *)world{ return initWithWorld:world] autorelease];}-(id)initWithFile:(NSString*)filename body:(b2Body*)body original:(BOOL)original{ NSAssert(filename != nil, @"Invalid filename for sprite"); CCTexture2D *texture = addImage: filename]; return ;}-(id)initWithTexture:(CCTexture2D*)texture body:(b2Body*)body original:(BOOL)original{ // gather all the vertices from our Box2D shape b2Fixture *originalFixture = body->GetFixtureList(); b2PolygonShape *shape = (b2PolygonShape*)originalFixture->GetShape(); int vertexCount = shape->GetVertexCount(); NSMutableArray *points = ; for(int i = 0; i < vertexCount; i++) { CGPoint p = ccp(shape->GetVertex(i).x * PTM_RATIO, shape->GetVertex(i).y * PTM_RATIO); ]; } if ((self = )) { _body = body; _body->SetUserData(self); _original = original; // gets the center of the polygon _centroid = self.body->GetLocalCenter(); // assign an anchor point based on the center self.anchorPoint = ccp(_centroid.x * PTM_RATIO / texture.contentSize.width, _centroid.y * PTM_RATIO / texture.contentSize.height); // more init stuff here later when you expand PolygonSprite } return self;}-(id)initWithWorld:(b2World *)world{ //nothing to do here return nil;}

Ähnlich wie bei Cocos2D sind alle spriteWith* -Methoden nur Autorelease-Gegenstücke der initWith* -Methoden, während initWithWorld für diese Klasse noch keine tatsächliche Verwendung hat, sondern später von den Unterklassen von PolygonSprite verwendet wird.

Der Großteil der Änderungen findet sich in den Methoden initWithFile und initWithTexture. Um den Fluss der Dinge zu erhalten, wird das Erstellen einer Frucht in dieser Reihenfolge aufgerufen:

 Init-Sequenz

  • initWithWorld: Dies ist für Unterklassen von PolygonSprite gedacht, sodass Sie nichts anderes tun, als nil zurückzugeben und sich später damit zu befassen.
  • initWithFile : Dies fügt die Textur aus unserer Datei hinzu und übergibt alles an initWithTexture .
  • initWithTexture: Unsere Hauptinitialisierung. PRFilledPolygon benötigt eine Textur und alle Scheitelpunkte des Polygons, das es ausfüllt. Da der vorherige Schritt bereits den Texturteil behandelt hat, werden in diesem Schritt die Scheitelpunkte behandelt, indem sie aus dem Box2D-Körper des Sprites gesammelt werden. Nachdem Sie sie an PRFilledPolygon übergeben haben, werden die zuvor deklarierten Variablen initialisiert.
  • initWithPoints: Alles, was dies tut, ist in PRKit enthalten, und das Gute ist, dass Sie PRKit jetzt, da Sie den Code aktualisiert haben, nicht mehr wirklich berühren müssen.

Noch drinnen PolygonSprite.mm , fügen Sie die folgenden Methoden hinzu:

-(void)setPosition:(CGPoint)position{ ; _body->SetTransform(b2Vec2(position.x/PTM_RATIO,position.y/PTM_RATIO), _body->GetAngle());}-(b2Body*)createBodyForWorld:(b2World *)world position:(b2Vec2)position rotation:(float)rotation vertices:(b2Vec2*)vertices vertexCount:(int32)count density:(float)density friction:(float)friction restitution:(float)restitution{ b2BodyDef bodyDef; bodyDef.type = b2_dynamicBody; bodyDef.position = position; bodyDef.angle = rotation; b2Body *body = world->CreateBody(&bodyDef); b2FixtureDef fixtureDef; fixtureDef.density = density; fixtureDef.friction = friction; fixtureDef.restitution = restitution; fixtureDef.filter.categoryBits = 0; fixtureDef.filter.maskBits = 0; b2PolygonShape shape; shape.Set(vertices, count); fixtureDef.shape = &shape; body->CreateFixture(&fixtureDef); return body;}-(void)activateCollisions{ b2Fixture *fixture = _body->GetFixtureList(); b2Filter filter = fixture->GetFilterData(); filter.categoryBits = 0x0001; filter.maskBits = 0x0001; fixture->SetFilterData(filter);}-(void)deactivateCollisions{ b2Fixture *fixture = _body->GetFixtureList(); b2Filter filter = fixture->GetFilterData(); filter.categoryBits = 0; filter.maskBits = 0; fixture->SetFilterData(filter);}

Im obigen Code überladen Sie zuerst die setPosition Methode von CCNode, sodass beim Aktualisieren der Position des Sprites auch die Position des zugehörigen Box2D-Körpers aktualisiert wird.

Sie erstellen dann eine bequeme Methode zum Erstellen und Definieren eines Box2D-Körpers. Um einen Body zu erstellen, müssen Sie eine Body-Definition, ein Body-Objekt, eine Form und eine Fixture-Definition definieren. Hier werden noch keine echten harten Werte zugewiesen, da diese Methode später von Unterklassen von PolygonSprite verwendet wird.

Das einzige, was zu beachten ist, sind die Kategoriebits und maskBits. Diese beiden werden zum Filtern von Kollisionen zwischen Objekten verwendet, sodass, wenn ein Kategoriebit eines Objekts mit einem Maskenbit eines anderen Objekts übereinstimmt und umgekehrt, eine Kollision zwischen diesen beiden Objekten auftritt. Sie setzen diese zuerst auf 0, da beim ersten Initialisieren der Objekte keine Kollisionen auftreten sollen.

Zuletzt definieren Sie zwei Methoden, die einfach die categoryBits und maskBits ersetzen, so dass Sie die Kollisionen unserer PolygonSprites aktivieren und deaktivieren können.

Es gibt noch etwas hinzuzufügen PolygonSprite.mm:

-(CGAffineTransform) nodeToParentTransform{ b2Vec2 pos = _body->GetPosition(); float x = pos.x * PTM_RATIO; float y = pos.y * PTM_RATIO; if ( !isRelativeAnchorPoint_ ) { x += anchorPointInPoints_.x; y += anchorPointInPoints_.y; } // Make matrix float radians = _body->GetAngle(); float c = cosf(radians); float s = sinf(radians); if( ! CGPointEqualToPoint(anchorPointInPoints_, CGPointZero) ){ x += c*-anchorPointInPoints_.x+ -s*-anchorPointInPoints_.y; y += s*-anchorPointInPoints_.x+ c*-anchorPointInPoints_.y; } // Rot, Translate Matrix transform_ = CGAffineTransformMake( c, s, -s,c, x,y ); return transform_;}

Erinnerst du dich, als ich erwähnte, dass du etwas von PhysicsSprite brauchst? Nun, das ist es. Dadurch wird sichergestellt, dass sich unsere Box2D-Form und unser Sprite beim Bewegen in derselben Position befinden. Es wird von Cocos2D für uns vorbereitet und das macht es umso großartiger.

Nach dem Kopieren des obigen Codes können Sie nun beide PhysicsSprite löschen.h und PhysicsSprite.mm aus dem Projekt, wie Sie vollständig ihre Nützlichkeit beseitigt haben.

Kompilieren und ausführen, und alles sollte in Ordnung sein. Sie sind jetzt mit PolygonSprite fertig.

Planung unserer Früchte

Bevor Sie Klassen für unsere Früchte erstellen, müssen Sie sich über die Regeln im Klaren sein, denen die Bilder und ihre Formen folgen müssen. Da Sie unsere Texturen einzelnen Box2D-Polygonen zuordnen, müssen Sie auch die Polygonbeschränkungen von Box2D einhalten. Sie müssen zwei Dinge beachten:

  • Polygone müssen konvex sein, was bedeutet, dass kein Innenwinkel größer als 180 ist.
  • Polygone dürfen 8 Eckpunkte nicht überschreiten.

Sie können diese Einschränkung tatsächlich umgehen, wenn Sie zulassen, dass jeder Körper mehrere Formen enthält. Box2D kann konkave Formen verarbeiten, wenn Sie eine Triangulationsmethode verwenden und die konkaven Formen aus mehreren Dreiecken erstellen.

Um die Dinge einfach zu halten, haben Sie in diesem Tutorial eine 1-Körper-zu-1-Form-Regel.

Hinweis: PhysicsEditor, das Werkzeug, das wir später in diesem Tutorial behandeln werden, enthält tatsächlich Code zum automatischen Triangulieren von Polygonen, die Sie in eine Reihe konvexer Formen zeichnen. Wie ich bereits sagte, versuchen wir jedoch, die Dinge hier einfach zu halten, um sicherzustellen, dass unsere Formen konvex gezeichnet werden, sodass wir nur eine Form pro Körper haben.

Schauen Sie sich diese beiden Früchte an:

 Konkav gegen konvex

Die Verwendung der Banane ist keine gute Idee, da sie von Natur aus konkav ist. Die Wassermelone hingegen ist sehr gut, da Sie ein konvexes Polygon definieren können, das seiner Form sehr ähnlich ist.

Wenn Sie Polygonformen definieren würden, die den Regeln von Box2D für die beiden Früchte folgen, würden Sie mehr oder weniger mit diesen enden:

Box2D Shape Outline

Das Wassermelonenpolygon passt perfekt zum Bild, während das Bananenpolygon einen großen leeren Raum hat, in dem sich das Bild nach innen krümmt. Box2D behandelt diesen Raum als Teil unseres Objekts und kann dazu führen, dass sich die Banane unnatürlich anfühlt, wenn sie mit anderen Objekten kollidiert oder wenn sie geschnitten wird.

Dies bedeutet nicht, dass Sie die Banane nicht verwenden können, sondern dass es einfach nicht empfohlen wird, die Banane zu verwenden. In der Tat wird das Spiel, das Sie in diesem Tutorial erstellen werden, dieselbe Banane verwenden.

Erstellen der ersten Frucht

Es ist Zeit, die erste Frucht zu erstellen: die Wassermelone (mindestens ein Stück davon).

Wenn Sie an den Ablauf des Initialisierungsprozesses unseres PolygonSprite denken, wissen Sie, dass initWithTexture einen Box2D-Body erwartet, aber der vorherige Schritt, initWithFile , bietet dies nicht.

Der Grund dafür ist, dass Sie den Körper individuell pro Frucht erstellen und definieren müssen, also muss es der allererste Schritt sein, initWithWorld , der den Körper erstellt und alle anderen Werte festlegt, die für jede Frucht spezifisch sind.

Um unseren Box2D-Körper zu erstellen, müssen Sie zuerst die Eckpunkte der Polygonform kennen, die Sie erstellen möchten. Es gibt verschiedene Möglichkeiten, dies zu tun, aber für dieses Tutorial verwenden Sie ein raffiniertes Tool namens PhysicsEditor. Dieses Werkzeug ist mit Funktionen gefüllt, aber Sie werden es nur verwenden, um die Koordinaten der Eckpunkte unseres Polygons abzurufen.

Wenn Sie es nicht haben, laden Sie PhysicsEditor herunter, installieren Sie es und starten Sie es. Sie erhalten ein leeres Projekt mit 3 Panels / Spalten.

Die Arbeit mit PhysicsEditor ist ziemlich einfach. Links legen Sie alle Bilder ab, mit denen Sie arbeiten möchten. In der Mitte definieren Sie visuell ein Polygon für Ihr Bild. Auf der rechten Seite haben Sie die Parameter für den Körper.

Physik-Editor!

Schnappen Sie sich Wassermelone.png aus dem Ordner Bilder des Resource Kits und ziehen Sie es in den linken Bereich. Sie sollten jetzt die Wassermelone in der Mitte sehen.

Erhöhen Sie die Vergrößerung am unteren Rand dieses Bedienfelds auf ein komfortables Maß und tippen Sie dann auf die Schaltfläche Fünfeck im oberen Teil dieses Bedienfelds, um eine 3-seitige Polygonform zu erstellen.

Klicken Sie mit der rechten Maustaste auf das Polygon und wählen Sie „Scheitelpunkt hinzufügen“, bis Sie insgesamt 5-8 Scheitelpunkte haben. Bewegen Sie die Eckpunkte um die Kanten der Wassermelone, während Sie zwei Dinge sicherstellen:

  • Das Polygon, das Sie erstellen, ist konvex.
  • Alle Pixel der Wassermelone befinden sich innerhalb des Polygons.

Hinweis: Eine weitere Verknüpfung zum Zeichnen der Formen ist die Verwendung des Zauberstab-Werkzeugs von PhysicsEditor. Stellen Sie einfach die Toleranz hoch (5-8), so dass Sie am Ende etwa 5-8 Punkte haben, und optimieren Sie die Punkte von dort aus.

Fügen Sie alle anderen Früchte und das Bombenbild aus dem Bilderordner des Resource Kits hinzu und machen Sie dasselbe für sie.

Sie sollten Formen für die folgenden Bilder definieren:

  • banane.png
  • Bombe.png
  • Trauben.png
  • Ananas.png
  • Erdbeere.png
  • Wassermelone.png

Wenn Sie fertig sind, ändern Sie in der oberen rechten Ecke den Wert von Exporter in „Box2D generic (PLIST)“, und Sie sollten so etwas erhalten:

 Definieren Sie Formen einfach mit PhysicsEditor!

Klicken Sie auf „Publish“ oder „Publish As“, um die PLIST-Datei mit den Vertex-Informationen zu exportieren. Speichern Sie die Datei als Früchte.plist.

Als Beispiel die Früchte.die Liste, die Sie für dieses Tutorial verwendet haben, befindet sich im Ordner Misc des Resource Kits.

Sie möchten nur die in der PLIST-Datei enthaltenen Informationen anzeigen, fügen Sie diese Datei also nicht zu Ihrem Projekt hinzu, sondern öffnen Sie sie einfach.plist mit Xcode, um seinen Inhalt auf organisierte Weise anzuzeigen.

Klicken Sie auf das Dreiecksymbol neben „Körper“, um diesen Abschnitt zu erweitern, und Sie sehen die Liste der Bilder, für die Sie Formen definiert haben. Sie müssen einen Drilldown auf die tiefste Ebene durchführen, um die Polygonscheitelpunkte der Wassermelone wie folgt zu erhalten:

 Die PhysicsEditor PLIST

Erweitern Sie unter/fixtures/Item 0/polygons und Sie sollten jetzt ein weiteres Item 0 vom Typ Array unter polygons sehen. Dieses letzte Array ist Ihre Form. Wenn Sie eine konvexe Form mit 8 oder weniger Eckpunkten richtig definiert hätten, sollten Sie nur ein Array unter Polygonen sehen.

Wenn Sie mehr als einen sehen, ich.e Element 0 Array, Element 1 Array usw., dann bedeutet dies, dass PhysicsEditor eine komplexe Form erstellt hat, weil Sie entweder zu viele Scheitelpunkte definiert oder ein konkaves Polygon gebildet haben. Wenn dies geschieht, gehen Sie zurück zu PhysicsEditor und fixieren Sie Ihre Form.

Erweitern Sie als Nächstes das Element 0 vom Typ Array, um die endgültige Liste der Elemente anzuzeigen. Dies sind Ihre Scheitelpunkte, und der Wert, den Sie auf der rechten Seite mit diesem Format { number, number } sehen, sind Ihre x & y-Koordinaten für jeden Scheitelpunkt.

Nachdem Sie nun die genauen Werte für die Eckpunkte Ihres Polygons haben, können Sie mit dem Erstellen der Watermelon-Klasse fortfahren.

Erstellen Sie in Xcode eine neue Datei mit iOS\cocos2d v2.x \CCNode Klasse Vorlage. Machen Sie es zu einer Unterklasse von PolygonSprite und nennen Sie es Wassermelone. Wassermelone öffnen.h und nehmen Sie die folgenden Änderungen vor:

// Add to top of file#import "PolygonSprite.h"

Wechseln Sie zu Wassermelone.m, umbenennen in Watermelon.mm , und fügen Sie die folgende init-Methode hinzu:

// Add inside the @implementation-(id)initWithWorld:(b2World *)world{ int32 count = 7; NSString *file = @"watermelon.png"; b2Vec2 vertices = { b2Vec2(5.0/PTM_RATIO,15.0/PTM_RATIO), b2Vec2(18.0/PTM_RATIO,7.0/PTM_RATIO), b2Vec2(32.0/PTM_RATIO,5.0/PTM_RATIO), b2Vec2(48.0/PTM_RATIO,7.0/PTM_RATIO), b2Vec2(60.0/PTM_RATIO,14.0/PTM_RATIO), b2Vec2(34.0/PTM_RATIO,59.0/PTM_RATIO), b2Vec2(28.0/PTM_RATIO,59.0/PTM_RATIO) }; CGSize screen = winSize]; b2Body *body = ; if ((self = )) { // We will initialize more values for the fruit here later } return self;}

Im obigen Code definieren Sie zuerst, wie viele Scheitelpunkte vorhanden sind, in diesem Fall 7. Als nächstes erstellen Sie ein Array von Scheitelpunkten, die alle Koordinaten enthalten, die Sie gerade in der Liste gesehen haben. Sie verwenden diese Informationen, um einen Body mit der in PolygonSprite definierten Convenience-Methode zu erstellen.

Du legst ein wenig Reibung ein, damit die Formen nicht endlos gleiten, und du legst auch eine kleine Wiederherstellung ein, damit die Formen nicht aufhören, wenn sie gegeneinander prallen.

Zuletzt erstellen Sie das Objekt, indem Sie die Initialisierung der Superklasse aufrufen und den Namen der Bilddatei, den Box2D-Body, übergeben und angeben, dass es sich um eine ursprüngliche Frucht handelt.

Sie benötigen die Wassermelonenbilder aus dem Resource Kit, daher wäre es jetzt ein guter Zeitpunkt, einfach alle Grafikressourcen hinzuzufügen, die Sie für den Rest des Tutorials benötigen.

Klicken Sie in Ihrem Projektnavigatorfenster mit der rechten Maustaste auf Ressourcen und wählen Sie „Dateien zu CutCutCut hinzufügen“. Fügen Sie den Ordner Bilder aus dem Resource Kit zum Projekt hinzu. Stellen Sie sicher, dass „Elemente in den Ordner der Zielgruppe kopieren“ aktiviert und „Gruppen für alle hinzugefügten Ordner erstellen“ ausgewählt ist.

Befolgen Sie die gleichen Schritte, um Banane, Trauben, Ananas, Erdbeere und Bombe zu erstellen.

Sie haben nur Schritt für Schritt die Erstellung der ersten Frucht in Angriff genommen, da es sich im Grunde genommen um einen & Wiederholungsprozess für jede Frucht handelt. Das Resource Kit enthält fertige Obst- und Bombenklassen im Klassenordner, die Sie sich ansehen können, falls Sie noch eine Anleitung benötigen, oder Sie können sie alle zu Ihrem Projekt hinzufügen, wenn Sie diesen Schritt überspringen möchten.

Kompilieren und ausführen und sicherstellen, dass alles in Ordnung ist.

Hinzufügen einer Frucht zur Szene

Bisher ist noch nichts auf dem Bildschirm passiert, und Sie jucken offensichtlich, die Früchte Ihrer Arbeit zu sehen – Wortspiel beabsichtigt! :]

Wechseln Sie zu HelloWorldLayer.h und nehmen Sie die folgenden Änderungen vor:

// Add to top of file#import "PolygonSprite.h"// Add inside the @interfaceCCArray *_cache;// Add after the @interface@property(nonatomic,retain)CCArray *cache;

Wechseln Sie zurück zu HelloWorldLayer.mm und nehmen Sie diese Änderungen vor:

// Add to top of file#import "Watermelon.h"// Add inside the @implementation@synthesize cache = _cache;// Add inside the init method, below ;// Add inside the dealloc method, before calling ;_cache = nil;// Add anywhere inside the @implementation and before the @end-(void)initSprites{ _cache = initWithCapacity:53]; // Just create one sprite for now. This whole method will be replaced later. PolygonSprite *sprite = initWithWorld:world]; ; ; ;}

Sie deklarieren ein Cache-Array, das alle Früchte und Bomben enthält, die Sie in Zukunft erstellen. Als nächstes erstellen Sie 1 Wassermelone und fügen sie der Szene hinzu. Sie rufen activateCollisions auf, damit die Wassermelone nicht durch die Wände dringt.

Kompilieren und ausführen, und Sie sollten eine Wassermelone sehen, die aus dem mittleren Bereich des Bildschirms fällt und unten landet, wie unten gezeigt.

Die Frucht deiner Arbeit

Möglicherweise bemerkst du, dass die Wassermelone nicht genau in der Mitte steht. Der Grund dafür ist, dass Sie das Objekt basierend auf seinem Box2D-Körper positioniert haben und der Ursprung unseres Box2D-Körpers in der unteren linken Ecke des Objekts liegt. Der dünne Umriss um die Wassermelone ist sichtbar, da der Debug-Zeichenmodus weiterhin aktiviert ist.

Wohin von hier aus?

Hier ist ein Beispielprojekt mit dem gesamten Code aus dem obigen Tutorial.

Das war’s für Teil 1 der Serie. Bisher haben Sie ein wassermelonentexturiertes Polygon, das nur auf den unteren Bildschirmrand fällt.

Aber anstatt ein rechteckiges Sprite mit transparentem Raum zu zeichnen, wie Sie es normalerweise in Box2D-Tutorials sehen, wird PRKit verwendet, um nur die Teile der Textur zu zeichnen, die den Eckpunkten des Box2D-Körpers entsprechen. Dies wird sich bald als nützlich erweisen!

Jetzt bist du bereit für Teil 2 des Tutorials, wo du die Fähigkeit hinzufügst, die Früchte zu schneiden!

Wenn Sie in der Zwischenzeit Fragen oder Kommentare zu diesem Teil haben, nehmen Sie bitte an der folgenden Forumsdiskussion teil!


Dies ist ein Beitrag von iOS Tutorial-Teammitglied Allen Tan, einem iOS-Entwickler und Mitbegründer von White Widget. Sie finden ihn auch auf Google+ und Twitter.

raywenderlich.com Wöchentlich

Die raywenderlich.com newsletter ist der einfachste Weg, um über alles auf dem Laufenden zu bleiben, was Sie als mobiler Entwickler wissen müssen.

Holen Sie sich eine wöchentliche Zusammenfassung unserer Tutorials und Kurse und erhalten Sie als Bonus einen kostenlosen ausführlichen E-Mail-Kurs!

Leave a Reply

Deine E-Mail-Adresse wird nicht veröffentlicht.