LoginSignup
30
32

More than 5 years have passed since last update.

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

Posted at

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

プレイヤーなどの設定

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

MyScene.m

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

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

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);

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

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;
    }
}

弾幕の表示

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

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
とします。

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

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

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

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を設定し、

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

以上です。

30
32
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
32