みなさん適当に敵の動きをつけたかと思いますが、このゲーム、簡単すぎると思いませんか?敵が一体だけだとよほどのことがない限り当たりませんよね…
この章では、「敵をたくさん作る」ことを目的にプログラミングして行きます。今までの描画方法を全く違うものにするためかなり長い章になるので心の準備を忘れずに。
さて、敵をたくさん作ることになったわけですが、実はTImageによる描画には弱点があります。
それは、キャラ毎にTImageを用意しなければならないことです。敵をたくさん作るためには画像もたくさん用意しなければならないのです。大変めんどくさいです。
そこで今回はDelphiXというものを使用したまったく新しい描画方法に変更します。
DelphiXはDelphiでDirectXを利用するためのものです。
DirectXは主にゲームの開発やミュージックプレイヤーに使われるAPI群です。
予めダブルバッファリングを行う機能がついており、画像の回転描画や半透明描画、3Dオブジェクトの表示とかもできます。とにかく、TImageなんかより全然高機能です。
じゃあなぜ今まで使わなかったのかというと(TImageと比べると)ちょっと難しいからです。
今まで使ったTImageであるZiki、Tekiはもう要らないので削除しちゃってください。また、OnCreateイベント内に記述したDoubleBuffered:=True;
を消去してください(なお、「procedure TForm1.Form1OnCreateなんたら」のところは消さないでおいてください)
次にTFormをDelphiX向けに作られたTDXFormに変換します。TFormはDirectXと相性が悪いので。DXClassユニットをUses節に追加し、TFormとなっているのをTDXFormと修正します。
これで下準備は終わりです。
コンポーネントパレットの「DelphiX」をクリックし、を貼り付けてください。対角線上に線の引かれた四角形のようなものが出てきます。
これはDirectXを利用した描画をする際に描画先となる…要するにキャンバスの役割をするものを含むコンポーネントです。これの大きさがそのままゲーム画面の大きさになるので、Form1ぎりぎりの大きさにするかAlignプロパティの「alClient」にしてください。
次に、画像を読み込むためのTDXImageListというものを利用します。TDXImageListは今までのTImageがリストになり、さらにDelphiX仕様になったものと考えて良いです。を先ほどと同じ要領で貼り付けてください。貼り付けたら画像を読み込むことになるのですが、今までのTImageと読み込み方が違い少し難しいので間違えないようにしてください。
ここまでで一応準備が終わりました。
さて、準備が終わったところでコンパイルしてみましょう。
多分、物凄い量のエラーが出たかと思います。今まで「Ziki.Top」等と書いてきたのにTImageの「Ziki」を消しちゃったんで当然ですね。当然ですので当然ながら修正しなくてはならないのですが、今回は「ZikiTop」等の変数を作って「Ziki.Top」を置換する…等といった事はせずに、「構造体」というものを勉強して今までの「Ziki」にあたるものをつくってしまいます。そちらの方が後々便利になるし、何よりも此処で勉強しないと勉強する機会がなくなってしまうので。
レコード型とも言います。構造体は1つもしくは複数の変数をまとめて格納できる型です。要するに複数の変数をまとめて新しい型を作ります。
構造体の宣言は次のように行います。
type <構造体名> = record
<フィールド名1> :<フィールド1の型名>;
<フィールド名2> :<フィールド2の型名>;
end;
フィールドってのは変数と同義と今のうちは思っておいてOKです。typeは変数の宣言をする前にvarと書いたように、構造体や後に出てくるクラス型を宣言する前に書くものです。
ちなみに,構造体で作った新しい型の名前の先頭には「T」をつけるのがデルファイの風習となっています。後々区別しやすいからでしょうね。ZikiやTekiで実装すると
type
TForm1 = class(TDXForm)
DXImageList1: TDXImageList;
DXDraw1: TDXDraw;
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
TZiki = record
X,Y:Integer;
Dx,Dy:Extended;
Width,Height:Integer;
end;
TTeki = record
X,Y:Integer;
Dx,Dy:Extended;
Width,Height:Integer;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
var
//ここに書いてあったDx,Dy,TDx,TDyは削除。
Ziki:TZiki;
Teki:TTeki;
今まで宣言してたDxとDyを削除したのはそっちの方が理にかなうからです。ZikiのものはZikiのものと明示したほうが絶対良いにきまってます。だよね?LeftはX、TopはYに変えます。X座標はX、Y座標はYと呼ぶ方が一般的ですからね。
で、TDxとTDyを消したせいで&LeftとTopをX座標とY座標に変えたせいでコンパイルしてもまだエラーがでます。ちょっとメンドクサイですけど修正してください。
Ctrl+Rで使える置換を使えば楽です…が、LeftとTopはGetKeyState(VK_Left) < 0
等というように使っているので手動でやってください…
修正すればもうエラーが出なくなる…はずなので晴れてコンパイルできます。コンパイルしてみましょう…といっても、描画をさせる命令をしてないので描画がされません。真っ黒な画面が表示されるだけです。
画面が真っ黒なため描画をしなければなりません。で、描画をするためには先ほど用意したTDXImageListを使うことになります。
TDXImageListで描画をするためには、Draw手続きを使います。
描画するTDXImageList.Items.Find('描画したいTDXImageListのアイテムのNameプロパティ').Draw(TDXDrawのサーフェイス,X座標,Y座標,0);
えっと…自分で書いててわかりにくいですね。てかこんなのおぼえらんないかも?まあCtrl+Spaceをうまく活用してください。で、うろ覚えでもよくわかってなくても良いのでとりあえず実装しましょう。Timer1のOnTimer部の最後らへん(end;より前、DxとDyに小数部を足した所の後が良いでしょう)に次の文を書いて下さい。
DXDraw1.Surface.Fill(0);//バックバッファを塗りつぶす。引数として色を数値として渡す。
with DXImageList1.Items do
begin
Find('Ziki').Draw(DXDraw1.Surface,Ziki.X,Ziki.Y,0);//自機画像描画
Find('Teki').Draw(DXDraw1.Surface,Teki.X,Teki.Y,0);//敵画像描画
end;
DXDraw1.Flip;///バックバッファの画面を表に転送
描画をDXDraw1.Surface.Fill(0);
とDXDraw1.Flip;
の間に書くことに注意してください。描画しても転送する前に塗りつぶされたら意味ないですからね。
これで描画ができると思います。実行してください。
まあ、Ziki.TopとかTeki.Widthとか最初代入してないからへんになるのも当たり前ですよね。ということで、Form1のOnCreateで初期化します。
procedure TForm1.FormCreate(Sender: TObject);
begin
with Ziki do
begin
X:=100;
Y:=100;//ここら辺は適当に
Width :=DXImageList1.Items.Find('Ziki').Picture.Width;//なんとなく意味わかるよね?
Height:=DXImageList1.Items.Find('Ziki').Picture.Height;
end;
with Teki do
begin
X:=400;
Y:=500;
Width :=DXImageList1.Items.Find('Teki').Picture.Width;
Height:=DXImageList1.Items.Find('Teki').Picture.Height;
end;
end;
うまく行けば、この章は終了です。