終了時稀に「使用されたパラメータは有効ではありません」というエラーボックスが表示されます。
その時のエラーメッセージは「System.ArgumentException」であり、おそらくThreadexplosionあたりが吐かれたというのも見当が付きました。
やはり終了時にThread::Abort()だけ呼び出すのはダメなのでしょうか。
御意見を頂きたいです。
#pragma once
#include "ball.h"
#include "bar.h"
#include "Block.h"
namespace blockBreaker {
/// <summary>
/// MyForm の概要
/// </summary>
public ref class MyForm : public System::Windows::Forms::Form
{
private:
// 各種クラスのインスタンス
Ball^ ball;
Bar^ bar;
Block^ block;
// キー情報の格納
Keys key;
// ウィンドウの画像データ
Bitmap^ back;
Graphics^ g;
// ボールとバーの座標データとブロック座標配列を持つ構造体
BlockBreakerPointData^ pointDate;
// データコンテナのロック用
Mutex^ mutex;
// 描画スレッド
Thread^ renderThread;
// バー更新スレッド
Thread^ barThread;
// ボール更新スレッド
Thread^ ballThread;
// ブロック削除用変数
int deleteNumber;
// フォント
System::Drawing::Font^ font;
// ゲーム終了フラグ
GameEndStatus Gameend = NoneEnd;
// ボールの射出フラグ
bool BallShots = false;
/// <summary>
/// 必要なデザイナー変数です。
/// </summary>
IContainer^ components;
public:
MyForm(void)
{
InitializeComponent();
//
//TODO: ここにコンストラクター コードを追加します
//
// ダブルバッファ許可設定
this->SetStyle(ControlStyles::DoubleBuffer, true);
this->SetStyle(ControlStyles::UserPaint, true);
this->SetStyle(ControlStyles::AllPaintingInWmPaint, true);
// 各種クラスインスタンスの生成と初期化
ball = gcnew Ball();
ball->Initialize();
bar = gcnew Bar();
bar->Initialize();
block = gcnew Block();
block->Initialize();
// 削除用変数の初期化
deleteNumber = -1;
// 背景画像の作成・それに描画するグラフィッククラスの生成
back = gcnew Bitmap(ClientSize.Width, ClientSize.Height);
g = Graphics::FromImage(back);
// キー状態を保存するメンバの初期化
key = Keys::None;
// フォントの初期化
font = gcnew System::Drawing::Font("HG行書体", 40);
mutex = gcnew Mutex();
// ゲーム上のデータを集約させた構造体の初期化
pointDate = gcnew BlockBreakerPointData(mutex);
pointDate->mutex = mutex;
{
pointDate->mutex->WaitOne();
pointDate->ballPoint = ball->getballPoint();
pointDate->barRect = bar->getRectangle();
for (int i = 0; i < BLOCK_NUM; i++){
pointDate->blockArray[i] = block->getBlockStatus(i);
}
pointDate->mutex->ReleaseMutex();
}
/***************************************/
/****** スレッド処理の生成・設定 ******/
/***************************************/
barThread = gcnew Thread(gcnew ParameterizedThreadStart(this, &MyForm::BarUpdate));
ballThread = gcnew Thread(gcnew ParameterizedThreadStart(this, &MyForm::BallUpdate));
renderThread = gcnew Thread(gcnew ParameterizedThreadStart(this, &MyForm::GameRender));
// スレッド初回起動
barThread->Start();
ballThread->Start();
renderThread->Start();
}
protected:
/// <summary>
/// 使用中のリソースをすべてクリーンアップします。
/// </summary>
~MyForm()
{
if (components)
{
delete components;
}
barThread->Abort();
ballThread->Abort();
renderThread->Abort();
delete ball;
delete bar;
delete block;
}
protected:
#pragma region Windows Form Designer generated code
/// <summary>
/// デザイナー サポートに必要なメソッドです。このメソッドの内容を
/// コード エディターで変更しないでください。
/// </summary>
void InitializeComponent(void)
{
this->SuspendLayout();
//
// MyForm
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 12);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(454, 612);
this->Name = L"MyForm";
this->Text = L"MyForm";
this->Load += gcnew System::EventHandler(this, &MyForm::MyForm_Load);
this->KeyDown += gcnew System::Windows::Forms::KeyEventHandler(this, &MyForm::MyForm_KeyDown);
this->KeyUp += gcnew System::Windows::Forms::KeyEventHandler(this, &MyForm::MyForm_KeyUp);
this->ResumeLayout(false);
}
#pragma endregion
private: System::Void MyForm_Load(System::Object^ sender, System::EventArgs^ e) {
}
// バーの更新処理
private: void BarUpdate(Object^ stateInfo) {
while (Gameend == NoneEnd)
{
if (pointDate->mutex->WaitOne())
{
bar->UpDate(key);
pointDate->barRect = bar->getRectangle();
pointDate->mutex->ReleaseMutex();
}
barThread->Sleep(25);
}
}
// ボールの更新処理
private: void BallUpdate(Object^ stateInfo) {
while (Gameend == NoneEnd){
if (pointDate->mutex->WaitOne())
{
deleteNumber = ball->UpDate(*pointDate->barRect, pointDate);
pointDate->ballPoint = ball->getballPoint();
// deleteNumberが-50(底に着いた)であった場合
// すべてのスレッドを止める
if (deleteNumber < -50){
EndChange(GameEndStatus::GameOver);
}
// もし削除するブロックが存在するならば
else if (deleteNumber > -1){
// 削除(という名前のstatus変更:実際にはデータの削除は行わない)
block->deleteBlocks(deleteNumber);
deleteNumber = -1;
}
// そうでなければ最新の情報に更新
else{
for (int i = 0; i < BLOCK_NUM; i++){
pointDate->blockArray[i] = block->getBlockStatus(i);
}
}
// もしすべてのブロックが破壊されたならすべてのスレッドを止める
if (block->getSurvivorBlockCount() == 0){
EndChange(GameEndStatus::GameClear);
}
pointDate->mutex->ReleaseMutex();
}
ballThread->Sleep(20);
}
}
// ゲーム描画処理
private: void GameRender(Object^ state){
while (1){
if (pointDate->mutex->WaitOne())
{
delete g; g = nullptr;
delete back; back = nullptr;
back = gcnew Bitmap(ClientSize.Width, ClientSize.Height);
g = Graphics::FromImage(back);
switch (Gameend)
{
case GameOver:
// 敗北時の描画
g->FillRectangle(Brushes::Black, Rectangle(0, 0, ClientSize.Width, ClientSize.Height));
g->DrawString("GAME OVER", font, Brushes::White, ClientSize.Width / 8 * 2.0f, ClientSize.Height / 2.0f);
break;
case GameClear:
// 勝利時の描画
g->FillRectangle(Brushes::Black, Rectangle(0, 0, ClientSize.Width, ClientSize.Height));
g->DrawString("GAME CLEAR", font, Brushes::White, ClientSize.Width / 8 * 2.0f, ClientSize.Height / 2.0f);
break;
case NoneEnd:
default:
// ゲームボードの描画
for (int i = 0; i < BLOCK_NUM; i++){
if (pointDate->blockArray[i]->status == Survivor){
g->FillRectangle(Brushes::Blue, *pointDate->blockArray[i]->rect);
}
}
g->FillRectangle(Brushes::Black, *pointDate->barRect);
g->FillEllipse(Brushes::Black, pointDate->ballPoint->X, pointDate->ballPoint->Y, BALL_WIDTH, BALL_HEIGHT);
break;
}
this->BackgroundImage = back;
pointDate->mutex->ReleaseMutex();
}
renderThread->Sleep(20);
}
}
private: System::Void MyForm_KeyDown(System::Object^ sender, System::Windows::Forms::KeyEventArgs^ e) {
if (e->KeyCode == Keys::Left || e->KeyCode == Keys::Right || e->KeyCode == Keys::Space){
if (pointDate->mutex->WaitOne())
{
key = e->KeyCode;
if (key == Keys::Space && !BallShots){
ball->GameStart(key);
BallShots = true;
}
pointDate->mutex->ReleaseMutex();
}
}
}
private: void EndChange(GameEndStatus s){
Gameend = s;
}
private: System::Void MyForm_KeyUp(System::Object^ sender, System::Windows::Forms::KeyEventArgs^ e) {
if (e->KeyCode == Keys::Left || e->KeyCode == Keys::Right){
if (pointDate->mutex->WaitOne())
{
key = Keys::None;
pointDate->mutex->ReleaseMutex();
}
}
}
};
}