3

SpriteKit の衝突検出を使用しています。次のようなコールバックがあります。

- (void)didBeginContact:(SKPhysicsContact *)contact

接触オブジェクトには 2 つの物理体があります。

SKPhysicsBody *bodyA;
SKPhysicsBody *bodyB;

私のゲームにはたくさんのオブジェクトがあり、もちろん をテストして、categoryBitMask何が何と衝突したかを調べることができます。しかし、私は多くの種類 (もちろん 32 以下) を持つつもりであり、新しい型を動的に導入する可能性があることを考えると、結果として生じる衝突、爆発、得点などのロジックをコーディングするために動的な二重ディスパッチを行う最もエレガントな方法は何ですか?これらすべての衝突から?もちろん、巨大な毛むくじゃらの if ステートメントを作成することはできますが、よりクリーンなものを望んでいました。

おそらく、適切なハンドラーのセレクターを格納するルックアップ テーブルですか? そして、categoryBitMasks?の組み合わせでルックアップ テーブルにインデックスを付けます。いくつかの提案を聞きたいです。

4

3 に答える 3

2

以下は、 Kobold Kitで連絡先ディスパッチがどのように機能するかです。

要点: 連絡している各ノードにメッセージを送信しdidBeginContact:withOtherBody:て、各ノード自体が他のどのボディと連絡を取り合ったか、または失ったかを知ることができます。他のボディのノードが必要な場合は、SKPhysicsBodynodeプロパティから取得できます。

-(void) didBeginContact:(SKPhysicsContact *)contact
{
    SKPhysicsBody* bodyA = contact.bodyA;
    SKPhysicsBody* bodyB = contact.bodyB;
    SKNode* nodeA = bodyA.node;
    SKNode* nodeB = bodyB.node;
    for (id<KKPhysicsContactEventDelegate> observer in _physicsContactObservers)
    {
        SKNode* observerNode = observer.node;
        if (observerNode == nodeA)
        {
            [observer didBeginContact:contact otherBody:bodyB];
        }
        else if (observerNode == nodeB)
        {
            [observer didBeginContact:contact otherBody:bodyA];
        }
    }
}

-(void) didEndContact:(SKPhysicsContact *)contact
{
    SKPhysicsBody* bodyA = contact.bodyA;
    SKPhysicsBody* bodyB = contact.bodyB;
    SKNode* nodeA = bodyA.node;
    SKNode* nodeB = bodyB.node;
    for (id<KKPhysicsContactEventDelegate> observer in _physicsContactObservers)
    {
        SKNode* observerNode = observer.node;
        if (observerNode == nodeA)
        {
            [observer didEndContact:contact otherBody:bodyB];
        }
        else if (observerNode == nodeB)
        {
            [observer didEndContact:contact otherBody:bodyA];
        }
    }
}
于 2013-11-09T21:39:14.037 に答える
2

選択したゲームとして Pong を使用して、SkPhysicsBodyContact の二重ディスパッチの実例を作成しました。作業コードは、私の github で入手できます。

https://github.com/kouky/iOS-SpriteKit-Pong

object-c ではクラス メソッドの引数をオーバーロードできないため、実際には Visitor パターンを使用して contact デリゲートで二重ディスパッチを実行する必要があります。

- (void)didBeginContact:(SKPhysicsContact *)contact
{

    SKPhysicsBody *firstBody, *secondBody;
    firstBody = contact.bodyA;
    secondBody = contact.bodyB;

    VisitablePhysicsBody *firstVisitableBody = [[VisitablePhysicsBody alloc] initWithBody:firstBody];
    VisitablePhysicsBody *secondVisitableBody = [[VisitablePhysicsBody alloc] initWithBody:secondBody];

    [firstVisitableBody acceptVisitor:[ContactVisitor contactVisitorWithBody:secondBody forContact:contact]];
    [secondVisitableBody acceptVisitor:[ContactVisitor contactVisitorWithBody:firstBody forContact:contact]];

}

VisitablePhysicsBodyContactVisitorは、二重ディスパッチを実行するために必要な仲介者です。これらは非常にシンプルで、ソース コードはプロジェクト リポジトリにあります。最終的には、特定のタイプのノードの連絡先を処理することに専念するクラスを持つことができます。

たとえば、私の Pong の例では、 BallNodeContactVisitor というクラスがあり、 BallNodeを含む連絡先が発生したときにのみメッセージを受信します。クラス内には命名規則に従うメソッドがあり、BallNode と PaddleNode などの他のノード タイプとの接触の結果を判断できます。

@implementation BallNodeContactVisitor

// Handles contacts with PlayfieldScene edges
- (void)visitPlayfieldScene:(SKPhysicsBody *)playfieldBody
{

    BallNode *ball = (BallNode *) self.body.node;
    PlayfieldScene *playfield = (PlayfieldScene *) playfieldBody.node;
    // Perform something
}

// Handles contacts with PaddleNodes
- (void)visitPaddleNode:(SKPhysicsBody *)paddleBody
{
    BallNode *ball = (BallNode *) self.body.node;
    PaddleNode *paddle= (PaddleNode *) paddleBody.node;
    // Perform something else
}

@end
于 2013-11-21T13:11:37.670 に答える
0

SKNode サブクラスを調べてみませんか?

次に、連絡先のクラスを簡単に検査body.nodeできます。次に、どのメソッドを呼び出すかを示す構成辞書を作成します。

いくつかのノード サブクラス...

Player : SKSpriteNode
Bullet : SKSpriteNode
Monster : SKSpriteNode

...次に、次のようなメソッドを作成します...

-(void)player:(Player*) player didBeginContactWithMonster:(Monster*) monster;
-(void)player:(Player*) player didBeginContactWithBullet:(Bullet*) bullet;

...次に、次のような構成を作成します...

NSDictionary *contactDispatch = @{
@"player:didBeginContactWithMonster:" : @[ [Player class], [Mosnter class] ],
@"player:didBeginContactWithBullet:" : @[ [Player class], [Bullet class] ]
};

したがって、接触したボディの内容を確認してから、 でセレクターをインスタンス化しNSSelectorFromString、 で呼び出すことができperformSelector:withObject:withObject:ます。

多くの点で最適化され、正規化されている可能性がありますが、私はクリーンなソリューションだと感じています。まだ証明していませんが、出くわしました。

質問自体の最後にほぼ同じことを書いていることに気づきました。とにかく投稿します。:D うわー、コメントの提案も同じです。ああ。

于 2014-02-19T02:46:24.847 に答える