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

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

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

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

DXライブラリ

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

Q&A

解決済

1回答

5119閲覧

楕円同士の当たり判定について

Exactly-N

総合スコア7

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

DXライブラリ

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

0グッド

0クリップ

投稿2016/09/18 09:46

編集2016/09/18 10:26

###前提・実現したいこと
実現したいこと
・DXライブラリを用いて楕円同士の衝突判定を行いたいです

実装した内容
・楕円の当たり判定域を赤色で描画
・楕円同士が接触した場合、楕円の色を緑へ変化させて画面下に"HIT"を表示

###発生している問題・エラーメッセージ
バグ
・楕円同士が接触していない状態でも衝突判定がtrueになるときがあります

その他
・真円同士の衝突判定は問題なく動作します

###該当のソースコード

C++

1// main.cpp 2#define _USE_MATH_DEFINES 3#include "DxLib.h" 4#include "DrawOval2.h" 5#include "EllipseCollider.h" 6#include <cmath> 7 8using namespace std; 9 10int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { 11 ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen(DX_SCREEN_BACK); //ウィンドウモード変更と初期化と裏画面設定 12 float theta = 0.0f; 13 float cx = 300.0f, cy = 300.0f; 14 float rx = 100.0, ry = 100.0; 15 16 EllipseCollider* rcol = new EllipseCollider(ELLIPSE(300, 300, 100, 50, 90)); 17 EllipseCollider* rcol2 = new EllipseCollider(ELLIPSE(300, 300, 100, 50, 225)); 18 19// EllipseCollider* rcol = new EllipseCollider(ELLIPSE(300, 300, 20, 10, 90)); 20// EllipseCollider* rcol2 = new EllipseCollider(ELLIPSE(300, 300, 10, 10, 225)); 21 22 // while( 裏画面を表画面に反映, メッセージ処理, 画面クリア ) 23 while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0) { 24 int mx, my; 25 GetMousePoint(&mx, &my); 26 rcol->setCenterPos(mx, my); 27 28 29 int color = 0xff0000; 30 if (rcol2->isCollisionWith(*rcol)) { 31 color = 0x00ff00; 32 DrawFormatString(400, 400, 0x77ff00, "HIT"); 33 //Beep(6543, 5); 34 } 35 else { 36 rcol->addAngle(1); 37 } 38 rcol->drawRange(color); 39 rcol2->drawRange(color); 40 41 rcol->drawParam(); 42 rcol2->drawParam(0, 100); 43 } 44 45 delete rcol; 46 delete rcol2; 47 DxLib_End(); // DXライブラリ終了処理 48 return 0; 49} 50

C++

1// DrawOval2.h 2#pragma once 3#define _USE_MATH_DEFINES 4#include "DxLib.h" 5#include <cmath> 6 7 8/* 9(cx,cy)を中心にangle==0の時のx方向の半径rx、y方向の半径ryの楕円をcolorで描画する。 10元の仕様:angleは右回り(時計回り)を正とし、ラジアンで表す。 11改変 :angleは度数法で表す 12create by みけCAT氏 13*/ 14namespace DO2_NS{ 15 inline float TO_RADIAN(float DEGREE) { 16 //return (DEGREE * (M_PI / 180.0f)); 17 return (DEGREE / 180.0f*DX_PI_F); 18 } 19} 20 21namespace { 22 int DrawOval2(int cx, int cy, int rx, int ry, int color, float angle, int thickness = 1) { 23 float sine, cosine; 24 const int BUNKATU = 512; 25 float px, py; 26 angle = DO2_NS::TO_RADIAN(angle); 27 sine = sin(angle); 28 cosine = cos(angle); 29 px = cosine*rx; py = sine*rx; 30 if (rx < 0)rx = -rx; 31 if (ry < 0)ry = -ry; 32 for (int i = 1; i <= BUNKATU; i++) { 33 float nowangle = i*2.0*DX_PI_F / BUNKATU; 34 float rawx, rawy; 35 float x, y; 36 rawx = rx*cos(nowangle); 37 rawy = ry*sin(nowangle); 38 x = cosine*rawx - sine*rawy; 39 y = sine*rawx + cosine*rawy; 40 DrawLine(cx + (int)px, cy + (int)py, cx + (int)x, cy + (int)y, color, thickness); 41 px = x; py = y; 42 } 43 DrawPixel(cx, cy, color); 44 45 // エラーチェック省略 46 return 0; 47 } 48}

C++

1// Ellipse.h 2#pragma once 3 4// 楕円構造体 5struct ELLIPSE 6{ 7 float fRad_X; // X軸径 8 float fRad_Y; // Y軸径 9 float fAngle; // 回転角度 10 float fCx; // 制御点X座標 11 float fCy; // 制御点Y座標 12 13 ELLIPSE() 14 { 15 fRad_X = 100.0f; 16 fRad_Y = 100.0f; 17 fAngle = 0.0f; 18 fCx = 0.0f; 19 fCy = 0.0f; 20 } 21 22 ELLIPSE( 23 float fCx, // 制御点X座標 24 float fCy, // 制御点Y座標 25 float fRad_X, // X軸径 26 float fRad_Y, // Y軸径 27 float fAngle // 回転角度 28 ) : 29 fCx(fCx), 30 fCy(fCy), 31 fRad_X(fRad_X), 32 fRad_Y(fRad_Y), 33 fAngle(fAngle) 34 { 35 36 } 37 38};

C++

1// EllipseCollider.h 2#pragma once 3#include "Ellipse.h" 4#include "DrawOval2.h" 5 6class EllipseCollider 7{ 8public: 9 EllipseCollider(ELLIPSE region = ELLIPSE()); 10 ~EllipseCollider(); 11 12 bool isCollisionWith(EllipseCollider& e_collider); // 衝突判定を行う 13 void setRegion(ELLIPSE region); // 当たり判定域を設定する 14 void setCenterPos(float cx, float cy); // 当たり判定域の中心座標を設定する 15 void addCenterPos(float add_x, float add_y); // 当たり判定域の中心座標を(add_x, add_y)だけオフセットする 16 void setAngle(float degree); // 当たり判定域の傾きを設定する 17 void addAngle(float add_degree); // 当たり判定域をadd_degreeだけ傾ける 18 void setRadius(float rx, float ry); // 当たり判定域の横軸径と縦軸径を設定する 19 void getRegion(ELLIPSE& region); 20 21 void drawRange(int color = 0xff0000); // 当たり判定域を表示する(デバッグ用) 22 void drawParam(int x = 0, int y = 0); // 当たり判定域の状態を表示する(デバッグ用) 23 24private: 25 ELLIPSE region; // 楕円の領域 26 27 inline void to_less_than_360f() { 28 while (region.fAngle > 360.0f) { 29 region.fAngle -= 360.0f; 30 } 31 } 32};

C++

1// EllipseCollider.cpp 2#include "EllipseCollider.h" 3#include <cmath> 4 5 6EllipseCollider::EllipseCollider(ELLIPSE region) 7{ 8 setRegion(region); 9} 10 11EllipseCollider::~EllipseCollider() 12{ 13} 14 15/////////////////////////// 16// 楕円衝突判定関数 17// IKD氏制作IKD::CollisionEllipseメソッドの中身を拝借 18//////////////////// 19bool EllipseCollider::isCollisionWith(EllipseCollider & e_collider) 20{ 21 ELLIPSE& E1 = this->region; 22 ELLIPSE& E2 = e_collider.region; 23 24 // STEP1 : E2を単位円にする変換をE1に施す 25 float DefAng = DO2_NS::TO_RADIAN(E1.fAngle - E2.fAngle); 26 float Cos = cos(DefAng); 27 float Sin = sin(DefAng); 28 float nx = E2.fRad_X * Cos; 29 float ny = -E2.fRad_X * Sin; 30 float px = E2.fRad_Y * Sin; 31 float py = E2.fRad_Y * Cos; 32 float ox = cos(E1.fAngle)*(E2.fCx - E1.fCx) + sin(E1.fAngle)*(E2.fCy - E1.fCy); 33 float oy = -sin(E1.fAngle)*(E2.fCx - E1.fCx) + cos(E1.fAngle)*(E2.fCy - E1.fCy); 34 35 // STEP2 : 一般式A~Gの算出 36 float rx_pow2 = 1 / (E1.fRad_X*E1.fRad_X); 37 float ry_pow2 = 1 / (E1.fRad_Y*E1.fRad_Y); 38 float A = rx_pow2*nx*nx + ry_pow2*ny*ny; 39 float B = rx_pow2*px*px + ry_pow2*py*py; 40 float D = 2 * rx_pow2*nx*px + 2 * ry_pow2*ny*py; 41 float E = 2 * rx_pow2*nx*ox + 2 * ry_pow2*ny*oy; 42 float F = 2 * rx_pow2*px*ox + 2 * ry_pow2*py*oy; 43 float G = (ox / E1.fRad_X)*(ox / E1.fRad_X) + (oy / E1.fRad_Y)*(oy / E1.fRad_Y) - 1; 44 45 // STEP3 : 平行移動量(h,k)及び回転角度θの算出 46 float tmp1 = 1 / (D*D - 4 * A*B); 47 float h = (F*D - 2 * E*B)*tmp1; 48 float k = (E*D - 2 * A*F)*tmp1; 49 float Th = (B - A) == 0 ? 0 : atan(D / (B - A)) * 0.5f; 50 51 // STEP4 : +1楕円を元に戻した式で当たり判定 52 float CosTh = cos(Th); 53 float SinTh = sin(Th); 54 float A_tt = A*CosTh*CosTh + B*SinTh*SinTh - D*CosTh*SinTh; 55 float B_tt = A*SinTh*SinTh + B*CosTh*CosTh + D*CosTh*SinTh; 56 float KK = A*h*h + B*k*k + D*h*k - E*h - F*k + G; 57 if (KK>0) KK = 0; // 念のため 58 float Rx_tt = 1 + sqrt(-KK / A_tt); 59 float Ry_tt = 1 + sqrt(-KK / B_tt); 60 float x_tt = CosTh*h - SinTh*k; 61 float y_tt = SinTh*h + CosTh*k; 62 float JudgeValue = x_tt*x_tt / (Rx_tt*Rx_tt) + y_tt*y_tt / (Ry_tt*Ry_tt); 63 64 if (JudgeValue <= 1) 65 return true; // 衝突 66 67 return false; 68} 69 70void EllipseCollider::drawRange(int color ) 71{ 72 DrawOval2(region.fCx, region.fCy, region.fRad_X, region.fRad_Y, color, region.fAngle); 73} 74 75void EllipseCollider::drawParam(int x, int y) 76{ 77 DrawFormatString(x, y, 0x77ff00, "座標 :(%3f, %3f)", region.fCx, region.fCy); 78 DrawFormatString(x, y + GetFontSize()*1, 0x77ff00, "横軸径:%3f, 縦軸径:%3f", region.fRad_X, region.fRad_Y); 79 DrawFormatString(x, y + GetFontSize()*2, 0x77ff00, "傾き :%3f", region.fAngle); 80} 81 82void EllipseCollider::setRegion(ELLIPSE region) 83{ 84 this->region = region; 85} 86 87void EllipseCollider::setCenterPos(float cx, float cy) 88{ 89 region.fCx = cx; 90 region.fCy = cy; 91} 92 93void EllipseCollider::addCenterPos(float add_x, float add_y) 94{ 95 region.fCx = add_x; 96 region.fCy = add_y; 97} 98 99void EllipseCollider::setAngle(float degree) 100{ 101 region.fAngle = degree; 102 to_less_than_360f(); 103} 104 105void EllipseCollider::addAngle(float add_degree) 106{ 107 region.fAngle += add_degree; 108 to_less_than_360f(); 109} 110 111void EllipseCollider::setRadius(float rx, float ry) 112{ 113 region.fRad_X = rx; 114 region.fRad_Y = ry; 115} 116 117void EllipseCollider::getRegion(ELLIPSE & region) 118{ 119 region = this->region; 120} 121

###試したこと
・度数法から弧度法への誤変換の確認
・角度が2πを超えた際の処理の追加
・楕円と真円との判定の際にも同様のバグが起こることを確認
・真円同士の判定の場合はバグが起こらないことを確認
・衝突判定がtrueの時の角度が分かるようにプログラムを変更

###補足情報(言語/FW/ツール等のバージョンなど)
OS : Windows 10
開発環境 : Visual Studio 2015
言語 : C++
ライブラリ: DXライブラリ

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

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

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

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

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

guest

回答1

0

ベストアンサー

全部細かく見たわけではありませんが、ELLIPSE::fAngleはデグリーなので、以下のコード内でラジアンへの変換がなされていないまま計算を行っているようです。

C++

1 float ox = cos(E1.fAngle)*(E2.fCx - E1.fCx) + sin(E1.fAngle)*(E2.fCy - E1.fCy); 2 float oy = -sin(E1.fAngle)*(E2.fCx - E1.fCx) + cos(E1.fAngle)*(E2.fCy - E1.fCy);

投稿2016/09/18 21:24

naomi3

総合スコア1105

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

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

Exactly-N

2016/09/19 05:24

回答していただきありがとうございます>< 指摘箇所を修正したところ、バグが解消されました! 本当に助かりました!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問