ページ 1 / 1
sin,cosを使って円を書くプログラム
Posted: 2011年5月29日(日) 18:59
by system32
龍神録の館で弾幕を作るために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です
こちらもよろしくお願いします
Re: sin,cosを使って円を書くプログラム
Posted: 2011年5月29日(日) 20:07
by dic
半径が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;
}
Re: sin,cosを使って円を書くプログラム
Posted: 2011年5月29日(日) 20:59
by system32
>dicさん
うーんと求めてたものとちょっと違うんです
上に出したソースはこの程度のsin,cosなら理解できてますよーって事で出したんです
回答して欲しいのは
初期化とか
↓
ぐるっとと描く円の中心座標は(320,240)
↓
上の座標を円の中心にして半径が30で角度が0*PI2の点を求め点をかく
↓
同じように1/8*PI2の座標の点を求め点をかく
↓
同じように2/8*PI2の座標の点を求め点をかく
↓
(中略)
↓
同じように8/8*PI2の座標の点を求め点をかく
↓
画面に円が描かれている!
この処理に従ったプログラムです
回答していただいたやつも求めているものと同じことができますが、あれは30になったら止めてるだけで本質的に違います
引き続きお願いします
Re: sin,cosを使って円を書くプログラム
Posted: 2011年5月29日(日) 21:56
by box
system32 さんが書きました:
初期化とか
ぐるっとと描く円の中心座標は(320,240)
上の座標を円の中心にして半径が30で角度が0*PI2の点を求め点をかく
同じように1/8*PI2の座標の点を求め点をかく
同じように2/8*PI2の座標の点を求め点をかく
(中略)
同じように8/8*PI2の座標の点を求め点をかく
画面に円が描かれている!
こういうのを考えたのですがプログラムがエラーはでないが思ったとおりの実行結果になってくれません
では、どういう実行結果になっているんですか?
まあ、用語の使い方だけの問題かもしれませんし、DxLibのことは全く知らない自分が言うのもアレですが、
点を8個なり9個なり描いても、円にはならないと思うのです。
最初の点の座標を覚えておくのはいいとして、
2個目以降は、「直前に覚えた点から今の点に向かって線を引く」という処理が必要ではないのでしょうか。
素人の浅はかな考えだったり、そんなことはもうわかっているということだったりしたらすみません。
あと、余談ですけど、円周率の値はたぶん math.h あたりで定義してあるはずなので、
自作の必要はないかなぁ、なんて思ったりしてます。
Re: sin,cosを使って円を書くプログラム
Posted: 2011年5月29日(日) 22:08
by dic
こういうことでしょうか?
===編集
すいません、sin, cos 使ってない方法でしたので消しました
Re: sin,cosを使って円を書くプログラム
Posted: 2011年5月29日(日) 22:25
by a5ua
ぐるっと円を描くというのがあいまいでよくわかりませんが、こんな感じですか?
コード:
#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;
}
Re: sin,cosを使って円を書くプログラム
Posted: 2011年5月29日(日) 22:36
by GRAM
まぁ質問者さんの考えをそのままコードにするとこんな感じですかね?
コード:
#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を増やしてけば勝手に円っぽくなるようにしました
このプログラムは無駄に重いですし、実行してみての通り、点の数を増やしても全然きれいになりません
まぁふつう円を描くプログラムはこんな実装になってないと思うので問題ありませんが。
より洗練された方法が気になるようでしたら「ブレゼンハム 円」で検索してみてください
Re: sin,cosを使って円を書くプログラム
Posted: 2011年5月29日(日) 23:33
by system32
>a5uaさん
ありがとうございます、そのとおりです
>GRAMさん
怒ってますか?
でも質問したときに目指していたプログラムは正しくコレです。ありがとうございます。
そして言い訳を。
別に円を書くプログラムを作るだけならDrawCircle関数を使えばいいだけです。
私が質問したのはsin,cosで最終的にa5uaさんの作ってくれたようなプログラムの書き方を知りたかっただけです。
>まぁ残念ながら画面に8個点が打たれるだけで円になりませんがね
>一応countを増やしてけば勝手に円っぽくなるようにしました
>このプログラムは無駄に重いですし、実行してみての通り、点の数を増やしても全然きれいになりません
>まぁふつう円を描くプログラムはこんな実装になってないと思うので問題ありませんが。
>より洗練された方法が気になるようでしたら「ブレゼンハム 円」で検索してみてください
8個打たれただけでも私には十分円のように見えます
そしてsin,cosの勉強したいだけなのでプログラムが重かろうか軽かろうが関係ありません(だいたい点の集まりで円描くわけがないだろ……)
それに私最初に
>龍神録の館で弾幕を作るためにsin,cosを学んでいます
って言いましたよね
より洗練された方法なんて全く気になりません、円を描くだけならDrawCircleで十分です
なんか勘違いされてるようなので(ついでに質問の内容全く読んでなかったようなので、そして言い方がかなり上から目線で鬱陶しかったので)
回答してくれた皆さんありがとうございました
Re: sin,cosを使って円を書くプログラム
Posted: 2011年5月30日(月) 01:09
by ISLe
box さんが書きました:あと、余談ですけど、円周率の値はたぶん math.h あたりで定義してあるはずなので、
自作の必要はないかなぁ、なんて思ったりしてます。
VCではmath.hをインクルードするより前に
#define _USE_MATH_DEFINES
とか書いておくとM_PIが使えます。
#わたしも鬱陶しいとか言われそうだ。くわばらくわばら。
Re: sin,cosを使って円を書くプログラム
Posted: 2011年5月30日(月) 02:24
by GRAM
べつに怒ってませんよww
そう見えますか?
上から目線で物を言ったつもりはないのですがね。
そう感じたなら申し訳ないです。
単純に円を描くにはどうしたらいいのかを考えてるのかなぁと思ったので方法を述べただけです。
勘違いだったみたいなのでそれはそれでよいですが
Re: sin,cosを使って円を書くプログラム
Posted: 2011年5月30日(月) 18:26
by system32
>ISleさん
VCは前から調子が悪くてずっとborland C++ compiler使ってるんですよね……
>GRAMさん
先日は失礼なことを行って申し訳ありませんでした
私は教えてもらえる身分なのであなたが上から目線なのは当たり前ですね
こっちこそあなたの好意を勘違いしていたようです
本当に申し訳ありませんでした
Re: sin,cosを使って円を書くプログラム
Posted: 2011年5月30日(月) 19:00
by ISLe
system32 さんが書きました:>ISleさん
VCは前から調子が悪くてずっとborland C++ compiler使ってるんですよね……
BorlandC++コンパイラやgccでは、math.hをインクルードするだけでM_PIが使えますよ。
Re: sin,cosを使って円を書くプログラム
Posted: 2011年5月30日(月) 22:53
by system32
ISLe さんが書きました:system32 さんが書きました:>ISleさん
VCは前から調子が悪くてずっとborland C++ compiler使ってるんですよね……
BorlandC++コンパイラやgccでは、math.hをインクルードするだけでM_PIが使えますよ。
マジですか
もうこれからはPIって定義せずにM_PI使っていきます
ありがとうございました
Re: sin,cosを使って円を書くプログラム
Posted: 2011年5月30日(月) 22:57
by h2so5
ちなみに、DXライブラリのヘッダでも
#define PHI (3.1415926535897932384626433832795 )
って定義されてますよ。
Re: sin,cosを使って円を書くプログラム
Posted: 2011年5月30日(月) 23:10
by ISLe
h2so5 さんが書きました:ちなみに、DXライブラリのヘッダでも
そう言えばTWO_PHIとかもありましたね。
後ろに_Fが付くfloat版も。