素人プログラマーの日常

効率の良いコード、美しいコードなんて書けません。

「真円プロジェクト」完成!

う~ん…。一応完成しましたが、やはりペンタブでの動作は確認出来ていません。これを機にペンタブを購入しようとも考えましたが、今は他に買いたいものがあるので、とりあえず購入リストに入れて後回しです。そんなこんなで、ペンタブでの動作を確認してくださる心優しい方を待っています。

 

f:id:dgen:20150830151101p:plain


↓「真円プロジェクト」はこちらからダウンロード出来ます。
http://tenkomorituuhan2.com/products/truecircle/top.html

ほとんど検索していないので確かではありませんが、おそらく真円具合を測定するソフトは他にありません。真円の練習には、是非とも「真円プロジェクト」をご使用ください。



さて、プログラミングの内容に移りますが、今回微妙に苦戦したのは不正処理です。

①途中から逆向きに円を描く
②画面からはみ出る
③一定時間経過

上記の行為を不正とみなし得点を0にしていますが、①の判定が思ったよりも複雑で、一周の判定とダブらないように不正をチェックさせるのに時間がかかりました。ここら辺も私の弱点ですね。最初からある程度設計してから取り掛かれば曲がりなりにも1つの関数にまとめたり出来るのでしょうが、思いつきで追加していくので、ぐだぐだと後から付け足して最終的に冗長したコードに変貌を遂げます。数をこなしていけば、自然と効率が良く読みやすいまとまったコードが書けるようになっていくのかな、なんて漠然と考えていますが、意識していかないとダメですね。



では、ソースコード公開です。今回のコードは短めですが、無理に理解しようとすると混乱します。私にだけ理解出来るコードです。なので、よほどチャレンジ精神旺盛でない限りコードの使用はお控えください。使用した結果、何が起きても一切責任は取れません。取りません。

/*###################################################

        True Circle Project 真円プロジェクト

###################################################*/

#include <math.h>
#include "DxLib.h"

#define WX 640        //ウインドウサイズ
#define WY 640

#define POSMAX 1000        //座標記憶最大数

#define PI 3.141592653589793238    //円周率

int x[ POSMAX ];        //座標
int y[ POSMAX ];
double dis[ POSMAX ];    //中心からの距離

char totalflag;

int highscore[ 5 ] = { 0 };
int score[ 3 ];

//ハンドル

int GHnum[ 3 ];        //数字
int GHword[ 9 ];        //文字(大)
int GHword2[ 6 ];        //文字(小)
int GHtitle;                //タイトル

//プロトタイプ宣言

void Title();
void DrawTitle();
void Circle();
int Play();
void FadeIn();
void FadeOut();
void Save();
void Load();

//WinMain
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
    SetOutApplicationLogValidFlag( FALSE );    //ログ出力しない
    SetMainWindowText( "True Circle Project" );        //ウインドウタイトル指定
    SetGraphMode( WX , WY , 32 );            //ウインドウの大きさとカラービット数指定
    ChangeWindowMode( TRUE );                //ウインドウで表示
    SetDrawScreen( DX_SCREEN_BACK );    //ちらつき防止設定
    SetWaitVSyncFlag( TRUE );                    //フレーム同期
    SetMouseDispFlag( TRUE );                    //マウスカーソル表示
    SetWindowUserCloseEnableFlag( FALSE );    //×ボタンで終了しない
    SetUseDirectInputFlag( FALSE );            //ペンタブ使用環境

    if( DxLib_Init() == -1 ) return -1;

    //画像読込
    LoadDivGraph( "img/num.png" , 3 , 3 , 1 , 60 , 64 , GHnum );
    LoadDivGraph( "img/word.png" , 9 , 9 , 1 , 128 , 64 , GHword );
    LoadDivGraph( "img/word2.png" , 6 , 6 , 1 , 160 , 48 , GHword2 );
    GHtitle = LoadGraph( "img/title.png" );

    //記録読込
    Load();

    //メインループ
    while( totalflag != -1 ){
        switch( totalflag ){
        case 0:
        case 2:    Title();        break;
        case 1:    Circle();        break;
        }
    }

    DxLib_End();

    return 0;
}


void Title(){        //タイトル ##############################

    DrawTitle();

    while( 1 ){
        if( ( GetMouseInput() & MOUSE_INPUT_LEFT ) != 0 ){
            totalflag = 1;
            break;
        }
        if( ( GetMouseInput() & MOUSE_INPUT_RIGHT ) != 0 ){
            totalflag = -1;
            break;
        }
        if( CheckHitKey( KEY_INPUT_ESCAPE ) == 1 ){
            totalflag = -1;
            break;
        }
        WaitVSync( 1 );
    }

    FadeOut();

}


void DrawTitle(){        //タイトル描画 ########################

    SetFontSize( 16 );
    DrawBox( 0 , 0 , WX , WY , 0xffffff , TRUE );
    if( totalflag == 2 ){
        DrawCircle( WY / 2 , WY / 2 , WY / 2 -20 , 0xaaaaaa , FALSE );
    } else {
        FadeIn();
        //ぐるっと円描画
        double s1 = 0;
        double s2 = WY / 2;
        double c1 = 0;
        double c2 = WY - 20;
        for( double i = 0 ; i > -PI * 2 - 0.03 ; i -= 0.03 ){
            s1 = ( WY / 2 + sin( i ) * ( WY / 2 - 20 ) );
            c1 = ( WY / 2 + cos( i ) * ( WY / 2 - 20 ) );
            DrawLine( ( int )s2 , ( int )c2 , ( int )s1 , ( int )c1 , 0xaaaaaa , 2 );
            s2 = s1;
            c2 = c1;
            ScreenFlip();
        }
    }

    DrawGraph( WY / 2 - 256 , 180 , GHtitle , TRUE );
    DrawGraph( WY / 2 - 196 , WY / 2 + 20 , GHword[ 7 ] , TRUE );
    for( int i = 0 ; i < 5 ; i++ ){
        DrawFormatString( WY / 2 - 160 , WY / 2 + 90 + i * 30 , 0x777777 , "%d" , highscore[ i ] );
    }
    DrawString( WY / 2 - 40 , WY / 2+60 ,"left click to START", 0x777777 );
    DrawString( WY / 2 - 40 , WY / 2 + 90 ,"right click or esc key to EXIT", 0x777777 );
    if( totalflag == 2 ){
        FadeIn();
    }
    ScreenFlip();

}


void Circle(){        //プレイサークル ########################

    for( int i = 0 ; i < 3 ; i++ ){
        DrawBox( 0 , 0 , WX , WY , 0xffffff , TRUE );
        //中心点
        DrawCircle( WY / 2 , WY / 2 , 3 , 0x999999 , TRUE );
        FadeIn();

        int GHfade = MakeGraph( WX , WY );
        GetDrawScreenGraph( 0 , 0 , WX , WY , GHfade );

        //「n回目」描画
        for( int j = 0 ; j < 256 ; j += 5 ){
            DrawGraph( 0 , 0 , GHfade , TRUE );
            SetDrawBlendMode( DX_BLENDMODE_ALPHA , j );
            DrawGraph( WY / 2 - 90 , 50 , GHnum[ i ] , TRUE );
            DrawGraph( WY / 2 - 30 , 50 , GHword[ 0 ] , TRUE );
            SetDrawBlendMode( DX_BLENDMODE_NOBLEND , 0 );
            ScreenFlip();
        }
        WaitVSync( 30 );
        for( int j = 255 ; j > -1 ; j -= 5 ){
            DrawGraph( 0 , 0 , GHfade , TRUE );
            SetDrawBlendMode( DX_BLENDMODE_ALPHA , j );
            DrawGraph( WY / 2 - 90 , 50 , GHnum[ i ] , TRUE );
            DrawGraph( WY / 2 - 30 , 50 , GHword[ 0 ] , TRUE );
            SetDrawBlendMode( DX_BLENDMODE_NOBLEND , 0 );
            ScreenFlip();
        }

        //「始め」描画
        DrawGraph( 0 , 0 , GHfade , TRUE );
        DrawGraph( WY / 2 - 64 , 50 , GHword[ 1 ] , TRUE );
        ScreenFlip();
        WaitVSync( 40 );
        DrawGraph( 0 , 0 , GHfade , TRUE );
        ScreenFlip();
        DeleteGraph( GHfade );

        score[ i ] = Play();
        FadeOut();
    }

    //総合結果
    SetFontSize( 32 );
    DrawGraph( WY / 2 -128 , 50 , GHword[ 5 ] , TRUE );
    DrawGraph( WY / 2 , 50 , GHword[ 3 ] , TRUE );
    for( int i = 0 ; i < 3 ; i++ ){
        DrawGraph( WY / 2 - 188 , i * 100 + 150 , GHnum[ i ] , TRUE );
        DrawGraph( WY / 2 - 128 , i * 100 + 150 , GHword[ 0 ] , TRUE );
        DrawFormatString( WY / 2 + 20 , i * 100 + 170 , 0x777777 , "%d" , score[ i ] );
    }
    int ttl = score[ 0 ] + score[ 1 ] + score[ 2 ];
    DrawGraph( WY / 2 - 128 , 450 , GHword[ 6 ] , TRUE );
    DrawFormatString( WY / 2 + 20 , 470 , 0x777777 , "%d" , ttl );

    //スコア更新
    if( highscore[ 4 ] < ttl ){
        highscore[ 4 ] = ttl;
        for( int i = 3 ; i > -1 ; i-- ){
            if( highscore[ i ] < ttl ){
                highscore[ i+1 ] = highscore[ i ];
                highscore[ i ] = ttl;
            }
        }
        DrawGraph( WY / 2 - 128 , 550 , GHword[ 7 ] , TRUE );
        DrawGraph( WY / 2 , 550 , GHword[ 8 ] , TRUE );
        Save();
    }

    ScreenFlip();

    while( ( GetMouseInput() & MOUSE_INPUT_LEFT ) != 0 ){
        WaitVSync( 1 );
    }
    while( ( GetMouseInput() & MOUSE_INPUT_LEFT ) == 0 ){
        WaitVSync( 1 );
    }
    FadeOut();
    totalflag = 2;

}


int Play(){            //真円描画 ###########################

    double radatan;        //中心からの角度
    int quart;        //円を4分割
    int qstart = 0;        //4分割の始点
    double start;            //始点の角度
    int frame = 0;            //フレーム数
    int round = 0;                //一周フラグ

    while( 1 ){            //クリック待ち
        WaitVSync( 1 );
        if( ( GetMouseInput() & MOUSE_INPUT_LEFT ) != 0 ){
            GetMousePoint( &x[ 0 ] , &y[ 0 ] );
            if( ( -1 < x[ 0 ] ) && ( x[ 0 ] < WY ) && ( -1 < y[ 0 ] ) && ( y[ 0 ] < WY ) ) break;
        }
    }

    int flag[ 4 ] = { 0 };        //4分割の通過フラグ
    int v = 0;            //右回り左回り
    int qtemp = -1;    //4分割の現在位置記憶

    while( frame < POSMAX ){

        //マウス座標取得
        GetMousePoint( &x[ frame ] , &y[ frame ] );
        //画面からはみ出し
        if( ( x[ frame ] < 0 )||( WX < x[ frame ] )||( y[ frame ] < 0 )||( WY < y[ frame ] ) ) break;
        //中心からの角度取得
        radatan = atan2( ( double )( x[ frame ] - WY / 2 ) , ( double )( y[ frame ] - WY / 2 ) );

        //4分割の現在位置
        quart = ( int )( ( radatan + PI ) / ( PI / 2 ) );
        if( quart < 0 ) quart = 0;
        if( quart > 3 ) quart = 3;

        //次の4分割に侵入
        if( quart != qtemp ){
            flag[ quart ]++;
            if( ( v == 0 ) && ( qtemp != -1 ) ){        //向きが決まっていなければ決める
                v = -1;
                if( qstart < quart ) v = 1;
                if( ( qstart == 3 ) && ( quart == 0 ) ) v = 1;
                if( ( qstart == 0 ) && ( quart == 3 ) ) v = -1;
            }
            //戻り不正
            if( flag[ 0 ] + flag[ 1 ] + flag[ 2 ] + flag[ 3 ] > 6 ) break;
            if( ( flag[ quart ] > 1 ) && ( flag[ 0 ] * flag[ 1 ] * flag[ 2 ] * flag[ 3 ] == 0 ) ) break;

            qtemp = quart;
        }

        if( frame > 0 ){
            //線描画
            DrawLine( x[ frame - 1 ] , y[ frame - 1 ] , x[ frame ] , y[ frame ] , 0x666666 );
        } else {
            //始点
            start = radatan;
            qstart = quart;
        }
        //中心からの距離取得
        int xf = x[ frame ] - WY / 2;
        int yf = y[ frame ] - WY / 2;
        dis[ frame ] = sqrt( (double)( xf * xf + yf * yf ) );

        //一周判定
        if( flag[ qstart ] == 2 ){
            int fx =  flag[ 0 ] * flag[ 1 ] * flag[ 2 ] * flag[ 3 ];
            if( v == 1 ){
                if(  ( start < radatan ) && ( fx > 1 ) ) round = 1;
                if( ( flag[ ( qstart + 1 ) % 4 ] == 2 ) && ( fx > 1 ) ) round = 1;
            } else {
                if( ( start > radatan ) && ( fx > 1 ) ) round = 1;
                int temp = qstart - 1;
                if( temp < 0 ) temp = 3;
                if( ( flag[ temp ] == 2 ) && ( fx > 1 ) ) round = 1;
            }
        }

        ScreenFlip();
        frame++;

        if( round == 1 ) break;
    }

    //結果
    if( flag[ 0 ] * flag[ 1 ] * flag[ 2 ] * flag[ 3 ] * round > 0  ){

        double min = 9999;
        double max = 0;
        double ave = 0;
        double bure = 0;
        int speed = ( POSMAX * 2 - frame ) / 20;
        int sc = 0;
        for( int i = 0 ; i < frame ; i++ ){
            if( min > dis[ i ] ) min = dis[ i ];
            if( max < dis[ i ] ) max = dis[ i ];
            ave += dis[ i ];
        }
        ave = ave / frame;
        bure = ( 1 - min / max ) * 100;
        sc = ( int )( ave / 6 * speed * ( 100 - bure ) * ( 100 - bure ) / 100 );

        DrawCircle( WY / 2 , WY / 2 , ( int )min , 0xffaaaa , FALSE );
        DrawCircle( WY / 2 , WY / 2 , ( int )max , 0xffaaaa , FALSE );
        DrawCircle( WY / 2 , WY / 2 , ( int )ave , 0xaaffff , FALSE );
        DrawGraph( WY / 2 - 64 , 30 , GHword[ 3 ] , TRUE );
        for( int i = 0 ; i < 6 ; i++ ){
            DrawGraph( WY / 2 - 180 , i * 50 + 100 , GHword2[ i ] , TRUE );
        }
        DrawFormatString( WY / 2 + 10 , 120 , 0x777777 , "%f" , max );
        DrawFormatString( WY / 2 + 10 , 170 , 0x777777 , "%f" , min );
        DrawFormatString( WY / 2 + 10 , 220 , 0x777777 , "%f" , ave );
        DrawFormatString( WY / 2 + 10 , 270 , 0x777777 , "%f" , bure );
        DrawFormatString( WY / 2 + 10 , 320 , 0x777777 , "%d" , speed );
        DrawFormatString( WY / 2 + 10 , 370 , 0x777777 , "%d" , sc );
        ScreenFlip();

        while( ( GetMouseInput() & MOUSE_INPUT_LEFT ) != 0 ){
            WaitVSync( 1 );
        }
        while( ( GetMouseInput() & MOUSE_INPUT_LEFT ) == 0 ){
            WaitVSync( 1 );
        }
        return sc;

    } else {
        if( frame == POSMAX ){
            //時間切れ
            DrawGraph( WY / 2 - 64 , 30 , GHword[ 4 ] , TRUE );
        } else {
            //不正(はみ出し・戻り)
            DrawGraph( WY / 2 - 64 , 30 , GHword[ 2 ] , TRUE );
        }
        ScreenFlip();
        while( ( GetMouseInput() & MOUSE_INPUT_LEFT ) != 0 ){
            WaitVSync( 1 );
        }
        while( ( GetMouseInput() & MOUSE_INPUT_LEFT ) == 0 ){
            WaitVSync( 1 );
        }
        return 0;
    }

}


void FadeIn(){        //フェードイン ##########################

    int GHfade = MakeGraph( WX , WY );
    GetDrawScreenGraph( 0 , 0 , WX , WY , GHfade );

    for( int i = 0 ; i < 256 ; i += 5 ){
        DrawBox( 0 , 0 , WX , WY , 0xffffff , TRUE );
        SetDrawBlendMode( DX_BLENDMODE_ALPHA , i );
        DrawGraph( 0 , 0 , GHfade , TRUE );
        SetDrawBlendMode( DX_BLENDMODE_NOBLEND , i );
        ScreenFlip();
    }

    DeleteGraph( GHfade );

}


void FadeOut(){        //フェードアウト ######################

    int GHfade = MakeGraph( WX , WY );
    GetDrawScreenGraph( 0 , 0 , WX , WY , GHfade );

    for( int i = 255 ; i > -1 ; i -= 5 ){
        DrawBox( 0 , 0 , WX , WY , 0xffffff , TRUE );
        SetDrawBlendMode( DX_BLENDMODE_ALPHA , i );
        DrawGraph( 0 , 0 , GHfade , TRUE );
        SetDrawBlendMode( DX_BLENDMODE_NOBLEND , i );
        ScreenFlip();
    }

    DeleteGraph( GHfade );

}


void Save(){        //書込み ################################

    FILE *fp;
    errno_t error;

    if( ( error = fopen_s( &fp , "save.sv" , "wb" ) ) != 0 ){
        //エラー
    }else{
        fwrite( highscore , sizeof( int ) , 5 , fp );
        fclose( fp );
    }

}


void Load(){        //読込み ################################

    FILE *fp;
    errno_t error;

    if( ( error = fopen_s( &fp , "save.sv" , "rb" ) ) != 0 ){
        //エラー
    }else{
        fread( highscore , sizeof( int ) , 5 , fp );
        fclose( fp );
    }