Sprite Kit におけるマルチタスキング

Sprite Kit を用いたアプリで、マルチタスクを用いる方法です。

結論としては、SKViewのpausedプロパティにYESを入れるだけ、です。

とてつもなく簡単ですね。

こちらのstack overflowの記事 を引用すると、

AppDelegate に spritekitをimportした上で、

- (void)applicationWillResignActive:(UIApplication *)application
{
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    // pause sprite kit
    SKView *view = (SKView *)self.window.rootViewController.view;
    view.paused = YES;
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    // resume sprite kit
    SKView *view = (SKView *)self.window.rootViewController.view;
    view.paused = NO;
}

上記二つのメソッドにpausedプロパティの変更を加えるだけでした。

 

iAdを使用していたり、AVAudioを使用していたりする場合、それが元でクラッシュする場合があるため、その処理も必要です。

 

Core Data を理解してみる

アプリケーション制作について、データ永続化はなくてはならないものです。

Objective-Cで提供されているデータ永続化の手段には色々ありますが、やはりCore Dataは避けては通れないでしょう。

しかしこのCoreDataの考え方がWEBで扱っているMySQLなどとだいぶ違うように思ったので、メモしておきます。

CoreDataの考え方はエンティティーリレーション。

MySQLなどにおいては、データはエクセルのように表で表現されており、各種関連性は設計者によって作成されると思います。

特にWEBではLEFT JOINなどはパフォーマンスの問題で滅多には使えないため、表同士をきちんと関連づけるための正規化をはじめとした手法が非常に重要です。

しかしエンティティーリレーションでは、あるエンティティに別のエンティティへの関連を埋め込みます。

エンティティAにエンティティBへの関連を名前Cで埋め込んだ場合、Aオブジェクトに対して、”C” というキーパスでエンティティBを参照出来ます。

このリレーションを1対多とすることが出来、その場合はコレクションを参照出来ます。

このリレーションは両方向のため、Aを取得する場合、(B内にDという名前でAにリレーションを埋め込んだ時) Bオブジェクトに対する”D”というキーパスでAを参照出来ます。

理解すればするほど、胸が踊ります!

[Cocos2D iphone] 縦向き表示にする

とっても基本的な事だけれど、version3の情報がすぐに出てこなかったりしたので、メモしておきます。

Cocos2D-iphone v3を使用するとき、縦向き表示したいことがあります。

AppDelegate.m

にその設定があります。

場所は application: didFinishLaunchWithOptions: メソッドです。

FindからdidFinishで検索出来ます。

このメソッドではこのように書かれています。


// This is the only app delegate method you need to implement when inheriting from CCAppDelegate.

// This method is a good place to add one time setup code that only runs when your app is first launched.

そして下の方に

//		CCSetupScreenMode: CCScreenModeFixed,

// Run in portrait mode.

// CCSetupScreenOrientation: CCScreenOrientationPortrait,

とあります。

portraitモードで動かすには、この下をコメントアウトしてね、ということですね。

この部分をコメントアウトすると、見事にportraitで動作しました。

Sprite Kitでゲームを作成しました。

SpriteKitでゲームを作成しました。

 

gitHub

[DL可能、参考可能。image/sprite/se/music等は全て再利用不可、ライブラリ以外のソースは再利用可能]

 

ゲームとして色々課題があり、現在修正していて最新版は別にありますが、

ひとまずの参考版として、上記に全部のソースをのせてあります。

キャラクター等のソースは借り物であり、このままゲームとして出すことは出来ませんが、自分の歴史、及び参考として残したいと思います。

 

現在はCocos2Dと、OpenGL の APIを用いる等を学んでいます。

かわいいよ、Objective-C、XCode

最近XCodeとObjective-Cになれてきました。というよりも、

「かわいいよ、XCode,Objective-C」

と思えています。

はじめ

class->do(a,b);

class::do(a,b);

class.do(a,b);

[class do:a do2:b do3:c];

となる仕様に、「なんて気持ち悪い言語なんだろう」と思っていました。

それもそのはず。

そもそもそれまで、僕の一番好きな(推しな)言語は「Javascript」だったのです。

非常に簡易な書き方で様々な概念を実現できる言語であり、とても美しいです。

なので、Objective-Cは好きになれないな、と勝手ながら思っていたのですが。

しかしなれてみると、Cより遥かに進んでいて、Cに類似する使いやすさはあり、

Cの面倒な点(importとか)はまったく改良されていて、ARCのおかげでメモリ問題もある程度難しくなくなった

Objective-Cは、なんだかとても使いやすいのです。

もともとlispも好きなので、この括弧も気にならなくなりました。

これからもがんばっていけそうです。

SpriteKit 物理演算を使ってみる 衝突編

SpriteKitは物理演算を使用する事が出来ます。

SpriteKitのチュートリアルを参考にして、物理演算を使って衝突判定を行う手順を簡単に抜き出して纏めました。

まず、SKSceneのinitWithSizeなどのinitメソッドにおいて、自分自身を物理世界であることを認識させます。

    self.physicsWorld.gravity = CGVectorMake(0,0);
    self.physicsWorld.contactDelegate = self;

このようにすると、自分自身が物理世界であることを認識します。

そして、衝突の際のデリゲートを自分自身に設定することで、衝突の際、自分自身のクラス内のデリゲートメソッドをcallさせます。

この時、自分のクラス名.h でデリゲート使用宣言をおいておきます。さもないとXCodeが文句を言います。

@interface xxxScene : SKScene  <SKPhysicsContactDelegate>

ちなみに、下方向に重力をかける場合は
CGVectorMake(0,-1);
です。

次に実際に加えたsprite自身に、物理体であることを認識させるため、spriteの物理体を設定します。

これも非常に簡単です。

    sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.size];

これだけです。

しかしもちろんこれだけでは、衝突の判定は出来ません。

実際に衝突すると、delegateメソッドがcallされます。

didBeginContact メソッドがそれです。

-(void)didBeginContact:(SKPhysicsContact *)contact{
    SKPhysicsBody *firstBody , *secondBody;

}

この中で、実際に衝突した二つの物理体を取得する事が出来ます。

その物理体が実際に何なのかを、ここで判定する必要があります。

そこで、ビットマスクを用います。

これはフラグです。

static const uint32_t category0 = 0x1 << 0;
static const uint32_t category1 = 0x1 << 1;

このフラグをセットしておくことで、didBeginContactメソッド内で実際に衝突したものは何だったのかを判定します。

先ほどあるspriteに追加した物理体の

・categoryBitMask

・contactTestBitMask

に値を入れます。

bitMaskなので、複数設定出来ます。

A|B のように扱います。

ある物体Aに対して、衝突検知したい物体B,C,Dを同時に設定出来ます。

// delete method of collition
-(void)didBeginContact:(SKPhysicsContact *)contact{
    SKPhysicsBody *firstBody , *secondBody;

    // 順序整理
    if( contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask){
        firstBody = contact.bodyA;
        secondBody = contact.bodyB;
    }else{
        firstBody = contact.bodyB;
        secondBody = contact.bodyA;
    }

    // 二つの物体が意図したものであれば、衝突メソッドをcall
    if((firstBody.categoryBitMask & category0 ) != 0 && (secondBody.categoryBitMask & category1 ) != 0 ){
        // do something...
    }
}

これで衝突検知できました!

SpriteKit ベクトル計算式 snippet memo

ベクトルは物理演算には不可欠なものです。

というか、ゲーム数学に不可欠です。

Unityなど、どのフレームワークを使用するにおいても、ないとはじまりません。

以下はSpriteKitのチュートリアルで使われていたベクトル演算です。抜粋してメモしておこうと思います。

static inline CGPoint rwAdd( CGPoint a , CGPoint b ){
    return CGPointMake(a.x+b.x,a.y+b.y);
}

static inline CGPoint rwSub( CGPoint a , CGPoint b ){
    return CGPointMake( a.x - b.x , a.y - b.y );
}

static inline CGPoint rwMult( CGPoint a , float b ){
    return CGPointMake( a.x * b , a.y * b );
}

static inline float rwLength( CGPoint a ){
    return sqrtf(a.x*a.x+a.y*a.y);
}

static inline CGPoint rwNormalize( CGPoint a ){
    float length = rwLength(a);
    return CGPointMake( a.x / length , a.y / length );
}

 

 

SpriteKit で アニメーションスプライト を動かす(textureAtlas使わない版)

SpriteKitでアニメーションスプライトを動かすには、

SKTexutreAtlasというものを使い、バラバラの画像をひとつのアトラスとして扱い、アニメーションします。

SKTexutreAtlas Apple Developer Refarence

チュートリアルも結構あります。

[iOS 7] Sprite Kit の Texture Atlas を使ってみた

Sprite Kit チュートリアル:アニメーションとテクスチャアトラス

が、しかし。

現在様々なところで公開されているアニメーション素材の使用、もしくは管理しやすくするため、等の理由で、バラバラの画像ではなく、ひとまとめにしているものも使いたいです。

一つ一つのアクションの入れ替えが簡単であるので、atlasファイルで管理する方がいいような気もしますが、いかんせん一枚のファイル=一つのキャラもしくはエフェクトという文化もあるので、一枚のスプライト画像を読み込んでアニメーションするメソッドを作成してみました。

まずいずれかのクラスに以下を作成し、読み込んだファイルをフレーム数からバラバラに分解し、SKTextureを複数含む配列に変換して返します。

このあたり、JavascriptではCSS3のBackground-positionひとつで出来るところですが、Web版とは違いhttp通信の遅延を考慮しなくていい分、Objective-Cの方が楽かもしれません。

// imageをxFrameとyFrameで切り出す
-(NSMutableArray *)explodeAtlas:(NSString *)imageName xFrame:(NSNumber *)xFrame yFrame:(NSNumber *)yFrame{
    UIImage *_c = [UIImage imageNamed:imageName];

    CGImageRef inner = _c.CGImage;
    int xFrameInt = [xFrame intValue];
    int yFrameInt = [yFrame intValue];

    int width = _c.size.width/xFrameInt;
    int height = _c.size.height/yFrameInt;
    float scale = _c.scale;

    NSMutableArray *ret = [@[] mutableCopy];

    for( int i = 0 ; i < yFrameInt; i++){
        for( int i2 = 0 ; i2 < xFrameInt ; i2++){
            CGRect rect = CGRectMake(
                                     0+(i2*width)*scale,
                                     0+(i*height)*scale,
                                     width*scale,
                                     height*scale);
            CGImageRef ref = CGImageCreateWithImageInRect(inner, rect);
            UIImage *rev = [UIImage imageWithCGImage:ref];
            [ret addObject:[SKTexture textureWithImage:rev]];
        }
    }

    return ret;
}

次に、このメソッドで取得したテクスチャーでSKSpriteNodeを作成し、Animationさせます。

Class名は、先ほどのメソッドを入れたクラスです。

僕はSingletonの機能クラスに纏めているのですが、alloc,init等は省きます。

    NSMutableArray *sprites = [xxxx explodeAtlas:@"sp_fire" xFrame:@8 yFrame:@1];
    SKSpriteNode *fire = [SKSpriteNode spriteNodeWithTexture:sprites[0]];
    fire.position = CGPointMake(300,200);

    [fire runAction:[SKAction repeatActionForever:[SKAction animateWithTextures:sprites timePerFrame:0.1f resize:NO restore:YES ]] withKey:@"main"];

とても綺麗に動きます!

SpriteKit で とあるSpriteNode への swipを取得する

SpriteKit使用時における、とあるSpriteNodeへのswipを取得して、動作させます。

Swipeを受け取るにはSKViewがロードされ、ViewControllerから移動された直後に、そのSKViewにUISwipeGestureRecognizerを設置します。

-(void)didMoveToView:(SKView *)view{
    // ジェスチャー機能を追加する
    UISwipeGestureRecognizer* swipeUpGesture =
    [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeUp:)];
    swipeUpGesture.direction = UISwipeGestureRecognizerDirectionUp;
    [view addGestureRecognizer:swipeUpGesture];
}

これにより、@selectorで指定したメソッドでswipeを受け取ることが出来るようになります。

Delegateメソッドとして実装する方法もあります。

また、touchされたときに、どのspriteNodeが指定されたかを見るには、spriteNodeにあらかじめ名前がつけられるので、その名前をつけておき、touchesBeginでタッチされたNodeを取得して名前を比較します。

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInNode:self];
    SKNode *node = [self nodeAtPoint:location];
    if( [node.name hasPrefix:@"xxx" ]){
        // do something
    }
}

swipを取得する場合、ここでフラグを立てておくことにより、swipe時にこのnodeからのswipを判定できます。

SpriteKitでイメージを反転描画する。

 

ゲームではよくある反転表現。
あれがないとゲームによっては画像リソースが倍かかったりする場合もあり、いたく基本的な表現です。

cocos2Dでは

sprite.flipX=YES;

ですね。

SpriteKitでは、

sprite.xScale = -1 

とxScaleに負の値を入れることで反転するようです。

 

実はこの方法がわからなかった時、アフィン変換によって再描画したコンテキストをtextureに入れるという方法を考え、メソッドを作成しました。

以下はそのソースで、後で参照出来る様にとってあります。

</span></span>
<pre><pre>-(SKSpriteNode *)createSpriteFlipped:(NSString *)imageName{
    UIImage *_c = [UIImage imageNamed:[NSString stringWithFormat:@"%@%@",imageName,@".png"]];

    CGImageRef inner = _c.CGImage;

    CGContextRef ref = CGBitmapContextCreate( NULL, CGImageGetWidth( inner ), CGImageGetHeight( inner ), CGImageGetBitsPerComponent( inner ), CGImageGetBytesPerRow( inner ), CGImageGetColorSpace( inner ), CGImageGetBitmapInfo( inner ));

    CGRect rect = CGRectMake( 0, 0, _c.size.width, _c.size.height );

    CGAffineTransform transform = CGAffineTransformMakeTranslation( rect.size.width, 0.0 );
    transform = CGAffineTransformScale( transform, -1, 1 );

    CGContextConcatCTM( ref, transform );

    CGContextDrawImage( ref, rect, inner);

    UIImage *ret = [UIImage imageWithCGImage:CGBitmapContextCreateImage(ref)];

    SKSpriteNode *node = [SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImage:ret]];
    return node;
}

 

TOP

solgear

ゲームを創るサークルです。

-- lauguages -- Ruby, objective-C, C++
Ruby, PHP,Python,Javascript,lisp,Objective-C,C,SpriteKit,Cocos2D,Unity,Luna, Sculptris,iDraw,Gimp,tjs,
Hype2,SAI,
Emacs,XCode