sin,cosを使って円を書くプログラム

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
system32

sin,cosを使って円を書くプログラム

#1

投稿記事 by system32 » 14年前

龍神録の館で弾幕を作るためにsin,cosを学んでいます
dixqさんの作られたエルシャダイの動画で斜め移動するときのsin,cosの使い方はわかりました
また過去にこのフォーラムに投稿された記事(リンク貼りたいんですが見つかりませんでした)を参考にこういうプログラムを作りました

コード:

#include "DxLib.h"
#include <math.h>
#define BALL_MAX 200
#define PI 3.14159265358979
#define PI2 PI*2
//円周率の二倍は360度に等しく、よく使うと考えられるのでここで定義してしまう

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd){
	ChangeWindowMode(TRUE);
	if(DxLib_Init() == -1) return -1;
	SetDrawMode( DX_DRAWMODE_BILINEAR );
	typedef struct{
		int flag;
		double x,y;
		double spd,angle;
	}ch_t;

	ch_t ball[BALL_MAX];

	char key[256];
	int white=GetColor(255,255,255);
	int user_spd=4;
	int frame=0;
	//初期化、[for関数]内で初期化してるからこれはおk
	for(int i=0;i<BALL_MAX;i++){
		ball[i].flag=0;
		ball[i].x=0;
		ball[i].y=0;
		ball[i].spd=0;
		ball[i].angle=0;
	}

	SetDrawScreen(DX_SCREEN_BACK);

	while(ProcessMessage()!=-1){
		frame+=1;
		clsDx();
		ClearDrawScreen();
		GetHitKeyStateAll(key);

		//弾のフラグをオンにする。
		//このとき弾の情報を一緒に入れておくことにより
		//毎ループ中央の座標が弾の座標として代入されることがなくなる
		//なお、単位円の円周はPIではなくPI2である。
		if(key[KEY_INPUT_X]){
			for(int i=0;i<BALL_MAX;i++){
				ball[i].flag=1;
				ball[i].x=320;
				ball[i].y=240;
				ball[i].spd=user_spd;
				ball[i].angle=i*PI2/BALL_MAX;
			}
		}
		if(key[KEY_INPUT_RIGHT] && frame%7==0){
			user_spd+=1;
		}
		else if(key[KEY_INPUT_LEFT] && frame%7==0){
			user_spd-=1;
		}
		for(int i=0;i<BALL_MAX;i++){
			if(ball[i].flag==1){
				ball[i].x+=ball[i].spd*cos(ball[i].angle);
				ball[i].y+=ball[i].spd*sin(ball[i].angle);
			}
		}

		//弾の描画
		for(int i=0;i<BALL_MAX;i++){
			if(ball[i].flag==1){
				DrawCircle(ball[i].x,ball[i].y,3,white,TRUE);
			}
		}
		if(frame==60){
			frame=0;
		}
		printfDx("%d",user_spd);
		ScreenFlip();
	}
	DxLib_End();
	return 0;
}
こんな感じで斜めに移動する奴は私的には完璧です(多分)

ここからが本題なのですが、ぐるっと円を描くようなプログラムの作り方がよくわかりません
アルゴリズムというのでしょうか?処理の手順はこんなの考えました
初期化とか
  ↓
ぐるっとと描く円の中心座標は(320,240)
  ↓
上の座標を円の中心にして半径が30で角度が0*PI2の点を求め点をかく
  ↓
同じように1/8*PI2の座標の点を求め点をかく
  ↓
同じように2/8*PI2の座標の点を求め点をかく
  ↓
(中略)
  ↓
同じように8/8*PI2の座標の点を求め点をかく
  ↓
画面に円が描かれている!

こういうのを考えたのですがプログラムがエラーはでないが思ったとおりの実行結果になってくれません
上に出した奴を改造した感じで誰か書いてくれませんか?
よろしくお願いします

あとこれは激しく関係ないことなんですが掲示板の模様替えとかされましたか?
黒と白の2色表示で見づらいのですが私だけでしょうか?
ちなみにfirefox4.01です
こちらもよろしくお願いします

dic
記事: 658
登録日時: 14年前
住所: 宮崎県
連絡を取る:

Re: sin,cosを使って円を書くプログラム

#2

投稿記事 by dic » 14年前

半径が30のときに停止するように改良しました
ただ、距離がぴったり30にならない場合の処理は書いてません
期待しているものかどうかはわかりませんが、こういったものでしょうか?

コード:

#include "DxLib.h"
#include <math.h>
#define BALL_MAX 200
#define PI 3.14159265358979
#define PI2 PI*2
//円周率の二倍は360度に等しく、よく使うと考えられるのでここで定義してしまう
 
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd){
    ChangeWindowMode(TRUE);
    if(DxLib_Init() == -1) return -1;
    SetDrawMode( DX_DRAWMODE_BILINEAR );
    typedef struct{
        int flag;
        double x,y;
        double spd,angle;
		double dis; // 320,240 からの距離
    }ch_t;
 
    ch_t ball[BALL_MAX];
 
    char key[256];
    int white=GetColor(255,255,255);
    int user_spd=4;
    int frame=0;
    //初期化、[for関数]内で初期化してるからこれはおk
    for(int i=0;i<BALL_MAX;i++){
        ball[i].flag=0;
        ball[i].x=0;
        ball[i].y=0;
        ball[i].spd=0;
        ball[i].angle=0;
		ball[i].dis=0;	//	追加
    }
 
    SetDrawScreen(DX_SCREEN_BACK);
 
    while(ProcessMessage()!=-1){
        frame+=1;
        clsDx();
        ClearDrawScreen();
        GetHitKeyStateAll(key);
 
        //弾のフラグをオンにする。
        //このとき弾の情報を一緒に入れておくことにより
        //毎ループ中央の座標が弾の座標として代入されることがなくなる
        //なお、単位円の円周はPIではなくPI2である。
        if(key[KEY_INPUT_X]){
            for(int i=0;i<BALL_MAX;i++){
                ball[i].flag=1;
                ball[i].x=320;
                ball[i].y=240;
                ball[i].spd=user_spd;
                ball[i].angle=i*PI2/BALL_MAX;
				ball[i].dis=0;	//	追加
            }
        }
        if(key[KEY_INPUT_RIGHT] && frame%7==0){
            user_spd+=1;
        }
        else if(key[KEY_INPUT_LEFT] && frame%7==0){
            user_spd-=1;
        }
        for(i=0;i<BALL_MAX;i++){
            if(ball[i].flag==1){
				if( ball[i].dis >= 30 )	//	半径30ピクセル
					continue;
                ball[i].x+=ball[i].spd*cos(ball[i].angle);
                ball[i].y+=ball[i].spd*sin(ball[i].angle);
				double dis_x = 320 - ball[i].x;
				double dis_y = 240 - ball[i].y;
				ball[i].dis = sqrt( (dis_x*dis_x)+(dis_y*dis_y) );
            }
        }

		//	追加
		DrawFormatString( 0, 20, GetColor(255,255,255), "半径:%f", ball[0].dis );
		DrawFormatString( 0, 40, GetColor(255,255,255), "速度:%f", ball[0].spd );
		DrawString( 0, 60, "右:速度アップ", GetColor(255,255,255) );
		DrawString( 0, 80, "左:速度ダウン", GetColor(255,255,255) );
 
        //弾の描画
        for(i=0;i<BALL_MAX;i++){
            if(ball[i].flag==1){
                DrawCircle(ball[i].x,ball[i].y,3,white,TRUE);
            }
        }
        if(frame==60){
            frame=0;
        }
        printfDx("%d",user_spd);
        ScreenFlip();
    }
   DxLib_End();
    return 0;
}

system32

Re: sin,cosを使って円を書くプログラム

#3

投稿記事 by system32 » 14年前

>dicさん
うーんと求めてたものとちょっと違うんです
上に出したソースはこの程度のsin,cosなら理解できてますよーって事で出したんです
回答して欲しいのは

初期化とか
  ↓
ぐるっとと描く円の中心座標は(320,240)
  ↓
上の座標を円の中心にして半径が30で角度が0*PI2の点を求め点をかく
  ↓
同じように1/8*PI2の座標の点を求め点をかく
  ↓
同じように2/8*PI2の座標の点を求め点をかく
  ↓
(中略)
  ↓
同じように8/8*PI2の座標の点を求め点をかく
  ↓
画面に円が描かれている!

この処理に従ったプログラムです
回答していただいたやつも求めているものと同じことができますが、あれは30になったら止めてるだけで本質的に違います
引き続きお願いします

box
記事: 2002
登録日時: 14年前

Re: sin,cosを使って円を書くプログラム

#4

投稿記事 by box » 14年前

system32 さんが書きました: 初期化とか
ぐるっとと描く円の中心座標は(320,240)
上の座標を円の中心にして半径が30で角度が0*PI2の点を求め点をかく
同じように1/8*PI2の座標の点を求め点をかく
同じように2/8*PI2の座標の点を求め点をかく
(中略)
同じように8/8*PI2の座標の点を求め点をかく
画面に円が描かれている!
こういうのを考えたのですがプログラムがエラーはでないが思ったとおりの実行結果になってくれません
では、どういう実行結果になっているんですか?
まあ、用語の使い方だけの問題かもしれませんし、DxLibのことは全く知らない自分が言うのもアレですが、
点を8個なり9個なり描いても、円にはならないと思うのです。
最初の点の座標を覚えておくのはいいとして、
2個目以降は、「直前に覚えた点から今の点に向かって線を引く」という処理が必要ではないのでしょうか。
素人の浅はかな考えだったり、そんなことはもうわかっているということだったりしたらすみません。

あと、余談ですけど、円周率の値はたぶん math.h あたりで定義してあるはずなので、
自作の必要はないかなぁ、なんて思ったりしてます。
最後に編集したユーザー box on 2011年5月29日(日) 22:17 [ 編集 1 回目 ]
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

dic
記事: 658
登録日時: 14年前
住所: 宮崎県
連絡を取る:

Re: sin,cosを使って円を書くプログラム

#5

投稿記事 by dic » 14年前

こういうことでしょうか?

===編集

すいません、sin, cos 使ってない方法でしたので消しました
添付ファイル
無題.JPG
無題.JPG (20.51 KiB) 閲覧数: 13616 回

アバター
a5ua
記事: 199
登録日時: 14年前

Re: sin,cosを使って円を書くプログラム

#6

投稿記事 by a5ua » 14年前

ぐるっと円を描くというのがあいまいでよくわかりませんが、こんな感じですか?

コード:

#include "DxLib.h"
#include <math.h>
#define BALL_MAX 200
#define PI 3.14159265358979
#define PI2 PI*2
//円周率の二倍は360度に等しく、よく使うと考えられるのでここで定義してしまう
 
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd){
    ChangeWindowMode(TRUE);
    if(DxLib_Init() == -1) return -1;
    SetDrawMode( DX_DRAWMODE_BILINEAR );
    typedef struct{
        int flag;
        double x,y;
        double spd,angle;
    }ch_t;
 
    ch_t ball[BALL_MAX];
 
    char key[256];
    int white=GetColor(255,255,255);
    int user_spd=4;
    int frame=0;
    //初期化、[for関数]内で初期化してるからこれはおk
    for(int i=0;i<BALL_MAX;i++){
        ball[i].flag=0;
        ball[i].x=0;
        ball[i].y=0;
        ball[i].spd=0;
        ball[i].angle=0;
    }
 
    SetDrawScreen(DX_SCREEN_BACK);

	int bullet_index = 0;	// 円の描画を制御
 
    while(ProcessMessage()!=-1){
        frame+=1;
        clsDx();
        ClearDrawScreen();
        GetHitKeyStateAll(key);
 

		// 入力を受け付ける条件
		if(bullet_index == 0) {
			if (key[KEY_INPUT_X]){
				// 位置だけ設定
				for(int i=0;i<BALL_MAX;i++){
					ball[i].flag=0;
					ball[i].x=320 + 30 * cos(PI2 / BALL_MAX * i);
					ball[i].y=240 + 30 * sin(PI2 / BALL_MAX * i);
					ball[i].spd=0.0;
					ball[i].angle=0.0;
				}
				// 最初の1個だけフラグON
				ball[0].flag = 1;
				++bullet_index;
			}
		} else if (bullet_index < BALL_MAX) {
			// 1フレームごとに順番にフラグをONにする
			ball[bullet_index].flag = 1;
			++bullet_index;
		} else {
			// 1周したら入力を受け付ける
			bullet_index = 0;
		}

        if(key[KEY_INPUT_RIGHT] && frame%7==0){
            user_spd+=1;
        }
        else if(key[KEY_INPUT_LEFT] && frame%7==0){
            user_spd-=1;
        }
        for(int i=0;i<BALL_MAX;i++){
            if(ball[i].flag==1){
                ball[i].x+=ball[i].spd*cos(ball[i].angle);
                ball[i].y+=ball[i].spd*sin(ball[i].angle);
            }
        }
 
        //弾の描画
        for(int i=0;i<BALL_MAX;i++){
            if(ball[i].flag==1){
                DrawCircle(ball[i].x,ball[i].y,3,white,TRUE);
            }
        }
        if(frame==60){
            frame=0;
        }
        printfDx("%d",user_spd);
        ScreenFlip();
    }
    DxLib_End();
    return 0;
}

アバター
GRAM
記事: 164
登録日時: 14年前
住所: 大阪

Re: sin,cosを使って円を書くプログラム

#7

投稿記事 by GRAM » 14年前

まぁ質問者さんの考えをそのままコードにするとこんな感じですかね?

コード:

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

static const double PI = 3.1415926535897932385626;
int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
        ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK );
        while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){
			int x = 320, y = 240;
			double r = 30.0;
			size_t count = 8;
			for(int i=0; i < count; ++i){
				double dx = cos(2*PI*i/count)*r;
				double dy = sin(2*PI*i/count)*r;
				DrawPixel( 320+static_cast<int>(dx), 240+static_cast<int>(dy) , 0xffff ) ;
			}
        }
        
        DxLib_End(); // DXライブラリ終了処理
        return 0;
} 
まぁ残念ながら画面に8個点が打たれるだけで円になりませんがね
一応countを増やしてけば勝手に円っぽくなるようにしました
このプログラムは無駄に重いですし、実行してみての通り、点の数を増やしても全然きれいになりません
まぁふつう円を描くプログラムはこんな実装になってないと思うので問題ありませんが。

より洗練された方法が気になるようでしたら「ブレゼンハム 円」で検索してみてください

system32

Re: sin,cosを使って円を書くプログラム

#8

投稿記事 by system32 » 14年前

>a5uaさん
ありがとうございます、そのとおりです

>GRAMさん
怒ってますか?
でも質問したときに目指していたプログラムは正しくコレです。ありがとうございます。
そして言い訳を。
別に円を書くプログラムを作るだけならDrawCircle関数を使えばいいだけです。
私が質問したのはsin,cosで最終的にa5uaさんの作ってくれたようなプログラムの書き方を知りたかっただけです。

>まぁ残念ながら画面に8個点が打たれるだけで円になりませんがね
>一応countを増やしてけば勝手に円っぽくなるようにしました
>このプログラムは無駄に重いですし、実行してみての通り、点の数を増やしても全然きれいになりません
>まぁふつう円を描くプログラムはこんな実装になってないと思うので問題ありませんが。

>より洗練された方法が気になるようでしたら「ブレゼンハム 円」で検索してみてください

8個打たれただけでも私には十分円のように見えます
そしてsin,cosの勉強したいだけなのでプログラムが重かろうか軽かろうが関係ありません(だいたい点の集まりで円描くわけがないだろ……)
それに私最初に
>龍神録の館で弾幕を作るためにsin,cosを学んでいます
って言いましたよね
より洗練された方法なんて全く気になりません、円を描くだけならDrawCircleで十分です
なんか勘違いされてるようなので(ついでに質問の内容全く読んでなかったようなので、そして言い方がかなり上から目線で鬱陶しかったので)

回答してくれた皆さんありがとうございました

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: sin,cosを使って円を書くプログラム

#9

投稿記事 by ISLe » 14年前

box さんが書きました:あと、余談ですけど、円周率の値はたぶん math.h あたりで定義してあるはずなので、
自作の必要はないかなぁ、なんて思ったりしてます。
VCではmath.hをインクルードするより前に
#define _USE_MATH_DEFINES
とか書いておくとM_PIが使えます。

#わたしも鬱陶しいとか言われそうだ。くわばらくわばら。

アバター
GRAM
記事: 164
登録日時: 14年前
住所: 大阪

Re: sin,cosを使って円を書くプログラム

#10

投稿記事 by GRAM » 14年前

べつに怒ってませんよww
そう見えますか?
上から目線で物を言ったつもりはないのですがね。
そう感じたなら申し訳ないです。
単純に円を描くにはどうしたらいいのかを考えてるのかなぁと思ったので方法を述べただけです。
勘違いだったみたいなのでそれはそれでよいですが

system32

Re: sin,cosを使って円を書くプログラム

#11

投稿記事 by system32 » 14年前

>ISleさん
VCは前から調子が悪くてずっとborland C++ compiler使ってるんですよね……

>GRAMさん
先日は失礼なことを行って申し訳ありませんでした
私は教えてもらえる身分なのであなたが上から目線なのは当たり前ですね
こっちこそあなたの好意を勘違いしていたようです
本当に申し訳ありませんでした

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: sin,cosを使って円を書くプログラム

#12

投稿記事 by ISLe » 14年前

system32 さんが書きました:>ISleさん
VCは前から調子が悪くてずっとborland C++ compiler使ってるんですよね……
BorlandC++コンパイラやgccでは、math.hをインクルードするだけでM_PIが使えますよ。

system32

Re: sin,cosを使って円を書くプログラム

#13

投稿記事 by system32 » 14年前

ISLe さんが書きました:
system32 さんが書きました:>ISleさん
VCは前から調子が悪くてずっとborland C++ compiler使ってるんですよね……
BorlandC++コンパイラやgccでは、math.hをインクルードするだけでM_PIが使えますよ。
マジですか
もうこれからはPIって定義せずにM_PI使っていきます
ありがとうございました

アバター
h2so5
副管理人
記事: 2212
登録日時: 14年前
住所: 東京
連絡を取る:

Re: sin,cosを使って円を書くプログラム

#14

投稿記事 by h2so5 » 14年前

ちなみに、DXライブラリのヘッダでも

#define PHI (3.1415926535897932384626433832795 )

って定義されてますよ。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: sin,cosを使って円を書くプログラム

#15

投稿記事 by ISLe » 14年前

h2so5 さんが書きました:ちなみに、DXライブラリのヘッダでも
そう言えばTWO_PHIとかもありましたね。
後ろに_Fが付くfloat版も。

閉鎖

“C言語何でも質問掲示板” へ戻る