www.pudn.com > Tank拊親蚔牁測鎢.rar > MAPUNIT.PAS
unit mapunit;
interface
uses Windows, SysUtils, Graphics, Classes, tileunit, util;
type
// 圖格破碎表格
PBreakMap = ^TBreakMap;
TBreakMap = array[0..3, 0..3] of Boolean; // 4 x 4 = 16 個小圖格, "破"or"沒破"
// 地圖圖格
TCell = class
private
FTileNo: Integer; // 圖格所放置的圖片編號
FLayer, FX, fy: Integer; // 圖層, 座標
FBreakPtr: PBreakMap; // 圖格破碎表格
FRect: TRect; // 圖格所佔區域, 會隨圖格破碎而變更
function GetTileAttr: TTileAttr;
procedure SetTileAttr(const Value: TTileAttr);
function GetTileBitmap: TBitmap; // 取得圖片 bitmap
function GetCanPass: Boolean; // 這個圖格能否通過 ?
protected
public
constructor Create(Layer, X, Y: Integer);
destructor Destroy; override;
// 載入及儲存
procedure LoadFromStream(Stream: TStream);
procedure SaveToStream(Stream: TStream);
// 重新計算圖格所佔區域
procedure CalcRect;
// 圖格破碎處理方法
procedure AllocBreakMap; // 建立圖格破碎表格
procedure DisposeBreakMap; // 釋放圖格破碎表格
procedure SetBreakMap(X, Y: Integer); // 設定圖格破碎表格
procedure BreakBy(ARect: TRect); // 依矩形區域設定圖格破碎表格
property TileNo: Integer read FTileNo write FTileNo;
property TileAttr: TTileAttr read GetTileAttr write SetTileAttr;
property TileBitmap: TBitmap read GetTileBitmap;
property Rect: TRect read FRect;
property CanPass: Boolean read GetCanPass;
end;
// 地圖
TMapArray = array[0..TILE_NUM_Y - 1, 0..TILE_NUM_X - 1] of TCell;
tmap = class
private
FMaps: array[0..LAYER_MAX] of TMapArray; // (LAYER_MAX + 1) 層地圖
FLevelNo: Integer; // 目前載入的關卡編號
FRole_X, FRole_Y: Integer; // 角色的起始位置
FBreakBitmap: TBitmap; // 用於破碎圖格的貼圖動作
procedure SetLevelNo(const Value: Integer);
function GetCell(Layer, X, Y: Integer): TCell;
procedure SetRole_X(const Value: Integer);
procedure SetRole_Y(const Value: Integer);
protected
public
constructor Create;
destructor Destroy; override;
function GetFileName: string; // 根據關卡編號, 傳回對應的檔名
procedure LoadFromFile;
procedure SaveToFile;
// 將某地圖層畫在 Canvas 上
procedure DrawMap(Layer: Integer; Canvas: TCanvas);
// 重設整張地圖, 或只重設某一層
procedure ResetAllMap;
procedure ResetMap(Layer: Integer);
property LevelNo: Integer read FLevelNo write SetLevelNo;
// 取得某格的地形及物品圖片編號, 這是預設屬性
property Cell[Layer, X, Y: Integer]: TCell read GetCell; default;
// 取得初始的角色位置
property Role_X: Integer read FRole_X write SetRole_X;
property Role_Y: Integer read FRole_Y write SetRole_Y;
end;
var
Map: tmap;
implementation
uses xFiles;
constructor TCell.Create(Layer, X, Y: Integer);
begin
inherited Create;
FLayer := Layer;
FX := X;
fy := Y;
FBreakPtr := nil;
CalcRect; // 計算所佔用區域
end;
destructor TCell.Destroy;
begin
DisposeBreakMap;
inherited Destroy;
end;
// 根據座標及破碎表格計算所佔用區域
procedure TCell.CalcRect;
label // goto 所使用的標籤, Ouch !!
OutLoop1, OutLoop2, OutLoop3, OutLoop4;
var
X, Y: Integer;
begin
if not Assigned(FBreakPtr) then // 若沒有破碎表格, 則佔用區域很好算
FRect := Classes.Rect(TILEWIDTH[FX], TILEHeight[fy], TILEWIDTH[FX + 1], TILEHeight[fy + 1])
else
begin
// 但若有破碎表格, 則佔用區域要針對上下左右分別取得
FRect := Classes.Rect(0, 0, 0, 0);
// 最左邊
for X := 0 to 3 do
for Y := 0 to 3 do
if not FBreakPtr^[Y, X] then
begin
FRect.Left := TILEWIDTH[FX] + X * SM_TILE_WIDTH;
goto OutLoop1;
end;
OutLoop1:
// 最右邊
for X := 3 downto 0 do
for Y := 0 to 3 do
if not FBreakPtr^[Y, X] then
begin
FRect.Right := TILEWIDTH[FX] + (X + 1) * SM_TILE_WIDTH;
goto OutLoop2;
end;
OutLoop2:
// 最上邊
for Y := 0 to 3 do
for X := 0 to 3 do
if not FBreakPtr^[Y, X] then
begin
FRect.Top := TILEHeight[fy] + Y * SM_TILE_HEIGHT;
goto OutLoop3;
end;
OutLoop3:
// 最下邊
for Y := 3 downto 0 do
for X := 0 to 3 do
if not FBreakPtr^[Y, X] then
begin
FRect.Bottom := TILEHeight[fy] + (Y + 1) * SM_TILE_HEIGHT;
goto OutLoop4;
end;
OutLoop4:
end;
end;
// 從資料流讀取圖格
procedure TCell.LoadFromStream(Stream: TStream);
var
bHasBreakPtr: Boolean;
begin
with Stream do
begin
read (FTileNo, sizeof(Integer));
read (bHasBreakPtr, sizeof(Boolean));
if bHasBreakPtr then // 若有破碎表格, 則讀取之
begin
AllocBreakMap;
read (FBreakPtr^, sizeof(TBreakMap));
end else DisposeBreakMap;
CalcRect; // 重新計算所佔用區域
end;
end;
// 將圖格寫入資料流
procedure TCell.SaveToStream(Stream: TStream);
var
bHasBreakPtr: Boolean;
begin
with Stream do
begin
write (FTileNo, sizeof(Integer));
bHasBreakPtr := Assigned(FBreakPtr);
write (bHasBreakPtr, sizeof(Boolean));
if bHasBreakPtr then // 若有破碎表格, 則寫入之
write (FBreakPtr^, sizeof(TBreakMap));
end;
end;
function TCell.GetTileAttr: TTileAttr;
begin
Result := Tiles[FTileNo].Attr;
end;
procedure TCell.SetTileAttr(const Value: TTileAttr);
begin
Tiles[FTileNo].Attr := Value;
end;
// 取得置於圖格的圖片
function TCell.GetTileBitmap: TBitmap;
begin
Result := Tiles[FTileNo].Bitmap;
end;
// 建立圖格破碎表格
procedure TCell.AllocBreakMap;
begin
if not Assigned(FBreakPtr) then
New(FBreakPtr);
FillChar(FBreakPtr^, sizeof(TBreakMap), 0);
end;
// 釋放圖格破碎表格
procedure TCell.DisposeBreakMap;
begin
if Assigned(FBreakPtr) then
begin
Dispose(FBreakPtr);
FBreakPtr := nil;
end;
end;
// 設定圖格破碎表格
procedure TCell.SetBreakMap(X, Y: Integer);
begin
if not Assigned(FBreakPtr) then
AllocBreakMap;
FBreakPtr^[Y, X] := True; // (X, Y) 格破掉了
CalcRect; // 重新計算所佔用區域
end;
// 依矩形區域設定圖格破碎表格
procedure TCell.BreakBy(ARect: TRect);
var
X, Y : Integer;
R, R1: TRect;
begin
if not Assigned(FBreakPtr) then // 若此時還沒有破碎表格, 就建一個
AllocBreakMap;
// 與 ARect 取交集, 有交集的小格則設為破掉
for Y := 0 to 3 do
for X := 0 to 3 do
begin
// 計算小格的矩形區域
R1 := Classes.Rect(TILEWIDTH[FX] + X * SM_TILE_WIDTH, TILEHeight[fy] + Y * SM_TILE_HEIGHT,
TILEWIDTH[FX] + (X + 2) * SM_TILE_WIDTH, TILEHeight[fy] + (Y + 2) * SM_TILE_HEIGHT);
// 若有交集, 則讓它破掉
if IntersectRect(R, ARect, R1) then
FBreakPtr^[Y, X] := True;
end;
CalcRect; // 重新計算所佔用區域
end;
// 此圖格是否能通過
function TCell.GetCanPass: Boolean;
begin
Result := (TileNo = 0) or (tacanpass in TileAttr);
end;
{ TMap class }
constructor tmap.Create;
var
L, X, Y: Integer;
begin
inherited Create;
FLevelNo := 0;
// 建立每一層的所有圖格
for L := 0 to LAYER_MAX do
for Y := 0 to TILE_NUM_Y - 1 do
for X := 0 to TILE_NUM_X - 1 do
FMaps[L][Y, X] := TCell.Create(L, X, Y);
// 用於破碎圖格貼圖動作的 bitmap
FBreakBitmap := TBitmap.Create;
FBreakBitmap.Width := SM_TILE_WIDTH;
FBreakBitmap.Height := SM_TILE_HEIGHT;
FBreakBitmap.Transparent := True;
FBreakBitmap.TransparentColor := TRANSPARENT_COLOR;
end;
destructor tmap.Destroy;
var
L, X, Y: Integer;
begin
for L := 0 to LAYER_MAX do
for Y := 0 to TILE_NUM_Y - 1 do
for X := 0 to TILE_NUM_X - 1 do
FMaps[L][Y, X].Free;
FBreakBitmap.Free;
inherited Destroy;
end;
function tmap.GetFileName: string;
begin
Result := Format('%s%s%d%s',[AppDir, FN_MAP_PREFIX, FLevelNo, FN_MAP_EXT]);
end;
// 從檔案讀取地圖
procedure tmap.LoadFromFile;
var
fs : TFileStream;
I, X, Y: Integer;
begin
fs := TFileStream.Create(GetFileName, fmOpenRead);
with fs do
try
CheckSignature(fs, SIG_MYFILE); // 檢查檔頭標籤
CheckSignature(fs, ClassName); // 檢查副檔頭標籤
// 讀取角色初始位置
read (FRole_X, sizeof(Integer));
read (FRole_Y, sizeof(Integer));
// 讀取每一層的所有圖格資料
for I := 0 to LAYER_MAX do
for Y := 0 to TILE_NUM_Y - 1 do
for X := 0 to TILE_NUM_X - 1 do
FMaps[I][Y, X].LoadFromStream(fs);
finally
Free;
end;
end;
// 將地圖寫入檔案
procedure tmap.SaveToFile;
var
fs : TFileStream;
S : string;
I, X, Y: Integer;
begin
fs := TFileStream.Create(GetFileName, fmCreate or fmOpenWrite);
with fs do
try
write (SIG_MYFILE, Length(SIG_MYFILE));
S := ClassName;
write (S[1], Length(S));
// 寫入角色初始位置
write (FRole_X, sizeof(Integer));
write (FRole_Y, sizeof(Integer));
// 寫入每一層的所有圖格資料
for I := 0 to LAYER_MAX do
for Y := 0 to TILE_NUM_Y - 1 do
for X := 0 to TILE_NUM_X - 1 do
FMaps[I][Y, X].SaveToStream(fs);
finally
Free;
end;
end;
procedure tmap.SetLevelNo(const Value: Integer);
begin
FLevelNo := Value;
LoadFromFile;
end;
// 將指定的地圖層畫在 Canvas 上
procedure tmap.DrawMap(Layer: Integer; Canvas: TCanvas);
procedure DrawBrokenCell(X, Y: Integer);
var
M, N: Integer; // 繪製破碎的小圖格
begin
// 若為破碎圖格, 則一一將仍存在的小圖格貼上
for N := 0 to 3 do
for M := 0 to 3 do
begin
// 若此小圖格破掉了就不用畫
if FMaps[Layer][Y, X].FBreakPtr[N, M] then continue;
// 先複製到另一個 bitmap
FBreakBitmap.Canvas.CopyRect(Rect(0, 0, SM_TILE_WIDTH,
SM_TILE_HEIGHT), FMaps[Layer][Y, X].TileBitmap.Canvas,
Rect(M * SM_TILE_WIDTH, N * SM_TILE_HEIGHT,
(M + 1) * SM_TILE_WIDTH, (N + 1) * SM_TILE_HEIGHT));
// 再貼到畫布上, 以達成透明貼圖效果
Canvas.Draw(TILEWIDTH[X] + M * SM_TILE_WIDTH,
TILEHeight[Y] + N * SM_TILE_HEIGHT, FBreakBitmap);
end;
end;
var
X, Y: Integer; // 繪製圖格
begin
if Layer = LAYER_TERR then // 若為地形層, 則直接貼圖
for Y := 0 to TILE_NUM_Y - 1 do // 對於每一圖格
for X := 0 to TILE_NUM_X - 1 do
BitBlt(Canvas.Handle, TILEWIDTH[X], TILEHeight[Y],
TILE_WIDTH, TILE_Height, FMaps[Layer][Y, X].TileBitmap.Canvas.Handle, 0, 0, SRCCOPY)
else
begin
// 對於其它地圖層, 則採用透明貼圖
for Y := 0 to TILE_NUM_Y - 1 do // 對於每一圖格
for X := 0 to TILE_NUM_X - 1 do
begin
// 編號為 0 表示沒放圖片
if FMaps[Layer][Y, X].TileNo = 0 then continue;
// 若圖格是完整的
if not Assigned(FMaps[Layer][Y, X].FBreakPtr) then
Canvas.Draw(TILEWIDTH[X], TILEHeight[Y],
FMaps[Layer][Y, X].TileBitmap)
else
DrawBrokenCell(X, Y); // 繪出破碎圖格
end;
end;
end;
// 重設地圖
procedure tmap.ResetAllMap;
var
I: Integer;
begin
for I := 0 to LAYER_MAX do
ResetMap(I);
FRole_X := 0;
FRole_Y := 0;
end;
// 重設指定的地圖層
procedure tmap.ResetMap(Layer: Integer);
var
X, Y: Integer;
begin
for Y := 0 to TILE_NUM_Y - 1 do
for X := 0 to TILE_NUM_X - 1 do
FMaps[Layer][Y, X].TileNo := 0;
end;
function tmap.GetCell(Layer, X, Y: Integer): TCell;
begin
Result := FMaps[Layer][Y, X];
end;
procedure tmap.SetRole_X(const Value: Integer);
begin
FRole_X := Value;
end;
procedure tmap.SetRole_Y(const Value: Integer);
begin
FRole_Y := Value;
end;
end.