はじめに
備忘録を兼ねてます。
Swiftでゲームを作るとなるとUIView(xib)
を駆使するか、ScenekKit
を使うか直接OpenGL ES
を触るしかない。
UIView(xib)
を使ってゲームを作る場合は動きが激しいゲームは作れないので、本格的にゲームを作るとなるとSceneKit
もしくはOpenGL ES
を使うしかない。
SceneKit
はあまり使ったことがないがTiled
などのツールがサポートされていない印象だ。
OpenGL ES
を生で触るには、かなりの技術力とコーディング力が必要になる。
ということで今回はSpriteBuilder
を使う方法について書く。
※3Dゲームを作るならUnity
だと思います。
※本記事は自分が使う限り更新し続ける予定です。(同じ路線でゲームを作りたい方は是非共有お願いいたします♪)
SpriteBuilderとは?
SpriteBuilderはCocos2dのView部分を担当するUIツールだ。
作られたファイルをCocos2dで読み込むことによってプログラムを簡易化する。
xCodeで言うxib
の部分だ。
cocos2d
はOpenGL ES
を簡単に操作できるプログラムだ。
このツールを使うことでSwiftでOpenGL ES
を簡易的に使うことができる。
デメリット
SpriteBuilder
には重大な欠点がある。
なので、先にデメリットを伝えておく。
SpriteBuilder
はすでに開発が終わっており、AppStoreなどでも配布されていない。
なのでGitHub
などからプログラムを持ってきてインストールする必要がある。
また、幾つかのバグがそのままになっているのだ。
(あと、基本的にはiOSアプリしか作れない。)
正気の人間ならこのツールを使う選択肢は生まれないと思う。
しかしSwift
で2Dゲームを作ろうとした場合にほとんど方法が残されていないため、このツールを使わざるをえない状況なのだ。
考察
メリットの前にもう一つ考察しておく。
もしこれを読んでいる読者がUnity
を良しとしているならこれ以上記事を読む必要はないだろう。
Cocos2d-x
のようなC++
で書ける強者も同様だ。
Cocos Creator
にチャレンジしている方も不要だろう。
しかしいずれの方法もSwift
ではプログラムを書くことができない。
僕のようにSwift
でゲームを作りたいという方は今回紹介する方法を検討してみてはいかがだろうか?
メリット
SpriteBuilder
を使った1番のメリットはSwift
でOpenGL ES
のプログラミングが簡単にできるところだ。
多分現時点で一番使いやすい言語はSwift
だと言っても良い言語を使ってプログラミングができるのは大きな魅力だろう。
Unity
やCocos Creator
などを使った場合はネイティブ部分を触れないため、課金処理やスレッド処理などの込み入ったことをする時にハードルが上がる。しかし、ネイティブであるSwift
ならその辺でつまづくことはあまりない。
なので、開発効率は圧倒的に高い。
また、幸いにもSpriteBuilder
が出力するCocos2d V3
はObjective-c
が大半なのでSwift
のバージョンが上がってもあまり影響を受けない。
そのため、しばらくは使い続けれるだろう。
オープンソースなのでプログラムを変更できるのも大きな特色だ。
やってみよう
何はともあれ、Swiftでゲームを作りたい人は乗っかってみよう。
わからないことなどがあればコメントに書いてくれればわかる範囲で回答するので。
以下、注意点をまとめます。
インストール
昔はAppStore
で配布されていたので、そのままインストールできます。
今はGitHubに書かれている方法でインストールできます。(筆者はAppStore
で入れたので実践はしてませんが・・・)
起動後にPublishするまで
起動時に言語を選べるのでSwift
を選びます。
(Objective-c
を選んでもMainScene
がObjective-c
で表示されるだけなので、どちらでもいいです。)
File -> Project setting..
でAndroidを外します。
Publish
するとxCode
のプロジェクトが作られるので、そこからAndroid
やMac
などの不要なフォルダやファイルは消しましょう。
Published-Android
なども不要です。ガシガシ消しちゃいましょう。
(Androidのチェックを外しても、なぜか始めの一回目だけは作られてしまいます。)
言語コードに関して
iOS9から言語コードの扱いが変わっているため以下の対応を行う。
- (void) loadStringsFile:(NSString*) file
{
// Load default localization dictionary
NSString* path = [[CCFileUtils sharedFileUtils] fullPathForFilename:file];
// Load strings file
NSDictionary* ser = [NSDictionary dictionaryWithContentsOfFile:path];
// Check that format of file is correct
NSAssert([[ser objectForKey:@"fileType"] isEqualToString:@"SpriteBuilderTranslations"], @"Invalid file format for SpriteBuilder localizations");
// Check that file version is correct
NSAssert([[ser objectForKey:@"fileVersion"] intValue] == 1, @"Translation file version is incompatible with this reader");
// Load available languages
NSArray* languages = [ser objectForKey:@"activeLanguages"];
// Determine which language to use
NSString* userLanguage = NULL;
NSArray* preferredLangs = [NSLocale preferredLanguages];
for (NSString* preferredLang in preferredLangs)
{
if ([languages containsObject:preferredLang])
{
userLanguage = preferredLang;
break;
}
}
// Create dictionary for translations
_translations = [[NSMutableDictionary alloc] init];
// Load translations
if (userLanguage != NULL)
{
NSArray* translations = [ser objectForKey:@"translations"];
for (NSDictionary* translation in translations)
{
NSString* key = [translation objectForKey:@"key"];
NSString* value = [(NSDictionary*)[translation objectForKey:@"translations"] objectForKey:userLanguage];
if (key != NULL && value != NULL)
{
[_translations setObject:value forKey:key];
}
}
}
}
このメソッドを以下のように変更します。
- (void) loadStringsFile:(NSString*) file
{
// Load default localization dictionary
NSString* path = [[CCFileUtils sharedFileUtils] fullPathForFilename:file];
// Load strings file
NSDictionary* ser = [NSDictionary dictionaryWithContentsOfFile:path];
// Check that format of file is correct
NSAssert([[ser objectForKey:@"fileType"] isEqualToString:@"SpriteBuilderTranslations"], @"Invalid file format for SpriteBuilder localizations");
// Check that file version is correct
NSAssert([[ser objectForKey:@"fileVersion"] intValue] == 1, @"Translation file version is incompatible with this reader");
// Load available languages
NSArray* languages = [ser objectForKey:@"activeLanguages"];
// Determine which language to use
NSString* userLanguage = NULL;
NSArray* preferredLangs = [NSLocale preferredLanguages];
for (NSString* preferredLang in preferredLangs)
{
// now loop thru languages from our cocosbuilder
for (NSString *localizedLanguage in languages)
{
// doing range of string as we might have en-GB set in our phone and that will match our en from the activeLanguages
if ([preferredLang rangeOfString:localizedLanguage].location != NSNotFound)
{
userLanguage = localizedLanguage;
break;
}
}
if (userLanguage != NULL) {
break;
}
}
// Create dictionary for translations
_translations = [[NSMutableDictionary alloc] init];
// Load translations
if (userLanguage != NULL)
{
NSArray* translations = [ser objectForKey:@"translations"];
for (NSDictionary* translation in translations)
{
NSString* key = [translation objectForKey:@"key"];
NSString* value = [(NSDictionary*)[translation objectForKey:@"translations"] objectForKey:userLanguage];
if (key != NULL && value != NULL)
{
[_translations setObject:value forKey:key];
}
}
}
}
画面遷移
iOS9・・・というか、Capitan
から以下のコードが落ちるようになりました。
(xCodeのバージョンのせいかもしれない。とにかく最新だと落ちます。)
-(void)draw:(CCRenderer *)renderer transform:(const GLKMatrix4 *)transform
{
typedef id (*Func)(id, SEL);
((Func)objc_msgSend)(self, _drawSelector);
}
この箇所は以下のように書き換えると動きます。
-(void)draw:(CCRenderer *)renderer transform:(const GLKMatrix4 *)transform
{
void (*Func)(id, SEL) = (void(*)(id, SEL)) objc_msgSend;
Func(self, _drawSelector);
}
iOS10から
バージョンの識別が正しくできていないので修正。
以下のコードに置き換える。(ver1と間違えて認識されてしまう。)
- (id) init
{
if(nil != (self = [super init]))
{
# if __CC_PLATFORM_IOS
NSString* versionStr = [[UIDevice currentDevice] systemVersion];
unichar ch = [versionStr characterAtIndex:0];
version = (float)(ch - '0');
if(ch < '0' || ch > '9' || [versionStr characterAtIndex:1] != '.')
{
// NSLog(@"Error: %s: Cannot parse iOS version string \"%@\"", __PRETTY_FUNCTION__, versionStr);
// Version 10以上
version = (float)(ch - '0') * 10 + (float)([versionStr characterAtIndex:1] - '0');
}
float multiplier = 0.1f;
NSUInteger vLength = [versionStr length];
for(NSUInteger i = 2; i < vLength; i++)
{
ch = [versionStr characterAtIndex:i];
if(ch >= '0' && ch <= '9')
{
version += (ch - '0') * multiplier;
multiplier /= 10;
}
else if('.' != ch)
{
break;
}
}
# else
version = 5;
# endif
}
return self;
}
既存コードを踏襲して直したが、上記のコードはもう少し良い方法があるきがする・・・。
Images.xcassetsについて
Images.xcassets
はCocos2d
のプロジェクトでも使われています。
なので、アイコンやスプラッシュなどを設定する際には注意して設定してください。
Cocos2d
プロジェクトのImages.xcassets
を誤って選ばないようにしてください。
(邪魔ら消しても良いと思います。)
大量ワーニングについて
ほっといても良いのですが、とにかくワーニングがひたすら出ます。
以下のワーニングが30個くらい出てきます。
ld: warning: object file (/.../Build/Products/Debug-iphonesimulator/libcocos2d.a(IOSVersion.o)) was built for newer iOS version (9.3) than being linked (8.0)
これはこちらのリンクの通りで、以下の対応をすればワーニングが出なくなります。
The culprit is the minimum deployment target for the ObjectAL library -
In Xcode delve into the cocos2d project -> external -> ObjectAL project:
Goto build settings and search for 'deployment target'
and set the value to be <= your main project minimum deployment target.
上記の通りで
「cocos2d project -> external -> ObjectAL」のフォルダをたどってから「build settings」を表示します。
その中の「deployment target」を自分のプロジェクトに合わせた最小ターゲットにすればOKです。
他にも出ますが、上記を削れば自分のプロジェクト内部のワーニングと切り分けれるくらい減ります。
SpriteBuilderのプロジェクトの保存について
プロジェクトはCtrl+S
で保存できるのですが、、、ここにもバグがあって画像のサイズ(1xや2xなど)は保存されません。
なので、通常は2x
で作っていてイレギュラーに4x
などを使うと保存されずに次回開く時に2x
で表示されてしまいます。
これは致命的なバグですが、回避策があります。
それはclose project
を行うことです。この際に画像サイズが保存されるようなので×
で消すのではなく、close project
を行ってから閉じてください。
SpriteBuilderの使い方について
途中までですがこちらに書いてます。
他にも探せばありますので、見てみると良いでしょう。
思っているよりも簡単にゲームを作れるので驚くでしょう。
(こんな名器が世に埋もれてしまうなんて・・・)
実績について
こちらにゲームを作った事について書いてます。
よければ参考にしてください。
ついでに、ゲームで遊んでください。
勇者の冒険
-itunesが開きます-