はじめに
cocosコマンドで生成されるデフォルトのCocos2d-xのプロジェクトでは、
現状Objective-Cが使われていますが、Swiftは使えないのか?と思い、試してみました。
まぁ最初にまとめておくと、SwiftからObjective-C++を呼ぶようにすれば、
それらしいことは可能でした。
概要としては
- アプリ起動時にAppController.swiftが呼ばれるようにする
- Objective-C++のBridge Fileを用意する
- AppController.swiftで(2)にて作ったBridge Fileを呼ぶ
です。
※Cocos2d-x ver3.8.1にて行いました。
1. アプリ起動時にAppController.swiftが呼ばれるようにする
デフォルトのプロジェクトだと、アプリ起動時にAppController.mmが呼ばれますが、
それを新規に作成するAppController.swiftが呼ばれるようにします。
まず、main.mファイルを削除してしまいます。
こいつが(ざっくり言うと)内部でAppController.mmを呼んでいるためです。
次にAppController.swiftを作成します。
(Xcodeでシングルビューアプリケーションを作成した際に生成されるAppDelegate.swiftをコピればいいと思います)
ポイントは@UIApplicationMain
です。
これが付くことでAppController.swiftがアプリ起動時に読み込まれるようになります。
import UIKit
@UIApplicationMain
class AppController: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
return true
}
func applicationWillResignActive(application: UIApplication) {}
func applicationDidEnterBackground(application: UIApplication) {}
func applicationWillEnterForeground(application: UIApplication) {}
func applicationDidBecomeActive(application: UIApplication) {}
func applicationWillTerminate(application: UIApplication) {}
}
2. Objective-C++のBridge Fileを用意する
肝となる部分。
今までAppController.mmでcocosの初期化などを行っていたが、それをAppController.swiftから呼ぶために、
Bridge Fileを作成し、Swift → Objective-C++ と連携させる。
NativeBridge.hとNativeBridge.mmを作成します。(この名前は良くないかな…)
NativeBridge.mmに元々AppController.mmで呼んでいたcocosの処理を書きます。(ちょっとだけリファクタリングしてます)
#import <Foundation/Foundation.h>
@interface NativeBridge : NSObject
+ (void) applicationDidFinishLaunchingWithOptions;
+ (void) applicationWillEnterForeground;
+ (void) applicationDidEnterBackground;
@end
#import "NativeBridge.h"
#import "cocos2d.h"
#import "platform/ios/CCEAGLView-ios.h"
#import "RootViewController.h"
@implementation NativeBridge
+ (void) applicationDidFinishLaunchingWithOptions {
cocos2d::Application *app = cocos2d::Application::getInstance();
app->initGLContextAttrs();
cocos2d::GLViewImpl::convertAttrs();
// Override point for customization after application launch.
// Add the view controller's view to the window and display.
UIWindow *window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
// Init the CCEAGLView
CCEAGLView *eaglView = [CCEAGLView viewWithFrame: [window bounds]
pixelFormat: (NSString*)cocos2d::GLViewImpl::_pixelFormat
depthFormat: cocos2d::GLViewImpl::_depthFormat
preserveBackbuffer: NO
sharegroup: nil
multiSampling: NO
numberOfSamples: 0 ];
// Enable or disable multiple touches
[eaglView setMultipleTouchEnabled:NO];
// Use RootViewController manage CCEAGLView
RootViewController *viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
viewController.view = eaglView;
[window setRootViewController:viewController];
[window makeKeyAndVisible];
[[UIApplication sharedApplication] setStatusBarHidden:true];
// IMPORTANT: Setting the GLView should be done after creating the RootViewController
cocos2d::GLView *glview = cocos2d::GLViewImpl::createWithEAGLView(eaglView);
cocos2d::Director::getInstance()->setOpenGLView(glview);
app->run();
}
+ (void) applicationWillEnterForeground {
cocos2d::Application::getInstance()->applicationWillEnterForeground();
}
+ (void) applicationDidEnterBackground {
cocos2d::Application::getInstance()->applicationDidEnterBackground();
}
@end
次にBuild Settings > Objective-C Bridging Header
に
$(SRCROOT)/ios/NativeBridge.h
みたいな感じで、上記で作成したNativeBridge.hが読み込まれるようにします。
これをすることでswiftファイルからNativeBridgeのメソッドなどが呼び出せるようになります。
3. AppController.swiftで2にて作ったBridge Fileを呼ぶ
最後に、AppController.swiftを下記のようにします。
(2)で作ったNativeBridgeのクラスメソッドを呼んでいます。
これで作業は終了です。
実行すればいつもの見慣れたcocosの変なマークのスクリーンが出てくるはず。
import UIKit
@UIApplicationMain
class AppController: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
NativeBridge.applicationDidFinishLaunchingWithOptions()
return true
}
func applicationWillResignActive(application: UIApplication) {}
func applicationDidEnterBackground(application: UIApplication) {
NativeBridge.applicationDidEnterBackground()
}
func applicationWillEnterForeground(application: UIApplication) {
NativeBridge.applicationWillEnterForeground()
}
func applicationDidBecomeActive(application: UIApplication) {}
func applicationWillTerminate(application: UIApplication) {}
}
以上…!
おわりに
これでcocosの開発でもswift使える!とか思ったのですが、一旦Bridge Fileを作らなきゃいけないのはちょっと面倒…
まぁでもAndroidの場合もjni経由じゃなきゃいけないし、ある意味同じ形になるのかな。
とりあえずswiftが使えるということだけで、嬉しい気がする。
(何か間違いがある場合、指摘して頂ければ幸いです。)