2011年7月27日水曜日

Xcode重すぎ(-。-;

(´・_・`) 今日も一日お疲れさん!



家に帰るのが憂鬱‥



今日も元気にiPhoneアプリを開発したよ‥



でも、Xcodeってさー



なんで、あんなに重たいんだろうね-_-b



固まるし、クラッシュするし、



まじなんなんなんだおー!(´Д` )



って、どーでもいーな。




UIImageViewで、コマ送り系のアニメーションやったお



でも、コレってアニメーション中に、アニメーションのスピードかえられないのよね‥(`_´)ゞ



使えないじゃないかい!!(;゜0゜)




なんかいい方法ないすか??(´・_・`)




これをクリアしないと‥



やっぱ、スピード変える時は、一旦、



[imageView stopAnimation]



してから、




スピード変えて、



[imageView startAnimation]



するしかないすか??




でも、コレってカレント画像(アニメーション中に今表示されている画像)ってわかるの?




とめて、スタートする時は、途中から始めたいじゃん?( ̄◇ ̄;)




明日、頑張ってためしてみるお♪( ´θ`)ノ

2011年7月7日木曜日

iPhone OpenGL + 加速度センサー(accelerometer) OR ジャイロ(Gyro)



(。・ω・)ノ゙ コンチャ♪


久々の書き込みでーっす!iPhoneアプリで久々に手こずりまくったんで、メモっときます‥


今回は、

「iPhone アプリで OpenGL加速度センサーかジャイロを用いて


OpenGLで描かれたオブジェクトを、端末の動きに合わせて回転させる!」


なんかこーゆーの‥



意外と、ブログだったり、ドキュメントないんだよね‥



みたいなものを作りたく、あーでもねー!!こーでもねー!!っとやってきましたが、


やっとそれなりのもんができましたとっ!
(*゚▽゚)/゚・:*【祝】*:・゚\(゚▽゚*)






じゃーその方法ですが、まずはOpenGLのお話からどーすお!






- OpenGL -


iPhoneSDKが新しくなってから??Xcodeのバージョンが新しくなってから??


どっちでもいいですが、OpenGL ES で新規でプロジェクト始めると、


よくある参考書などと内容が異なっていて、使えねぇー!!!!






っと思いきや、じつは対して変わってないのですお!!




前の バージョンで言うところの


- ES1Renderer.mのinitメソッド部分は、


"自分でつけたプロジェクト名ViewController.m"のawakeFromNibで、


- ES1Renderer.mのrenderメソッドは、


"自分でつけたプロジェクト名ViewController.m"のdrawFrameで、


って感じです(ノ゚ρ゚)ノ ォォォ・・ォ・・ォ・・・・




じゃーとりあえず、デフォルトのプロジェクトだと、


オブジェクトが激しい上下運動しててうざいんで、止めましょう!




止め方


- "プロジェクト名"ViewController.mのdrawFrameの中に、



    glTranslatef(0.0f, (GLfloat)(sinf(transY)/2.0f), 0.0f);
    transY += 0.075f;

っていうのがあって、これをコメントアウトする


そーすっと止まる。




とりあえず、コードの流れは、デリゲードが呼ばれて、ぐじゃぐじやって、




awakeFromNibに入って、コンテキスト作ったり(OpenGLで必要な事)して、


同時にStartAnimationが呼ばれると、リンク(CADisplayLinkとかいう‥)でdrawFrame
が呼ばれ続け、呼ばれたらさっき消したコードで、上下運動し続けるっていう仕組みっす。
さっきのコードが上限運動するコードなんで‥大雑把ですが‥


で、とりあえず、静止したオブジェクトが表示されていれば、


ココまではOKですお∑d(≧▽≦*)OK!!



おし!次!






- 加速度センサーやジャイロ -


- 加速度センサー


まず加速度センサーとはなんですか‥ヾ(・・;)ォィォィ




‥‥簡単に言うと、iPhone の傾きがどの軸に対して傾いているか的な!?


そういう事です‥ココ見てください→http://japan.internet.com/developer/20100803/26.html




で、これは、UIAccelerometerって言うもので、これを使うのもよし!


これならば、iPhone3系にも対応しています。






じゃーこれと、OpenGLどー絡めんの??って話ですが、


絡めましたが、うまいこといかず却下!!!


却下なんで、ソースコード載せなくていーっすか‥






じゃー次!




- CoreMotion


iPhone4からこーいーのが使えるんですお!


しかも、少々ややこしいのですが、CoreMotionの中にも3種類あります。


1. Device Motion
2. Accelerometer
3. Gyro


っていう3種類です。2番目は、UIAccelerometerと同じでしょー。
で、1のDeviceMotionの中にも4つ種類があってですね‥




1-1. CMAttribute デバイスの向き
 → なんかGyroっぽいんですが、おそらくこんな感じ
   motion.attribute.pitch X軸の回転角度
   motion.attribute.roll    Y軸の回転角度
   motion.attribute.yaw Z軸の回転角度


1-2. CMRotationRate 
 → 使った事ないんで何とも‥名前的に、回転速度じゃね?


1-3. CMAcceleration gravity
 → 重力速度だって‥ってことは、地球じゃ必ず1じゃねーの?あ、誤差で0.98になるんだって‥はい!必要なし!


1-4. CMAcceleration userAcceleration
 → UIAccelerometerと同じじゃね??




3は回転の速度なので、、、2のAccelerometerと、3-1のattributeを使ってみーよお




- CoreMotion Accelerometer


じゃーまず来れから!


startAnimation メソッドで



    if (!animating) {

        motionManager = [[CMMotionManager alloc] init];




        if(motionManager.accelerometerAvailable) {
            motionManager.accelerometerUpdateInterval = 1.0f / 5.0f;

            

            CMAccelerometerHandler accelerometerHandler;

            accelerometerHandler = ^ (CMAccelerometerData *accelerometerData, NSError* error) {

                

                if(error) {

                    

                }

                else {

                    /*
                    if (accelerometerData.acceleration.x > power){
                        NSLog(@"Accelerometer x:->");
                    }
                    if (accelerometerData.acceleration.x < -1*power){
                        NSLog(@"Accelerometer x:<-");
                    }
                    if (accelerometerData.acceleration.y > power){
                        NSLog(@"Accelerometer y : ↑");
                    }
                    if (accelerometerData.acceleration.y < -1*power){
                        NSLog(@"Accelerometer y : ↓");
                    }
                    if (accelerometerData.acceleration.z > power){
                        NSLog(@"Accelerometer z : 手前");
                    }
                    if (accelerometerData.acceleration.z < -1*power){
                        NSLog(@"Accelerometer z : ");        
                    }*/
                    
// これは.hでCGFloatで定義しておいてくらはい
                    accelX = accelerometerData.acceleration.x;
                    accelY = accelerometerData.acceleration.y;
                    accelZ = accelerometerData.acceleration.z;

                }
            };
            // 向きの更新通知を開始する
            [motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue
                                                withHandler:accelerometerHandler];
        }

        CADisplayLink *aDisplayLink = [[UIScreen mainScreen] displayLinkWithTarget:self selector:@selector(drawFrame)];
        [aDisplayLink setFrameInterval:animationFrameInterval];
        [aDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        self.displayLink = aDisplayLink;
        
        animating = TRUE;
    }

accelX accelY accelZの3つの値は.hで、定義してくださーい‥


で、この数値を使って、OpenGLのオブジェクトをうまく回転させる事ができれば、

デバイスの動きと、オブジェクトが連動して動かせるという理屈ですが‥いかんせん!

うまくいかん_!!!(`(エ)´)ノ_彡☆ブーブー!!


(  ̄っ ̄)ムゥ というか、加速度でやってて気がついてしまった!!!


できなくね‥??



あーできねーさ、、なぜならこの図をみてくれぇ!



ちょっとややこしいんですが、三角関数の計算とか出てきちゃうけど‥

X軸の回転は、iPhoneを図のように向きにして、手前、奥に傾ける(回転)ことにより、

その加速度センサーから、動いた角度を三角関数にならって求める事ができる‥

Z軸の回転は、今度は、図で言うと、左右に傾ける(回転)事によって求める事が可能

しかーし!Y軸って‥

良ーく考えると、このように図の向きで持ったときに、Y軸を中心にまわすと、

iPhoneをY軸の矢印のようにまわしたところで、加速度センサーって反応しないんじゃん。


加速度センサーは、重力の方向に(つまり下)働いてる力を取るんで、加速度センサーは、

Y軸に、-1のまま変わらないんです‥つまり垂直方向の回転は、とれないということになる‥


なんで、、、却下!!!ボツ!ハイ次!!!!





- CoreMotion DiviceMotion  -

よし!これが最終手段であります‥これでできなかったらわからん‥みんなやってるけど‥

とりあえず、ソース

さっきのstartAnimationを変更です‥

Accelerometerの所を下記に


               // 1 Device Motion.

        if (motionManager.deviceMotionAvailable) {
            // 更新の間隔を設定する
            
            motionManager.deviceMotionUpdateInterval = 1.0f / 5.0f;
            
            
            CMDeviceMotionHandler   deviceMotionHandler;
            deviceMotionHandler = ^ (CMDeviceMotion* motion, NSError* error) {
                // デバイスの向きを表示する
                /*
                if (motion.attitude.pitch > power){
                    NSLog(@"x:->");
                }
                if (motion.attitude.pitch < -1*power){
                    NSLog(@"x:<-");
                }
                if (motion.attitude.yaw > power){
                    NSLog(@"y : ↑");
                }
                if (motion.attitude.yaw < -1*power){
                    NSLog(@"y : ↓");
                }
                if (motion.attitude.roll > power){
                    NSLog(@"z : 手前");
                }
                if (motion.attitude.roll < -1*power){
                    NSLog(@"z : ");        
                }*/
                
                gyroX = motion.attitude.roll;
                gyroY = motion.attitude.pitch;
                gyroZ = motion.attitude.yaw;
                NSLog(@"X %f Y %f Z %f", gyroX, gyroY, gyroZ);
            };
            
            // 向きの更新通知を開始する
            [motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue
                                               withHandler:deviceMotionHandler];
        }





よーし、これで、drawFrameをこんなん



- (void)drawFrame
{
    [(EAGLView *)self.view setFramebuffer];
    
    // Replace the implementation of this method to do your own custom drawing.
    static const GLfloat squareVertices[] = {
        -0.5f, -0.5,
        0.5f, -0.5f,
        -0.5f0.5f,
        0.5f0.5f,
    };
    
    static const GLubyte squareColors[] = {
        255, 255,   0, 255,
        0,   255, 255, 255,
        0,     0,   0,   0,
        255,   0, 255, 255,
    };
        
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    
    
    //glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f); // もともとは平行投影。なので最近、と最遠の設定に意味はない。
    glFrustumf(-1.0f, 1.0f, -1.5, 1.5, 3.0f, 100.0f); // 透視投影。最遠はともかく最近は
glTranslatef(0.0f, 0.0f, -3.5f);

    glMatrixMode(GL_MODELVIEW);


    glRotatef(gyroX, 1.0f, 0, 0);
    glRotatef(gyroY, 0, 1.0f, 0);
    glRotatef(gyroZ, 0, 0, 1.0f);


    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);    

    glVertexPointer(2, GL_FLOAT, 0, squareVertices);
    glEnableClientState(GL_VERTEX_ARRAY);
    glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors);
    glEnableClientState(GL_COLOR_ARRAY);
    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
    [(EAGLView *)self.view presentFramebuffer];
}


これでどうじゃ!!



回転させると、ふわふわ動いてなかなかいい感じだったが、、、

ぴたっと収まらない、、止まらない。

僕みたいに、ゆるーく、だらしがない感じになってしまって、いまいち‥


おしーけど、だめっすよ。。



じゃー次は、OpenGLでglLoadMatrixfという、4x4の行列で、回転を表現しよー


glRotatefはやめて。


ヽ(´o`; 行列でばしっと決めちゃえば、ちゃんとぴたっといくのではないかと‥


OpenGLの行列の各軸の回転の定義はこーっすよ


[X軸回転]

[ 1,     0,      0,   0]
[ 0, cos(angle), -sin(angle), 0]
[ 0, sin(angle), cos(angle), 0]
[ 0,     0,      0,   1]


[Y軸回転]

[cos(angle),  0, sin(angle),   0]
[0,      1,     0,    0]
[-sin(angle), 0, cos(angle),   0]
[0,      0,      0,    1]


[Z軸回転]

[cos(angle), -sin(angle), 0, 0]
[sin(angle), cos(angle), 0, 0]
[ 0,     0,    1,   0]
[ 0,     0,    0,   1]


行列を使うには、sinとcosの値が必要なり

( 」´0`)」 ここで!三角関数を思い出さねばならんのですたい!!


忘れた人は、Google先生に聞いてください‥

ここの最初の図のように、
http://www8.plala.or.jp/ap2/suugaku/sankakukansuunoshoho.html

角度がわかってるので、sinとcosはそのまま使えます‥

なので、X軸の回転の場合は、sin(gyroX)、cos(gyroX)とかで、いいんですねぇ〜

これを応用して、3つの軸ごとに4x4の行列を作りますよー


そしてそれを、乗算します!!

行列の乗算って確かかなりめんどくさー!!だからソースこんなんなっちゃました!!!


drawFrameを下記に修正



- (void)drawFrame

{

    [(EAGLView *)self.view setFramebuffer];

    
    // Replace the implementation of this method to do your own custom drawing.
    static const GLfloat squareVertices[] = {
        -0.5f, -0.5,
        0.5f, -0.5f,
        -0.5f0.5f,
        0.5f0.5f,
    };
    
    static const GLubyte squareColors[] = {
        255, 255,   0, 255,
        0,   255, 255, 255,
        0,     0,   0,   0,
        255,   0, 255, 255,
    };
        
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    
    
    //glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f); // もともとは平行投影。なので最近、と最遠の設定に意味はない。
    glFrustumf(-1.0f, 1.0f, -1.5, 1.5, 3.0f, 100.0f); // 透視投影。最遠はともかく最近は
glTranslatef(0.0f, 0.0f, -3.5f);

    glMatrixMode(GL_MODELVIEW);
glPushMatrix();

const float X[16] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, cos(gyroX), -sin(gyroX), 0.0f,
0.0f, sin(gyroX), cos(gyroX), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
};
    const float Y[16] = {
cos(gyroY), 0.0f, sin(gyroY), 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
-sin(gyroY), 0.0f, cos(gyroY), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
};
    const float Z[16] = {
cos(gyroZ), -sin(gyroZ), 0.0f, 0.0f,
sin(gyroZ), cos(gyroZ), 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
};
    // 3つの行列を乗算
    const float XY[16] = {
        X[0]*Y[0]+X[1]*Y[4]+X[2]*Y[8]+X[3]*Y[12], X[0]*Y[1]+X[1]*Y[5]+X[2]*Y[9]+X[3]*Y[13], X[0]*Y[2]+X[1]*Y[6]+X[2]*Y[10]+X[3]*Y[14], X[0]*Y[3]+X[1]*Y[7]+X[2]*Y[11]+X[3]*Y[15],
        X[4]*Y[0]+X[5]*Y[4]+X[6]*Y[8]+X[7]*Y[12], X[4]*Y[1]+X[5]*Y[5]+X[6]*Y[9]+X[7]*Y[13], X[4]*Y[2]+X[5]*Y[6]+X[6]*Y[10]+X[7]*Y[14], X[4]*Y[3]+X[5]*Y[7]+X[6]*Y[11]+X[7]*Y[15],
        X[8]*Y[0]+X[9]*Y[4]+X[10]*Y[8]+X[11]*Y[12], X[8]*Y[1]+X[9]*Y[5]+X[10]*Y[9]+X[11]*Y[13], X[8]*Y[2]+X[9]*Y[6]+X[10]*Y[10]+X[11]*Y[14], X[8]*Y[3]+X[9]*Y[7]+X[10]*Y[11]+X[11]*Y[15],
        X[12]*Y[0]+X[13]*Y[4]+X[14]*Y[8]+X[15]*Y[12], X[12]*Y[1]+X[13]*Y[5]+X[14]*Y[9]+X[15]*Y[13], X[12]*Y[2]+X[13]*Y[6]+X[14]*Y[10]+X[15]*Y[14], X[12]*Y[3]+X[13]*Y[7]+X[14]*Y[11]+X[15]*Y[15],
    };
    const float scaleMatrix[16] = {
        XY[0]*Z[0]+XY[1]*Z[4]+XY[2]*Z[8]+XY[3]*Z[12], XY[0]*Z[1]+XY[1]*Z[5]+XY[2]*Z[9]+XY[3]*Z[13], XY[0]*Z[2]+XY[1]*Z[6]+XY[2]*Z[10]+XY[3]*Z[14], XY[0]*Z[3]+XY[1]*Z[7]+XY[2]*Z[11]+XY[3]*Z[15],
        XY[4]*Z[0]+XY[5]*Z[4]+XY[6]*Z[8]+XY[7]*Z[12], XY[4]*Z[1]+XY[5]*Z[5]+XY[6]*Z[9]+XY[7]*Z[13], XY[4]*Z[2]+XY[5]*Z[6]+XY[6]*Z[10]+XY[7]*Z[14], XY[4]*Z[3]+XY[5]*Z[7]+XY[6]*Z[11]+XY[7]*Z[15],
        XY[8]*Z[0]+XY[9]*Z[4]+XY[10]*Z[8]+XY[11]*Z[12], XY[8]*Z[1]+XY[9]*Z[5]+XY[10]*Z[9]+XY[11]*Z[13], XY[8]*Z[2]+XY[9]*Z[6]+XY[10]*Z[10]+XY[11]*Z[14], XY[8]*Z[3]+XY[9]*Z[7]+XY[10]*Z[11]+XY[11]*Z[15],
        XY[12]*Z[0]+XY[13]*Z[4]+XY[14]*Z[8]+XY[15]*Z[12], XY[12]*Z[1]+XY[13]*Z[5]+XY[14]*Z[9]+XY[15]*Z[13], XY[12]*Z[2]+XY[13]*Z[6]+XY[14]*Z[10]+XY[15]*Z[14], XY[12]*Z[3]+XY[13]*Z[7]+XY[14]*Z[11]+XY[15]*Z[15],
    };

    glLoadMatrixf(scaleMatrix);

    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);    

    glVertexPointer(2, GL_FLOAT, 0, squareVertices);
    glEnableClientState(GL_VERTEX_ARRAY);
    glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors);
    glEnableClientState(GL_COLOR_ARRAY);
    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
    glPopMatrix();
    [(EAGLView *)self.view presentFramebuffer];
}

4x4x4の行列の計算はまじパネェーっす(* ̄o ̄)ゝ


あい!!これでできたー!
ばっちり。でもこの方法であってるのかは、なぞ!


何か質問などあったらおねがいしやす‥



あ、回転軸と、面、角度やジャイロについてまとめたものです。
何かの参考にどうぞ