🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Visual C++

Microsoft Visual C++はWindowsのCとC++の統合開発環境(IDE)であり、コンパイラやデバッガを含んでいます。

Visual Studio

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

セグメンテーション違反

セグメンテーション違反とは、ソフトウェア実行時に発生するエラーのひとつであり、許可されていないメモリにアクセスしたときに起きます。しばしば、ポインタの不適切な使用、またはバッファオーバーフローによって起こります。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

1回答

1641閲覧

”アクセス違反”のせいで製作が進まない・・・!

kogarashi0917

総合スコア12

Visual C++

Microsoft Visual C++はWindowsのCとC++の統合開発環境(IDE)であり、コンパイラやデバッガを含んでいます。

Visual Studio

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

セグメンテーション違反

セグメンテーション違反とは、ソフトウェア実行時に発生するエラーのひとつであり、許可されていないメモリにアクセスしたときに起きます。しばしば、ポインタの不適切な使用、またはバッファオーバーフローによって起こります。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2021/02/22 00:20

前提・実現したいこと

Dxlibにて2Dアクションを作っています。
二次元配列に沿ってマップを描画するようなコードを書いていて、1ステージ目の走破は出来るのですが、2ステージ目に進む前にゲームが強制終了してしまいます。
エラーメッセージには
”ハンドルされない例外が 0x00007FF6E90BD6E5 (2DActionGame.exe) で発生しました: 0xC0000005: 場所 0x0000000000000105 の読み取り中にアクセス違反が発生しました。”
と書いており、ローカルや呼び出し履歴を見ても明らかにおかしいのはわかるのですが、対処の仕方がわかりません。
他の方の質問を色々調べてみたのですが、当方のプログラミングスキルはアクセス違反の意味を理解することがやっとのレベルです。
どなたか教えていただけると助かります。

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

ハンドルされない例外が 0x00007FF6E90BD6E5 (2DActionGame.exe) で発生しました: 0xC0000005: 場所 0x0000000000000105 の読み取り中にアクセス違反が発生しました。

該当のソースコード

C++

1#include "Stage.h" 2#include "DxLib.h" 3 4//サンプルマップデータ 5char g_MapDataA[STAGE_H][STAGE_W] = { 6 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 9 1, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10 1, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 11 1, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 12 13 1, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 14 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 3, 0, 1, 15 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 16 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 17 1, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 18 19 1, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0, 3, 3, 0, 0, 0, 1, 20 1, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, 3, 0, 0, 0, 1, 21 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 1, 22 1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 23 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 24}; 25 26//クラスに宣言した関数の実装法 27//戻り値 クラス名::関数名(引数) 28Stage::Stage() //コンストラクタのため戻り値なし 29 :m_PlayerStartPos() 30{ 31 for (int y = 0; y < STAGE_H; y++) 32 { 33 for (int x = 0; x < STAGE_W; x++) 34 { 35 switch (g_MapDataA[y][x]) 36 { 37 case ChipType_StartPos: 38 m_PlayerStartPos.x = (CHIP_SIZE * x) + (HALF_CHIP); 39 m_PlayerStartPos.y = (CHIP_SIZE * y) + (HALF_CHIP); 40 m_Data[y][x] = ChipType_Non; 41 break; 42 default: 43 m_Data[y][x] = g_MapDataA[y][x]; 44 break; 45 } 46 } 47 } 48} 49 50Stage::~Stage() 51{ 52} 53 54void Stage::Draw() 55{ 56 for (int y = 0; y < STAGE_H; y++) 57 { 58 for (int x = 0; x < STAGE_W; x++) 59 { 60 //処理しない条件で判定 61 if (m_Data[y][x] == ChipType_Non) 62 { 63 continue; 64 } 65 /* 66 ここを通過した時点で、何かしら描画する必要がある 67 →DrawBoxを共通化できる 68 */ 69 unsigned int color = 0; //色はマップチップごとに違う 70 71 //マップチップごとに分岐処理 72 switch (m_Data[y][x]) 73 { 74 case ChipType_Wall: color = GetColor(125, 125, 125); break; 75 case ChipType_Ground: color = GetColor(0, 200, 0); break; 76 case ChipType_Footing: color = GetColor(128, 0, 0); break; 77 case ChipType_GoalPos: color = GetColor(255, 0, 0); break; 78 } 79 80 DrawBox( 81 x * CHIP_SIZE, y * CHIP_SIZE, 82 x * CHIP_SIZE + CHIP_SIZE, y * CHIP_SIZE + CHIP_SIZE, 83 color, 84 TRUE 85 ); 86 } 87 } 88} 89 90//プレイヤーの初期座標の取得 91//constメンバ関数…関数名にconstが後置されている関数 92//関数内でメンバ変数の変更を行っていないことを保証する 93const Position& Stage::GetPlayerStartPos() const 94{ 95 return m_PlayerStartPos; 96} 97 98//マップとの当たり判定 99//もし当たっていた場合、めり込まないように移動量 moveX_, moveY_ を補正する 100//当たっている辺の情報をbitフラグにして返す 101int Stage::CheckCollision(const Position& pos_, float* moveX_, float* moveY_) const 102{ 103 //当たった辺の種類 104 int hitFlg = 0; 105 106 //現在座標に移動量を加算して、移動後の座標を出す 107 float afterX = pos_.x; 108 float afterY = pos_.y; 109 afterX += (moveX_ == nullptr ? 0.0f : *moveX_); 110 afterY += (moveY_ == nullptr ? 0.0f : *moveY_); 111 112 //移動後の座標にどのようなマップチップがあるかをチェック 113 //もし何かあれば、必要な処理を行う 114 if (GetChipType(afterX, afterY) != ChipType_Non && GetChipType(afterX, afterY) != ChipType_GoalPos) 115 { 116 //2020/12/14現在、この中にはChipType_Wallの時しか来ない 117 //すなわち、壁に接触している間の処理をここで行う 118 119 //マップチップの上下左右の座標を計算 120 float chipLeft = int (afterX / CHIP_SIZE) * CHIP_SIZE; //左端 121 float chipRight = chipLeft + CHIP_SIZE; //右端 122 float chipTop = int (afterY / CHIP_SIZE) * CHIP_SIZE; //上端 123 float chipBottom = chipTop + CHIP_SIZE; //下端 124 125 //y方向のチェック 126 if (moveY_ != nullptr) 127 { 128 //マップチップの上端に当たっていた場合の処理 129 if (*moveY_ > 0.0f) 130 { 131 *moveY_ = chipTop - pos_.y - 1.0f; 132 hitFlg |= (1 << CollisionDir_Up); 133 } 134 //マップチップの下端に当たっていた場合の処理 135 else if (*moveY_ < 0.0f) 136 { 137 *moveY_ = chipBottom - pos_.y + 1.0f; 138 hitFlg |= (1 << CollidionDir_Down); 139 } 140 } 141 142 //x方向のチェック 143 if (moveX_ != nullptr) 144 { 145 //マップチップの左端に当たっていた場合の処理 146 if (*moveX_ > 0.0f) 147 { 148 *moveX_ = chipLeft - pos_.x - 1.0f; 149 hitFlg |= (1 << CollisionDir_Left); 150 } 151 //マップチップの右端に当たっていた場合の処理 152 else if (*moveX_ < 0.0f) 153 { 154 *moveX_ = chipRight - pos_.x + 1.0f; 155 hitFlg |= (1 << CollisionDir_Right); 156 } 157 } 158 } 159 return hitFlg; 160} 161 162/* 163 @param x_ スクリーン座標x 164 y_ スクリーン座標y 165*/ 166ChipType Stage::GetChipType(float x_, float y_) const 167{ 168 //画面上の座標から、配列上の座標へ変換する 169 const int x = int(x_ / CHIP_SIZE); 170 const int y = int(y_ / CHIP_SIZE); 171 172 /* 173 例題:配列[1][10]はマップチップ上のどこに描画されるか? 174 マップチップの縦横はCHIP_SIZEとする。 175 y = CHIP_SIZE * 1 = 32 176 x = CHIP_SIZE * 10 = 320 177 */ 178 179 //配列のx、yが計算出来たら… 180 if (x < 0 || y < 0 || x >= STAGE_W || y >= STAGE_H) 181 { 182 //何もなかったことにする 183 return ChipType_Non; 184 } 185 186 //指定の座標に該当するマップの情報を返す 187 return ChipType(m_Data[y][x]); //←ここでアクセス違反が起こる 188} 189 190

試したこと

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

使用ツール:
Visual Studio 2019
Dxlib

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

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

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

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

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

guest

回答1

0

ベストアンサー

エラーの原因なのですが、今回提示して頂いている場所とは違うように思えます。
C++言語において、マイナスの数値が入らない限り、STAGE_WやSTAGE_Hを少し超えたぐらいの値ならメモリの壁をぶち破ってアクセスすることが可能です(ようはそのようなエラーにはならない)
そして一番の原因として考えられるのは、インスタンス側でDeleteした後にも関わらず呼び出しを行っているケースです。

二次元配列に沿ってマップを描画するようなコードを書いていて、1ステージ目の走破は出来るのですが、2ステージ目に進む前にゲームが強制終了してしまいます。

と発言していますので、1ステージ目の最後にStageクラスを解放していないか、ご確認をお願いします。

投稿2021/02/22 01:20

編集2021/02/22 01:21
stdio

総合スコア3307

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

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

kogarashi0917

2021/02/22 01:30

朝早くからご回答いただきありがとうございます。 「Delete後に呼び出す」というのは、つまりはdeleteしたのちに、nullptrを代入するなということでしょうか? Stageクラスのdeleteにかかわるスクリプトも添付しますので、ご指摘いただけると助かります。 #include "InGameScene.h" #include "DxLib.h" #include "../Manager/GameManager.h" #include "../Manager/InputManager.h" #include "../Manager/SceneManager.h" #include "../Manager/FadeManager.h" //もし複数ステージor複数キャラクターがいるなら、同様にインクルードする #include "../Stage/Stage.h" #include "../Object/Player/Player.h" //実行ステップ定義 enum { STEP_FADE_IN, STEP_INPUT, STEP_FADE_OUT, STEP_END, }; /* 無名の列挙体を用いて、このcpp内のみで扱う定数を宣言しておくと、 マジックナンバーによる混乱を防げる。 */ //コンストラクタ InGameScene::InGameScene() :SceneBase() ,m_pStage(nullptr) ,m_pPlayer(nullptr) { m_Step = STEP_END; //コンストラクタでnewすることで、 //クラス内でm_pStage, m_pPlayerが存在していることを保証する m_pStage = new Stage(); m_pPlayer = new Player(); /* switch (選択したキャラ番号) { case マリオ: m_pPlayer = new Mario(); case ルイージ: m_pPlayer = new Luigi(); ・ ・ ・ 以下、キャラクター分を逐次追加していく } */ //ゲーム管理クラスへプレイヤーとステージを登録 GameManager* gameMng = GetGameManager(); gameMng->ResisterPlayer(m_pPlayer); gameMng->ResisterStage(m_pStage); } //デストラクタ InGameScene::~InGameScene() { //ゲーム管理クラスへ登録したプレイヤーとステージを解除 //登録した順とは逆順に解除 GameManager* gameMng = GetGameManager(); gameMng->UnresisterStage(); gameMng->UnresisterPlayer(); delete m_pPlayer; m_pPlayer = nullptr; delete m_pStage;    ←おそらくここのことでしょうか? m_pStage = nullptr; } //実行 void InGameScene::Exec() { switch (m_Step) { case STEP_FADE_IN: step_FadeIn(); break; case STEP_INPUT: step_Input(); break; case STEP_FADE_OUT: step_FadeOut(); break; default: break; } m_pPlayer->Exec(); } //描画 void InGameScene::Draw() { m_pStage->Draw(); m_pPlayer->Draw(); //デバッグ表示 DrawString(20, 20, "Stage 1", GetColor(0, 0, 0)); } //シーン初期化 void InGameScene::Init() { //スタート位置を設定した地点に m_pPlayer->SetPos(m_pStage->GetPlayerStartPos()); GetFadeManager()->StartFadeIn(DEFAULT_FADE_FRAME); set_Step(STEP_FADE_IN); } //シーンが終了しているか bool InGameScene::IsEnd() const { return (m_Step == STEP_END); } //フェードイン void InGameScene::step_FadeIn() { if (!GetFadeManager()->IsFadeFinished()) { return; } set_Step(STEP_INPUT); } //入力受付 void InGameScene::step_Input() { const Position& playerPos = m_pPlayer->GetPos(); if (m_pStage->GetChipType(playerPos.x, playerPos.y) == ChipType_GoalPos) { GetFadeManager()->StartFadeOut(DEFAULT_FADE_FRAME); set_Step(STEP_FADE_OUT); } } //フェードアウト void InGameScene::step_FadeOut() { if (!GetFadeManager()->IsFadeFinished()) { return; } GetSceneManager()->SetNextScene(SceneID_InGameB); set_Step(STEP_END); }
stdio

2021/02/22 01:56

> おそらくここのことでしょうか? そこが呼ばれてからCheckCollision又はGetChipTypeが呼ばれていないでしょうか? 1. deleteのところにブレークポイントをおいた状態で、ゲームを開始 2. ブレイクポイントで止める 3. GetChipTypeにブレイクポイントをおいて通っていないかチェック この手順に従って試してみてください。多分通ってしまっていると思います。 又はm_pStageのインスタンスを見直して下さい。今の状態ではポインター定義をしている意味がさほどないように思えます。
kogarashi0917

2021/02/22 02:30

教えていただいた手順で確認してみたところ、確かにdeleteした後にGetChipTypeに通ってしまっています。 ただ、エラーが起こった時点で確認できた値が、最初はm_pStage = 0x0000000000000000だったのに対し、GetChipTypeにブレイクポイントを置いた場合はm_pStage = 0xccccccccccccccccとなっています。 この場合はどう対処すればよいのでしょうか?
stdio

2021/02/23 01:16 編集

それはアドレスの値ですね。deleteした直後はnullptrが入っているので、0となっています。 私にも詳しい原因は分かりませんが、C/C++の言語の特徴であるメモリの壁を破壊した結果、よく分からない値が取れてきてしまっているのだと考えられます。 つまり、対処方はありません。プログラムの動きを確認して、削除後は絶対にそこを通らないように設計してあげて下さい。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問