C言語Ⅱ⑥(2025/10/23)

EditSceneでEditAreaClassを使えるようにする。
(EditScene.h)

#pragma once

#include"StDefine.h"
#include"InfoArea.h"

class EditScene
{

public:
	EditScene(void);
	~EditScene(void);

	bool SystemInit(void);
	void Update(void);
	void Draw(void);
	bool Release(void);

	int GetLandImageHndl(eLandFormKind lnd) { return landImage[lnd]; }
	int GetPrevButtonStat(void) { return prevButtonStat; }
	int GetNowButtonStat(void) { return nowButtonStat; }

private:

	InfoArea* infArea;		//情報表示エリアClassのインスタンスのポインタ

	int landImage[E_LAND_MAX];		//マップ地形画像のハンドル番号テーブル

	int prevButtonStat;				//直前のマウスボタンの状態
	int nowButtonStat;				//現在のマウスボタンの状態

};

InfoAreaの実装
(EditScene.h)

//----------------------------------------------------
// map editor
// name:ゆき
//----------------------------------------------------
//EditScene class
#include<DxLib.h>
#include"Application.h"
#include"EditScene.h"

EditScene::EditScene(void)
{
	infArea = nullptr;
}
EditScene::~EditScene(void)
{

}

bool EditScene::SystemInit(void)
{
	infArea = new InfoArea(this);
	if (infArea == nullptr)return false;

        if(infArea->SystemInit())return false;

	int err = LoadDivGraph("image/TileMap.png", E_LAND_MAX, 10, 3, MAP_CHIP_WID, MAP_CHIP_HIG, landImage);
	if (err == -1)return false;

	prevButtonStat = nowButtonStat = 0;	//マウスボタンの状態

	return true;
}

void EditScene::Update(void)
{
	prevButtonStat = nowButtonStat;		//直前のフレームのボタン状態を退避
	nowButtonStat = GetMouseInput();	//現フレームのボタン状態を取得

    infArea->Update();
}

void EditScene::Draw(void)
{
	infArea->Draw();//まずInfoAreaのスクリーンに描画

	SetDrawScreen(DX_SCREEN_BACK);		//裏画面を描画対象に設定
	ClearDrawScreen();			//描画対象画面をクリア(消去)

	//ウインドウ背景を描画
	DrawBox(0, 0, Application::WINDOW_SIZE_WID, Application::WINDOW_SIZE_HIG, GetColor(0, 0, 255), true);

	int scrnHndl;
	//編集エリアのスクリーンを裏画面に貼り付ける

	//情報エリアのスクリーンを裏画面に貼り付ける
	scrnHndl = infArea->GetInfoScreenHandle();
	DrawGraph(INFO_AREA_SX, INFO_AREA_SY, scrnHndl, true);

	ScreenFlip();						//描画画面を入れ替え
}

bool EditScene::Release(void)
{
	infArea->Release();
	delete infArea;
	infArea = nullptr;

	return true;
}

いまのコードの超ざっくりフロー

main → Application
WinMainApplication を作って、SystemInit → Run(ループ) → Release の順に回す。

Application.Run の中
毎フレーム Update()Draw() を呼ぶ。Escape で終了。

EditScene の責務

  • SystemInit():画像を分割読み込み、(本当はここで InfoArea も初期化)
  • Update():入力を読む。
  • Draw()
    オフスクリーン(infScrn)に InfoArea を先に描く
    裏画面(DX_SCREEN_BACK)をクリア
    ③ 背景や本体を描く
    infScrn を右側に貼る
    ScreenFlip()
  1. InfoArea
  • SystemInit():右ペイン用のオフスクリーンMakeScreen で作る(infScrn)。
  • Draw()SetDrawScreen(infScrn) に切り替えて、白背景・「地形一覧」文字・タイル一覧を描く。

この「素材(infScrn)を先に作る → 裏画面へ合成」がコツ。

[InfoArea::Draw] ──(SetDrawScreen: infScrn)──> [白背景+タイルを infScrn に描く]
                                        ↓
                              (戻って EditScene::Draw)
                                        ↓
[裏画面へ戻す] ──> [裏画面クリア+背景] ──> [infScrn を右側に貼る] ──> Flip

消去用タイルの描画を追加。
(InfoArea.cpp)

//----------------------------------------------------
// map editor
// name:ゆき
//----------------------------------------------------
//InfoArea class
#include<DxLib.h>
#include"InfoArea.h"
#include"EditScene.h"

InfoArea:: InfoArea(EditScene* eds)
{
	edScene = eds;
	infScrn = -1;
}

InfoArea::~InfoArea(void)
{

}

bool InfoArea::SystemInit(void)
{
	//スクリーンの作成
	infScrn = MakeScreen(INFO_AREA_WID,INFO_AREA_HIG,true);
	if (infScrn == -1)return false;

	selChipKind = E_LAND_NON;

	return true;
}

void InfoArea::Update(void)
{

}

void InfoArea::Draw(void)
{
	SetDrawScreen(infScrn);	//情報画面用のスクリーンに切り替え

	DrawBox(0, 0, INFO_AREA_WID, INFO_AREA_HIG, GetColor(255, 255, 255), true);
	DrawString(INFO_INDEX_SX, INFO_INDEX_LIST_SY, "地形一覧", GetColor(247, 14, 70));

	//地形選択画像リストの描画
	for (int xy = 0; xy < E_LAND_MAX; xy++)
	{
		//座標計算だけ先に行う
		int dx = INFO_CHIP_LIST_SX + (MAP_CHIP_WID + INFO_CHIP_DISTANCE_DX) * (xy % INFO_CHIP_DSP_XNUM);
		int dy = INFO_CHIP_LIST_SY + (MAP_CHIP_HIG + INFO_CHIP_DISTANCE_DY) * (xy / INFO_CHIP_DSP_XNUM);
		//描画したい画像のハンドルを取得する
		int land = edScene->GetLandImageHndl((eLandFormKind)xy);//引数の型が違うのでキャストする
		DrawGraph(dx, dy, land, true);
	}
	//消去用地形
	DrawBox(INFO_CHIP_CLR_SX, INFO_CHIP_CLR_SY, INFO_CHIP_CLR_SX + MAP_CHIP_WID, INFO_CHIP_CLR_SY + MAP_CHIP_HIG, GetColor(0, 0, 0), true);
	DrawString(INFO_CHIP_LIST_SX + 8, INFO_CHIP_CLR_SY + 8, "消", GetColor(255, 255, 255));
//選択地形の描画
DrawString(INFO_INDEX_SX, INFO_INDEX_SELCHIP_SY, "選択地形", GetColor(23u, 14, 70));
if (selChipKind == E_LAND_NON || selChipKind == E_LAND_CLEAR) {
	DrawBox(INFO_SELCHIP_DX,INFO_SELCHIP_DY,INFO_SELCHIP_DX+MAP_CHIP_WID,INFO_SELCHIP_DY+MAP_CHIP_HIG,GetColor(0, 0, 0), true);
	if (selChipKind == E_LAND_CLEAR) {
		DrawString(INFO_SELCHIP_DX + 8, INFO_SELCHIP_DY + 8, "消", GetColor(255, 255, 255));
	}
}
else {
	int hndl = edScene->GetLandImageHndl((eLandFormKind)selChipKind);//描画中の地形番号をキャストして渡して、ハンドル番号をhndlで受ける
	DrawGraph(INFO_SELCHIP_DX, INFO_SELCHIP_DY, hndl, true);
}
}

bool InfoArea::Release(void)
{

	return true;
}

-現状-

//----------------------------------------------------
// map editor
// name:ゆき
//----------------------------------------------------
//InfoArea class
#include<DxLib.h>
#include"InfoArea.h"
#include"EditScene.h"




InfoArea:: InfoArea(EditScene* eds)
{
	edScene = eds;
	infScrn = -1;
}

InfoArea::~InfoArea(void)
{

}

bool InfoArea::SystemInit(void)
{
	//スクリーンの作成
	infScrn = MakeScreen(INFO_AREA_WID,INFO_AREA_HIG,true);
	if (infScrn == -1)return false;

	selChipKind = E_LAND_NON;



	return true;
}

void InfoArea::Update(void)
{
	int mx, my, ax, ay;//mx-my:ウインドウ内座標、ax-ay:InfoArea内座標
	int prevBtn = edScene->GetPrevButtonStat();	//直前のマウスボタンを取得
	int nowBtn = edScene->GetNowButtonStat();	//今のマウスボタン状態を取得

	GetMousePoint(&mx,&my);	//現在のマウスの座標(ウインドウ内)を取得(mx,myに格納される)

	//情報エリアにマウスカーソルが載っているかどうかを判定
	if (mx >= INFO_AREA_SX && mx < INFO_AREA_SX + INFO_AREA_WID && my >= INFO_AREA_SY && my < INFO_AREA_SY + INFO_AREA_HIG) {
		//情報エリア内の座標に変換する
		ax = mx - INFO_AREA_SX;
		ay = my - INFO_AREA_SY;

		if ((prevBtn && MOUSE_INPUT_LEFT) != 0 && (nowBtn && MOUSE_INPUT_LEFT) == 0) {
			//アップトリガーで左ボタンの押下判定(押された場合)
			//選択一覧内のマップチップが選択されたかどうかを調べる
			for (int ii = 0; ii < E_LAND_MAX; ii++) {
				int dx = INFO_CHIP_LIST_SX + (MAP_CHIP_WID + INFO_CHIP_DISTANCE_DX) * (ii % INFO_CHIP_DSP_XNUM);
				int dy = INFO_CHIP_LIST_SY + (MAP_CHIP_HIG + INFO_CHIP_DISTANCE_DY) * (ii / INFO_CHIP_DSP_XNUM);
				if (ax >= dx && ax < dx + MAP_CHIP_WID && ay >=dy&&ay<dy+MAP_CHIP_HIG) {
					selChipKind = ii;
					break;
				}
			}
		}

	}
		
}

void InfoArea::Draw(void)
{
	SetDrawScreen(infScrn);	//情報画面用のスクリーンに切り替え

	DrawBox(0, 0, INFO_AREA_WID, INFO_AREA_HIG, GetColor(255, 255, 255), true);
	DrawString(INFO_INDEX_SX, INFO_INDEX_LIST_SY, "地形一覧", GetColor(247, 14, 70));

	//地形選択画像リストの描画
	for (int xy = 0; xy < E_LAND_MAX; xy++)
	{
		//座標計算だけ先に行う
		int dx = INFO_CHIP_LIST_SX + (MAP_CHIP_WID + INFO_CHIP_DISTANCE_DX) * (xy % INFO_CHIP_DSP_XNUM);
		int dy = INFO_CHIP_LIST_SY + (MAP_CHIP_HIG + INFO_CHIP_DISTANCE_DY) * (xy / INFO_CHIP_DSP_XNUM);
		//描画したい画像のハンドルを取得する
		int land = edScene->GetLandImageHndl((eLandFormKind)xy);//引数の型が違うのでキャストする
		DrawGraph(dx, dy, land, true);



	}
	//消去用地形
	DrawBox(INFO_CHIP_CLR_SX, INFO_CHIP_CLR_SY, INFO_CHIP_CLR_SX + MAP_CHIP_WID, INFO_CHIP_CLR_SY + MAP_CHIP_HIG, GetColor(0, 0, 0), true);
	DrawString(INFO_CHIP_LIST_SX + 8, INFO_CHIP_CLR_SY + 8, "消", GetColor(255, 255, 255));

	//選択地形の描画
	DrawString(INFO_INDEX_SX, INFO_INDEX_SELCHIP_SY, "選択地形", GetColor(23u, 14, 70));
	if (selChipKind == E_LAND_NON || selChipKind == E_LAND_CLEAR) {
		DrawBox(INFO_SELCHIP_DX,INFO_SELCHIP_DY,INFO_SELCHIP_DX+MAP_CHIP_WID,INFO_SELCHIP_DY+MAP_CHIP_HIG,GetColor(0, 0, 0), true);
		if (selChipKind == E_LAND_CLEAR) {
			DrawString(INFO_SELCHIP_DX + 8, INFO_SELCHIP_DY + 8, "消", GetColor(255, 255, 255));
		}
	}
	else {
		int hndl = edScene->GetLandImageHndl((eLandFormKind)selChipKind);//描画中の地形番号をキャストして渡して、ハンドル番号をhndlで受ける
		DrawGraph(INFO_SELCHIP_DX, INFO_SELCHIP_DY, hndl, true);
	}
}

bool InfoArea::Release(void)
{

	return true;
}

とりあえず、地形チップをクリックすることで選択地形が変化するところまでは確認。


現状のフローとロジックの要点

全体フロー(呼び出しと描画の大づかみ)

起動〜終了

  1. WinMain → Application
    WinMainApplication を作成 → SystemInit()Run()Release() の順。
  2. Application.SystemInit
    DxLibの初期化/ウィンドウ設定 → EditScene を new → EditScene.SystemInit() を呼ぶ(※現状は戻り値を無視しているので要改善)。
  3. Application.Run(メインループ)
    毎フレーム Update()Draw()。Esc で終了。
  4. Application.Update/Draw
    中では単に edScene->Update() / edScene->Draw() を呼ぶ。

EditScene 側の1フレームの流れ

  1. EditScene.SystemInit
    InfoArea を生成→初期化、タイル画像を分割ロード(10×3で E_LAND_MAX 種)、マウス状態を初期化。
  2. EditScene.Update
    前フレームのボタン状態を退避 → 現在のボタン状態を取得 → InfoArea.Update() に入力を委譲。
  3. EditScene.Draw(描画の肝)
    (1) まず InfoArea のオフスクリーンInfoArea.Draw() で描く
    (2) 裏画面に切替 → クリア → ウィンドウ背景を塗る
    (3) 右側に InfoArea のスクリーンを貼る → ScreenFlip()
    この「素材(オフスクリーン)を先に作る → 裏画面で合成」が基本パターン。

ASCII 図

[InfoArea::Draw] ──(SetDrawScreen: infScrn)→ infScrn を完成
           ↓(戻る)
[EditScene::Draw] → DX_SCREEN_BACK をクリア → 背景描画
                              → DrawGraph(...) で infScrn を右側に貼る → Flip

InfoArea の責務

  • SystemInit: 右ペイン用オフスクリーン infScrnMakeScreen で作成、選択タイル初期値をセット。
  • Update: マウス座標を取得→右ペイン内なら、パレット上のどのタイル上にあるかを計算、クリックで選択。
  • Draw: SetDrawScreen(infScrn) で白背景・見出し・パレット(タイル一覧)・消去ボックス・現在選択中のタイルのプレビューを描画。

局所ロジック(要点&落とし穴)

1) Application.SystemInit の戻り値チェック

現状: edScene->SystemInit(); の戻り値を見ていない。
推奨:

edScene = new EditScene();
if (edScene == nullptr) return false;
if (!edScene->SystemInit()) return false; // ← 失敗時は即終了

これで画像読み込み失敗などの不整合時に早期に落とせます。


2) EditScene.Draw の順序はOK(“素材→合成”)

先に infArea->Draw()infScrn を完成させ、その後に裏画面へ戻して背景→貼付→Flip。
→ 現在の順序で理にかなっています(あなたが試してうまく行った順番のままでOK)。


3) InfoArea.Update のクリック判定(ここだけバグ修正推奨)

ボタン状態の判定が 論理 AND(&& になっており、ビット演算の AND(& が正解です。
「左ボタンのアップトリガ」を取りたい意図なら:

現状(NG)

if ((prevBtn && MOUSE_INPUT_LEFT) != 0 && (nowBtn && MOUSE_INPUT_LEFT) == 0) {
    ...
}

修正(OK)

if ( (prevBtn & MOUSE_INPUT_LEFT) != 0 && (nowBtn & MOUSE_INPUT_LEFT) == 0 ) {
    // 前フレは押されていた&今フレは離れた → アップトリガ
    ...
}

理由: GetMouseInput() はビットフラグ。論理演算子ではなく ビット演算子 & で判定する必要があります。)

参考:ダウンで取りたいなら
(prev & LEFT) == 0 && (now & LEFT) != 0


4) パレットの並べ方(座標式は正しい)

  • 横の個数 INFO_CHIP_DSP_XNUM
    (INFO_AREA_WID - INFO_CHIP_LIST_SX) / (MAP_CHIP_WID + INFO_CHIP_DISTANCE_DX)
  • パレット内で xy 番目のタイルの配置は
    • X: LIST_SX + (W + DX) * (xy % XNUM)
    • Y: LIST_SY + (H + DY) * (xy / XNUM)Y は XNUM で割る

5) “消去”と“選択中タイル”の描画

  • 消去用矩形(黒)+「消」表示。
  • 選択が E_LAND_NON / E_LAND_CLEAR なら黒枠&必要なら「消」、それ以外は選択中のタイル画像を表示。
    (※ E_LAND_NON / E_LAND_CLEAR の enum は StDefine.h 前提)

6) “描画先の戻し忘れ”対策(任意)

InfoArea::Draw() の中で SetDrawScreen(infScrn) を呼ぶ設計なので、呼び出し側で裏画面に戻す今のスタイルでOK。
もし将来的に戻し忘れを避けたいなら、ScopedDrawTarget みたいなRAIIガードを用意して自動復帰させるのが鉄板です(設計の話・実装は任意)。


まとめ(脳内の最小モデルだけ握っておけばOK)

  • アプリ構造: WinMain → Application → EditScene(→ InfoArea)
  • 毎フレーム: Update() で入力更新 → Draw()
    「InfoArea を先に描く(オフスクリーン)→ 裏画面を塗る → 右に貼る → Flip」
  • 右ペインのクリック: ペイン内座標に変換 → グリッド式で該当タイルを算出 → ビット演算で クリックトリガ判定。
    (by ChatGPT)

コメント

タイトルとURLをコピーしました