6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Delphi VCLで3Dダンジョンを作ってみよう【マップエディタ編】

Last updated at Posted at 2024-12-13

お題 VCLで3Dダンジョンを作ります

右へ左へと折れ曲がり続く地下迷宮を、DelphiのVCL (Visual Component Library) でつくります。と、言っても、ごく簡単に。

説明0000.png

つくるアプリは2つです。
12/14 マップエディター編: 地面を掘って、地下迷宮を作ります。
12/15 迷路編      : 迷宮地図データを、壁が立ち並ぶ3D迷宮に描きます。

今回は、マップエディターを作成します。と、いってもすごく簡単なものです。

環境と準備

言語 Delphi 12 Community Edition
ターゲットプラットフォーム Windows 32ビット
※64ビットでもできますが、32ビットの方がデバッグしやすいため、32ビットがお勧め。完成したら、64ビットに切り替えてコンパイルしても良いです。

画面を準備します

まず、エクスプローラーでマイドキュメントを開いて、
C:\Users\【中略】\ドキュメント\Embarcadero\Studio\Projects
の下に、「MapEditer」 みたいな、適当な名前で作業用のフォルダーを作ってください。

つぎに、Delphi 12 Community Editionを起動します。
ファイル > 新規作成 > Windows VCLアプリケーション
まず、マップ作製アプリ(簡易版)のプロジェクトを作ります。

MapEdter-001.png

画面は、こんな感じで準備してください。

操作方法

StringGrid1を、マウスでクリックすると1と0が反転します。これで迷路を描きます。
[計算ボタン]を押すと、StringGrid2に計算結果を出力して、保存ダイアログが開きます。
マップデータをCSVファイルで出力します。

MapEdter-002.png

マップエディターは、左側のStringGrid1をクリックして、通路を描きます。
StringGridの onDrawCell イベントで、セルの値が 1 ならclGray 0 ならclSkyBlue で FillRectしてマップを描いています。

3D迷路のデータ構造について

この項でつくる3D迷路は、こんな感じです。白が壁で、黒が通路になります。
自機がいるセルと前方2セルまでを表示します。
4番の通路の部分ですが、曲がり角の向こうも表示されるように作りますが、今回のマップエディター編では説明を割愛します。

迷路説明図003.png

丸数字が、上図の迷路の描画と、下図の説明で対応しています。
この例だと、現在X:2 Y:7 のセルで Arrow:0 上を向いています。
3 4 6 9 の壁がありません。通過可能です。

通路と壁の関係1.png

計算ボタンを押したら、セルの値はこうなります

計算ボタンをクリックすると、StringGrid1のセルを forループでスキャンして、結果をStringGrid2 へ書き込みます。
マップエディターは、各セルに対して、周囲4セルを調べて、壁か 通路かを判定します。判定結果を、そのセルのデータとします。

迷路説明図023.png

できあがったデータは、 TStringList に ADD で書き足して、ファイル出力します。
データ形式は、「,」を足して、CSV形式にしてあります。

もうひとつ。明日のメニューになりますけど、迷路描画プログラムは、自機がいるセルから、周囲プラスマイナス3セルを調べて、マップを表示します。なので、計算を単純にするために、マップは、外周3セル分を固定壁扱いにしています。

ここで作ったマップデータは、明日、3D迷路作成で使用します。
以上が、マップエディターになります。

プログラム全文を載せます。

▼▼▼ クリックして 開いてください ▼▼▼

TfmMapEditer (maps.pas) のプログラム全文 [クリックで開いてください]
unit Maps;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.ExtCtrls, Vcl.StdCtrls;

type
  TfmMapEditer = class(TForm)
    Panel1: TPanel;
    StringGrid1: TStringGrid;
    StringGrid2: TStringGrid;
    Button1: TButton;
    Button2: TButton;
    SaveDialog1: TSaveDialog;
    procedure FormCreate(Sender: TObject);
    procedure StringGrid1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: LongInt;
      Rect: TRect; State: TGridDrawState);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  fmMapEditer: TfmMapEditer;

implementation

{$R *.dfm}

procedure TfmMapEditer.Button2Click(Sender: TObject);
var
  x,y:longint;
  M,Wall,path:String;
  StList:TStringList;

begin
  StList:=TStringList.Create;  //文字列リスト作成

  try
  with StringGrid1 do   //外周2セル分は壁なので、2-18を読みます
    for y:=3 to 18 do
    begin
      M:='';

      for x:=3 to 18 do
      begin
        Wall:='';

        if cells[x,y-1]='1' then  //前のセル
          Wall:=Wall+'1'
        else
          Wall:=Wall+'0';

        if cells[x+1,y]='1' then  //右のセル
          Wall:=Wall+'1'
        else
          Wall:=Wall+'0';

        if cells[x,y+1]='1' then  //後のセル
          Wall:=Wall+'1'
        else
          Wall:=Wall+'0';

        if cells[x-1,y]='1' then  //左のセル
          Wall:=Wall+'1'
        else
          Wall:=Wall+'0';

        if Cells[x,y]='1' then    //自セルが1は全面壁
          Wall:='1111';

        StringGrid2.cells[x,y]:=Wall;  //表示用。StringGrid2に書き込み
        M:=M+Wall+',';                 //出力用 カンマ切り付加
      end;

      Stlist.add(M);   //文字列リストに追加
    end;

    path:=ExtractFilePath(Application.Exename);  //このアプリのパス
    SaveDialog1.InitialDir:=path;
    SaveDialog1.filename:='WallMap.csv';
    if SaveDialog1.Execute=true then
      StList.SaveToFile(SaveDialog1.filename);        //文字列リストを保存

  finally
    StList.free;                                 //文字列り糸を解放
  end;
end;

procedure TfmMapEditer.Button1Click(Sender: TObject);
var
  x,y:longint;
begin
  for y:=0 to 20 do
    for X:=0 to 20 do
      StringGrid1.cells[x,y]:='1';    //すべてのセルを壁する

end;

procedure TfmMapEditer.FormCreate(Sender: TObject);
var
  x,y:longint;
begin

  with StringGrid1 do   //StringGrid1 マップを描く方を用意
  begin
    Align:=alLeft;
    RowCount:=21;       //縦横21セル 
    ColCount:=21;       //ただし外周2セル分は壁で固定

    for y:=0 to 20 do
      ColWidths[y] := 20;   //セルの大きさ 20✕20pix

    for x:=0 to 20 do
      RowHeights[x] := 20;

    width:=(ColCount+1)*ColWidths[0]+10;
  end;

  with StringGrid2 do   //StringGrid2 計算後のデータを表示
  begin
    Width:=StringGrid1.width;
    Align:=alLeft;
    RowCount:=21;       //縦横21セル
    ColCount:=21;       //ただし外周2セル分は壁で固定

    for y:=0 to 20 do
      ColWidths[y] := 40;  //セルの大きさ 40✕40pix
                           //文字を表示するため大きめ
    for x:=0 to 20 do
      RowHeights[x] := 40;
  end;

  Button1Click(Sender);    //StringGrid1をクリアしてマップ作成準備
end;

procedure TfmMapEditer.StringGrid1Click(Sender: TObject);
begin
  with StringGrid1 do     //外周3セル分は壁固定なのでexit
  begin
    if (col<3)or(col>17) or (row<3)or(row>17) then
      exit;

    if Cells[col,row]='0' then  //1と空白をクリックで切り替え
      Cells[col,row]:='1'
    else
      Cells[col,row]:='0';
  end;
end;

procedure TfmMapEditer.StringGrid1DrawCell(Sender: TObject; ACol, ARow: LongInt;
  Rect: TRect; State: TGridDrawState);
begin
  With StringGrid1.Canvas do  //StringGrid1を塗ります
  begin
    Brush.Color:=clSkyBlue;   //通路は青空色

    if StringGrid1.Cells[Acol,Arow]='1' then
      if (Acol<3)or(Acol>17)or(Arow<3)or(Arow>17) then
        Brush.Color:=clGray         //外周2セルは固定壁でグレー色
      else
        Brush.Color:=clSilver;      //書き換え壁は銀色

    FillRect(Rect);                 //セルを塗りつぶし

    textout(Rect.left+8,Rect.top+2,StringGrid1.cells[Acol,Arow]);
  end;

end;

end.

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?