iOS Sprite Kitで使って弾幕シューティングのようなゲームを作る。(とりあえず弾幕のみ

<img src="http://oneworld2.sakura.ne.jp/blog/danmaku.gif">

Sprite Kitを使って弾幕のようなものを実現してみたいと思います。

##プレイヤーなどの設定

とりあえずは、ドラッグで位置を変更できるプレイヤーを配置などを。

```objc:MyScene.m

enum
{
    kDragNone,  //初期値
    kDragStart, //Drag開始
    kDragEnd,   //Drag終了
};

@implementation MyScene
{
    int lastShotTime1;
    int lastShotTime2;
    int lastShotTime3;
    SKSpriteNode *player;
    int playerstatus;
}

```


```objc:MyScene.m
-(id)initWithSize:(CGSize)size {    
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */
        
        self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
        self.physicsWorld.gravity = CGVectorMake(0,0);
        self.physicsWorld.contactDelegate = self;
        [self setUpPlayer];
    }
    return self;
}

-(void) setUpPlayer{
    player = [SKSpriteNode spriteNodeWithImageNamed:@"Spaceship"];
    player.size = CGSizeMake(25, 25);
    player.position =CGPointMake(160, 20);
    player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:player.size];
    player.physicsBody.contactTestBitMask = 0x1<<0;
    player.physicsBody.dynamic = NO;
    player.name = @"player";
    [self addChild:player];
}

```

今回は重力を利用しないため、
以下の記述で無効にしています。
        self.physicsWorld.gravity = CGVectorMake(0,0);


まだ、ドラッグで移動できるように。

```objc:MyScene.m
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */
    
    for (UITouch *touch in touches) {
        CGPoint location = [touch locationInNode:self];
        SKNode *node = [self nodeAtPoint:location];
        if(node != nil && [node.name isEqualToString:@"player"]) {
            playerstatus = kDragStart;
            break;
        }
    }
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    if(playerstatus == kDragStart ){
        UITouch *touch = [touches anyObject];
        CGPoint touchPos = [touch locationInNode:self];
        player.position = CGPointMake(touchPos.x, touchPos.y);
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    if(playerstatus == kDragStart ){
        playerstatus = kDragEnd;
    }
}

```


###弾幕の表示

それ用のメソッドを用意します。

```objc:MyScene.m
-(void)shotAtPoint:(CGPoint)point
{
    int separate = 30;
    point = CGPointMake(point.x + arc4random_uniform(10) , point.y + + arc4random_uniform(10));
    for(int i=0;i<separate;i++) {
        SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:@"ball"];
        sprite.position = point;
        sprite.speed = 0.4;
        float pr = sprite.size.width/2;
        sprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:pr];
        sprite.physicsBody.contactTestBitMask = 0x1<<1;
        sprite.physicsBody.categoryBitMask    = 0x1<<0;
        sprite.physicsBody.collisionBitMask   = 0x1<<1;
        float r  = self.size.height;
        float x  = r * cos( i * (2 * M_PI ) / separate );
        float y  = r * sin( i * (2 * M_PI ) / separate );
        SKAction* action = [SKAction sequence:@[[SKAction moveTo:CGPointMake(point.x+x, point.y+y) duration:1],
                                                [SKAction removeFromParent]]];
        [sprite runAction:[SKAction repeatActionForever:action]];
        [self addChild:sprite];
    }
}

```

なお、ball.pngは
![ball.png](https://qiita-image-store.s3.amazonaws.com/0/35343/ff14d9c3-46f4-c6af-6ba3-e3bd45b17cda.png)
とします。

このメソッド実行のタイミングでは中心位置が少しずれるように
乱数を生成しています。

変数separateは、360度をどれだけ分割させるかコントロールするために
変数です。

あとは、このメソッドを呼び込むタイミングを調整します。

```objc:MyScene.m

-(void)update:(CFTimeInterval)currentTime {
    if(lastShotTime1 == 0 )
    {
        lastShotTime1 = currentTime;
        lastShotTime2 = currentTime;
        lastShotTime3 = currentTime;
        
    }
    /* Called before each frame is rendered */
    if(lastShotTime1 + [self randFloat:2] < currentTime) {
        [self shotAtPoint:CGPointMake(80, 350)];
        lastShotTime1 = currentTime;
    }
    if(lastShotTime2 + [self randFloat:2] < currentTime) {
        [self shotAtPoint:CGPointMake(160, 420)];
        lastShotTime2 = currentTime;
    }
    if(lastShotTime3 + [self randFloat:2] < currentTime) {
        [self shotAtPoint:CGPointMake(240, 350)];
        lastShotTime3 = currentTime;
        
    }
}

-(CGFloat)randFloat:(int)x
{
	return ( arc4random_uniform(x) + 1 )+ (arc4random_uniform(RAND_MAX) / (RAND_MAX * 1.0)) ;
}

```

配信タイミングも乱数を使って少しずれるように調整しています。

###衝突について

そのまま弾幕を表示しようとすると、
それぞれがぶつかり合って表示がおかしくなります。
そこで、弾幕用のNodeの
collisionBitMask
を0x1<<1にすることで、弾幕用Node間の衝突はしないようします。
※あるノードのcollisionBitMaskと、衝突したノードのcategoryBitMaskのANDをとり
 0であればぶつからない。


また、プレイアーと弾幕用のNodeの
categoryBitMask
collisionBitMaskを設定し、

```objc:MyScene.m
- (void)didBeginContact:(SKPhysicsContact *)contact
{
    [contact.bodyA.node removeFromParent];
    [contact.bodyB.node removeFromParent];
}
```

以上です。