最後に、簡単なメニューを作成します。メニュー自体の作成の仕方は、「メニューを作ろう」または、「もぐら叩きを作ってみよう その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.