Game Engine Collison Bitmask… Why 0x01 etc?

前端 未结 3 1925
旧时难觅i
旧时难觅i 2020-12-09 21:38

Coming across this situation both in Sprite Kit (iOS Development) and in Cocos2d-x (which I know was pretty much the inspiration for Sprite Kit, hence why they use a lot of

3条回答
  •  悲&欢浪女
    2020-12-09 22:22

    luk2302's answer is great, but just to go a bit further and in other directions...

    Why hex notation? (0x1 << 2 etc)

    Once you know that bit positions are the important part, it is (as mentioned in comments) just a matter of style/readability. You could just as well do:

    let catA = 0b0001
    let catB = 0b0010
    let catC = 0b0100
    

    But binary literals like that are (as far as Apple tools are concerned) new to Swift and not available in ObjC.

    You could also do:

    static const uint32_t catA =  1 << 0;
    static const uint32_t catB =  1 << 1;
    static const uint32_t catC =  1 << 2;
    

    or:

    static const uint32_t catA =  1;
    static const uint32_t catB =  2;
    static const uint32_t catC =  4;
    

    But, for historical/cultural reasons, it's become common convention among programmers to use hexadecimal notation as a way of reminding oneself/other readers of your code that a particular integer literal is significant more for its bit pattern than its absolute value. (Also, for the second C example you have to remember which bit has which place value, whereas with << operator or binary literals you can emphasize the position.)

    Why bit patterns? Why not ___?

    Using bit patterns / bit masks is a performance optimization. To check for collisions, a physics engine must examine every pair of objects in the world. Because it's pairwise, the performance cost is quadratic: if you have 4 objects, you have 4*4 = 16 possible collisions to check... 5 objects is 5*5 = 25 possible conditions, etc. You can cut that list down with some obvious exclusions (no worries about an object colliding with itself, A collides with B is the same as B collides with A, etc), but the growth is still proportional to a quadratic; that is, for n objects, you have O(n2) possible collisions to check. (And remember, we're counting total objects in the scene, not categories.)

    Many interesting physics games have a lot more than 5 total objects in the scene, and run at 30 or 60 frames per second (or at least want to). That means the physics engine has to check all those possible collision pairs in 16 milliseconds. Or preferably, much less than 16 ms, because it still has other physics-y stuff to do before/after finding collisions, and the game engine needs time to render, and you probably want time for your game logic in there, too.

    Bit mask comparisons are very fast. Something like the mask comparison:

    if (bodyA.categoryBitMask & bodyB.collisionBitMask != 0)
    

    ...is one of the quickest things you can ask an ALU to do — like one or two clock cycles fast. (Anyone know where to track down actual cycles per instruction figures?)

    By contrast, string comparison is an algorithm in itself, requiring a lot more time. (Not to mention some easy way to have those strings express the combinations of categories that should result in collisions.)

    A challenge

    Since bit masks are a performance optimization, they might as well be a (private) implementation detail. But most physics engines, including SpriteKit's, leave them as part of the API. It'd be nicer to have a way of saying "these are my categories, these are how they should interact" at a high level, and let someone else handle the details of translating that description into bit masks. Apple's DemoBots sample code project appears to have one idea for simplifying such things (see ColliderType in the source)... feel free to use it design your own.

提交回复
热议问题