3D野球ゲームの作成(FMX) ~Delphiソースコード集
野球場のイラストを用意する
Pixabay 様のサイト
https://pixabay.com/ja/vectors/%E9%87%8E%E7%90%83-%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89-%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%84-3778774/
から野球場のイラストをダウンロードします。

私はホームベースや1塁3塁など、少し加工させていただいたものを使用しました。
Delphiを起動してプロジェクトを作成し、オブジェクトを配置し、プロパティを設定する。
Delphiを起動し[ファイル]→[新規作成]→[マルチデバイスアプリケーション-Delphi]をクリックします。
[空のアプリケーション]を選択して[OK]ボタンを押します。
ツールパレットからTViewport3Dをドラッグ&ドロップします。
ツールパレットからTTextureMaterialSourceをドラッグ&ドロップします。
Textureプロパティの右側の(Bitmap Empty)と表示されている箇所をダブルクリックし、上記でダウンロードした野球場イラストを読み込んで設定します。
ツールパレットからTLightMaterialSourceを2個ドラッグ&ドロップします。
ツールパレットからTColorMaterialSourceをドラッグ&ドロップします。
ツールパレットからTTimerをドラッグ&ドロップします。
ツールパレットからTButtonを5個ドラッグ&ドロップします。
ツールパレットからTLabelをドラッグ&ドロップします。
ソースコードの記述
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
FMX.Controls.Presentation, FMX.StdCtrls, FMX.Viewport3D, System.Math.Vectors,
FMX.Types3D, FMX.Controls3D, FMX.Objects3D, FMX.MaterialSources,
FMX.ScrollBox, FMX.Memo, FMX.Memo.Types;
type
TForm1 = class(TForm)
Viewport3D1: TViewport3D;
Button1: TButton;
TextureMaterialSource1: TTextureMaterialSource;
LightMaterialSource1: TLightMaterialSource;
LightMaterialSource2: TLightMaterialSource;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
Timer1: TTimer;
ColorMaterialSource1: TColorMaterialSource;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ private 宣言 }
Camera:TCamera;
Light:TLight;
Plane1: TPlane;
d0,d1,d2,d3,d4,dBat:TDummy;
r11,r12,r13,r14:TRectangle3D;
r15,r16:TRectangle3D;
r21,r22,r23,r24,r25,r26:TRectangle3D;
disk:TDisk;
s1,s2:TSphere;
VBall:TPoint3D;//ボールの速度
c1:TCone;
Step:Integer; //ボールのモーション用
BStep:Integer; //バットのモーション用
HitFlag:Integer;
CamId:Integer; //カメラの位置と方位
procedure SetCameraPos();
public
{ public 宣言 }
end;
var
Form1: TForm1;
const
//カメラの位置
CamPos:array[0..3] of array[0..2] of Single=(
( 0, -6, -66),
( 0, -2, -66),
(-40, -4, -40),
( 0, -30, -100)
);
//カメラの向き
CamAngle:Array[0..3] of array[0..2] of Single=(
(-9,0,0),
(-3,0,0),
(0,90,0),
(-20,0,0)
);
//ボールの最初の位置
BallPosition:TPoint3D=(X:0;Y:-2;Z:-16);
implementation
{$R *.fmx}
procedure TForm1.Button1Click(Sender: TObject);
begin
//バットを振る
if BStep=0 then BStep:=1;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
//カメラの位置と向きを設定
CamId:=0;
SetCameraPos();
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
//カメラの位置と向きを設定
CamId:=1;
SetCameraPos();
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
//カメラの位置と向きを設定
CamId:=2;
SetCameraPos();
end;
procedure TForm1.Button5Click(Sender: TObject);
begin
//カメラの位置と向きを設定
CamId:=3;
SetCameraPos();
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
LightMaterialSource2.Diffuse:=TAlphaColorRec.Brown;
Plane1:=TPlane.Create(self);
Plane1.MaterialSource:=TextureMaterialSource1;
Plane1.Parent:=Viewport3D1;
//カメラの設定
Camera:=TCamera.Create(self);
Camera.Parent:=Viewport3D1;
Camera.AngleOfView:=26;
Viewport3D1.Camera:=Camera;
Viewport3D1.Color:=$FFCCDDFF;
Viewport3D1.UsingDesignCamera:=False;
CamId:=0;
SetCameraPos();
//ライトの設定
Light:=TLight.Create(self);
Light.Parent:=Viewport3D1;
Light.RotationAngle.X:=-30;
Light.RotationAngle.Y:=-60;
//地面の設定
Plane1.MaterialSource:=TextureMaterialSource1;
Plane1.RotationAngle.X:=270;
Plane1.Position.X:=0;
Plane1.Position.Y:=0;
Plane1.Position.Z:=0;
Plane1.Width:=140;
Plane1.Height:=140;
Plane1.Scale.X:=1;
Plane1.Scale.Y:=1;
plane1.Scale.Z:=1;
//ピッチャー
d0:=TDummy.Create(self);
d0.Parent:=Viewport3D1;
d0.Position.X:=0.6;
d0.Position.Y:=0;
d0.Position.Z:=-16;
r11:=TRectangle3D.Create(self);
r11.Parent:=d0;
r11.Width:=0.2;
r11.Height:=1;
r11.Depth:=0.2;
r11.Position.X:=-0.2;
r11.Position.Y:=-0.5;
r11.Position.Z:=0;
r11.MaterialSource:=LightMaterialSource1;
r11.MaterialBackSource:=LightMaterialSource1;
r11.MaterialShaftSource:=LightMaterialSource1;
r12:=TRectangle3D.Create(self);
r12.Parent:=d0;
r12.Width:=0.2;
r12.Height:=1;
r12.Depth:=0.2;
r12.Position.X:=0.2;
r12.Position.Y:=-0.5;
r12.Position.Z:=0;
r12.MaterialSource:=LightMaterialSource1;
r12.MaterialBackSource:=LightMaterialSource1;
r12.MaterialShaftSource:=LightMaterialSource1;
r13:=TRectangle3D.Create(self);
r13.Parent:=d0;
r13.Width:=0.6;
r13.Height:=1;
r13.Depth:=0.2;
r13.Position.X:=0;
r13.Position.Y:=-1.5;
r13.Position.Z:=0;
r13.MaterialSource:=LightMaterialSource1;
r13.MaterialBackSource:=LightMaterialSource1;
r13.MaterialShaftSource:=LightMaterialSource1;
r14:=TRectangle3D.Create(self);
r14.Parent:=d0;
r14.Width:=0.3;
r14.Height:=0.3;
r14.Depth:=0.3;
r14.Position.X:=0;
r14.Position.Y:=-2.1;
r14.Position.Z:=0;
r14.MaterialSource:=LightMaterialSource1;
r14.MaterialBackSource:=LightMaterialSource1;
r14.MaterialShaftSource:=LightMaterialSource1;
//ピッチャーの左腕
d1:=TDummy.Create(self);
d1.Parent:=Viewport3D1;
r15:=TRectangle3D.Create(self);
r15.Width:=0.2;
r15.Height:=0.8;
r15.Depth:=0.2;
r15.Position.X:=0;
r15.Position.Y:=0.4;
r15.Position.Z:=0;
r15.Parent:=d1;
r15.MaterialSource:=LightMaterialSource1;
r15.MaterialBackSource:=LightMaterialSource1;
r15.MaterialShaftSource:=LightMaterialSource1;
d1.Position.X:=0.9;
d1.Position.Y:=-1.94;
d1.Position.Z:=-16;
d1.RotationAngle.Z:=-180 div 8;
//ピッチャーの右腕
d2:=TDummy.Create(self);
d2.Parent:=Viewport3D1;
r16:=TRectangle3D.Create(self);
r16.Width:=0.2;
r16.Height:=0.8;
r16.Depth:=0.2;
r16.Position.X:=0.1;
r16.Position.Y:=0.4;
r16.Position.Z:=0;
r16.Parent:=d2;
r16.MaterialSource:=LightMaterialSource1;
r16.MaterialBackSource:=LightMaterialSource1;
r16.MaterialShaftSource:=LightMaterialSource1;
d2.Position.X:=0.2;
d2.Position.Y:=-1.96;
d2.Position.Z:=-16;
d2.RotationAngle.Z:=180 div 8;
//ボール
d3:=TDummy.Create(self);
d3.Parent:=Viewport3D1;
d3.Position.X:=BallPosition.X;
d3.Position.Y:=BallPosition.Y;
d3.Position.Z:=BallPosition.Z;
s1:=TSphere.Create(self);
s1.Parent:=d3;
s1.Position.X:=0;
s1.Position.Y:=0.8;
s1.Position.Z:=0;
s1.Width:=0.4;
s1.Height:=0.4;
s1.Depth:=0.4;
s1.MaterialSource:=LightMaterialSource1;
//ボールの影
disk:=TDisk.Create(self);
disk.Width:=0.4;
disk.Height:=0.4;
disk.Position.Y:=-0.01;
disk.Position.X:=BallPosition.X;
disk.Position.Z:=BallPosition.Z;
disk.Parent:=Viewport3D1;
disk.Opacity:=0.3;
ColorMaterialSource1.Color:=$ff000000;
disk.MaterialSource:=ColorMaterialSource1;
//バッター
d4:=TDummy.Create(self);
d4.Parent:=Viewport3D1;
d4.Position.X:=-1.8;
d4.Position.Y:=0;
d4.Position.Z:=-46;
d4.RotationAngle.Y:=-90;
r21:=TRectangle3D.Create(self);
r21.Parent:=d4;
r21.Width:=0.2;
r21.Height:=1;
r21.Depth:=0.2;
r21.Position.X:=-0.2;
r21.Position.Y:=-0.5;
r21.Position.Z:=0;
r21.MaterialSource:=LightMaterialSource1;
r21.MaterialBackSource:=LightMaterialSource1;
r21.MaterialShaftSource:=LightMaterialSource1;
r22:=TRectangle3D.Create(self);
r22.Parent:=d4;
r22.Width:=0.2;
r22.Height:=1;
r22.Depth:=0.2;
r22.Position.X:=0.2;
r22.Position.Y:=-0.5;
r22.Position.Z:=0;
r22.MaterialSource:=LightMaterialSource1;
r22.MaterialBackSource:=LightMaterialSource1;
r22.MaterialShaftSource:=LightMaterialSource1;
r23:=TRectangle3D.Create(self);
r23.Parent:=d4;
r23.Width:=0.6;
r23.Height:=1;
r23.Depth:=0.2;
r23.Position.X:=0;
r23.Position.Y:=-1.5;
r23.Position.Z:=0;
r23.MaterialSource:=LightMaterialSource1;
r23.MaterialBackSource:=LightMaterialSource1;
r23.MaterialShaftSource:=LightMaterialSource1;
r24:=TRectangle3D.Create(self);
r24.Parent:=d4;
r24.Width:=0.3;
r24.Height:=0.3;
r24.Depth:=0.4;
r24.Position.X:=0;
r24.Position.Y:=-2.1;
r24.Position.Z:=0;
r24.MaterialSource:=LightMaterialSource1;
r24.MaterialBackSource:=LightMaterialSource1;
r24.MaterialShaftSource:=LightMaterialSource1;
r25:=TRectangle3D.Create(self);
r25.Parent:=d4;
r25.Width:=0.2;
r25.Height:=0.8;
r25.Depth:=0.2;
r25.Position.X:=0.2;
r25.Position.Y:=-1.6;
r25.Position.Z:=-0.3;
r25.RotationAngle.X:=-180 div 3;
r25.RotationAngle.Z:=180 div 6;
r25.MaterialSource:=LightMaterialSource1;
r25.MaterialBackSource:=LightMaterialSource1;
r25.MaterialShaftSource:=LightMaterialSource1;
r26:=TRectangle3D.Create(self);
r26.Parent:=d4;
r26.Width:=0.2;
r26.Height:=0.8;
r26.Depth:=0.2;
r26.Position.X:=-0.5;
r26.Position.Y:=-1.6;
r26.Position.Z:=-0.3;
r26.RotationAngle.X:=-180 div 3;
r26.RotationAngle.Z:=180 div 6;
r26.MaterialSource:=LightMaterialSource1;
r26.MaterialBackSource:=LightMaterialSource1;
r26.MaterialShaftSource:=LightMaterialSource1;
//バット
dBat:=TDummy.Create(self);
dBat.Parent:=Viewport3D1;
dBat.Position.X:=-1.2;
dBat.Position.Y:=-1.45;
dBat.Position.Z:=-46;
s2:=TSphere.Create(self);
s2.Parent:=dBat;
s2.Width:=0.3;
s2.Height:=0.1;
s2.Depth:=0.3;
s2.MaterialSource:=LightMaterialSource2;
c1:=TCone.Create(self);
c1.Parent:=dBat;
c1.Width:=0.16;
c1.Height:=1.6;
c1.Depth:=0.16;
c1.Position.X:=0;
c1.Position.Y:=-0.75;
c1.Position.Z:=0;
c1.RotationAngle.X:=180;
c1.MaterialSource:=LightMaterialSource2;
//ヒットかホームラン時に使用するボールの速度ベクトル
VBall.X:=0;
VBall.Y:=0;
VBall.Z:=0;
Step:=0;
BStep:=0;
//ストライクかヒットかホームランか
HitFlag:=0;
end;
procedure TForm1.SetCameraPos;
begin
Camera.Position.X:=CamPos[CamId][0];
Camera.Position.Y:=CamPos[CamId][1];
Camera.Position.Z:=CamPos[CamId][2];
Camera.ResetRotationAngle;
Camera.RotationAngle.X:=CamAngle[CamId][0];
Camera.RotationAngle.Y:=CamAngle[CamId][1];
Camera.RotationAngle.Z:=CamAngle[CamId][2];
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var xx:Integer;
begin
if (HitFlag=0) or (HitFlag=1) then
begin
if Step<100 then
begin
//何もしない
end
else if Step<120 then
begin
d2.RotationAngle.X:=d2.RotationAngle.X+180/20;
d3.RotationAngle.X:=d3.RotationAngle.X+180/20;
end
else if Step<140 then
begin
d2.RotationAngle.X:=d2.RotationAngle.X+180/20;
d3.Position.Z:=d3.Position.Z-0.3;
d3.Position.Y:=d3.Position.Y+0.012;
disk.Position.Z:=d3.Position.Z;
end
else if Step<=250 then
begin
d2.ResetRotationAngle;
d3.Position.Z:=d3.Position.Z-0.3;
d3.Position.Y:=d3.Position.Y+0.012;
disk.Position.Z:=d3.Position.Z;
end
else if Step<=320 then
begin
d2.ResetRotationAngle;
d3.Position.Z:=d3.Position.Z-0.3;
d3.Position.Y:=d3.Position.Y+0.012;
disk.Position.Z:=d3.Position.Z;
if HitFlag=0 then
begin
HitFlag:=1;
end;
end
else
begin
d2.ResetRotationAngle;
d3.ResetRotationAngle;
d3.Position.X:=BallPosition.X;
d3.Position.Y:=BallPosition.Y;
d3.Position.Z:=BallPosition.Z;
disk.Position.Z:=d3.Position.Z;
Step:=0;
HitFlag:=0;
end;
end
else if (HitFlag=2) or (HitFlag=3) then
begin
VBall.Y:=VBall.Y+0.005;
d3.Position.X:=d3.Position.X+VBall.X;
d3.Position.Y:=d3.Position.Y+VBall.Y;
d3.Position.Z:=d3.Position.Z+VBall.Z;
disk.Position.X:=d3.Position.X;
disk.Position.Z:=d3.Position.Z;
if d3.Position.Y>0.5 then
begin
d3.Position.Y:=0.5;
VBall.X:=VBall.X*0.8;
VBall.Y:=-VBall.Y*0.5;
VBall.Z:=VBall.Z*0.8;
end;
if Step>300 then
begin
HitFlag:=0;
Step:=0;
d2.ResetRotationAngle;
d3.ResetRotationAngle;
d3.Position.X:=BallPosition.X;
d3.Position.Y:=BallPosition.Y;
d3.Position.Z:=BallPosition.Z;
disk.Position.X:=d3.Position.X;
disk.Position.Z:=d3.Position.Z;
camera.Target:=nil;
Label1.Text:='';
end;
end;
inc(Step);
if BStep=0 then
begin
dBat.ResetRotationAngle;
end
else
begin
if BStep<7 then
begin
xx:=BStep;
end
else
begin
xx:=6;
if (BStep=7) and ((HitFlag<>2) and (HitFlag<>3)) then
begin
if(d3.Position.Z<-45.7) and (d3.Position.Z>-46.3) then
begin
camera.Target:=d3;
HitFlag:=3;
VBall.X:=-(d3.Position.Z+50)/20;
VBall.Y:=-0.5;
VBall.Z:=0.5;
Step:=0;
Label1.Text:='ホームラン';
end
else if(d3.Position.Z<-44.2) and (d3.Position.Z>-47.8) then
begin
camera.Target:=d3;
HitFlag:=2;
VBall.X:=-(d3.Position.Z+46)/40;
VBall.Y:=-0.3;
VBall.Z:=0.4;
Step:=0;
Label1.Text:='ヒット';
end
else
begin
HitFlag:=1;
Label1.Text:='ストライク';
end;
end;
end;
dBat.ResetRotationAngle;
dBat.RotationAngle.X:=180/2/6*xx;
dBat.RotationAngle.Z:=180/2/6*BStep;
inc(BStep);
if BStep>18 then BStep:=0;
end;
end;
end.
実行ボタンを押す
実行ボタンを押すと、コンパイルして起動します。
「Button1」をクリックすると、バットを振ります。
「Button2」~「Button5」をクリックすると視点が変わります。
