もぐら叩きを作ろう その5

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

up next
Last update: 2002/12/28