2013年11月21日木曜日
2013年11月14日木曜日
配列にNULL(nil)を入れる
配列にnilは入れられないので、NSNullを使う
//配列にNullをを入れる [array addObject:[NSNull null]]; //Null判定 if ([array objectAtIndex:index] == [NSNull null]){ NSLog(@"This is null!!"); }
2013年11月6日水曜日
SQLite(FMDB)
FMDBを使用
#import "FMDatabase.h" FMDatabase* _db; NSString* DB_FILE = @"sample.sqlite3"; //DBオープン if (![self openDatabase]) { //失敗 } //更新 if (![self executeUpdate]) { //失敗 } //参照 [self executeQuery]; //DBクローズ [self closeDatabase]; - (BOOL)openDatabase { //DBファイルへのパスを取得 //パスは~/Documents/配下に格納される。 NSString *dbPath = nil; NSArray *documentsPath = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); //取得データ数を確認 if ([documentsPath count] >= 1) { //固定で0番目を取得でOK dbPath = [documentsPath objectAtIndex:0]; //パスの最後にファイル名をアペンドし、DBファイルへのフルパスを生成。 dbPath = [dbPath stringByAppendingPathComponent:DB_FILE]; NSLog(@"db path : %@", dbPath); } else { //error NSLog(@"search Document path error. database file open error."); return false; } //DBファイルがDocument配下に存在するか判定 NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:dbPath]) { //存在しない //デフォルトのDBファイルをコピー(初回のみ) //ファイルはアプリケーションディレクトリ配下に格納されている。 NSBundle *bundle = [NSBundle mainBundle]; NSString *orgPath = [bundle bundlePath]; //初期ファイルのパス。(~/XXX.app/sample.db) orgPath = [orgPath stringByAppendingPathComponent:DB_FILE]; //デフォルトのDBファイルをDocument配下へコピー if (![fileManager copyItemAtPath:orgPath toPath:dbPath error:nil]) { //error NSLog(@"db file copy error. : %@ to %@.", orgPath, dbPath); return false; } } //open database with FMDB. _db = [FMDatabase databaseWithPath:dbPath]; return [_db open]; } - (void)executeQuery { //クエリ実行 FMResultSet *rs = [_db executeQuery:@"select * from items where word = ? ",@"abc"]; if ([_db hadError]) { NSLog(@"Err %d: %@", [_db lastErrorCode], [_db lastErrorMessage]); } //結果の取得(カラム名指定) while ([rs next]) { //[rs dateForColumn:@"n"]; //[rs stringForColumn:@"t"]; //[rs doubleForColumn:@"r"]; NSLog(@"id->%d", [rs intForColumn:@"item_id"]); } //close ResultSet. [rs close]; } - (BOOL)executeUpdate { BOOL result = TRUE; //トランザクション開始(exclusive) [_db beginTransaction]; //ステートメントの再利用フラグ //おそらくループ内で同一クエリの更新処理を行う場合バインドクエリの準備を何回 //も実行してしまうのためこのフラグを設定する。 //このフラグが設定されているとステートメントが再利用される。 [_db setShouldCacheStatements:YES]; //update [_db executeUpdate:@"update items set correct_count = correct_count + ? ", [NSNumber numberWithInt:1]]; //check if ([_db hadError]) { result = FALSE; NSLog(@"Err %d: %@", [_db lastErrorCode], [_db lastErrorMessage]); } //commit [_db commit]; return result; } - (void)closeDatabase { if (_db) { [_db close]; } }
2013年10月23日水曜日
cocos2dでTextField
CCUIViewWrapperを使用
以下のURLよりソースを取得
※そのままではBuild時に警告が出るので以下を修正
[[[[CCDirector sharedDirector] view] window] addSubview: uiItem]; //[[[CCDirector sharedDirector] openGLView] addSubview:uiItem]; /****************************************/ //if(!p.isRelativeAnchorPoint) if(p.ignoreAnchorPointForPosition) transform = CGAffineTransformTranslate(transform, p.anchorPoint.x, p.anchorPoint.y); //transform = CGAffineTransformTranslate(transform, p.anchorPointInPixels.x, p.anchorPointInPixels.y); /****************************************/ transform = CGAffineTransformTranslate(transform, -p.anchorPoint.x, -p.anchorPoint.y); //transform = CGAffineTransformTranslate(transform, -p.anchorPointInPixels.x, -p.anchorPointInPixels.y);
UITextFieldを実装
ヘッダー
@interface GameLayer : CCLayer <UITextFieldDelegate> { CCUIViewWrapper *textFieldWrapper; UITextField *textBox; } @end
実装
- (void)addTextField { textBox = [[[UITextField alloc] init] autorelease]; textBox.frame = CGRectMake(175, 120, 110, 25); textBox.borderStyle = UITextBorderStyleRoundedRect; textBox.placeholder = @"hogehoge"; textBox.returnKeyType = UIReturnKeyDone; textBox.clearButtonMode = UITextFieldViewModeWhileEditing; textBox.autocapitalizationType = UITextAutocapitalizationTypeNone; textBox.enablesReturnKeyAutomatically = YES; textBox.contentVerticalAlignment = UIControlContentHorizontalAlignmentCenter; textBox.textAlignment = NSTextAlignmentCenter; textFieldWrapper = [CCUIViewWrapper wrapperForUIView:textBox]; textBox.delegate = self; [self addChild:textFieldWrapper]; } - (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; return YES; } - (void)textFieldDidEndEditing:(UITextField *)textField { CCLOG(@"text->%@", textBox.text); }
URL encode/decode
//encoding NSString *escapedUrlString = (NSString *)CFURLCreateStringByAddingPercentEscapes( NULL, (CFStringRef)plainString, NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8 ); //decoding NSString *decodedUrlString = (NSString *) CFURLCreateStringByReplacingPercentEscapesUsingEncoding( NULL, (CFStringRef) escapedUrlString, CFSTR(""), kCFStringEncodingUTF8);
XMLパーサー
ヘッダー
@interface XMLParserWrapper : NSObject <NSXMLParserDelegate> { NSString *searchElementString; } - (void)parseXMLFileAtURL:(NSURL *)URL parseError:(NSError **) error ; - (void)parseXMLFileAtData:(NSData *)data parseError:(NSError **) error ; @end
実装
@implementation XMLParserWrapper //URLで解析 - (void)parseXMLFileAtURL:(NSURL *)URL parseError:(NSError **) error { NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:URL]; [self parseXMLFile:parser parseError:error]; } //Dataで解析 - (void)parseXMLFileAtData:(NSData *)data parseError:(NSError **) error { NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data]; [self parseXMLFile:parser parseError:error]; } - (void)parseXMLFile:(NSXMLParser *)parser parseError:(NSError **) error { [parser setDelegate:self]; [parser setShouldProcessNamespaces:NO]; [parser setShouldReportNamespacePrefixes:NO]; [parser setShouldResolveExternalEntities:NO]; [parser parse]; NSError *parseError = [parser parserError]; if (parseError && error) { *error = parseError; } [parser release]; } - (void)parserDidStartDocument:(NSXMLParser *)parser { searchElementString = nil; } - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { if ([elementName isEqualToString:@"取得したい属性名"]) { searchElementString = [NSString stringWithString:elementName]; } } - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { if ([elementName isEqualToString:@"取得したい属性名"]) { searchElementString = nil; } } - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { if ([searchElementString isEqualToString:@"取得したい属性名"]) { //stringに取得したい属性の値 } } @end
使い方
NSError *parseError = nil; XMLParserWrapper *parser = [[XMLParserWrapper alloc] init]; [parser parseXMLFileAtData:receivedData parseError:&parseError]; if (parseError) { //parseエラー NSLog(@"Parse Error->%@", parseError); }
非同期通信
NSMutableData *receivedData; //非同期通信 - (void)startConnection{ NSURL *url = [NSURL URLWithString:@"http://xxxx.jp/"]; NSURLRequest *request=[NSURLRequest requestWithURL:url]; NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]; if (!connection) { //エラー } } //通信開始 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { receivedData = [[NSMutableData data] retain]; } //通信中(何度も呼ばれる) - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ // データを追加する [receivedData appendData:data]; } //通信終了 - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSString *dataStr = [[[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding] autorelease]; //受信データをログに表示 NSLog(@"%@", dataStr); [receivedData release]; receivedData = nil; [connection release]; } //通信エラー - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { //アラートを出す NSString *erroMessage = [error localizedDescription]; UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:@"Error" message:erroMessage delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] autorelease]; [alert show]; }
2013年10月15日火曜日
Google Analytics for iOS v3(Beta)
※注意:この内容は古い情報です
http://kojisatoapp.blogspot.jp/2014/02/iosgoogle-analytics.html
ライブラリ追加
- GAI.h
- GAITracker.h
- GAITrackedViewController.h
- GAIDictionaryBuilder.h
- GAIFields.h
- GAILogger.h
- libGoogleAnalyticsServices.a
フレームワークの追加
- AdSupport.framework
- libGoogleAnalyticsServices.a
- CoreData.framework
- SystemConfiguration.framework
- libz.dylib
初期処理
#import "GAI.h" - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Optional: automatically send uncaught exceptions to Google Analytics. [GAI sharedInstance].trackUncaughtExceptions = YES; // Optional: set Google Analytics dispatch interval to e.g. 20 seconds. [GAI sharedInstance].dispatchInterval = 20; // Optional: set Logger to VERBOSE for debug information. [[[GAI sharedInstance] logger] setLogLevel:kGAILogLevelVerbose]; // Initialize tracker. [[GAI sharedInstance] trackerWithTrackingId:@"UA-XXXX-Y"]; }
ページのトラッキング(自動計測)
#import "GAITrackedViewController.h" @interface HomeViewController : GAITrackedViewController - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; self.trackedViewName = @"About Screen"; }
ページのトラッキング(手動)
#import "GAI.h" #import "GAIFields.h" #import "GAIDictionaryBuilder.h" // May return nil if a tracker has not already been initialized with a // property ID. id tracker = [[GAI sharedInstance] defaultTracker]; // This screen name value will remain set on the tracker and sent with // hits until it is set to a new value or to nil. [tracker set:kGAIScreenName value:@"Home Screen"]; [tracker send:[[GAIDictionaryBuilder createAppView] build]];
イベントのトラッキング
// May return nil if a tracker has not already been initialized with a property // ID. id<GAITracker> = [[GAI sharedInstance] defaultTracker]; [tracker send:[[GAIDictionaryBuilder createEventWithCategory:@"ui_action" // Event category (required) action:@"button_press" // Event action (required) label:@"play" // Event label value:nil] build]]; // Event value
アラート表示
アラート
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:@"タイトル" //タイトル delegate:self //デリゲートを自分自身に cancelButtonTitle:nil //キャンセルボタン otherButtonTitles:@"OK", nil]; //確認ボタン [alertView show]; [alertView release];
コンファーム
UIAlertView* confirmView = [[[UIAlertView alloc] initWithTitle: @"ログインしていません" message: @"ログインしますか" delegate: self cancelButtonTitle: @"いいえ" otherButtonTitles: @"はい", nil] autorelease]; [confirmView show]; - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { if (buttonIndex==[alertView cancelButtonIndex]) { //キャンセルボタン return; } //キャンセル以外の処理 }
2013年10月11日金曜日
GameCenter
事前準備
iTunesConnectでアプリを登録し、GameCenterを有効にするInfo.plistの設定
Required device capabilitiesに「gamekit Boolean YES」を追加認証
// GameCenter認証 [[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:^(NSError *error) { if (error != nil) { //エラー処理 } }];
GKLeaderboardViewControllerDelegateプロトコル適用
//ヘッダーファイルにプロトコル追加 @interface TestLayer : CCLayer <GKLeaderboardViewControllerDelegate>
得点を送信
//iTunesConnectで設定したリーダーボードIDを設定 GKScore *scoreReporter = [[[GKScore alloc] initWithCategory:@"Leaderboard ID"] autorelease]; scoreReporter.value = HiScore; [scoreReporter reportScoreWithCompletionHandler:^(NSError *error) { if (error != nil) { //エラー処理 } }];
リーダーボードの表示
GKLeaderboardViewController *lbController = [[GKLeaderboardViewController alloc] init]; if (lbController != nil) { lbController.leaderboardDelegate = self; [[CCDirector sharedDirector] presentModalViewController:lbController animated:YES]; } //リーダーボードを抜ける時の処理 - (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController { [[CCDirector sharedDirector] dismissViewControllerAnimated:YES completion:^{ // }]; }
2013年10月7日月曜日
2013年10月3日木曜日
ノードの削除
自分自身を削除
//YESでアクションやスケジュールもクリーンアップ [self removeFromParentAndCleanup:YES]; //removeFromParentAndCleanup:YESと同じ [self removeFromParent]
親ノードから削除
[self removeChildByTag:101]
2013年10月2日水曜日
ベジェ曲線での移動
ccBezierConfig bezConf; bezConf.controlPoint_1 = ccp(0, 0); bezConf.controlPoint_2 = ccp(30, 0); bezConf.endPosition = ccp(30, 10); id move = [CCBezierBy actionWithDuration:1.0 bezier:bezConf]; [self runAction:move];
2013年10月1日火曜日
Parse issue: Unknown type name 'ClassName'
ビルドが通らず表題のメッセージが出る場合、import文の循環参照が疑わしい
.hファイル
#import "class.h" を @class class に書き換え.mファイル
#import "class.h" を加える時間調整のアクション
//時間調整 id delay = [CCDelayTime actionWithDuration:1.0]; CCSequence* seq = [CCSequence actions:delay, action ,nil]; [self runAction:seq];
座標の絶対値、相対値変換
スプライトからの相対値(ローカル座標)を、画面上の絶対座標(ワールド座標)に変換
//絶対値→相対値 CGPoint local = [sprite convertToNodeSpace:ccp(x, y)]; //相対値→絶対値 CGPoint world = [sprite convertToWorldSpace:ccp(x, y)];
2013年9月27日金曜日
スプライトにタッチを実装
ヘッダー
@interface SpriteWithTouch : CCSprite <CCTouchOneByOneDelegate>
実装
- (void) onEnter { [super onEnter]; [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES]; } - (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event { CGPoint touchLocation = [touch locationInView:touch.view]; CGPoint location = [[CCDirector sharedDirector] convertToGL:touchLocation]; //CCSpriteの描画領域でタッチされているか判定 if (CGRectContainsPoint(self.boundingBox, location)) { CCLOG(@"touch!"); return YES; } else { return NO; } } - (void) onExit { [super onExit]; [[[CCDirector sharedDirector] touchDispatcher] removeDelegate:self]; }
図形描画
矩形描画
- (void)draw { [super draw]; ccDrawColor4B(255, 128, 0, 255); glLineWidth(5); ccDrawRect(ccp(0, 0), ccp(100, 100)); }
2013年9月26日木曜日
cocos2dでテクスチャアトラスを使う
スプライト
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"sprite.plist"]; CCSprite *hoge = [[CCSprite node] initWithSpriteFrameName:@"hoge.png"];
アニメーション
CCAnimation *anime = [CCAnimation animation]; for (int cnt=1; cnt<=5; cnt++) { [anime addSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:@"hoge%02d.png", cnt]]]; } anime.delayPerUnit = 0.1; anime.loops = 1; //キャッシュに追加 [[CCAnimationCache sharedAnimationCache] addAnimation:anime name:@"anime01"];
注意点
画像の最大サイズは各端末毎に異なる
端末 | 最大サイズ |
---|---|
iPhone4, iPad2, iPodtouch 4th | 2048 * 2048 |
iPhone5, iPhone4S, iPad3, iPodtouch 5th | 4096 * 4096 |
iPhone3Gなど古い端末 | 1024 * 1024 |
2013年9月25日水曜日
通知
送信
受信
削除
//通知送信 NSNotification *notification = [NSNotification notificationWithName:@"hoge" object:self]; [[NSNotificationCenter defaultCenter] postNotification:notification];
受信
//通知受信 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc addObserver:self selector:@selector(test) name:@"hoge" object:nil]; //呼ばれる関数 - (void)test { NSLog(@"notification!"); }
削除
- (void) dealloc { //通知受信の削除 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc removeObserver:self name:@hoge" object:nil]; [super dealloc]; }
2013年9月24日火曜日
プログレスバー
//initの処理 CCProgressTimer *timer = [CCProgressTimer progressWithSprite:[CCSprite spriteWithFile:@"test.png"]]; timer.type = kCCProgressTimerTypeRadial; timer.percentage = 100; timer.position = ccp(winSize.width/2, winSize.height/2); [self addChild:timer z:2 tag:123]; [self scheduleUpdate]; - (void) update:(ccTime)delta { CCProgressTimer *timer = (CCProgressTimer*)[self getChildByTag:123]; timer.percentage -= delta * 150; if (timer.percentage <= 0) { timer.percentage = 100; } }
2013年9月19日木曜日
画像、アニメのキャッシュ削除
- (void) dealloc { //画像、アニメのキャッシュ削除 [CCAnimationCache purgeSharedAnimationCache]; [[CCTextureCache sharedTextureCache] removeAllTextures]; [super dealloc]; }
アニメーションの逆再生、順次実行
スプライトアニメーションは逆再生(リバース)したり、順番に実行することができる
CCAnimation* anime = [[CCAnimationCache sharedAnimationCache] animationByName:@"anime"]; id action = [CCAnimate actionWithAnimation:anime]; //リバース id back = [action reverse]; //順番に実行 CCSequence* seq = [CCSequence actions:action, back, nil]; [self runAction:seq];
2013年9月17日火曜日
スプライトのテクニック
CCSpriteBatchNode
- 同じテクスチャを使って多くのスプライトを描画する場合、効率的である
- 追加されたスプライトはすべて同じ深度(Zオーダー)になる
- スプライトを最初に全て生成し非表示にして、必要に応じてアクティブにする(オブジェクトプーリング)
テクスチャアトラス
- テクスチャアトラスとは複数の画像を一つにまとめた大きな画像
- 幅と高さは2の累乗(256,512)にする
- .plistファイルにどの部分をスプライトとして使うのか定義する
- スプライトアニメーションにテクスチャアトラスを使用すると効率的
- カテゴリを使いアニメーションのヘルパーを作ると楽になる
- テクスチャアトラスはZwoptexやTexturePackerProなどのツールを使用するとよい
ゲームの実装方法
- 画面遷移にトランジションを使うとonEnterなどのタイミングが変わるので注意
- onEnterなどでsuperのメソッドを呼び出さないと入力に反応しなくなる
- onExitなどでsuperのメソッドを呼び出さないとメモリが解放されなくなる
- 重い画面から重い画面に遷移する場合、その間に1つ軽い画面を挟むと良い
- initメソッドではCCDireoctorのreplaceSceneを呼び出さないように注意
- ccp○○○○メソッドでCGPointの差分などの計算ができる
- CGRectContainsPoint(スプライトのバウンディングボックス,タッチ座標)で当たり判定できる
レイヤーを分ける
CCScene
initで複数のレイヤーをaddChildする自分自身を変数に持ちsharedメソッドでそれを渡す半シングルトンとする
(deallocで自分自身を保持する変数をnilにする)
子レイヤーにはゲッターメソッドかreadonlyのプロパティを使用しアクセスする
CCLayer
registerWithTouchDispatcherのpriorityでタッチ入力の優先度をつける(小さい値が優先となる)
ccTouchBeganでNOを返せば次の優先度のレイヤが入力を受ける
レベルの切り替えはシーンを変える、レイヤーを変えるのどちらかで行う
背景色はCCColorLayerクラスを使用する
CCColorLayer* colorLayer = [CCColorLayer layerWithColor:ccc4(255, 0, 255, 255)]; [self addChild:colorLayer z:0];
スプライトの実装
ゲームのキャラクターなどはサブクラス化よりコンポジションの方がよい
オートリリースイニシャライザで初期化
+(id) spiderWithParentNode:(CCNode*)parentNode { return [[[self alloc] initWithParentNode:parentNode] autorelease]; }
CCSchedulerクラスを使用しupdateメソッドをスケジュールする
// initなどで更新をスケジュール [[[CCDirector sharedDirector] scheduler] scheduleUpdateForTarget:self priority:0 paused:NO] //[[CCScheduler sharedScheduler] scheduleUpdateForTarget:self priority:0 paused:NO]; //deallocで解除 [[[CCDirector sharedDirector] scheduler] unscheduleUpdateForTarget:self]; //[[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self];
CCStandardTouchDelegateまたはCCTargetedTouchDelegateプロトコルを実装すればタッチを受け取れる
// initなどでこのクラスをターゲットタッチイベントのレシーバーとして追加する [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:-1 swallowsTouches:YES]; //deallocで解除 [[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
CCNodeの派生クラス
CCProgressTimerでプログレスバーを追加できる
CCParallaxNodeでパララックス処理(多重スクロール)を表現できる
CCRibbonでドラッグの軌跡に画像を描く処理ができる
CCMotionStreakはCCRibbonの描画したものが数秒でフェードアウトするもの
2013年9月13日金曜日
加速度センサー入力
加速度センサーによるスプライトの移動
スムーズに動かすためには下記に修正
-(id) init { if( (self=[super init]) ) { [self setAccelerometerEnabled:YES]; } } - (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { CGPoint pos = sprite.position; pos.x += acceleration.x * 10; pos.y += acceleration.y * 10; sprite.position = pos; }だが、これでは反応が鈍い
スムーズに動かすためには下記に修正
-(id) init { if( (self=[super init]) ) { [self setAccelerometerEnabled:YES]; //updateをフレーム毎に呼び出す [self scheduleUpdate]; } } - (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { //速度の制御 float deceleration = 0.4f; //減速の制御 float sensitivity = 6.0f; //感度 //速度調整 moveSpeed.x = moveSpeed.x * deceleration + acceleration.x * sensitivity; moveSpeed.y = moveSpeed.y * deceleration + acceleration.y * sensitivity; } -(void)update:(ccTime)delta { CGPoint pos = sprite.position; pos.x += moveSpeed.x; pos.y += moveSpeed.y; sprite.position = pos; }
アクション
どのノードでも利用可能。
CCMoveTo* move = [CCMoveTo actionWithDuration:3 postion:CGPointMake(100,200)];//(100,200)へ3秒以内に移動 [myNode runAction:move];
アクションにはインスタンスアクションとインターバルアクションの2種類がある。インスタンスアクションは、基本的にはvisibleやflipXのようなノードプロパティを設定するのと同じ。インターバルアクションは上の移動のように一定時間にわたって実行される。
アクションは終わると自動的にノードから削除され、メモリから解放される。
アクションを繰り返す
CCRotateBy* rotateBy = [CCRotateBy actionWithDuration:2 angle:360]; CCRepeateForever* repeat = [CCRepeatForever actionWithAction:rotateBy]; [myNode runAction:repeat];//永遠に回転
イーズアクション
一定の動きではなく徐々に変化させたりするアクション。
CCMoveTo* move = [CCMoveTo actionWithDuration:3 position:CGPointMake(100,200)]; CCEaseInOut* ease = [CCEaseInOut actionWithAction:move rate:4];//ゆっくりと加速したあと減速する [myNode runAction:ease];
CCActionEase
- CCEaseIn, CCEaseOut, CCEaseInOut
- CCEaseBounceIn, CCEaseBounceOut, CCEaseBounceInOut
- CCEaseElasticIn, CCEaseElasticOut, CCEaseElasticInOut
- CCEaseExponentialIn, CCEaseExponentialOut, CCEaseExponentialInOut
- CCEaseIn, CCEaseOut, CCEaseInOut
- CCEaseSineIn, CCEaseSineOut, CCEaseSineInOut
アクションシーケンス
通常、複数のアクションを登録するとすべてのアクションがそれぞれのタスクを同時に実行する。アクションを順番に実行するにはシーケンスにする。
CCTintTo *tint1 = [CCTintTo actionWithDuration:3 red:255 green:0 blue:0]; CCTintTo *tint2 = [CCTintTo actionWithDuration:3 red:0 green:0 blue:255]; CCTintTo *tint3 = [CCTintTo actionWithDuration:3 red:0 green:255 blue:0]; CCSequence *sequence = [CCSequence actions:tint1, tint2, tint3, nil]; [label runAction:sequence]; //RepeatForeverを使う場合 CCSequence *sequence2 = [CCSequence actions:tint1, tint2, tint3, nil]; CCRepeatForever *repeat = [CCRepeatForever actionWithAction:sequence2]; [label runAction:repeat];
インスタントアクション
プロパティでも変更可能な効果だがシーケンスに組み込める。
CCCallFuncアクション・・・シーケンス終了などのタイミングで通知メッセージを送信できる。
CCTintTo *tint1 = [CCTintTo actionWithDuration:3 red:255 green:0 blue:0]; CCTintTo *tint2 = [CCTintTo actionWithDuration:3 red:0 green:0 blue:255]; CCTintTo *tint3 = [CCTintTo actionWithDuration:3 red:0 green:255 blue:0]; CCCallFunc *func = [CCCallFunc actionWithTarget:self selector:@selector(onCallFunc)]; CCCallFuncN *funcN = [CCCallFuncN actionWithTarget:self selector:@selector(onCallFuncN)]; CCCallFuncND *funcND = [CCCallFuncND actionWithTarget:self selector:@selector(onCallFuncND:data:) data:background]; CCSequence *sequence = [CCSequence actions:tint1, func, tint2, funcN tint3, funcND, nil]; [label runAction:sequence];
CCSprite
//ファイルからCCSprite作成 CCSprite *sprite = [CCSprite spriteWithFile:@"Default.png"];] [self addChild:sprite];
※iOSデバイス上ではファイル名の大文字と小文字が区別されるので注意
アンカーポイント
テクスチャのオフセット。デフォルトは0.5
テクスチャのサイズ
4,8,16,32,64,128,256,512,1024ピクセルのいずれか
第三世代では2048が加わる。テクスチャは正方形でなくてもいい。
シーンとレイヤ
CCSceneとCCLayerはCCNodeと同様ビジュアル表現を持たない抽象概念
シーングラフの出発点はCCScene
CCLayerはノードのグループ化、入力の処理など
CCScene
[[CCDirector sharedDirector] runWithScene:[HelloWorld scene]]; //最初のシーン登録 [[CCDirector sharedDirector] replaceScene:[HelloWorld scene]]; //置き換え新しいシーンを読み込むときは古いシーンが破棄される前なので一時的にメモリが増大する
シーンの作成と破棄でログを吐くといい。切替時にdeallocのログメッセージが送信されていなかったらシーン全体がメモリリークしていることになる。
ノードのメモリ管理はcocos2dに任せないと危険。
シーンのプッシュとポップ
pushSceneとpopScene。古いシーンを破棄せずに新しいシーンを実行複数の場所で使われる共通のシーン、ボリュームなどの設定画面の呼び出しなどに使う。
素早く変更できる反面メモリを食う。
スタックに積むので気をつけないと積み過ぎになったり取り過ぎになったりしてしまう。
[[CCDirector sharedDirector] pushScene:[Stettings scene]]; //プッシュ [[CCDirector sharedDirector] popScene]; //ポップ
CCTransitionScene
見た目は良いが切り替えに時間が掛かる。1秒以内か使わないのが妥当。//3秒で赤にフェード CCTransitionFade *tran = [CCTransitionFate transitionWithDuration:3 scene:[HelloWorld scene] withColor:ccRED]; [[CCDirector sharedDirector] replaceScene:tran];replaceSceneやpushSceneで使えるがpopSceneでは別のテクニックが必要
Transitionの種類
- CCTransitionFade
- CCTransitionFlipAngular
- CCTransitionShrinkGrow
- CCTransitionMoveInB
- CCTransitionMoveInT
- CCTransitionMoveInL
- CCTransitionMoveInR
- CCTransitionFadeTR
- CCTransitionFadeUp
- CCTransitionFlipX
- CCTransitionFlipY
- CCTransitionPageTurn
- CCTransitionCrossFade
CCLayer
シーンによっては複数のCCLayerが必要になることがある。その場合はシーンの作成時にレイヤを追加する必要がある。たとえば静的なフレームの下にスクロールする背景がある場合など。背景レイヤを動かすとそのレイヤにある要素は追従するので簡単に移動できる。またレイヤのZオーダーに応じて同じレイヤのオブジェクトが前面、背面に移動可能
レイヤはグループ化の概念。まとめて移動、回転、拡縮。
いくつ作ってもいいが、タッチ入力や加速度センサーを受け取るレイヤを作り過ぎないようにする。必要に応じて転送するなど
タッチイベントの受け取り
タッチの開始、移動、終了、中止が報告されるが、中止はほとんど無いので無視するか終了に転送する。タッチイベントはCocoaTouchAPIによって受け取られるため、OpenGL座標に変換する必要がある
self.isTouchEnabled = YES; -(CGPoint) locationFromTouch:(UITouch *t)touch{ CGPoint touchLocation = [touch locationInView: [touch view]]; return [[CCDirector sharedDirector] convertToGL:touchLocation]; }※マルチタッチに対応するには[glView setMultipleTouchEnabled:YES]する必要がある
・ターゲットタッチハンドラは一連のタッチを別々のイベントに分割する。
特定のタッチをイベントキューから削除することで他のレイヤに転送しないようにできる。
ターゲットハンドラを有効にするには
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES];タッチ入力メソッドはtouchesではなくtouch
加速度センサー
self.isAccelerometerEnabled = YES; - (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { //3方向の加速度 CCLOG(@"acceleration x:%f / y:%f / z:%f",acceleration.x,acceleration.y,acceleration.z); }
CCNodeクラス
CCNodeはすべてのノードの親クラス
ノードを操作する
CCNode* childNode = [CCNode node]; //新しいノード作成 [myNode addChild:childNode z:0 tag:123]; //子を追加。zの小さいものから順に描画。zが同じ場合は追加順 CCNode* retrievedNode = [myNode getChildByTag:123]; //子を取得 [myNode removeChildByTag:123 cleanup:YES]; //タグで子を削除。cleanupは実行中のアクションも全て停止 [myNode removeChild:retrievedNode]; //ノードポインタで削除 [myNode removeAllChildrenWithCleanup:YES]; //すべての子ノード削除 [myNode removeFromParentAndCleanup:YES]; //親ノード(myNode)を削除同じタグをつけるとその中から最初のノードしか取り出せない
ノードとアクションのタグは競合しない
アクションを操作する
ノードではアクションも実行できる。移動、回転、拡大縮小などCCAction* action = [CCBlink actionWithDuration:10 blinks:20]; //アクションを宣言する action.tag = 234; [myNOde runAction:action]; //ノードを点滅させるアクションを実行 CCAction* retrievedAction = [myNode getActionByTag:234]; //タグを使ってアクションを取得 [myNode stopActionByTag:234]; //タグを使って停止 [myNode stopAction:action]; //ポインタで停止 [myNode stopAllActions]; //ノードのすべてのアクションを停止
スケジュールされたメッセージ
[self scheduleUpdate]でupdateを設定すると-(void) update:(ccTime)delta
が各フレームで呼び出される。
別のメソッド、あるいは別の間隔で呼び出したい場合は
[self schedule:@selector(updateTenTimesPerSecond:) interval:0.1f];とすると
-(void) updateTenTimesPerSecond:(ccTime)delta
が0.1秒おきに呼び出される
ノードのすべてのセレクタを停止させるには
[self unscheduleAllSelectors];
特定のメッセージを停止するには
[self unschedule:@selector(updateTenTimesPerSecond:)];//セレクタを指定した場合
[self unscheduleUpdate];//scheduleUpdateで呼び出した場合
セレクタ内で停止させるには
[self unschedule:_cmd];//_cmdは現在のメソッドを表す省略表記
ノードごとにupdateに優先順位を付けられる
[self scheduleUpdateWithPriority:-1];//デフォルトは0なので-1が先に呼び出される
Director
Directorには以下の用途がある
4種類のDirectorから選択可能(細部が異なる)
最もよく使われるのはCCDirectorDisplayLink(iOS3.1以降のバージョンでしか利用できない)
CocoaTouchと並行する場合はCCDirectorFastThreaded
- シーンへのアクセスと変更
- cocos2dの設定情報へのアクセス
- ビュー(OpenGL,UIView,UIWindow)へのアクセス
- ゲームの一時停止、再開、終了
- UIKitおよびOpenGL座標の変換
4種類のDirectorから選択可能(細部が異なる)
最もよく使われるのはCCDirectorDisplayLink(iOS3.1以降のバージョンでしか利用できない)
CocoaTouchと並行する場合はCCDirectorFastThreaded
シングルトン
cocos2dは以下のようにシングルトンがよく使われている
シングルトン実装例
CCActionManager* sharedManager = [CCActionManager sharedManager]; CCDirector* sharedDirector = [CCDirector sharedDirector]; CCSpriteFrameCache* sharedCache = [CCSpriteFrameCache sharedSpriteFrameCache]; CCTextureCache* sharedTexCache = [CCTextureCache sharedTextureCache]; CCTouchDispatcher* sharedDispatcher = [CCTouchDispatcher sharedDispatcher]; CDAudioManager* sharedManager = [CDAudioManager sharedManager]; SimpleAudioEngine* sharedEngine = [SimpleAudioEngine sharedEngine];
シングルトン実装例
static MyManager *sharedManager = nil; + (MyManager*) sharedManager { if (sharedManager == nil) { sharedManager = [[MyManager alloc] init]; } return sharedManager; }
selectorの記述
- (void) example:(ccTime)delta sender:(id)sender flag:(bool)aBool
上記メソッドに対応する@selectorは以下
@selector(example:sender:flag:)
シーン階層
ノードを階層的に構築できる
-(id) init { if ((self=[super init]) ) { CCSprite *hoge = [CCSprite spriteWithFile:@"hoge.png"]; hoge.tag = 13; [self addChild:hoge]; //hogeの子要素 CCSprite *sub = [CCSprite spriteWithFile:@"sub.png"]; [hoge addChild:sub]; } } - (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event { CCSprite *hoge = (CCSprite*)[self getChildByTag:13]; //hogeとsubの両方縮小される hoge.scale = 0.5; return YES; }
オートリリースを使用する
CCNodeの派生クラスのオブジェクトはオートリリースで使用できる
以下のように記述すればインスタンス変数が不要となる
- (void) init { CCSprite *hoge = [CCSprite spriteWithFile:@"hoge.png"]; hoge.position = ccp(100, 00); //検索用のタグ(数字) hoge.tag = 13; [self addChild:hoge]; } - (void) update:(ccTime)delta { //タグをキーに取得 CCSprite *hoge = (CCSprite*)[self getChildByTag:13]; }
Objective-Cの記述
以下の記述は同じ
if ((self=[super init]) ) { // }
self = [super init]; if (self != nil) { // }
2013年9月12日木曜日
アクション後に次の処理を実行
移動後に画像切り替えの例
CCMoveBy* moveBy = [CCMoveBy actionWithDuration:1.0 position:ccp(100, 100)]; CCCallBlock* block = [CCCallBlock actionWithBlock:^{ //アクション後に画像切り替え CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:@"image.png"]; [sprite setTexture:texture]; [sprite setTextureRect:CGRectMake(0, 0, texture.contentSize.width, texture.contentSize.height)]; }]; CCSequence* seq = [CCSequence actions:moveBy, block, nil]; [sprite runAction:seq];
2013年9月11日水曜日
タッチメソッド
-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event -(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event -(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
スプライトの当たり判定
タップ時の判定
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event { //座標を取得 CGPoint location =[touch locationInView:[touch view]]; //OpenGL系の座標に変換 location =[[CCDirector sharedDirector] convertToGL:location]; //スプライトの当たり判定 if (CGRectContainsPoint(hito.boundingBox, location)) { NSlog(@"hit!"); } }
アニメーション
CCAnimationを使う
CCAnimation *danceAnime = [CCAnimation animation]; [danceAnime addSpriteFrameWithFilename:@"dance01.png"]; [danceAnime addSpriteFrameWithFilename:@"dance02.png"]; //画像1枚毎の表示時間 danceAnime.delayPerUnit = 0.1; //ループ回数(-1で無限) danceAnime.loops = 1; [dancerSprite runAction:[CCAnimate actionWithAnimation:danceAnime]];
iOSデバイス解像度
デバイス名称 | 画面サイズ | 解像度 (px) | 解像度 (dpi) |
iPhone3G / 3GS | 3.5 inch | 320 * 480 | 163 dpi |
iPhone4 / 4S | 3.5 inch | 640 * 960 | 326 dpi |
iPhone5 | 4 inch | 640 * 1136 | 326 dpi |
iPad / iPad2 | 9.7 inch | 768 * 1024 | 132 dpi |
iPad 第3世代 | 9.7 inch | 1,536 * 2048 | 264 dpi |
iPad mini | 7.9 inch | 768 * 1024 | 163 dpi |
2013年9月10日火曜日
スプライトの画像切り替え
setTextureを使う
CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:@"hoge.png"]; [hoge setTexture:texture]; //新しい画像にサイズを合わせる [hoge setTextureRect:CGRectMake(0, 0, texture.contentSize.width, texture.contentSize.height)];
テクスチャアトラスを使用している場合
[hoge setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"hoge.png"]];
2013年9月9日月曜日
シェイク
accelerometerEnabled設定
[self setAccelerometerEnabled:YES];
accelerometerメソッド追加
- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { if (abs(acceleration.x-oldX) > 0.7 || abs(acceleration.y - oldY) > 0.7) { NSLog(@"shake!!!"); } oldX = acceleration.x; oldY = acceleration.y; }
※oldX,oldYはインスタンス変数
だが、このやり方では一回のシェイクで何度もログが表示される
accelerometerEnabled編集
- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { float THRSHOLD = 2.0; if (acceleration.x > THRSHOLD || acceleration.x < -THRSHOLD || acceleration.y > THRSHOLD || acceleration.y < -THRSHOLD || acceleration.z > THRSHOLD || acceleration.z < -THRSHOLD ) { if (!shakedOnce) { shakedOnce = YES; NSLog(@"shake!!!"); } else { shakedOnce = NO; } } }
※shakedOnceはインスタンス変数
こうすれば何度も呼ばれることは少なくなる
2013年9月6日金曜日
サウンド
インポート
#import "SimpleAudioEngine.h"
preload
[[SimpleAudioEngine sharedEngine] preloadBackgroundMusic:@"BGM.mp3"]; [[SimpleAudioEngine sharedEngine] preloadEffect:@"soundEffect.mp3"];
SE
[[SimpleAudioEngine sharedEngine] playEffect:@"soundEffect.mp3"]; [[SimpleAudioEngine sharedEngine] playEffect:@"soundEffect.mp3" pitch:1.0f pan:0.0f gain:1.0f];
BGM
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"BGM.mp3"]; [[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"BGM.mp3" loop:YES]; //停止、一時停止、再開 [[SimpleAudioEngine sharedEngine] stopBackgroundMusic]; [[SimpleAudioEngine sharedEngine] pauseBackgroundMusic]; [[SimpleAudioEngine sharedEngine] resumeBackgroundMusic]; //先頭へ戻す [[SimpleAudioEngine sharedEngine] rewindBackgroundMusic]; //音量 [[SimpleAudioEngine sharedEngine] setBackgroundMusicVolume:1];
備考
ロードできる曲数は32曲まで それを超える場合はunloadしてから追加する画面遷移
画面遷移
[[CCDirector sharedDirector] replaceScene:[HogeLayer scene]];
トランジションを使って遷移
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:1.0 scene:[HogeLayer scene] ]];
画面回転対応
Info.plist
Supported interface orientationsにサポートする向きを入れる
プロジェクトを選択→TARGETS→Summary→Supported interface orientations
※item0が初期表示
AppDelegate
iOS4 / 5
shouldAutorotateToInterfaceOrientationを修正
※全部の向きOKの場合は return YES
iOS6
supportedInterfaceOrientationsを修正
※全部の向きOKの場合は return UIInterfaceOrientationMaskAll
2013年9月5日木曜日
新規Layerの追加
プロジェクトにCCNode classを追加
CCLayerを継承する
ヘッダーファイル
クラスメソッドsceneを追加
#import "cocos2d.h" @interface TitleLayer : CCLayer { } + (CCScene *) scene; @end
- 「 #import <Foundation/Foundation.h> 」は消す
(これがあるとdeallocが呼ばれない等の不具合あり)
実装ファイル
クラスメソッドsceneを実装
+ (CCScene *) scene { CCScene *scene = [CCScene node]; TitleLayer *layer = [TitleLayer node]; [scene addChild:layer]; return scene; }
iOSアプリ実機動作
iOSアプリを実機動作させる
前提として「iOS Developer Program」の登録必須
Xcodeで実機動作可能になるまで
以下のサイト参照
流れ
- 証明書作成
- デバイス登録
- App ID作成
- Provisioning Profileの作成
- Xcodeの設定
- App IDを開発などで使い回す場合はWildcard App IDが便利
- ProjectのBundle IdentifierとApp IDを合わせる
iOS Developer Program 登録
iOS Developer Program
iOSアプリを実記で動作確認するには登録必須
登録
以下のサイトを参照
流れ
- iOS Dev Center のサイトでApple ID(英語)を作成
- 同じサイトでiOS Developer Programを申し込み
- Apple Storeに飛ぶのでそこで注文(日本語)、支払いはカード or 銀行振込
- Activation Codeがメールで送られてくるのでそこのリンクをクリック
- Activationでエラーが出る場合はDev Center の「contact us」から問い合わせ
iOS開発環境構築
Apple IDの作成
英語で情報入力したIDを作成する
- 日本語で情報が入力されている場合、色々と不都合が起こるため
Xcodeのダウンロード
App Storeよりインストール
- 新規のApple IDだとクレジットカード情報入力を求められるが、既に無料アプリなどを落としたことのあるIDを使用すれば回避できる
2013年9月4日水曜日
タッチされた場所にスプライトを移動する
ccTouchBeganメソッド編集
CGPoint location = [self convertTouchToNodeSpace:touch]; [hoge stopAllActions]; id move = [CCMoveTo actionWithDuration:0.5f position:location]; [hoge runAction:move];
このコードは以下の処理を実施している
- touchオブジェクトから、タッチされた座標を取得
- hogeが実行しているすべてのアクションをストップ
- タッチした場所へ0.5秒かけて移動するアクションmoveを作成
- アクションmoveを、スプライトhogeに実行させる
タッチを有効にする
registerWithTouchDispatcherメソッド追加
- (void) registerWithTouchDispatcher { [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES]; }
ccTouchBeganメソッド追加
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event { return YES; }
touchEnabled設定
self.touchEnabled = YES;
スプライト表示
画像の追加
Xcodeにドラッグ&ドロップで画像を追加する
インスタンス変数の追加
CCSprite *hoge;
スプライト表示
hoge = [[CCSprite alloc] initWithFile:@"hoge.png"]; [self addChild:hoge];
登録:
投稿 (Atom)