Das Ermitteln von Kollisionen an mit dem Pixel vollkommenen Grenzen ist der heilige Gral der Kollisionsentdeckung in 2. Spielen. Als solcher ist es dem Ideal ähnlich, wenn nicht vollkommen, Lösung zur Kollisionsentdeckung im Allgemeinen zu sagen. Und doch wird es auch ganz kompliziert, und die aufrichtigen Lösungen leisten sehr gut nicht, bis Sie anfangen, den Code zu optimieren. Yet, it’s also quite complicated and the straightforward solutions don’t perform very well until you start optimizing the code.
Dieser erste Posten konzentriert sich darauf, eine Pixel-Maske zu schaffen, die rohen Bilddaten, wie vorgeschlagen, vor mehr als 3 Jahren durch Ernesto Corvi analysierend. Es ist die schnellste Lösung, wenn Sie prüfen wollen, wenn ein Punkt mit einem Pixel auf einem Image kollidiert, das auch für rotieren gelassene und schuppige Elfen arbeitet. Jedoch bringt es wirklich etwas Optimierung, um Ermitteln-Kollisionen zwischen einem begrenzenden Kasten eines Knotens und der Pixel-Maske, oder den zwei Pixel-Masken zu beschleunigen. However it does take some optimizing to speed up detecting collisions between a bounding box of a node and the pixel mask, or two pixel masks.
Die Alternativlösung ist, die zwei kollidierenden Gegenstände auf einen CCRenderTexture, wie entwickelt, durch Dani und andere auf dem Cocos2D Forum zu machen. Es ist im Stande, Kollisionen willkürlich rotieren gelassener und schuppiger Elfen zu entdecken, aber kann erwartet werden, merklich langsamer zu sein, als eine Pixel-Maske. Ich werde diese Lösung in einer Zukunft iDevBlogADay Posten besprechen. I will discuss this solution in a future iDevBlogADay post.
Die Ergebnisse werden ihren Weg in Kobold2D finden, um die Lösungen sogleich verfügbar für alle Entwickler zu machen.
Der Ernesto Corvi mit dem Pixel vollkommene CollisionMap Strategie
Die von Ernesto angeschlagene Pseudocodeannäherung ist mit dem Schaffen eines 32-Bit-UIImage von einer Datei und dem Zuteilen eines pixelMask verbunden (er nennt es collisionMap) Puffer, um das Kollidieren/nicht zu halten, das Staaten für jedes Pixel im Image kollidiert. Dann sein gerade eine Sache des Wiederholens über die Bildpixel, den Alpha-Teil maskierend, um zu bestimmen, ob das Pixel völlig undurchsichtig ist, und wenn es ist, wird die Kollisionsfahne im pixelMask gesetzt.
Diese Lösung hat einige Bedürfnisse nach Verbesserungen. Es ist rudimentärer Code, der für Netzhaut-Anzeigen und-hd Images nicht verantwortlich ist und den pixelMask als umgekehrt Image wie der UIImage verlässt. Es erlaubt dem Benutzer nicht, eine Schwelle für den Alpha-Wert (0 bis der 255. anordnen) anzugeben, im Stande zu sein, Pixel zu denken, die nicht völlig undurchsichtig sind. Und der Code stellt eine Lösung nicht zur Verfügung, um Rechteck-Kreuzungen der Pixel-Maske ganz zu schweigen von der Kreuzung von zwei Pixel-Masken zu prüfen. Es ist, worüber dieser Posten ist. It doesn’t allow the user to specify a threshold for the alpha value (0 to 255 range), to be able to consider pixels which aren’t fully opaque. And the code doesn’t provide a solution for testing rectangle intersections of the pixel mask, let alone the intersection of two pixel masks. That’s what this post is about.
Das Einführen KKPixelMaskSprite
KKPixelMaskSprite ist eine Mehrwegklasse, die von CCSprite erbt, der den pixelMask und mehrere Kollisionsentdeckungsmethoden hinzufügt. Indem Sie das USE_BITARRAY Makro auf 1 setzen, können Sie die Klasse ändern, um einen BitArray stattdessen zu verwenden. Anstatt nicht unterzeichnete Rotforelle-Typen (BOOL) es zu versorgen, versorgt der BitArray Bit individuell, so Speicherverbrauch zu 1/8. (oder 12.5 %) reduzierend. Zum Beispiel 512×512 wird Pixel-Maske, BOOL (nicht unterzeichnete Rotforelle) verwendend, 256 Kilobytes des Gedächtnisses verlangen, wohingegen dieselbe Pixel-Maske wie wenig Reihe nur 32 Kilobytes des Gedächtnisses verlangen würde. Instead of storing unsigned char (BOOL) types it the BitArray stores bits individually, thus reducing memory consumption to 1/8th (or 12.5%). For example, a 512×512 pixel mask using BOOL (unsigned char) will require 256 KB of memory whereas the same pixel mask as a bit array would only require 32 KB of memory.
Ich beschloss, die Bit-Manipulationsbibliothek von Michael Dipperstein zu verwenden, weil sie laut der LGPL-Lizenz veröffentlicht wird - hatte dieselbe Lizenz Cocos2D verwendet, bevor sie die MIT-Lizenz annahm - und weil sie Durchführungen sowohl für C (verwendet) als auch für C ++ zur Verfügung stellt.
Wollen Spaziergang durch den Code wir, der das Image lädt und die PixelMask-Reihe zuteilt:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//das stellt sicher, dass wir den-hd Aktivposten auf Netzhaut-Geräten laden NSString* fullpath = [CCFileUtils fullPathFromRelativePath:filename];
= [CCFileUtils fullPathFromRelativePath:filename];
UIImage* Image = [[UIImage alloc] initWithContentsOfFile:fullpath];
= [[UIImage alloc] initWithContentsOfFile:fullpath];
//bekommen Sie die ganze Bildinformation, die wir brauchen pixelMaskWidth = image.size.width;
image.size.width;
pixelMaskHeight = image.size.height;
image.size.height;
pixelMaskSize = pixelMaskWidth * pixelMaskHeight;
pixelMaskWidth * pixelMaskHeight;
//teilen Sie zu und klären Sie den pixelMask Puffer #if USE_BITARRAY pixelMask = BitArrayCreate (pixelMaskSize);
BitArrayCreate(pixelMaskSize);
BitArrayClearAll (pixelMask);
);
#else pixelMask = malloc (pixelMaskSize * sizeof (BOOL));
malloc(pixelMaskSize * sizeof(BOOL));
memset (pixelMask, 0, pixelMaskSize * sizeof (BOOL));
, 0, pixelMaskSize * sizeof(BOOL));
#endif |
Linie 2 stellt sicher, dass die-hd Nachsilbe am Dateinamen angehangen wird, wenn es eine HD Version des verfügbaren Images gibt und Netzhaut-Anzeige ermöglicht wird. Linien 6-8 bekommen die Bildinformation und den resultierenden pixelMaskSize, der einfach die Zahl von Pixeln im Image ist. Abhängig vom USE_BITARRAY Makro wird der pixelMask entweder als bit_array_t* pixelMask erklärt; (BitArray) oder BOOL* pixelMask; (BOOL Reihe). Depending on the USE_BITARRAY macro the pixelMask is either declared as bit_array_t* pixelMask; (BitArray) or BOOL* pixelMask; (BOOL array).
Des Zeichens ist hier, dass ich beschloss, den Typ BOOL einfach zu verwenden, weil es der heimische boolean Datentyp im Ziel-C ist. BOOL ist ein typedef zur nicht unterzeichneten Rotforelle, so verwendet es denselben zu Grunde liegenden Datentyp wie der Code von Ernesto. Seien Sie bewusst, dass BOOL und bool (bemerken den Kleinbuchstaben), zwei verschiedene Typen sind! Der Kleinbuchstabe bool ist eine ganze 32-Bit-Zahl. Be aware that BOOL and bool (note the lowercase) are two different types! The lowercase bool is a 32-Bit integer.
Mit der pixelMask Puffereinstellung und geklärt können wir jetzt die Pixel des Images scannen und bestimmen, ob der Alpha-Wert jedes Pixels ihr erlaubt, zum pixelMask hinzugefügt zu werden, oder nicht. Für die Klarheit wird auf den USE_BITARRAY BitArray und Makrodurchführungsteilen von hier nicht gezeigt.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
//bekommen Sie die Pixel-Daten (richtiger: texels) als nicht unterzeichnete ganze 32-Bit-Zahlen CFDataRef imageData =
=
CGDataProviderCopyData (CGImageGetDataProvider (Image. CGImage));
(image.CGImage));
const UInt32* imagePixels = (const UInt32 *) CFDataGetBytePtr (imageData);
imagePixels = (const UInt32*)CFDataGetBytePtr(imageData);
UInt32 alphaValue = 0, x = 0, y = pixelMaskHeight - 1;
= 0, x = 0, y = pixelMaskHeight - 1;
UInt8 Alpha = 0;
= 0;
für (NSUInteger i = 0; ich <pixelMaskSize; ich ++) NSUInteger i = 0; i < pixelMaskSize; i++) { //stellen Sie sicher, dass der pixelMask in der Spitze geschaffen wird, um Orientierung zu ergründen NSUInteger Index = y * pixelMaskWidth + x; x ++; wenn (x == pixelMaskWidth) { x = 0; y-; } //Maske die Farben, so dass nur der Alpha-Wert (obere 8 Bit) bleibt alphaValue = imagePixels [ich] & 0xff000000; wenn (alphaValue> 0) { //bekommen Sie den Alpha-Wert, dann vergleichen Sie Alpha mit der Alpha-Schwelle Alpha = (UInt8) (alphaValue>> 24); wenn (Alpha> alphaThreshold)
)
{
pixelMask [Index] = JA;
] = YES;
}
}
}
CFRelease (imageData);
);
imageData = Null;
nil;
[Bildausgabe];
];
Image = Null; nil; |
Linien 2-4 Gewinn-Zugang zur rohen Pixel-Information im Image. Das schließt das Verwenden mehrerer Kernfundament-Funktionen ein. Der UIImage gibt Ihnen Zugang zum zu Grunde liegenden Quarz CGImage durch das CGImage Eigentum. Vom CGImage wird ein CGDataProvider geschaffen, der den rohen Daten des Images erlaubt, zu einem CFDataRef kopiert zu werden. The UIImage gives you access to the underlying Quartz CGImage through the CGImage property. From the CGImage a CGDataProvider is created which allows the image’s raw data to be copied to a CFDataRef.
CFDataRef ist bloß ein Zeigestock zu einem inneren Kernfundament struct genannt __ CFData. Die Details dieses struct werden Entwicklern nicht ausgestellt, aber er kann in CFData.c auf opensource.apple.com nachgeschlagen werden. Die CFDataGetBytePtr-Funktion gibt einen Zeigestock in die Bytes zurück, die in __ CFData struct versorgt sind. Was bleibt, soll einfach den Zeigestock zu einem UInt32 werfen (ein typedef für die nicht unterzeichnete interne Nummer), weil der UIImage ein Image mit 32 Bit pro Pixel (RGBA8888) ist. The CFDataGetBytePtr function returns a pointer to the bytes stored in the __CFData struct. What remains is simply to cast the pointer to an UInt32 (a typedef for unsigned int) because the UIImage is an image with 32-Bits per pixel (RGBA8888).
Was in Linien 8-30 folgt, ist eine Schleife, die jedes Pixel im Image untersucht, um zu bestimmen, ob dieses Pixel hinzugefügt werden sollte, weil eine Kollision zum pixelMask biss oder nicht. Die erste Besonderheit fängt mit Linien 11-17 an, der den Index in den pixelMask berechnet. So tuend, befestigt es das Problem, dass UIImage Images umgekehrt sind, Bit zum pixelMask Puffer in von unten nach oben Weise hinzufügend, indem sie über den imagePixels Puffer von oben bis unten wiederholen. By doing so it fixes the issue that UIImage images are upside down by adding bits to the pixelMask buffer in a bottom up manner while iterating over the imagePixels buffer from top to bottom.
In der Linie 20 wird der Alpha-Teil (leftmost 8 Bit) jedes 32-Bit-Pixel-Werts maskiert. Das setzt alle Farbenbit (0-23) auf 0 und verlässt nur die höchsten Bit, die den 8-Bit-Alpha-Wert enthalten. Wenn alphaValue 0 ist, ist das Pixel völlig durchsichtig, und wir können gerade mit der folgenden Wiederholung weitergehen. Sonst schaffen Linien 24 bis 28 den UInt8 (typedef für die nicht unterzeichnete Rotforelle) Wert, der das Alpha des Pixels enthält (0 bis der 255. anordnen), der dann im Vergleich zum alphaThreshold ist. Wenn der Test erfolgreich ist, wird das entsprechende Bit im pixelMask auf JA gesetzt. Schließlich führen Linien 32-35 die notwendige Reinigung durch und veröffentlichen das Bildgedächtnis. If alphaValue is 0 the pixel is fully transparent and we can just move on with the next iteration. Otherwise lines 24 to 28 create the UInt8 (typedef for unsigned char) value containing the pixel’s alpha (0 to 255 range) which is then compared with the alphaThreshold. If the test succeeds, the corresponding bit in the pixelMask is set to YES. Finally lines 32-35 perform the necessary cleanup and release the image memory.
Ein KKPixelMaskSprite kann mit spriteWithFile und einem fakultativen AlphaThreshold-Wert initialisiert werden:
|
1 |
spriteB = [KKPixelMaskSprite spriteWithFile:@ "imageB.png" alphaThreshold:100]; [KKPixelMaskSprite spriteWithFile:@"imageB.png" alphaThreshold:100]; |
Prüfung für die Kollision eines Punkts in der Pixel-Maske
Prüfend, wenn wenig an einem angegebenen x, y Position in der Pixel-Maske gesetzt wird, ist rechenbetont billig, wenn nicht trivial. Es verlangt einfach das Rechnen des Index in die Reihe plus einige Grenze-Kontrollen:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
- (BOOL) pixelMaskBitAt: (CGPoint) Punkt
) pixelMaskBitAt:(CGPoint)point
{
//um den Punkt koordiniert zur nächsten ganzen Zahl interne Nummer x = (interne Nummer) (point.x + 0.5f);
= (int)(point.x + 0.5f);
interne Nummer y = (interne Nummer) (point.y + 0.5f);
= (int)(point.y + 0.5f);
wenn (x <0 || y <0 || x> = pixelMaskWidth || y> = pixelMaskHeight)
x < 0 || y < 0 || x >= pixelMaskWidth || y >= pixelMaskHeight)
{
kehren Sie NEIN zurück;
;
}
NSUInteger Index = y * pixelMaskWidth + x;
= y * pixelMaskWidth + x;
geben Sie pixelMask [Index] zurück;
[index];
} |
Die einzige spezielle Rücksicht, um zu machen, besteht darin, dass Punkte Bruchteile haben können, aber wir müssen den Index berechnen, der auf Werte der ganzen Zahl (Linien 4-5) basiert ist. Das natürliche Runden eines Schwimmpunkts zur ganzen Zahl zu berücksichtigen, schätzt den einfachen Trick hinzuzufügen, dass 0.5f verwendet wird. Das stellt sicher, dass jeder Bruchteil-Wert, der größer ist als 0.5f, zur folgenden höheren ganzen Zahl vergrößert wird, und seit dem Gussteil zur ganzen Zahl einfach den Bruchteil entfernt, enden wir mit Werten 1.0 zu 1.4999, um zu 1 nach unten abgerundet zu werden, wohingegen Werte von 1.5 bis 1.9999 zu 2 zusammengetrieben werden. This ensures that every fraction value greater than 0.5f is increased to the next higher integer, and since casting to integer simply removes the fractional part, we end up with values of 1.0 to 1.4999 to be rounded down to 1 whereas values from 1.5 to 1.9999 will be rounded up to 2.
Das Grenze-Check-In der Linie 7 vermeidet, einen Index zu berechnen, der aus ist, der den app veranlassen könnte abzustürzen. In der Linie 12 wird der pixelMask Index basiert auf die vertraute Formel berechnet, die eine 2. Position auf eine 1-dimensionale Reihe kartografisch darstellt. Schließlich wird das Bit an diesem Index zurückgegeben. Insgesamt verwendet dieser Test einfachen arithmetics und ist aufflammend schnell. Finally the bit at that index is returned. Overall this test uses simple arithmetics and is blazingly fast.
Aber es ist nur ein Teil der Geschichte. Die pixelMaskBitAt Methode ist wirklich eine private Methode der KKPixelMaskSprite Klasse. Als ein Benutzer der Klasse werden Sie die pixelMaskContainsPoint Nachricht stattdessen senden. Ich werde warum nach der Codeprobe erklären: As a user of the class you will be sending the pixelMaskContainsPoint message instead. I’ll explain why after the code sample:
|
1 2 3 4 5 6 7 8 9 |
- (BOOL) pixelMaskContainsPoint: (CGPoint) Punkt
) pixelMaskContainsPoint:(CGPoint)point
{
//die Punkt-Koordinaten müssen hinsichtlich des Raums des Knotens sein weisen Sie = [selbst convertToNodeSpace:point] hin;
[self convertToNodeSpace:point];
//exklusiver Punkt zu Netzhaut-Pixeln nötigenfalls weisen Sie = ccpMult (Punkt, CC_CONTENT_SCALE_FACTOR ()) hin;
ccpMult(point, CC_CONTENT_SCALE_FACTOR());
kehren Sie [selbst pixelMaskBitAt:point] zurück;
self pixelMaskBitAt:point];
} |
Da wir mit Cocos2D arbeiten, müssen wir die Möglichkeit denken, dass die Elfe mit der Pixel-Maske rotieren gelassen oder, oder beide erklettert wird. Die convertToNodeSpace Methode in der Linie 4 passt darauf für uns auf. Und es führt eine andere wichtige Aufgabe durch sicherstellend, dass die Punkt-Koordinate hinsichtlich der Elfe-Textur-Ursprung-Koordinate (tiefer verlassen Ecke) ist. Der pixelMask hat die Breite und Höhe des Images, es mit Schirm-Koordinaten mit einem Inhaltsverzeichnis versehend (dessen Ursprung an der niedrigeren linken Ecke des Schirms ist), würde falsche Ergebnisse zurückgeben. And it performs another important task by ensuring that the point coordinate is relative to the sprite texture’s origin coordinate (lower left corner). The pixelMask has the width and height of the image, indexing it with screen coordinates (whose origin is at the lower left corner of the screen) would return incorrect results.
Linie 6 stellt auch sicher, dass der Punkt zu einer Pixel-Koordinate umgewandelt wird, sie mit CC_CONTENT_SCALE_FACTOR () multiplizierend. Der zufriedene Einteilungsfaktor ist 2 auf Netzhaut-Geräten mit der ermöglichten Netzhaut-Anzeige, sonst ist es 1. Das nimmt an, dass, wenn Sie Netzhaut-Weise ermöglichen und den app auf einem Netzhaut-Gerät führen, entsprechende-hd Images für alle Pixel-Maske-Elfen zur Verfügung gestellt werden. This assumes that if you enable Retina mode and run the app on a Retina device, corresponding -hd images will be provided for all pixel mask sprites.
Setzen Sie fort, Teil 2 Zu lesen …
Ich musste diesen Posten in zwei, wegen einer PHP Beschränkung spalten.
Setzen Sie bitte mit dem zweiten Posten dieses Artikels fort zu erfahren, wie die Rechteck-Kreuzung und pixelMask mit der pixelMask Kollisionsentdeckung getan werden.
| Folgen Sie @gaminghorror | Folgen Sie @kobold2d |
|









[...] Schnelle mit dem Pixel vollkommene Kollisionsentdeckung für Cocos2D mit dem Beispiel-Code (1/2) [...]
großer … und vollkommen
[...] KKPixelMaskSprite, wie beschrieben, in der Schnellen mit dem Pixel vollkommenen Kollisionsentdeckung für Cocos2D [...]