ページ 1 / 1
ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月10日(土) 17:14
by Hiragi(GKUTH)
いつもお世話になっております。
最近、高校物理で波に関することを学びまして、その中で
ウェーブマシンというものを見ました。
[youtube]
[/youtube]
それをDxLibで再現できないかと思い、プログラムを組んでいたのですが、早々に詰まってしまいました。
wave.cpp
コード:
#include "DxLib.h"
#include "KeyBoard.h"
const double G = 0.5;
typedef struct
{
int x;
int y;
double v;
}dot_t;
typedef struct
{
dot_t dot[640];
int late;
}wave_t;
void Wave_Init();
void Wave_Update();
void Wave_Draw();
wave_t wave;
void Wave_Init()
{
//遅延量(単位:frame)
wave.late = 1;
for (int i = 0; i < 640; i++)
{
wave.dot[i].x = i;
wave.dot[i].y = 240;
wave.dot[i].v = 0;
}
}
void Wave_Update()
{
//中心に向かって加速度をつける
if (wave.dot[0].y < 240) wave.dot[0].v -= G;
if (wave.dot[0].y > 240) wave.dot[0].v += G;
//加速度のコピー
for (int i = 0; i < 640; i++)
{
//一つ前の点の加速度を今の点の加速度へコピー
wave.dot[i + 1].v = wave.dot[i].v;
//座標に対して移動させる
wave.dot[i].y -= wave.dot[i].v;
}
//Zキーで加速度をつける
if (KeyBoard_Get(KEY_INPUT_Z) > 1) wave.dot[0].v = 2;
}
void Wave_Draw()
{
//描画
for (int i = 0; i < 640; i++)
{
DrawPixel(wave.dot[i].x, wave.dot[i].y, GetColor(255, 255, 255));
}
}
(グローバル変数などでとてもマズい設計をしていますが、その方が楽だったので...)
動画を見る限り、初めに左端のオレンジの点が動かされ、その右、その右へと力が伝わっていくように見えたので、
オレンジの点を1ドットとして、それを指定フレームごとに一つ右のドットへ加速度をコピーして描画し、波に見立てようとしました。
が、コードを見ての通り1フレームでそれをすべてしてしまっていて、見かけ上すべて同時に動いて見えます。
指定フレームごとに値をコピーし、描画する方法が思いつかなかったのでココに投稿させていただきました。
イメージとしては
コード:
1 frame:wave.dot[0].v = 2 , wave.dot[1].v = 0 , wave.dot[2].v = 0...
2 frame:wave.dot[0].v = 4 , wave.dot[1].v = 2 , wave.dot[2].v = 0...
3 frame:wave.dot[0].v = 6 , wave.dot[1].v = 4 , wave.dot[2].v = 2...
4 frame:wave.dot[0].v = 8 , wave.dot[1].v = 6 , wave.dot[2].v = 4...
......
のような感じです。よろしくお願いします。
OS:Win8
開発環境:Visual Studio 2013 for Desktop + DxLib
int KeyBoard_Get(キーコード) 指定されたキーコードの押されているフレーム数を整数値で返す。
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月10日(土) 17:55
by Tatu
今は線が上下に動くだけでこれを波を描くようにしたいということですよね?

- 無題.png (35.3 KiB) 閲覧数: 12980 回
加速度を伝える計算を左からではなく、右から行ってはどうでしょうか。
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月10日(土) 18:17
by Hiragi(GKUTH)
はい、私も先ほどソレを思いついて
コード:
void Wave_Update()
{
//中心に向かって加速度をつける
if (wave.dot[0].y < 240) wave.dot[0].v -= G;
if (wave.dot[0].y > 240) wave.dot[0].v += G;
//加速度のコピー
for (int i = 640; i < 0; i--)
{
//一つ前の点の加速度を今の点の加速度へコピー
wave.dot[i-1].v = wave.dot[i].v;
//座標に対して移動させる
wave.dot[i].y -= wave.dot[i].v;
}
//Zキーで加速度をつける
if (KeyBoard_Get(KEY_INPUT_Z) > 1) wave.dot[0].v = 2;
}
のように変更しましたが、そもそも動かなくなってしまいました。
原因考えてたところなんですが、今から少し用事があるのでまた後ほど考えようかと思っていたところです。
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月10日(土) 18:55
by みけCAT
「現在の波」と「次の波」でバッファを2本用意するといいかもしれません。
コード:
#include "DxLib.h"
#include "KeyBoard.h"
const double G = 0.5;
typedef struct
{
int x;
int y;
double v;
}dot_t;
typedef struct
{
dot_t dot[640];
int late;
}wave_t;
void Wave_Init();
void Wave_Update();
void Wave_Draw();
wave_t wave[2];
wave_t *current_wave = &wave[0]; // 現在時刻での波
wave_t *next_wave = &wave[1]; // 次の時刻での波
void Wave_Init()
{
//遅延量(単位:frame)
current_wave->late = 1;
for (int i = 0; i < 640; i++)
{
current_wave->dot[i].x = i;
current_wave->dot[i].y = 240;
current_wave->dot[i].v = 0;
}
}
void Wave_Update()
{
// 設計がよくわからなかったので、書き換えを行いませんでした。
// 現在の波のデータがcurrent_wave->dotに格納されています。
// このデータをもとに、次の波のデータをnext_wave->dotに格納してください。
/*
//中心に向かって加速度をつける
if (wave.dot[0].y < 240) wave.dot[0].v -= G;
if (wave.dot[0].y > 240) wave.dot[0].v += G;
//加速度のコピー
for (int i = 0; i < 640; i++)
{
//一つ前の点の加速度を今の点の加速度へコピー
wave.dot[i + 1].v = wave.dot[i].v;
//座標に対して移動させる
wave.dot[i].y -= wave.dot[i].v;
}
//Zキーで加速度をつける
if (KeyBoard_Get(KEY_INPUT_Z) > 1) wave.dot[0].v = 2;
*/
// 波のデータのバッファを入れ替える
{
wave_t *temp = current_wave;
current_wave = next_wave;
next_wave = temp;
}
}
void Wave_Draw()
{
//描画
for (int i = 0; i < 640; i++)
{
DrawPixel(current_wave->dot[i].x, current_wave->dot[i].y, GetColor(255, 255, 255));
}
}
※コンパイルしていないので、間違っていたらごめんなさい
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月10日(土) 19:19
by みけCAT
とりあえず他の部分を補ってコンパイルできるようにしてみました。
反射はしませんが、波が描けました。
コード:
#include "DxLib.h"
#include "KeyBoard.h"
const double G = 0.5;
typedef struct
{
int x;
int y;
double v;
}dot_t;
typedef struct
{
dot_t dot[640];
int late;
}wave_t;
void Wave_Init();
void Wave_Update();
void Wave_Draw();
wave_t wave[2];
wave_t *current_wave = &wave[0]; // 現在時刻での波
wave_t *next_wave = &wave[1]; // 次の時刻での波
void Wave_Init()
{
//遅延量(単位:frame)
current_wave->late = 1;
for (int i = 0; i < 640; i++)
{
current_wave->dot[i].x = i;
current_wave->dot[i].y = 240;
current_wave->dot[i].v = 0;
}
}
void Wave_Update()
{
//中心に向かって加速度をつける
if (current_wave->dot[0].y < 240) {
next_wave->dot[0].v = current_wave->dot[0].v - G;
} else if (current_wave->dot[0].y > 240) {
next_wave->dot[0].v = current_wave->dot[0].v + G;
} else {
next_wave->dot[0].v = current_wave->dot[0].v;
}
//加速度のコピー
for (int i = 0; i < 640; i++)
{
if (i + 1 < 640)
{
//一つ前の点の加速度を今の点の加速度へコピー
next_wave->dot[i + 1].v = current_wave->dot[i].v;
}
//座標に対して移動させる
next_wave->dot[i].x = current_wave->dot[i].x;
next_wave->dot[i].y = current_wave->dot[i].y - next_wave->dot[i].v;
}
//Zキーで加速度をつける
if (KeyBoard_Get(KEY_INPUT_Z) > 1) next_wave->dot[0].v = 2;
// 波のデータのバッファを入れ替える
{
wave_t *temp = current_wave;
current_wave = next_wave;
next_wave = temp;
}
}
void Wave_Draw()
{
//描画
for (int i = 0; i < 640; i++)
{
DrawPixel(current_wave->dot[i].x, current_wave->dot[i].y, GetColor(255, 255, 255));
}
}
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月10日(土) 20:01
by softya(ソフト屋)
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月10日(土) 20:43
by Hiragi(GKUTH)
みけCAT さんが書きました:とりあえず他の部分を補ってコンパイルできるようにしてみました。
反射はしませんが、波が描けました。
コード:
#include "DxLib.h"
#include "KeyBoard.h"
const double G = 0.5;
typedef struct
{
int x;
int y;
double v;
}dot_t;
typedef struct
{
dot_t dot[640];
int late;
}wave_t;
void Wave_Init();
void Wave_Update();
void Wave_Draw();
wave_t wave[2];
wave_t *current_wave = &wave[0]; // 現在時刻での波
wave_t *next_wave = &wave[1]; // 次の時刻での波
void Wave_Init()
{
//遅延量(単位:frame)
current_wave->late = 1;
for (int i = 0; i < 640; i++)
{
current_wave->dot[i].x = i;
current_wave->dot[i].y = 240;
current_wave->dot[i].v = 0;
}
}
void Wave_Update()
{
//中心に向かって加速度をつける
if (current_wave->dot[0].y < 240) {
next_wave->dot[0].v = current_wave->dot[0].v - G;
} else if (current_wave->dot[0].y > 240) {
next_wave->dot[0].v = current_wave->dot[0].v + G;
} else {
next_wave->dot[0].v = current_wave->dot[0].v;
}
//加速度のコピー
for (int i = 0; i < 640; i++)
{
if (i + 1 < 640)
{
//一つ前の点の加速度を今の点の加速度へコピー
next_wave->dot[i + 1].v = current_wave->dot[i].v;
}
//座標に対して移動させる
next_wave->dot[i].x = current_wave->dot[i].x;
next_wave->dot[i].y = current_wave->dot[i].y - next_wave->dot[i].v;
}
//Zキーで加速度をつける
if (KeyBoard_Get(KEY_INPUT_Z) > 1) next_wave->dot[0].v = 2;
// 波のデータのバッファを入れ替える
{
wave_t *temp = current_wave;
current_wave = next_wave;
next_wave = temp;
}
}
void Wave_Draw()
{
//描画
for (int i = 0; i < 640; i++)
{
DrawPixel(current_wave->dot[i].x, current_wave->dot[i].y, GetColor(255, 255, 255));
}
}
わざわざ回答のコードの提示有難うございます。
私の求めていた回答はほぼ53行目にあるようです。右側からずらす以外にこのような方法があるとは...(気が付かなかっただけだが)
また、波のデータ自体を二つ用意するというのも全く思いつきませんでした... しかもまだ上記のコードをすべて理解できていません。
多分フレームごとに前回の座標を元に変化していくようなモノにはバッファを複数用意する方法が良いのだとは思いますが。
それに加えて今のコードだと波の中心となる座標がどんどんズレていってしまうことも含めて、今原因を模索中です。
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月10日(土) 20:52
by Hiragi(GKUTH)
私が求めている最終目標がコレです。といっても高校でまだ波が終わっていませんし、(というかまだ物理基礎という基礎レベルの物理。)
固定端反射、自由端反射や波の重ね合わせといった各質点の物理法則を厳密に再現するほど知識も技術もありませんので、これらの実装は
それなりに先になりそうですが...(考えるのは楽しいんですが定期考査が。)
いずれ挑戦してみようかと思います。
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月10日(土) 23:18
by Hiragi(GKUTH)
申し訳ありません。2時間ほど考えましたがみけCATさんの投稿されたコードの67-71行目の内容が全く理解できません。
現在のフレームが次のフレームになるのと次のフレームが現在のフレームになるのとの違いは何か?全くわかりません、何を言ってるか分からないかもしれませんが...
そもそも時間は一方通行なのに「移動」ではなく「交換」しているのは何故か?
加えて、Tetuさんの右側からずらすように処理を描き直してみましたが、2回に1回しか描画されない上、波に変化がありません。
というかダブルバッファという仕組みすら2時間考えた末に全く理解できませんでした。次が現在になる、現在が次になる。どちらも同じ意味になってしまうのが
気持ち悪くて訳分からなくなってきます...
コード:
wave_t *current_wave = &wave[0];
wave_t *next_wave = &wave[1];
void Wave_Init()
{
//遅延量(単位:frame)
current_wave->late = 1;
for (int i = 0; i < 640; i++)
{
current_wave->dot[i].x = i;
current_wave->dot[i].y = 240;
current_wave->dot[i].v = 0;
}
}
void Wave_Update()
{
//中心に向かって加速度をつける
if (current_wave->dot[0].y < 240) {
next_wave->dot[0].v = current_wave->dot[0].v - G;
}
if (current_wave->dot[0].y > 240) {
next_wave->dot[0].v = current_wave->dot[0].v + G;
}
//加速度のコピー
for (int i = 640; i < 0; i++)
{
//次のフレームは、現在のフレームの一つ左の点の加速度になる、
next_wave->dot[i+1].v = current_wave->dot[i].v;
//次のフレームのx座標は、現在のフレームに等しい
next_wave->dot[i].x = current_wave->dot[i].x;
//次のフレームのy座標は、現在のフレームに次のフレームの加速度を反映させた位置である。(?)
next_wave->dot[i].y = current_wave->dot[i].y - next_wave->dot[i].v;
}
//次のフレームは、Zキーが押された場合、加速度が2になる。
if (KeyBoard_Get(KEY_INPUT_Z) > 1) next_wave->dot[0].v = 2;
// 波のデータのバッファを入れ替える
{
wave_t *temp = current_wave;
current_wave = next_wave;
next_wave = temp;
}
}
void Wave_Draw()
{
//描画
for (int i = 0; i < 640; i++)
{
DrawPixel(current_wave->dot[i].x, current_wave->dot[i].y, GetColor(255, 255, 255));
}
}
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月10日(土) 23:28
by みけCAT
Hiragi(GKUTH) さんが書きました:現在のフレームが次のフレームになるのと次のフレームが現在のフレームになるのとの違いは何か?全くわかりません、何を言ってるか分からないかもしれませんが...
申し訳ありません。確かに何を言ってるか分からない気がします。
Hiragi(GKUTH) さんが書きました:そもそも時間は一方通行なのに「移動」ではなく「交換」しているのは何故か?
「移動」(データのコピー)をすると無駄な処理時間が生じると思うからです。
コード:
// ここに書かれているのはnext_waveに時刻t+1での波の状態を格納する処理
// 波のデータのバッファを入れ替える
{
wave_t *temp = current_wave; // 時刻tでの波の状態が格納されたバッファのアドレスをtempに入れる
current_wave = next_wave; // current_waveに時刻t+1での波の状態を格納する
next_wave = temp; // next_waveに時刻tでの波の状態が格納されたバッファのアドレスを入れる
// 時刻tでの波の状態はもう使わないので、これは時刻t+2での波の状態を格納するバッファになる
}
Hiragi(GKUTH) さんが書きました:加えて、Tetuさんの右側からずらすように処理を描き直してみましたが、2回に1回しか描画されない上、波に変化がありません。
コード:
for (int i = 640; i < 0; i++)
640は0より小さくないので、このfor文の中身は一度も実行されないはずです。
2回に1回描画されるのは、wave[0]のdot
.xのデータが消されないからです。
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月11日(日) 00:25
by Hiragi(GKUTH)
みけCAT さんが書きました:
Hiragi(GKUTH) さんが書きました:加えて、Tetuさんの右側からずらすように処理を描き直してみましたが、2回に1回しか描画されない上、波に変化がありません。
コード:
for (int i = 640; i < 0; i++)
640は0より小さくないので、このfor文の中身は一度も実行されないはずです。
2回に1回描画されるのは、wave[0]のdot
.xのデータが消されないからです。
ただのミスでした、申し訳ないです。
コード:
void Wave_Update()
{
//中心に向かって加速度をつける
if (next_wave->dot[0].y < 240) {
next_wave->dot[0].v = current_wave->dot[0].v - G;
} else if (next_wave->dot[0].y > 240) {
next_wave->dot[0].v = current_wave->dot[0].v + G;
} else {
next_wave->dot[0].v = current_wave->dot[0].v;
}
printfDx("next:%0.0f current:%0.0f\n", next_wave->dot[0].v, current_wave->dot[0].v);
//加速度のコピー
for (int i = 639; i >= 0; i--)
{
//次のフレームは、現在のフレームの一つ左の点の加速度になる、
next_wave->dot[i+1].v = current_wave->dot[i].v;
//次のフレームのx座標は、現在のフレームに等しい
next_wave->dot[i].x = current_wave->dot[i].x;
//次のフレームのy座標は、現在のフレームに次のフレームの加速度を反映させた位置である。(?)
next_wave->dot[i].y = current_wave->dot[i].y - next_wave->dot[i].v;
}
//次のフレームは、Zキーが押された場合、加速度が0.5になる。
if (KeyBoard_Get(KEY_INPUT_Z) > 1) next_wave->dot[0].v += 0.5;
if (KeyBoard_Get(KEY_INPUT_X) > 1) next_wave->dot[0].v -= 0.5;
// 波のデータのバッファを入れ替える
{
wave_t *temp = current_wave;
current_wave = next_wave;
next_wave = temp;
}
}
とりあえず、その部分を修正しましたが、
このコードでは最初から何故か下方向に加速度がついてしまいます。
30分ほど考えましたが分からず...
1,初期状態では0以外代入されていない(4-10のifも通過しない)のにかかわらず変化するのは何故か
2,4-10行目をコメントアウトすると取り敢えず最初から下方向への加速度はナシ、
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月11日(日) 00:30
by みけCAT
これで改善するかはわかりませんが、とりあえず
Hiragi(GKUTH) さんが書きました:コード:
//加速度のコピー
for (int i = 639; i >= 0; i--)
{
//次のフレームは、現在のフレームの一つ左の点の加速度になる、
next_wave->dot[i+1].v = current_wave->dot[i].v;
//次のフレームのx座標は、現在のフレームに等しい
next_wave->dot[i].x = current_wave->dot[i].x;
//次のフレームのy座標は、現在のフレームに次のフレームの加速度を反映させた位置である。(?)
next_wave->dot[i].y = current_wave->dot[i].y - next_wave->dot[i].v;
}
この場所で、i=639のときnext_wave->dot[i+1]は配列の確保された範囲の外であり、アクセスするのは危険です。
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月11日(日) 00:39
by みけCAT
Hiragi(GKUTH) さんが書きました:1,初期状態では0以外代入されていない(4-10のifも通過しない)のにかかわらず変化するのは何故か
本当に「4-10のifも通過しない」ことをprintfDxなどで確認しましたか?
Hiragi(GKUTH) さんが書きました:コード:
//中心に向かって加速度をつける
if (next_wave->dot[0].y < 240) {
next_wave->dot[0].v = current_wave->dot[0].v - G;
} else if (next_wave->dot[0].y > 240) {
next_wave->dot[0].v = current_wave->dot[0].v + G;
} else {
next_wave->dot[0].v = current_wave->dot[0].v;
}
最初のフレームでは、next_wave->dot[0].yは(明示的に初期化されていないグローバル変数なので)0であり、
当然240より小さいです。従って、
コード:
next_wave->dot[0].v = current_wave->dot[0].v - G;
が実行されるはずです。
コード:
//中心に向かって加速度をつける
if (current_wave->dot[0].y < 240) {
next_wave->dot[0].v = current_wave->dot[0].v - G;
} else if (current_wave->dot[0].y > 240) {
next_wave->dot[0].v = current_wave->dot[0].v + G;
} else {
next_wave->dot[0].v = current_wave->dot[0].v;
}
としてみてください。
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月11日(日) 00:41
by みけCAT
Hiragi(GKUTH) さんが書きました:1,初期状態では0以外代入されていない(4-10のifも通過しない)のにかかわらず変化するのは何故か
初期状態ではnext_wave->dot[0].yに
0以外代入されていないので、
4-10のifを通過(スキップ)せずに中の文が実行されるため、vが
変化します。
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月11日(日) 00:48
by みけCAT
Hiragi(GKUTH) さんが書きました:コード:
//加速度のコピー
for (int i = 639; i >= 0; i--)
{
//次のフレームは、現在のフレームの一つ左の点の加速度になる、
next_wave->dot[i+1].v = current_wave->dot[i].v;
//次のフレームのx座標は、現在のフレームに等しい
next_wave->dot[i].x = current_wave->dot[i].x;
//次のフレームのy座標は、現在のフレームに次のフレームの加速度を反映させた位置である。(?)
next_wave->dot[i].y = current_wave->dot[i].y - next_wave->dot[i].v;
}
さらにi>0のとき、ここでnext_wave->dot
.yを計算するときにはまだnext_wave->dot.vを計算していない
(意味のない古い値またはゼロが入っている)ため、計算結果が想定外のものになるかもしれません。
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月11日(日) 00:55
by Hiragi(GKUTH)
>>みけCATさん
とても早い返答、ありがとうございます。
next_wave[0]の内容が初期化されていないことに気づいていませんでした、修正しました。
ひとまずNo.9以降の問題が解決しました、ありがとうございます。
No.7の私の投稿の最後の行の問題がまだ残っていますので、もう少し考えてみます。
加えて、current_wave->dot[0].x = 0のyの値が異常であるのが分かっているので、それも考えてみます。
コード:
void Wave_Update()
{
//中心に向かって加速度をつける
if (current_wave->dot[0].y < 240) {
next_wave->dot[0].v = current_wave->dot[0].v - G;
} else if (current_wave->dot[0].y > 240) {
next_wave->dot[0].v = current_wave->dot[0].v + G;
} else {
next_wave->dot[0].v = current_wave->dot[0].v;
}
// printfDx("next:%0.0f current:%0.0f\n", next_wave->dot[0].v, current_wave->dot[0].v);
//加速度のコピー
for (int i = 638; i >= 0; i--)
{
//次のフレームは、現在のフレームの一つ左の点の加速度になる、
next_wave->dot[i+1].v = current_wave->dot[i].v;
//次のフレームのx座標は、現在のフレームに等しい
next_wave->dot[i].x = current_wave->dot[i].x;
//次のフレームのy座標は、現在のフレームに次のフレームの加速度を反映させた位置である。(?)
next_wave->dot[i].y = current_wave->dot[i].y - next_wave->dot[i].v;
}
//次のフレームは、Zキーが押された場合、加速度が1になる。
if (KeyBoard_Get(KEY_INPUT_Z) > 1) next_wave->dot[0].v += 1;
if (KeyBoard_Get(KEY_INPUT_X) > 1) next_wave->dot[0].v -= 1;
// 波のデータのバッファを入れ替える
{
wave_t *temp = current_wave;
current_wave = next_wave;
next_wave = temp;
}
}
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月11日(日) 09:03
by みけCAT
私がNo: 15で指摘した問題がまだ修正されていないようですね。
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月11日(日) 11:21
by Hiragi(GKUTH)
みけCAT さんが書きました:私がNo: 15で指摘した問題がまだ修正されていないようですね。
そのようです。
今のところとりあえずNo.7最後の行の問題と、No.15で指摘して頂いた点は改善しました。(wave.cpp Line:36)
現時点での動作するソースコードを張っておきます。
wave.h
コード:
#ifndef DEF_WAVE_H
#define DEF_WAVE_H
//中心に向かっての加速度
const double G = 1;
//一つの質点が持つ情報
typedef struct
{
//座標
int x;
int y;
//加速度
double v;
}dot_t;
//一つの波が持つ情報
typedef struct
{
//質点
dot_t dot[640];
//遅延量(未実装)
int late;
//損質量(未実装)
double lost;
}wave_t;
void Wave_Init();
void Wave_Update();
void Wave_Draw();
#endif
wave.cpp
コード:
#include "DxLib.h"
#include "KeyBoard.h"
#include "wave.h"
wave_t wave[2];
wave_t *current_wave = &wave[0];
wave_t *next_wave = &wave[1];
void Wave_Init()
{
//遅延量(単位:frame)
current_wave->late = 1;
//損質量(単位:加速度/frame)
current_wave->lost = 0.2;
for (int i = 0; i < 640; i++)
{
current_wave->dot[i].x = i;
current_wave->dot[i].y = 240;
current_wave->dot[i].v = 0;
}
}
void Wave_Update()
{
//中心に向かって加速度をつける
if (current_wave->dot[0].y < 240)
{
next_wave->dot[0].v = current_wave->dot[0].v - G;
} else if (current_wave->dot[0].y > 240)
{
next_wave->dot[0].v = current_wave->dot[0].v + G;
} else
{
//何も押されていない場合、次フレームに現在のフレームの加速度を代入、よっていずれの場合も、次フレームnext_wave->dot[0].vには現在のフレームの情報を含むものが入る。
next_wave->dot[0].v = current_wave->dot[0].v;
}
//次のフレームは、Zキーが押された場合、加速度が1になる。
if (KeyBoard_Get(KEY_INPUT_Z) > 1)
{
next_wave->dot[0].v += 1;
}
else if (KeyBoard_Get(KEY_INPUT_X) > 1)
{
next_wave->dot[0].v -= 1;
}
//デバッグ用
DrawFormatString(0, 24, GetColor(255,255,255), "next:%0.0f current:%0.0f\n", next_wave->dot[0].v, current_wave->dot[0].v);
//加速度のコピー
for (int i = 638; i >= 0; i--)
{
//次のフレームは、現在のフレームの一つ左の点の加速度になる、
next_wave->dot[i + 1].v = current_wave->dot[i].v;
//次のフレームのx座標は、現在のフレームに等しい
next_wave->dot[i].x = current_wave->dot[i].x;
//次のフレームのy座標は、現在のフレームに次のフレームの加速度を反映させた位置である。(?)
next_wave->dot[i].y = current_wave->dot[i].y - next_wave->dot[i].v;
}
// 波のデータのバッファを入れ替える
{
wave_t *temp = current_wave;
current_wave = next_wave;
next_wave = temp;
}
}
void Wave_Draw()
{
//中心線の描画
DrawLine(0, 240, 640, 240, GetColor(255, 0, 0), 3);
//描画
for (int i = 0; i < 638; i++)
{
DrawLine(current_wave->dot[i].x, current_wave->dot[i].y, current_wave->dot[i+1].x, current_wave->dot[i+1].y, GetColor(255, 255, 255),1);
//DrawBox(current_wave->dot[i].x, current_wave->dot[i].y, current_wave->dot[i].x+4, current_wave->dot[i].y+4, GetColor(255, 255, 255),true);
}
}
KeyBoard.h
コード:
#ifndef DEF_KEYBOARD_H
#define DEF_KEYBOARD_H
int KeyBoard_Update();
int KeyBoard_Get(int KeyCode);
#endif DEF_KEYBOARD_H
KeyBoard.cpp
コード:
#include "DxLib.h"
#include "KeyBoard.h"
static int m_Key[256];
// キーの入力状態を更新する
int KeyBoard_Update()
{
char tmpKey[256]; // 現在のキーの入力状態を格納する
GetHitKeyStateAll(tmpKey); // 全てのキーの入力状態を得る
for (int i = 0; i<256; i++)
{
if (tmpKey[i] != 0)
{ // i番のキーコードに対応するキーが押されていたら
m_Key[i]++; // 加算
}
else { // 押されていなければ
m_Key[i] = 0; // 0にする
}
}
return 0;
}
int KeyBoard_Get(int KeyCode)
{
return m_Key[KeyCode];
}
Main.cpp
コード:
#include "DxLib.h"
#include "KeyBoard.h"
#include "wave.h"
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
ChangeWindowMode(TRUE);
DxLib_Init();
SetDrawScreen(DX_SCREEN_BACK);
Wave_Init();
while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0 && KeyBoard_Get(KEY_INPUT_ESCAPE) == 0)
{
KeyBoard_Update();
Wave_Update();
Wave_Draw();
}
printfDx("メインループを抜けました。何かキーを押すと終了します。");
WaitKey();
DxLib_End();
return 0;
}
x=1の時のyの値が異常であることについては「何故か」解決してしまっています。多分No.15で指摘していただいたことに関連しているか、たまたまであるかと思いますが。
このコードに問題があったとしても、この後 今日、明日と用事があって申し訳ありませんが、返信がかなり遅れるかもしれません.
:追記: 動作しなかったので必要なコードの追加
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月12日(月) 21:43
by GRAM
最低限のコードで反射を含む実装を考えてみましたが、意外と難しいですね。これ。
自分のアプローチの仕方が悪い可能性が十分にありますが・・・。
ばねそのものをシミュレーションしてみた感じです。
右が自由端、左が固定端になります。
反射もそれっぽく表せてますが、なぞの振動が消せない・・・。
以下コピペで動くコード。
今後の参考になれば。
Z押してる間持ち上げます。
► スポイラーを表示
コード:
#include "DxLib.h"
#include <vector>
using namespace std;
static char key[256];
static const int Width = 600;
static const int Center = 240;
class Dot
{
public:
static const double k;
Dot(double x, double y)
:x_(x), y_(y), vy_(0)
{
}
void Update1(Dot* lhs, Dot* rhs)
{
//コアになるコード
if (lhs == 0)
lhs = this;
if (rhs == 0)
rhs = this;
double dly = lhs->y_ - y_;
double dry = rhs->y_ - y_;
double fl = dly*k;
double fr = dry*k;
vy_ += fl + fr;
vy_ *= 0.999;//これはエネルギー損失を表す。
}
void Update2()
{
y_ += vy_;
}
void SetY(double y)
{
y_ = y;
}
void SetVY(double vy)
{
vy_ = vy;
}
void Draw()
{
DrawPixel(x_, y_, GetColor(255, 255, 255));
}
private:
double x_;
double y_;
double vy_;
};
const double Dot::k = 0.9;
class Wave
{
public:
Wave()
{
pick_ = Center;
vdot_.reserve(Width);
for (int i = 0; i < Width; ++i)
{
vdot_.push_back(Dot(i, Center));
}
}
void Pick( double y)
{
pick_ = y;
}
void Update()
{
for (int i = 0; i < Width; ++i)
{
if (i == 0)
{
vdot_[0].Update1(0, &(vdot_[1]) );
continue;
}
if (i == Width - 1)
{
vdot_[i].Update1(&(vdot_[i-1]),0);
continue;
}
vdot_[i].Update1(&(vdot_[i - 1]), &(vdot_[i + 1]));
}
vdot_[0].SetY(pick_);
vdot_[0].SetVY(0);
for (auto& dot : vdot_){
dot.Update2();
}
}
void Draw()
{
for(auto dot : vdot_){
dot.Draw();
}
}
private:
double pick_;
std::vector<Dot> vdot_;
};
int WINAPI WinMain(HINSTANCE, HINSTANCE, TCHAR*, int) {
ChangeWindowMode(TRUE);
DxLib_Init();
SetDrawScreen(DX_SCREEN_BACK);
Wave w;
while (ClearDrawScreen() == 0 && ProcessMessage() == 0 && GetHitKeyStateAll(key) == 0) {
// ESCキーで終了
if (key[KEY_INPUT_ESCAPE] == 1) break;
if (key[KEY_INPUT_Z] != 0)
w.Pick(Center-120);
else
w.Pick(Center);
if (key[KEY_INPUT_Q] != 0)
w = Wave();
clsDx();
printfDx("%f", Dot::k);
w.Update();
w.Draw();
ScreenFlip();
}
DxLib_End();
return 0;
}
Re: ウェーブマシンのようなモノを描画したい。
Posted: 2015年1月13日(火) 01:11
by GRAM
↑の自分のコードですが
計算誤差の問題だと思ってましたが、たぶんそうではなくて
先ほどのコードのばね定数を1にした場合は非常にうまくいくことがわかりました。
理由は・・・なんでだろう?ちょっとよくわかりませんけど何か理由があると思います・・・
(たぶんこれだという理由がありそう。コードのミスかそれども原理的な問題かはすぐにはわかりませんが)
オフトピック
(追記:もはや質問のレベルから外れるけれどたぶんCFDでいうクーラン数相当の条件だと思います。伝播速度が粒子サイズを上回ると計算が発散する
波の伝播速度を調整したいなら、1秒間に行う計算回数を増やす必要があるということ。確信は持てないですが…)
まぁいずれにしても以下のコードはうまく動きます。参考までに。
A:ポーズ
Q:初期化
Z:波の作成。
固定端反射、自由端反射、さらに定常波も確認できます。
► スポイラーを表示
コード:
#include "DxLib.h"
#include <vector>
using namespace std;
static char key[256];
static const int Width = 600;
static const int Center = 240;
class Dot
{
public:
Dot(double x, double y)
:x_(x), y_(y), vy_(0)
{
}
void Update1(Dot* lhs, Dot* rhs)
{
if (lhs == 0)
lhs = this;
if (rhs == 0)
rhs = this;
double dly = lhs->y_ - y_;
double dry = rhs->y_ - y_;
double fl = dly;
double fr = dry;
vy_ += fl + fr;
}
void Update2()
{
y_ += vy_;
}
void SetY(double y)
{
y_ = y;
}
void SetVY(double vy)
{
vy_ = vy;
}
void Draw()
{
DrawPixel(x_, y_, GetColor(255, 255, 255));
}
private:
double x_;
double y_;
double vy_;
};
class Wave
{
public:
Wave()
{
pick_ = Center;
vdot_.reserve(Width);
for (int i = 0; i < Width; ++i)
{
vdot_.push_back(Dot(i, Center));
}
}
void Pick( double y)
{
pick_ = y;
}
void Update()
{
for (int i = 0; i < Width; ++i)
{
if (i == 0)
{
vdot_[0].Update1(0, &(vdot_[1]) );
continue;
}
if (i == Width - 1)
{
vdot_[i].Update1(&(vdot_[i-1]),0);
continue;
}
vdot_[i].Update1(&(vdot_[i - 1]), &(vdot_[i + 1]));
}
for (auto& dot : vdot_){
dot.Update2();
}
vdot_[0].SetY(pick_);
vdot_[0].SetVY(0);
}
void Draw()
{
for(auto dot : vdot_){
dot.Draw();
}
}
private:
double pick_;
std::vector<Dot> vdot_;
};
int WINAPI WinMain(HINSTANCE, HINSTANCE, TCHAR*, int) {
ChangeWindowMode(TRUE);
DxLib_Init();
SetDrawScreen(DX_SCREEN_BACK);
Wave w;
while (ClearDrawScreen() == 0 && ProcessMessage() == 0 && GetHitKeyStateAll(key) == 0) {
// ESCキーで終了
if (key[KEY_INPUT_ESCAPE] == 1) break;
if (key[KEY_INPUT_Z] != 0)
{
double s = (GetNowCount() % 1000) / 1000.0 * DX_PI * 2;
w.Pick(Center - sin(s)*100 );
}
else
w.Pick(Center);
if (key[KEY_INPUT_Q] != 0)
w = Wave();
if (key[KEY_INPUT_A] == 0)
w.Update();
w.Draw();
ScreenFlip();
}
DxLib_End();
return 0;
}