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];
}
}
このメソッド実行のタイミングでは中心位置が少しずれるように
乱数を生成しています。
変数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];
}
以上です。