2018年05月24日
《その393》2つのボール間の距離・壁との位置関係
2つのボール間の距離・壁との位置関係
今回はのアプリは、前回とほとんど同じですが、球が2つある点が異なります。
球と球が衝突したときの はね返りについては、計算が難しそうなのでスルーして (*^▽^*)ゞ
適当にはね返るようにしてあります(お互いの速度を交換するだけ)が、実行してみるとそんなに不自然には見えません
球が2つになると、お互いの位置関係もチェックしなければなりません。
コスト削減のために、
(2球の x座標の差 < 半径の和) && (2球の y座標の差 < 半径の和)
のときだけ、2球の距離を計算しています。
球と壁との位置関係,球と床・天井との位置関係,2球の位置関係 といった個々の位置関係チェックだけでは対応できないような、複数の位置関係に起因するエラーが発生する可能性があります。
下記のプログラムでは、例えば、2球が衝突したときにお互いの x座標をいきなり 少しだけ拡げていますが、これがないと、ごく稀にですが 2球が合体してしまうエラーが発生します。
以下は、MainPage.xaml.cpp です。
//
// MainPage.xaml.cpp
// MainPage クラスの実装。
//
#include "pch.h"
#include "MainPage.xaml.h"
using namespace App11;
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
double current_w = 0; // ウィンドウ幅
double current_h = 0; // 高さ
double x01 = 0; // 橙色ボールの x座標
double y01 = 0; // y座標
double x02 = 0; // 赤色ボールの x座標
double y02 = 0; // y座標
// 橙色ボールの初期速度
double vx01 = 4; // x方向速度
double vy01 = 1; // y方向速度
// 赤色ボールの初期速度
double vx02 = -5; // x方向速度
double vy02 = -5; // y方向速度
MainPage::MainPage()
{
InitializeComponent();
// 画面サイズを取得
current_w = Window::Current->Bounds.Width;
current_h = Window::Current->Bounds.Height;
// 赤色ボールの最初の位置は ウィンドウの右下です。
x02 = current_w - 80;
y02 = current_h - 80;
// ボールを動かし始めます。
StartTimer();
}
void MainPage::StartTimer() {
auto timer = ref new Windows::UI::Xaml::DispatcherTimer();
TimeSpan span;
span.Duration = 1000;
timer->Interval = span;
timer->Start();
auto rcpt
= timer->Tick +=
ref new EventHandler<Object^>(this, &MainPage::OnTick);
}
void MainPage::OnTick(Object^ sender, Object^ e) {
// 橙色ボールと右壁
if (x01 > current_w - 80) {
vx01 = -vx01; x01 = current_w - 80;
}
// 橙色ボールと床
if (y01 > current_h - 80) {
vy01 = -vy01; y01 = current_h - 80;
}
// 橙色ボールと左壁
if (x01 < 0) {
vx01 = -vx01; x01 = 0;
}
// 橙色ボールと天井
if (y01 < 0) {
vy01 = -vy01; y01 = 0;
}
// 橙色ボールの位置
x01 += vx01;
y01 += vy01;
// 赤色ボールと右壁
if (x02 > current_w - 80) {
vx02 = -vx02; x02 = current_w - 80;
}
// 赤色ボールと床
if (y02 > current_h - 80) {
vy02 = -vy02; y02 = current_h - 80;
}
// 赤色ボールと左壁
if (x02 < 0) {
vx02 = -vx02; x02 = 0;
}
// 赤色ボールと天井
if (y02 < 0) {
vy02 = -vy02; y02 = 0;
}
// 赤色ボールの位置
x02 += vx02;
y02 += vy02;
// 橙色ボールと赤色ボール
if (x02 - x01 > 80 || x02 - x01 < -80)
goto LABEL;
if (y02 - y01 > 80 || y02 - y01 < -80)
goto LABEL;
// 橙色ボールと赤色ボール
double d = sqrt((x02 - x01) * (x02 - x01) + (y02 - y01) * (y02 - y01));
if (d < 80) {
double temp = vx01;
vx01 = vx02;
vx02 = temp;
temp = vy01;
vy01 = vy02;
vy02 = temp;
if (x01 <= x02) {
if (x01 > 10) x01 -= 5;
else x02 += 5;
}
else {
if (x02 > 10) x02 -= 5;
else x01 += 5;
}
}
LABEL:;
// 2球をそれぞれの位置に移動させます。
img1->Margin = Windows::UI::Xaml::Thickness(x01, y01, 0, 0);
img2->Margin = Windows::UI::Xaml::Thickness(x02, y02, 0, 0);
}
// ウィンドウサイズ変更時のイベントハンドラ
void App11::MainPage::
page_SizeChanged(Platform::Object^ sender,
Windows::UI::Xaml::SizeChangedEventArgs^ e)
{
current_w = Window::Current->Bounds.Width;
current_h = Window::Current->Bounds.Height;
// 以下は、
// ドラッグによるアプリウィンドウの急激な縮小が
// 行われた場合の対策です。
if (x01 > current_w - 80) {
x01 = current_w - 80;
if (sqrt((x02 - x01) * (x02 - x01) + (y02 - y01) * (y02 - y01)) < 80)
x02 = x01 - 80;
}
if (x02 > current_w - 80) {
x02 = current_w - 80;
if (sqrt((x02 - x01) * (x02 - x01) + (y02 - y01) * (y02 - y01)) < 80)
x01 = x02 - 80;
}
if (y01 > current_h - 80) {
y01 = current_h - 80;
if (sqrt((x02 - x01) * (x02 - x01) + (y02 - y01) * (y02 - y01)) < 80)
y02 = y01 - 80;
}
if (y02 > current_h - 80) {
y02 = current_h - 80;
if (sqrt((x02 - x01) * (x02 - x01) + (y02 - y01) * (y02 - y01)) < 80)
y01 = y02 - 81;
}
}
この記事へのコメント
コメントを書く
この記事へのトラックバックURL
https://fanblogs.jp/tb/7692842
※ブログオーナーが承認したトラックバックのみ表示されます。
この記事へのトラックバック