お題 VCLで3Dダンジョンを作ります
右へ左へと折れ曲がり続く地下迷宮を、DelphiのVCL (Visual Component Library) でつくります。と、言っても、ごく簡単に。
つくるアプリは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アプリケーション
まず、マップ作製アプリ(簡易版)のプロジェクトを作ります。
画面は、こんな感じで準備してください。
操作方法
StringGrid1を、マウスでクリックすると1と0が反転します。これで迷路を描きます。
[計算ボタン]を押すと、StringGrid2に計算結果を出力して、保存ダイアログが開きます。
マップデータをCSVファイルで出力します。
マップエディターは、左側のStringGrid1をクリックして、通路を描きます。
StringGridの onDrawCell イベントで、セルの値が 1 ならclGray 0 ならclSkyBlue で FillRectしてマップを描いています。
3D迷路のデータ構造について
この項でつくる3D迷路は、こんな感じです。白が壁で、黒が通路になります。
自機がいるセルと前方2セルまでを表示します。
4番の通路の部分ですが、曲がり角の向こうも表示されるように作りますが、今回のマップエディター編では説明を割愛します。
丸数字が、上図の迷路の描画と、下図の説明で対応しています。
この例だと、現在X:2 Y:7 のセルで Arrow:0 上を向いています。
3 4 6 9 の壁がありません。通過可能です。
計算ボタンを押したら、セルの値はこうなります
計算ボタンをクリックすると、StringGrid1のセルを forループでスキャンして、結果をStringGrid2 へ書き込みます。
マップエディターは、各セルに対して、周囲4セルを調べて、壁か 通路かを判定します。判定結果を、そのセルのデータとします。
できあがったデータは、 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.