Обнаружение столкновений на прекрасных пикселом границах является святым Граалем обнаружения столкновений в 2-ых играх. Также, на идеал, походит, если не сказать прекрасный, решение обнаружения столкновений вообще. Все же это является также вполне сложным, и прямые решения не выступают очень хорошо, пока Вы не начинаете оптимизировать кодекс. Yet, it’s also quite complicated and the straightforward solutions don’t perform very well until you start optimizing the code.
Эта первая почта сосредотачивается на том, чтобы создавать маску пиксела, анализируя сырые данные изображения, как предложено более чем 3 года назад Эрнесто Корви. Это - самое быстрое решение, если Вы хотите проверить, если пункт сталкивается с пикселом на изображении, которое также работает на вращаемых и чешуйчатых эльфов. Однако действительно требуется некоторая оптимизация, чтобы ускорить столкновения обнаружения между ограничивающим прямоугольником узла и маской пиксела, или двумя масками пиксела. 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.
Альтернативное решение состоит в том, чтобы отдать два сталкивающихся объекта на CCRenderTexture, как развито Дэни и другими на форуме Cocos2D. Это в состоянии обнаружить столкновения произвольно вращаемых и чешуйчатых эльфов, но, как могут ожидать, будет заметно медленнее чем маска пиксела. Я буду обсуждать это решение в будущем iDevBlogADay почта. I will discuss this solution in a future iDevBlogADay post.
Результаты найдут свой путь в Kobold2D, чтобы сделать решения легко доступными всем разработчикам.
Эрнесто Корви Прекрасная пикселом Стратегия CollisionMap
Псевдокодовый подход, объявленный Эрнесто, вовлекает создание 32-битового UIImage от файла и распределения pixelMask (он звонит, это collisionMap) буферизуют, чтобы держать столкновение/не, сталкивающееся государства для каждого пиксела по изображению. Тогда его только вопрос повторения по пикселам изображения, кашируя альфа-часть, чтобы определить, полностью непрозрачен ли пиксел, и если это, флаг столкновения в pixelMask установлен.
У этого решения есть несколько потребностей в усовершенствованиях. Это - элементарный кодекс, который не составляет показы Сетчатки и-hd изображения и оставляет pixelMask как перевернутое изображение как UIImage. Это не позволяет пользователю определять порог для альфа-ценности (от 0 до 255 диапазонов), быть в состоянии рассмотреть пикселы, которые не полностью непрозрачны. И кодекс не обеспечивает решения для того, чтобы оно проверило пересечения прямоугольника маски пиксела, уже не говоря о пересечении двух масок пиксела. Это - то, о чем эта почта. 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.
Представление KKPixelMaskSprite
KKPixelMaskSprite - класс многократного использования, наследующий CCSprite, который добавляет pixelMask и несколько методов обнаружения столкновений. Устанавливая макрос USE_BITARRAY в 1 Вы можете изменить класс, чтобы использовать BitArray вместо этого. Вместо того, чтобы хранить неподписанную случайную работу (BOOL) печатает это, BitArray хранит биты индивидуально, таким образом уменьшая потребление памяти до 1/8-ого (или 12.5 %). Например, 512×512 маска пиксела, используя BOOL (неподписанная случайная работа) потребует 256 КБ памяти, тогда как та же самая маска пиксела как маленькое множество только потребовала бы 32 КБ памяти. 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.
Я хотел пользоваться Библиотекой Побитовой обработки от Михаэля Дипперштайна, потому что она выпущена в соответствии с лицензией LGPL – та же самая лицензия Cocos2D использовала прежде, чем она приняла Лицензию Массачусетского технологического института – и потому что она обеспечивает выполнение и для (используемого) C и для C ++.
Давайте идти через кодекс, который загружает изображение и ассигнует множество pixelMask:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//это гарантирует, что мы загружаем-hd актив на устройствах Сетчатки NSString* fullpath = [CCFileUtils fullPathFromRelativePath:filename];
= [CCFileUtils fullPathFromRelativePath:filename];
Изображение UIImage* = [[UIImage alloc] initWithContentsOfFile:fullpath];
= [[UIImage alloc] initWithContentsOfFile:fullpath];
//получите всю информацию изображения, в которой мы нуждаемся pixelMaskWidth = image.size.width;
image.size.width;
pixelMaskHeight = image.size.height;
image.size.height;
pixelMaskSize = pixelMaskWidth * pixelMaskHeight;
pixelMaskWidth * pixelMaskHeight;
//ассигнуйте и очистите буфер pixelMask #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 |
Линия 2 гарантирует, что-hd суффикс приложен к имени файла, если есть версия HD доступного изображения, и показ Сетчатки позволен. Линии 6-8 получают информацию изображения и получающийся pixelMaskSize, который является просто числом пикселов по изображению. В зависимости от макроса USE_BITARRAY pixelMask или объявлен как bit_array_t* pixelMask; (BitArray) или BOOL* pixelMask; (множество BOOL). Depending on the USE_BITARRAY macro the pixelMask is either declared as bit_array_t* pixelMask; (BitArray) or BOOL* pixelMask; (BOOL array).
Знаменитый вот то, что я хотел использовать тип BOOL просто, потому что это - родной тип булевых данных в Цели-C. BOOL - typedef к неподписанной случайной работе, таким образом, это использует тот же самый основной тип данных в качестве кодекса Эрнесто. Имейте в виду, что BOOL и bool (отмечают строчные буквы) являются двумя различными типами! Строчные буквы bool являются 32-битовым целым числом. Be aware that BOOL and bool (note the lowercase) are two different types! The lowercase bool is a 32-Bit integer.
С установкой буфера pixelMask и очищенный, мы можем теперь просмотреть пикселы изображения и определить, позволяет ли альфа-ценность каждого пиксела ей быть добавленной к pixelMask или нет. Для ясности на макросе USE_BITARRAY и частях выполнения BitArray не показывают отсюда.
|
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 |
//получите пиксельные данные (более правильно: texels) как 32-битовые неподписанные целые числа CFDataRef imageData =
=
CGDataProviderCopyData (CGImageGetDataProvider (изображение. CGImage));
(image.CGImage));
константа UInt32* imagePixels = (константа UInt32 *) CFDataGetBytePtr (imageData);
imagePixels = (const UInt32*)CFDataGetBytePtr(imageData);
UInt32 alphaValue = 0, x = 0, y = pixelMaskHeight - 1;
= 0, x = 0, y = pixelMaskHeight - 1;
Альфа UInt8 = 0;
= 0;
для (NSUInteger i = 0; я <pixelMaskSize; я ++) NSUInteger i = 0; i < pixelMaskSize; i++) { //убедитесь, что pixelMask создан в ориентации от начала до конца Индекс NSUInteger = y * pixelMaskWidth + x; x ++; если (x == pixelMaskWidth) { x = 0; y-; } //кашируйте цвета так, чтобы только альфа-ценность осталась (верхние 8 битов) alphaValue = imagePixels [я] & 0xff000000; если (alphaValue> 0) { //получите альфа-ценность, затем сравните альфу с альфа-порогом альфа = (UInt8) (alphaValue>> 24); если (альфа> alphaThreshold)
)
{
pixelMask [индекс] = ДА;
] = YES;
}
}
}
CFRelease (imageData);
);
imageData = ноль;
nil;
[выпуск изображения];
];
изображение = ноль; nil; |
Линии 2-4 получают доступ к сырой информации о пикселе по изображению. Это вовлекает использование нескольких Основных функций Фонда. UIImage предоставляет Вам доступ к основному Кварцу CGImage через собственность CGImage. От CGImage создан CGDataProvider, который позволяет исходным данным изображения быть скопированными к CFDataRef. 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 - просто указатель на внутренний Основной Фонд struct названный __ CFData. Детали этого struct не выставлены разработчикам, но он может искаться в CFData.c на opensource.apple.com. Функция CFDataGetBytePtr возвращает указатель на байты, сохраненные в __ CFData struct. То, что остается, должно просто бросить указатель на UInt32 (typedef для неподписанного интервала), потому что UIImage - изображение с 32 битами за пиксел (RGBA8888). 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).
То, что следует в линиях 8-30, является петлей, которая осматривает каждый пиксел по изображению, чтобы определить, должен ли тот пиксел быть добавлен, поскольку столкновение укусило к pixelMask или нет. Первая особенность начинается с линий 11-17, который вычисляет индекс в pixelMask. Делая, таким образом, это устанавливает проблему, что изображения UIImage перевернуты, добавляя биты к буферу pixelMask в восходящей манере, повторяя по буферу imagePixels сверху донизу. 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.
В линии 20 кашируется альфа-часть (крайние левые 8 битов) каждой 32-битовой ценности пиксела. Это устанавливает все цветные биты (0-23) в 0 и оставляет только самые высокие биты, которые содержат 8-битовую альфа-ценность. Если alphaValue 0, пиксел полностью прозрачен, и мы можем только идти дальше со следующим повторением. Иначе линии 24 - 28 создают UInt8 (typedef для неподписанной случайной работы) ценность, содержащая альфу пиксела (от 0 до 255 диапазонов), который является тогда по сравнению с alphaThreshold. Если тест преуспевает, соответствующий бит в pixelMask установлен в ДА. Наконец линии 32-35 выполняют необходимую уборку и выпускают память изображения. 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.
KKPixelMaskSprite может калиброваться с spriteWithFile и дополнительной ценностью alphaThreshold:
|
1 |
spriteB = [KKPixelMaskSprite spriteWithFile:@ "imageB.png" alphaThreshold:100]; [KKPixelMaskSprite spriteWithFile:@"imageB.png" alphaThreshold:100]; |
Тестирование на столкновение пункта в маске пиксела
Проверяя, если немного в указанном x, y местоположение в маске пиксела установлено, в вычислительном отношении недорог, если не тривиальный. Это просто требует вычисления индекса во множество плюс некоторые граничные проверки:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
- (BOOL) pixelMaskBitAt: (CGPoint) пункт
) pixelMaskBitAt:(CGPoint)point
{
//вокруг пункта координирует к самому близкому целому числу интервал x = (интервал) (point.x + 0.5f);
= (int)(point.x + 0.5f);
интервал y = (интервал) (point.y + 0.5f);
= (int)(point.y + 0.5f);
если (x <0 || y <0 || x> = pixelMaskWidth || y> = pixelMaskHeight)
x < 0 || y < 0 || x >= pixelMaskWidth || y >= pixelMaskHeight)
{
возвратитесь НЕТ;
;
}
Индекс NSUInteger = y * pixelMaskWidth + x;
= y * pixelMaskWidth + x;
возвратите pixelMask [индекс];
[index];
} |
Единственное специальное соображение, чтобы сделать состоит в том, что у пунктов могут быть фракционные части, но мы должны вычислить индекс, основанный на целочисленных значениях (линии 4-5). Чтобы учесть естественное округление плавающей запятой к целочисленным значениям, простая уловка добавления 0.5f используется. Это гарантирует, что каждая ценность фракции, больше чем 0.5f, увеличена до следующего более высокого целого числа, и начиная с кастинга к целому числу просто удаляет фракционную часть, мы заканчиваем с ценностями 1.0 к 1.4999, чтобы быть округленными в меньшую сторону к 1, тогда как ценности от 1.5 до 1.9999 будут окружены к 2. 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.
Граничная проверка в линии 7 избегает вычислять индекс, который является за пределы, который мог бы заставить приложение терпеть крах. В линии 12 pixelMask индекс вычислен основанный на формуле, которой доверяют, которая наносит на карту 2-ое положение на 1-мерное множество. Наконец бит в том индексе возвращен. Повсюду этот тест использует простую арифметику и ослепительно быстр. Finally the bit at that index is returned. Overall this test uses simple arithmetics and is blazingly fast.
Но это - только одна часть истории. pixelMaskBitAt метод - фактически частный метод класса KKPixelMaskSprite. Как пользователь класса Вы будете посылать pixelMaskContainsPoint сообщение вместо этого. Я объясню почему после кодового образца: 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) пункт
) pixelMaskContainsPoint:(CGPoint)point
{
//координаты пункта должны быть относительно пространства узла укажите = [сам convertToNodeSpace:point];
[self convertToNodeSpace:point];
//высококлассный пункт к пикселам Сетчатки в случае необходимости укажите = ccpMult (пункт, CC_CONTENT_SCALE_FACTOR ());
ccpMult(point, CC_CONTENT_SCALE_FACTOR());
возвратитесь [сам pixelMaskBitAt:point];
self pixelMaskBitAt:point];
} |
Так как мы работаем с Cocos2D, мы должны рассмотреть возможность, что эльф с маской пиксела вращается или измерен, или оба. convertToNodeSpace метод в линии 4 заботится об этом для нас. И это выполняет другую важную задачу, гарантируя, что координата пункта относительно координаты происхождения структуры эльфа (левый нижний угол). У pixelMask есть ширина и высота изображения, внося это в указатель с координатами экрана (чье происхождение в левом нижнем углу экрана), возвратил бы неправильные результаты. 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.
Линия 6 также удостоверяется, что пункт преобразован в координату пиксела, умножая ее с CC_CONTENT_SCALE_FACTOR (). Довольный коэффициент пропорциональности 2 на устройствах Сетчатки с позволенным показом Сетчатки, иначе это 1. Это предполагает, что, если Вы позволяете способ Сетчатки и управляете приложением на устройстве Сетчатки, соответствующие-hd изображения будут обеспечены для всех эльфов маски пиксела. 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.
Продолжите Читать Часть 2 …
Я должен был расколоть эту почту на два, из-за ограничения PHP.
Пожалуйста, продолжите со второй почтой этой статьи изучать, как пересечение прямоугольника и pixelMask с pixelMask обнаружением столкновений сделаны.
| Следуйте за | Следуйте за |
|









[...] Быстрое Прекрасное пикселом Обнаружение столкновений для Cocos2D с Примером кода (1/2) [...]
большой … и прекрасный
[...] KKPixelMaskSprite как описано в Быстром Прекрасном пикселом Обнаружении столкновений для Cocos2D [...]