最後に、簡単なメニューを作成します。メニュー自体の作成の仕方は、「メニューを作ろう」または、「もぐら叩きを作ってみよう その5」を参考にして下さい。
それでは、フォームに Standard ページにあります MainMenu コンポーネントを貼り付け、以下の図のようにメニューを作成してください。
出来ましたら、以下のようにプログラムコードを書きます。
procedure TForm1.mmExitClick(Sender: TObject); begin Close; end; procedure TForm1.mmStartClick(Sender: TObject); begin GameManager.GameInit; end;
そして最後に、TGameManager クラスのインスタンスを Form1 の OnCreate で生成し、OnDestroy で破棄させるようにします。また、このプログラムでは Random 関数を使用していますので、OnCrete で Randomize 関数を呼ぶようにします。そして Timer コンポーネントの OnTimer に TGameManager クラスの GameLoop メソッドをタイプし、Form1 の OnMouseDown にもぐらが叩かれたかどうかをチェックする CheckMogState メソッドをタイプして完成です。
type
TForm1 = class(TForm)
Timer1: TTimer;
MainMenu1: TMainMenu;
mmFile: TMenuItem;
mmStart: TMenuItem;
mmExit: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Timer1Timer(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure mmExitClick(Sender: TObject);
procedure mmStartClick(Sender: TObject);
private
{ Private 宣言 }
GameManager: TGameManager; // 追加
public
{ Public 宣言 }
end;
.............................
procedure TForm1.FormCreate(Sender: TObject);
begin
Randomize; // 乱数生成関数の初期化
GameManager := TGameManager.Create;
// カーソルの設定
Screen.Cursors[crUp] := LoadCursor(hInstance, 'UP');
Screen.Cursors[crDown] := LoadCursor(hInstance, 'DOWN');
Screen.Cursor := TCursor(crUp);
// 各種設定
ClientWidth := 489;
ClientHeight := 324;
Form1.Timer1.Enabled := false;
Timer1.Interval := 100;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
GameManager.Free;
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Screen.Cursor := TCursor(crDown);
// マウスがクリックされた場合、モグラが叩かれたかどうかチェック
GameManager.CheckMogState(X, Y);
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
GameManager.GameLoop; // メインループ
end;
以上で完成です。これまでの全てのプログラムは、以下のようになります。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, Unit2, Menus;
const
crUp : Integer = 1;
crDown: Integer = 2;
type
TForm1 = class(TForm)
Timer1: TTimer;
MainMenu1: TMainMenu;
mmFile: TMenuItem;
mmStart: TMenuItem;
mmExit: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Timer1Timer(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure mmExitClick(Sender: TObject);
procedure mmStartClick(Sender: TObject);
private
{ Private 宣言 }
GameManager: TGameManager;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{$R Mogura.res}
procedure TForm1.FormCreate(Sender: TObject);
begin
Randomize; // 乱数生成関数の初期化
GameManager := TGameManager.Create;
// カーソルの設定
Screen.Cursors[crUp] := LoadCursor(hInstance, 'UP');
Screen.Cursors[crDown] := LoadCursor(hInstance, 'DOWN');
Screen.Cursor := TCursor(crUp);
// 各種設定
ClientWidth := 489;
ClientHeight := 324;
Timer1.Enabled := false;
Timer1.Interval := 100;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
GameManager.Free;
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Screen.Cursor := TCursor(crDown);
// マウスがクリックされた場合、モグラが叩かれたかどうかチェック
GameManager.CheckMogState(X, Y);
end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Screen.Cursor := TCursor(crUp);
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
GameManager.GameLoop; // メインループ
end;
procedure TForm1.FormShow(Sender: TObject);
begin
// 各種設定
Color := clGreen;
Canvas.Brush.Color := clGreen;
Canvas.Font.Color := clWhite;
Canvas.Font.Size := 20;
end;
procedure TForm1.mmExitClick(Sender: TObject);
begin
Close;
end;
procedure TForm1.mmStartClick(Sender: TObject);
begin
GameManager.GameInit;
end;
end.
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls;
const
MAX_HOLES = 5; // モグラが出現する穴は全部で 5 つあります。
type
TBaseMog = class(TObject)
private
fLiveTime: Integer; // 叩かれていない絵を表示する時間
fDeadTime: Integer; // 叩かれた絵を表示する時間
fLiveBmp: TBitmap; // 叩かれていないビットマップ
fDeadBmp: TBitmap; // 叩かれたビットマップ
fScore: Integer; // 叩いた時に得られるスコア
public
constructor Create(LiveTime, DeadTime: Integer;
LiveBmp, DeadBmp: string; Score: Integer);
destructor Destroy; override;
property LiveTime: Integer read fLiveTime;
property DeadTime: Integer read fDeadTime;
property LiveBmp: TBitmap read fLiveBmp;
property DeadBmp: TBitmap read fDeadBmp;
property Score: Integer read fScore;
end;
THole = class(TObject)
private
fExistence: Boolean; // 穴からモグラが出ているか
fIsDead: Boolean; // モグラが、既に叩かれていた状態か
fHolePos: TPoint; // 穴の位置
fMogura: TBaseMog; // その穴から出ているモグラ(の種類)
fLimitTime: Integer; // モグラを表示していられる時間
public
constructor Create(HolePos: TPoint);
procedure DecLimitTime; // モグラの表示時間を減らす
procedure DeleteMog; // モグラを消します
function HoleIsFull: Boolean; // モグラが出現しているか
property LimitTime: Integer read fLimitTime write fLimitTime;
property IsDead: Boolean read fIsDead write fIsDead;
property Existence: Boolean read fExistence write fExistence;
property HolePos: TPoint read fHolePos;
property Mogura: TBaseMog read fMogura write fMogura;
end;
TGameManager = class(TObject)
private
fHoles: array[0..MAX_HOLES-1] of THole;
fNormalMog: TBaseMog;
fYellowMog: TBaseMog;
fGameTime: Integer;
fGameScore: Integer;
public
constructor Create;
destructor Destroy; override;
// ハンマーがモグラに当たったかどうか
procedure CheckMogState(X, Y: Integer);
procedure GameLoop; // メインループ
procedure GameOver;
procedure GameInit; // ゲームの初期化
procedure DecGameTime;
function GetKindOfMog: TBaseMog;
procedure UpdateGameScore;
procedure UpdateGameTime;
// 叩かれていないモグラを表示
procedure ShowLiveMog(HoleNumber: Integer; Mog: TBaseMog);
// 叩かれたモグラを表示
procedure ShowDeadMog(HoleNumber: Integer; Mog: TBaseMog);
end;
implementation
uses Unit1;
{ THole }
constructor THole.Create(HolePos: TPoint);
begin
fHolePos := HolePos;
fLimitTime := -1;
fExistence := false;
end;
procedure THole.DecLimitTime;
begin
if fLimitTime > 0 then // 表示しているモグラがいたら
begin
Dec(fLimitTime, 100); // 表示時間を減らす
if fLimitTime = 0 then
DeleteMog; // 表示時間がゼロになったらモグラを消します
end;
end;
procedure THole.DeleteMog;
begin
fExistence := false; // 穴が空きました。
fIsDead := false; // まだ叩かれていないを設定
Form1.Canvas.FillRect(Rect(HolePos.X,
HolePos.Y,
HolePos.X+fMogura.LiveBmp.Width,
HolePos.Y+fMogura.LiveBmp.Height));
end;
function THole.HoleIsFull: Boolean;
begin
Result := Existence;
end;
{ TBaseMog }
constructor TBaseMog.Create(LiveTime, DeadTime: Integer;
LiveBmp, DeadBmp: string; Score: Integer);
begin
// 初期化
fLiveTime := LiveTime;
fDeadTime := DeadTime;
fScore := Score;
fLiveBmp := TBitmap.Create;
fDeadBmp := TBitmap.Create;
fLiveBmp.LoadFromResourceName(hInstance, LiveBmp);
fDeadBmp.LoadFromResourceName(hInstance, DeadBmp);
end;
destructor TBaseMog.Destroy;
begin
fLiveBmp.Free;
fDeadBmp.Free;
inherited;
end;
{ TGameManager }
procedure TGameManager.CheckMogState(X, Y: Integer);
var
i: Integer;
begin
for i := 0 to MAX_HOLES-1 do
if (fHoles[i].IsDead = false) and fHoles[i].HoleIsFull then
// モグラが叩かれたかどうかの判定
if (X > fHoles[i].HolePos.X) and
(X < fHoles[i].HolePos.X+fHoles[i].Mogura.LiveBmp.Width) and
(Y > fHoles[i].HolePos.Y) and
(Y < fHoles[i].HolePos.Y+fHoles[i].Mogura.LiveBmp.Height) then
begin
fHoles[i].IsDead := true; // モグラが叩かれました。
ShowDeadMog(i, fHoles[i].Mogura); // 叩かれた絵を表示
// 得点の追加
Inc(fGameScore, fHoles[i].Mogura.Score);
end;
end;
constructor TGameManager.Create;
begin
// 穴の位置を設定
fHoles[0] := THole.Create(Point(10, 10));
fHoles[1] := THole.Create(Point(200, 10));
fHoles[2] := THole.Create(Point(100, 100));
fHoles[3] := THole.Create(Point(10, 200));
fHoles[4] := THole.Create(Point(200, 200));
// 普通のモグラの生成
fNormalMog := TBaseMog.Create(1000, 800, 'Mog1', 'Mog2', 300);
// 黄色いモグラの生成
fYellowMog := TBaseMog.Create(500, 500, 'Mog3', 'Mog4', 700);
GameInit; // ゲームの初期化
end;
procedure TGameManager.DecGameTime;
begin
Dec(fGameTime, 100); // 制限時間を減らします。
// 制限時間がゼロになるとゲームオーバー
if fGameTime = 0 then GameOver;
UpdateGameTime; // 制限時間の更新
end;
destructor TGameManager.Destroy;
var
i: Integer;
begin
for i := 0 to MAX_HOLES-1 do
fHoles[i].Free;
fNormalMog.Free;
fYellowMog.Free;
inherited;
end;
procedure TGameManager.GameInit;
begin
fGameTime := 30000; // 制限時間 (ms)
fGameScore := 0;
Form1.Canvas.FillRect(Form1.ClientRect);
Form1.Timer1.Enabled := true;
end;
procedure TGameManager.GameLoop;
var
HoleNum: Integer;
i: Integer;
begin
HoleNum := Random(17);
if (HoleNum < 5) then
if not fHoles[HoleNum].HoleIsFull then
ShowLiveMog(HoleNum, GetKindOfMog); // モグラを表示します
for i := 0 to MAX_HOLES-1 do
fHoles[i].DecLimitTime; // 表示時間を減らします
DecGameTime; // 制限時間を減らします。
UpdateGameScore; // スコアの表示
end;
procedure TGameManager.GameOver;
begin
Form1.Timer1.Enabled := false;
Form1.Canvas.FillRect(Rect(0, 0, 300, 300));
Form1.Canvas.TextOut(100, 100, 'Game Over');
end;
function TGameManager.GetKindOfMog: TBaseMog;
begin
// 制限時間が 5〜10秒の時だけ黄色モグラを出現させる
if (fGameTime < 10000) and (fGameTime > 5000) then
Result := fYellowMog
else
Result := fNormalMog;
end;
procedure TGameManager.ShowDeadMog(HoleNumber: Integer; Mog: TBaseMog);
begin
with fHoles[HoleNumber] do
begin
LimitTime := Mog.DeadTime;
Form1.Canvas.Draw(HolePos.X, HolePos.Y, Mog.DeadBmp);
end;
end;
procedure TGameManager.ShowLiveMog(HoleNumber: Integer; Mog: TBaseMog);
begin
with fHoles[HoleNumber] do
begin
Existence := true;
LimitTime := Mog.LiveTime;
Mogura := Mog;
Form1.Canvas.Draw(HolePos.X, HolePos.Y, Mog.LiveBmp);
end;
end;
procedure TGameManager.UpdateGameScore;
begin
Form1.Canvas.TextOut(300, 80, Format('Score: %d', [fGameScore]));
end;
procedure TGameManager.UpdateGameTime;
begin
Form1.Canvas.TextOut(300, 30, Format('GameTime: %.3d', [fGameTime div 100]));
end;
end.