質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

DXライブラリ

DXライブラリとは、DirectXを使ったWindowsソフトの開発に必ず付いて回るDirectXやWindows関連のプログラムを使い易くまとめた形で利用できるようにしたC++言語用のゲームライブラリです。

Q&A

1回答

1905閲覧

円の衝突から反発させたい

KaD3

総合スコア0

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

DXライブラリ

DXライブラリとは、DirectXを使ったWindowsソフトの開発に必ず付いて回るDirectXやWindows関連のプログラムを使い易くまとめた形で利用できるようにしたC++言語用のゲームライブラリです。

0グッド

0クリップ

投稿2021/05/15 07:56

DXライブラリでc言語を使用し円同士の衝突に関するシステムを作っています。
https://hakuhin.jp/as/collide.html
こちらのサイトを参考にしながら衝突から反発まで作っていたのですが、上手くいかなかったです。
どこに問題があるか調べても私ではわからなく質問させていただきました。

発生している問題・エラーメッセージ

円の中心座標をpx,pyとしています。
実行するとpy座標が高速でプラスされていき座標が#QNANOになってしまいます。
実行してもエラーメッセージは出ませんでした。

該当のソースコード

#include "DxLib.h" #include <math.h> const int WIN_WIDTH = 600; //ウィンドウ横幅 const int WIN_HEIGHT = 400;//ウィンドウ縦幅 int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { ChangeWindowMode(TRUE); //ウィンドウモードに設定 //ウィンドウサイズを手動では変更できず、かつウィンドウサイズに合わせて拡大できないようにする SetWindowSizeChangeEnableFlag(FALSE, FALSE); SetGraphMode(WIN_WIDTH, WIN_HEIGHT, 32); //画面サイズの最大サイズ、カラービット数を設定(モニターの解像度に合わせる) SetWindowSizeExtendRate(1.0); //画面サイズを設定(解像度との比率で設定) SetBackgroundColor(0x00, 0x00, 0x00); // 画面の背景色を設定する //Dxライブラリの初期化 if (DxLib_Init() == -1) { return -1; } //(ダブルバッファ)描画先グラフィック領域は裏面を指定 SetDrawScreen(DX_SCREEN_BACK); //ゲームループで使う変数の宣言 char keys[256] = { 0 }; //最新のキーボード情報用 char oldkeys[256] = { 0 };//1ループ(フレーム)前のキーボード情報 float vx = 0; float vy = 0; float len = 0; float distance = 0; float ax = 150; float ay = 150; float bx = 180; float by = 150; float _a = 0; float _b = 0; float _c = 0; float _d = 0; float f0 = 0; float f1 = 0; float t = 0; float amx = 0;//移動運動を発生させるベクトルx float amy = 0;//移動運動を発生させるベクトルy float bmx = 0;//移動運動を発生させるベクトルx float bmy = 0;//移動運動を発生させるベクトルy float arx = 0;//回転運動を発生させるベクトルx float ary = 0;//回転運動を発生させるベクトルy float brx = 0;//回転運動を発生させるベクトルx float bry = 0;//回転運動を発生させるベクトルy float e = 1.0;//反発係数 float adx = 0; float bdx = 0; float ady = 0; float bdy = 0; //円A struct circle1 { float px; float py; float dx; float dy; float r; int m; }; struct circle1 A; A.px = 150; A.py = 150; A.dx = 2; A.dy = 3; A.r = 10; A.m = 1; struct circle1 B; B.px = 180; B.py = 150; B.dx = 2; B.dy = 3; B.r = 10; B.m = 1; // ゲームループ while (1) { //最新のキーボード情報だったものは1フレーム前のキーボード情報として保存 //最新のキーボード情報を取得 GetHitKeyStateAll(keys); //画面クリア ClearDrawScreen(); //--------- ここからプログラムを記述 ----------// //更新処理 //移動 if (keys[KEY_INPUT_A]) { A.px--; } if (keys[KEY_INPUT_D]) { A.px++; } if (keys[KEY_INPUT_W]) { A.py--; } if (keys[KEY_INPUT_S]) { A.py++; } //円Aと円Bのめり込んだ量を調べる vx = A.px - B.px; vy = A.py - B.py; len = sqrt(vx * vx + vy * vy); distance = (A.r + B.r) - len; //円Aの補正方向を調べる if (len > 0) len = 1 / len; vx *= len; vy *= len; //円Aと円Bを重ならない位置まで補正する distance /= 2.0; A.px += vx * distance; A.py += vy * distance; B.px -= vx * distance; B.py -= vy * distance; //フレーム時間を計算する _a = (A.dx * A.dx) - 2 * (A.dx * B.dx) + (B.dx * B.dx) + (A.dy * A.dy) - 2 * (A.dy * B.dy) + (B.dy * B.dy); _b = 2 * (A.px * A.dx) - 2 * (A.px * B.dx) - 2 * (A.dx * B.px) + 2 * (B.px * B.dx) + 2 * (A.py * A.dy) - 2 * (A.py * B.dy) - 2 * (A.dy * B.py) + 2 * (B.py * B.dy); _c = (A.px * A.px) - 2 * (A.px * B.px) + (B.px * B.px) + (A.py * A.py) - 2 * (A.py * B.py) + (B.py * B.py) - (A.r + B.r) * (A.r + B.r); _d = sqrt(_b * _b - 4 * _a * _c); if (_d <= 0) { //当たり無し } else { //当たりあり _d = sqrt(_d); f0 = (-_b - _d) / (2 * _a); //接触する瞬間 f1 = (-_b + _d) / (2 * _a); //離れる瞬間 } //衝突する瞬間の座標を計算する ax = A.px + A.dx * f0; ay = A.py + A.dy * f0; bx = B.px + B.dx * f0; by = B.py + B.dy * f0; //速度ベクトルを重心方向と垂直な方向に分離する vx = (B.px - A.px); vy = (B.py - A.py); t = -(vx * A.dx + vy * A.dy) / (vx * vx + vy * vy); arx = A.dx + vx * t; ary = A.dy + vy * t; t = -(-vy * A.dx + vx * A.dy) / (vy * vy + vx * vx); amx = A.dx - vy * t; amy = A.dy - vx * t; t = -(vx * B.dx + vy * B.dy) / (vx * vx + vy * vy); brx = B.dx + vx * t; bry = B.dy + vy * t; t = -(-vy * B.dx + vx * B.dy) / (vy * vy + vx * vx); bmx = B.dx - vy * t; bmy = B.dy - vx * t; adx = (A.m * amx + B.m * bmx + bmx * e * B.m - amx * e * B.m) / (A.m + B.m); bdx = -e * (bmx - amx) + adx; ady = (A.m * amy + B.m * bmy + bmy * e * B.m - amy * e * B.m) / (A.m + B.m); bdy = -e * (bmy - amy) + ady; A.dx = adx + arx; A.dy = ady + ary; B.dx = bdx + brx; B.dy = bdy + bry; A.px += A.dx; A.py += A.dy; B.px += B.dx; B.py += B.dy; //描画処理 DrawFormatString(300, 0, GetColor(0, 255, 255), "len %f", len); DrawFormatString(300, 20, GetColor(0, 255, 255), "px %f", A.px); DrawFormatString(300, 40, GetColor(0, 255, 255), "px %f", B.px); DrawFormatString(300, 60, GetColor(0, 255, 255), "py %f", A.py); DrawFormatString(300, 80, GetColor(0, 255, 255), "py %f", B.py); DrawCircle(A.px, A.py, A.r, GetColor(255, 255, 255), TRUE); DrawCircle(B.px, B.py, B.r, GetColor(0, 255, 255), TRUE); //--------- ここまでにプログラムを記述 ---------// ScreenFlip();//(ダブルバッファ)裏面 // 20ミリ秒待機(疑似60FPS) WaitTimer(20); // Windows システムからくる情報を処理する if (ProcessMessage() == -1) { break; } // ESCキーが押されたらループから抜ける if (CheckHitKey(KEY_INPUT_ESCAPE) == 1) { break; } } //Dxライブラリ終了処理 DxLib_End(); return 0; }

試したこと

A.px += A.dx;
A.py += A.dy;
B.px += B.dx;
B.py += B.dy;
こちらの4行をコメントアウトしてみました。
すると、実行開始時から円Bに円Aがくっついている状態で座標を直接動かしても離れませんでした。

補足情報(FW/ツールのバージョンなど)

VisualStudio2019を使用しています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

円どうしの当たり判定なら、自前で計算しなくてもDxLibの算術演算関数でベクトルを計算すれば比較的簡単にできます。

 算術演算関数のリファレンスのページ
(三次元ベクトル用の関数ですが、zの数値を0にすれば二次元のベクトルとして、問題なく動作します。)

C++

1#include "DxLib.h" 2 3const int WIN_WIDTH = 600; //ウィンドウ横幅 4const int WIN_HEIGHT = 400;//ウィンドウ縦幅 5 6//構造体宣言 7typedef struct 8{ 9 VECTOR v; 10 float r; 11}Circle; 12 13int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 14{ 15 ChangeWindowMode(TRUE); //ウィンドウモードに設定 16 SetGraphMode(WIN_WIDTH, WIN_HEIGHT, 32); //画面サイズ・ビット数を設定 17 18 //Dxライブラリの初期化 19 if (DxLib_Init() == -1) { return -1; } 20 21 //描画先を指定 22 SetDrawScreen(DX_SCREEN_BACK); 23 SetBackgroundColor(0x00, 0x00, 0x00); // 画面の背景色を設定する 24 25 //キーボード情報用 26 char keys[256] = { 0 }; 27 28 //円の情報用 29 Circle A = { {150.f,150.f,0.f } ,10 }, 30 B = { {180.f,150.f,0.f } ,10 }; 31 32 //あたり判定効率化用(同じ計算を二度しない用の変数) 33 VECTOR disV; 34 float disF; 35 36 // ゲームループ 37 while (!ProcessMessage()) 38 { 39 //最新のキーボード情報を取得 40 GetHitKeyStateAll(keys); 41 42 //画面クリア 43 ClearDrawScreen(); 44 45 //移動キー判定 46 if (keys[KEY_INPUT_A])A.v.x--; 47 if (keys[KEY_INPUT_D])A.v.x++; 48 if (keys[KEY_INPUT_W]) A.v.y--; 49 if (keys[KEY_INPUT_S])A.v.y++; 50 51 disV = VSub(B.v, A.v);//BからAのベクトルを減算してdisVへ代入 52 disF = VSize(disV);//disVのベクトルサイズを計算する 53 54 if (disF < A.r + B.r) { //disVベクトルがAとBの半径の合計より小さいならめり込んでいる 55 float Scale = (disF - (A.r + B.r))/2; //めり込んでいる量の半分をScaleに代入 56 VECTOR AddV = VScale(VNorm(disV), Scale); //disV(BからAのベクトルを減算した物)を正規化してScaleを乗算しAddVに代入 57 A.v = VAdd(A.v, AddV); //Aの座標にAddVを加算 58 AddV = VScale(VNorm(VSub(A.v, B.v)), Scale);//AからBのベクトルを減算し正規化してScaleを乗算しAddVに代入 59 B.v = VAdd(B.v, AddV); //Bの座標にAddVを加算 60 } 61 62 //描画処理 63 DrawString(0, 0,"WASDキーで移動" ,0xffffff); 64 DrawFormatString(300, 20, 0x00ffff, "px %f", A.v.x); 65 DrawFormatString(300, 40, 0x00ffff, "px %f", B.v.x); 66 DrawFormatString(300, 60, 0x00ffff, "py %f", A.v.y); 67 DrawFormatString(300, 80, 0x00ffff, "py %f", B.v.y); 68 DrawCircle(int(A.v.x), int(A.v.y), int(A.r), 0xffffff, TRUE); 69 DrawCircle(int(B.v.x), int(B.v.y), int(B.r), 0x00ffff, TRUE); 70 71 ScreenFlip();//裏面 72 WaitTimer(20);//待機 73 if (keys[KEY_INPUT_ESCAPE])break; 74 } 75 76 //Dxライブラリ終了処理 77 DxLib_End(); 78 79 return 0; 80}

まあ勉強のために自作したいって事なら求めてる答えじゃ無いかもしれませんが、
ずっと回答付かないようなので回答しました。

投稿2021/05/30 11:31

kousatu

総合スコア225

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2021/05/31 04:24

これは > 実行するとpy座標が高速でプラスされていき座標が#QNANOになってしまいます。 という問題点に対して,何を回答しているのでしょうか? VECTORという型とその関連関数を使う形に書きかえれば問題点が解決するのでしょうか?
kousatu

2021/05/31 04:35

タイトルの「円の衝突から反発させたい」だけならもっと簡単な方法もありますよってだけです。 回答にも書きましたが、長い事誰も回答付けて無いようなので書きました。 気に触ったのなら、すいません。
fana

2021/05/31 05:12

単に,意図が読めなかったので問うただけです. 例えば, 「質問に提示されているコードのように要素毎に細々と書いていると,1要素だけ記述を間違うようなミスが出やすい」 という意見を持っていて,(あるいは本当にそういう類のミスを質問コードから見つけていて) 「そういうミスはベクトルの形で演算を書けば防げる」みたいな話(助言)なのかな…? とか.
kousatu

2021/05/31 05:57

「すでにある関数や構造体を使ってみては?」という提案です。 せっかくDxLibにはベクトル用の構造体や関数が用意されてるので…。
fana

2021/05/31 06:43

意図はわかりました. ところでこれって,2D版は無いんですかね? (リファレンスらしきページをぱっと見では見当たらない.2Dのベクトル演算には(この回答のように)3Dのを流用しろ,という文化なのかな?)
kousatu

2021/05/31 07:54

>>ところでこれって,2D版は無いんですかね? そうなんですよ。ないんですよね。 私もあった方が便利だと思うんですがね…。 一応似た感じので、FLOAT2型があって計算用の関数も「F2Add」みたいに「V」を「F2」すれば一部使用できますが、「VNorm」とか「VSize」など対応してないのがあるので置き換えはできませんね。 (メンバ名がxyでなくuvなので、ベクトル用じゃなくてUV座標用ですね、多分) >>2Dのベクトル演算には(この回答のように)3Dのを流用しろ,という文化なのかな?) いつ頃かは覚えてないですが、DxLib本家の掲示板で過去にVECTORのzを0にすれば2Dで使えるって記載はたしかありましたね。
fana

2021/05/31 08:30

なるほど. C言語の範疇で書く場合だと,2Dは{自前で用意するか,VECTORを流用するか}という感じになりそうですね.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問