人型の3Dメッシュ(TMesh)をキーボード操作に応答してアニメーション表示(FMX) ~Delphiソースコード集
キーボードの上矢印↑キーで前進、左矢印←キーで左回転、右矢印→キーで左回転、スペースキーでジャンプする、 以下のようなアプリケーションを作成します。
Delphiを起動して新規作成を行い、必要なコンポーネントをドラッグ&ドロップする
Delphi起動⇒ファイル⇒新規作成⇒マルチデバイスアプリケーションを選択し、「空のアプリケーション」を選択してOKボタンをクリックします。「TPanel」をフォームにドラッグ&ドロップし、プロパティ「Align」を[Top]に設定します。
Panel1に「TSwitch」をドラッグ&ドロップします。
「TViewport3D」をフォームにドラッグ&ドロップし、プロパティ「Align」を[Client]に設定します。
TTimerをフォームへドラッグ&ドロップします。
ユニットの追加(Unit2)
「ファイル」⇒「新規作成」⇒「ユニット Delphi」から新しいユニットを作成し、以下のソースコードを記述します。
unit Unit2;
interface
uses
System.Classes,System.SysUtils,
FMX.Types,FMX.Forms,
FMX.Types3D, System.Math.Vectors,
FMX.Objects3D, FMX.MaterialSources,
FMX.Controls.Presentation, FMX.StdCtrls, FMX.Controls3D,
FMX.Viewport3D
;
type
T3DState=(Normal,Walk,Jump);
T3DActionAngle=record
Step,LegLN,LegRN,LegLT,LegRT,ArmLE,ArmRE,ArmLH,ArmRH,BodyY,Angle,x,z:Single;
end;
T3DActionAngles=array of T3DActionAngle;
P3DActionAngles=^T3DActionAngles;
TBase3D=class(TObject)
private
FParent:TFMXObject;
FDBase:TDummy;//全体
FDArmLE:TDummy;//左肩から肘
FDArmRE:TDummy;
FDArmLH:TDummy;//右肘から
FDArmRH:TDummy;
FDLegLN:TDummy;//ももから
FDLegRN:TDummy;
FDLegLT:TDummy;//膝から
FDLegRT:TDummy;
FBody:TMesh; //胴体
FHead:TMesh; //頭
FArmLE:TMesh;//L肩から肘
FArmRE:TMesh;//R肩から肘
FArmLH:TMesh;//L肘から手
FArmRH:TMesh;//R肘から手
FLegLN:TMesh;//Lももから膝
FLegRN:TMesh;//Rももから膝
FLegLT:TMesh;//L膝からつま先
FLegRT:TMesh;//R膝からつま先
FDisk:TDisk;
FCamera:TCamera;
FMate:TLightMaterialSource;
FDir:Single;
FState:T3DState;
FNormal:T3DActionAngles;
FWalk:T3DActionAngles;
FJump:T3DActionAngles;
FOldAct:T3DActionAngle;
FNowAct:T3DActionAngle;//現在のアクション
{
ボーン構成(TDummyはボールジョイント)
FCamera
│
(TDummy) FHead
FDBase ───────┐ │
│ │
│ │
(TDummy) │ │ (TDummy)
FArmLE ─ FDArmLE ─ FBody ─ FDArmRE ─ FArmRE
│ │ │
(TDummy) │ (TDummy)
FDArmLH │ FDArmRH
│ │ │
FArmLH │ FArmLH
(TDummy) │ (TDummy)
FDLegLN─┴─FDLegRN
│ │
FLegLN FLegRN
│ │
(TDummy) (TDummy)
FDLegLT FDLegRT
│ │
FLegLT FLegRT
X:→ Y:↓ Z:奥行き方向が正
変更したければTDummyをTViewport3Dに配置し、
Scale.Yを-1(Y:↑方向になる)にたり、
Scale.Zを-1(手前方向が正となる)してローカル座標空間を作り、
配置する全てのオブジェクトの親をこのTDummuとすればよい
FDBase
RotationAngle.Y 方位
FDArmLE FDArmRE
RotationAngle.X >0で肩前方向回転
FDArmLH FDArmLH
RotationAngle.X >0で肘前方向回転(<0はない)
FDLegLN FDLegLN
RotationAngle.X >0で太もも前方向回転
FDLegLT FDLegLT
RotationAngle.X <0で膝を曲げる(>0はない)
}
procedure SetAction();
procedure OriginalRender(Sender:TObject;Context:TContext3D);
public
Constructor Create(parent:TFMXObject);
Destructor Destroy();override;
procedure Action(state:T3DState;RelativeAngle,Stride:Single);
property State:T3DState read FState;
property Camera:TCamera read FCamera;
end;
implementation
{ TBase3D }
uses Unit1;
procedure TBase3D.Action(state:T3DState;RelativeAngle,Stride:Single);
var PAct:P3DActionAngles;
i,l:Integer;
rate,deg:Single;
begin
{
FMX.Ani.TAnimator.AnimateFloatDelay
を使いたいが、RotationAngle.XYZで使用すると想定外の方向に回転してしまう
Delphi XE10.2 Tokyoの場合、
RotationAngle.X:=- 30に設定しても、
RotationAngle.X:=+330に変わっている場合があるのが原因!!!
自力でソースを記述する
}
if (FState<>state) then
begin
if(FState=T3DState.Jump)and((FNowAct.Step+0.1)<(FJump[length(FJump)-1].Step)) then
begin
FNowAct.Step:=FNowAct.Step+0.1;
end
else
begin
FOldAct:=FNowAct;
FNowAct.Step:=0;
FState:=state;
FOldAct.Step:=0;
end;
end
else
begin
if Stride<>0 then
FNowAct.Step:=FNowAct.Step+Abs(Stride/2)
else
FNowAct.Step:=FNowAct.Step+0.1;
end;
if FState=T3DState.Walk then
PAct:=@FWalk
else if FState=T3DState.Jump then
PAct:=@FJump
else
PAct:=@FNormal;
FNowAct.Angle:=FNowAct.Angle+RelativeAngle;
FNowAct.x:=FNowAct.x+cos((FNowAct.Angle-90)/180*PI)*Stride;
FNowAct.z:=FNowAct.z-sin((FNowAct.Angle-90)/180*PI)*Stride;
l:=Length(PAct^);
if FNowAct.Step<=((PAct^)[0].Step) then
begin
rate:=FNowAct.Step/(PAct^)[0].Step;
deg:=FOldAct.LegLN*(1-rate)+(PAct^)[0].LegLN*rate;
FNowAct.LegLN:=deg;
deg:=FOldAct.LegRN*(1-rate)+(PAct^)[0].LegRN*rate;
FNowAct.LegRN:=deg;
deg:=FOldAct.LegLT*(1-rate)+(PAct^)[0].LegLT*rate;
FNowAct.LegLT:=deg;
deg:=FOldAct.LegRT*(1-rate)+(PAct^)[0].LegRT*rate;
FNowAct.LegRT:=deg;
deg:=FOldAct.ArmLE*(1-rate)+(PAct^)[0].ArmLE*rate;
FNowAct.ArmLE:=deg;
deg:=FOldAct.ArmRE*(1-rate)+(PAct^)[0].ArmRE*rate;
FNowAct.ArmRE:=deg;
deg:=FOldAct.ArmLH*(1-rate)+(PAct^)[0].ArmLH*rate;
FNowAct.ArmLH:=deg;
deg:=FOldAct.ArmRH*(1-rate)+(PAct^)[0].ArmRH*rate;
FNowAct.ArmRH:=deg;
deg:=FOldAct.BodyY*(1-rate)+(PAct^)[0].BodyY*rate;
FNowAct.BodyY:=deg;
end
else
begin
if FNowAct.Step>(PAct^)[l-1].Step then
begin
if FState=T3DState.Normal then
FNowAct.Step:=(PAct^)[0].Step
else
begin
if FState<>T3DState.Walk then
begin
FState:=T3DState.Normal;
FNowAct.Step:=0;
end
else
begin
FNowAct.Step:=(PAct^)[0].Step;
end;
end;
end
else
begin
i:=0;
while i<(l-1) do
begin
if (FNowAct.Step>=(PAct^)[i].Step) and (FNowAct.Step<=(PAct^)[i+1].Step) then
break;
inc(i);
end;
rate:=(FNowAct.Step-(PAct^)[i].Step)/((PAct^)[i+1].Step-(PAct^)[i].Step);
deg:=(PAct^)[i].LegLN*(1-rate)+(PAct^)[i+1].LegLN*rate;
FNowAct.LegLN:=deg;
deg:=(PAct^)[i].LegRN*(1-rate)+(PAct^)[i+1].LegRN*rate;
FNowAct.LegRN:=deg;
deg:=(PAct^)[i].LegLT*(1-rate)+(PAct^)[i+1].LegLT*rate;
FNowAct.LegLT:=deg;
deg:=(PAct^)[i].LegRT*(1-rate)+(PAct^)[i+1].LegRT*rate;
FNowAct.LegRT:=deg;
deg:=(PAct^)[i].ArmLE*(1-rate)+(PAct^)[i+1].ArmLE*rate;
FNowAct.ArmLE:=deg;
deg:=(PAct^)[i].ArmRE*(1-rate)+(PAct^)[i+1].ArmRE*rate;
FNowAct.ArmRE:=deg;
deg:=(PAct^)[i].ArmLH*(1-rate)+(PAct^)[i+1].ArmLH*rate;
FNowAct.ArmLH:=deg;
deg:=(PAct^)[i].ArmRH*(1-rate)+(PAct^)[i+1].ArmRH*rate;
FNowAct.ArmRH:=deg;
deg:=(PAct^)[i].BodyY*(1-rate)+(PAct^)[i+1].BodyY*rate;
FNowAct.BodyY:=deg;
end;
end;
SetAction();
end;
constructor TBase3D.Create(parent: TFMXObject);
var PointsStr,TriangleIndicesStr:String;
BodyStandY:Single;
begin
FDir:=0;
FState:=T3DState.Normal;
FParent:=parent;
FMate:=TLightMaterialSource.Create(nil);
FMate.Parent:=nil;
FMate.Ambient:=$FF303030;
FMate.Diffuse:=$FFC0C0C0;
FMate.Specular:=$FF606060;
FMate.Shininess:=40;
FDBase:=TDummy.Create(nil);
FDBase.Parent:=FParent;
FCamera:=TCamera.Create(nil);
FCamera.Parent:=FDBase;
FCamera.Position.X:=0;
FCamera.Position.Y:=-9;
FCamera.Position.Z:=-16;
FCamera.RotationAngle.X:=-20;
//位置を示すためのディスク(円)を表示する場合
FDisk:=TDisk.Create(nil);
FDisk.Parent:=FDBase;
FDisk.Width:=4;
FDisk.Depth:=4;
FDisk.Opacity:=0;//プリミティブ自体は表示しない
FDisk.OnRender:=OriginalRender;//独自関数で表示させる
//■点の座標
//X:→方向 Y:↓方向 Z:奥行き方向 で指定
PointsStr:=
'-1 -1 1 ,1 -1 1 ,-1 1 1 ,1 1 1 ,-1 -1 -1 ,1 -1 -1 ,-1 1 -1 ,1 1 -1';
//0左上奥 1右上奥 2左下奥 3右下奥 4左上前 5右上前 6左下前 7右下前
//■3角形ポリゴンを構成する3点を指定し、ポリゴン12個で構成する立方体メッシュ
// 奥 前
// 01 45
// 23 67
// 奥面 前面 左面 右面 上面 下面
// 10 103 45 456 04 042 51 517 01 014 67 672
// 32 302 67 657 26 246 73 713 45 415 23 273
TriangleIndicesStr:=
'1 0 3, 3 0 2, 4 5 6, 6 5 7, 0 4 2, 2 4 6, '+
'5 1 7, 7 1 3, 0 1 4, 4 1 5, 6 7 2, 2 7 3';
FBody:=TMesh.Create(nil);
FBody.Parent:=FDBase;
//点座標を指定
FBody.Data.Points:=PointsStr;
//参考:テクスチャを貼る場合(各点座標へのテクスチャのXY座標割当)
//FBody.Data.TexCoordinates:=
// '1 0 ,0 0 ,1 1 ,0 1 ,0 0 ,1 0 ,0 1 ,1 1';
// 左上奥 右上奥 左下奥 右下奥 左上前 右上前 左下前 右下前
//3角形12個で構成される立方体メッシュを指定
FBody.Data.TriangleIndices:=TriangleIndicesStr;
//法線ベクトルの自動計算
FBody.Data.CalcSmoothNormals();
//TMeshWrapMode.Fit
// TMeshのWidth,Height,Depthの中に収まるようにフィット
// 比率は保たれ、原点は自動計算される
//TMeshWrapMode.Original
// TMeshのWidth,Height,Depth倍された大きさになり、原点は0,0,0
//TMeshWrapMode.Resize
// 基本はFitと同じだが原点は0,0,0
//TMeshWrapMode.Stretch
// TMeshのWidth,Height,Depthの中に収まるようにストレッチされ
// 比率は保たれない、原点は自動計算される
FBody.WrapMode:=TMeshWrapMode.Original;
FBody.Height:=1;
FBody.Width:=1;
FBody.Depth:=0.8;
FBody.HitTest:=False;
FBody.MaterialSource:=FMate;
BodyStandY:=-1-0.6*2-0.8*2; //FBody.Yの立っている時の値
FBody.Position.Y:=BodyStandY;
FHead:=TMesh.Create(nil);
FHead.Parent:=FBody;
FHead.Data.Points:=PointsStr;
FHead.Data.TriangleIndices:=TriangleIndicesStr;
FHead.Data.CalcSmoothNormals();
FHead.WrapMode:=TMeshWrapMode.Original;
FHead.Height:=0.4;
FHead.Width:=0.4;
FHead.Depth:=0.4;
FHead.HitTest:=False;
FHead.MaterialSource:=FMate;
FHead.Position.Y:=-1.5;
FDArmLE:=TDummy.Create(nil);//左肩
FDArmLE.Parent:=FBody;
FDArmLE.Position.X:=-1.3;
FDArmLE.Position.Y:=-0.8;
FDArmLE.RotationAngle.Z:=8;//左方向が正
FDArmLE.RotationAngle.X:=0;//上方向が正に向く
FArmLE:=TMesh.Create(nil);
FArmLE.Parent:=FDArmLE;
FArmLE.Data.Points:=PointsStr;
FArmLE.Data.TriangleIndices:=TriangleIndicesStr;
FArmLE.Data.CalcSmoothNormals();
FArmLE.WrapMode:=TMeshWrapMode.Original;
FArmLE.Height:=0.6;
FArmLE.Width:=0.3;
FArmLE.Depth:=0.3;
FArmLE.HitTest:=False;
FArmLE.MaterialSource:=FMate;
FArmLE.Position.Y:=0.6;
FDArmLH:=TDummy.Create(nil);//左ひじ
FDArmLH.Parent:=FArmLE;
FDArmLH.Position.Y:=0.6;
FDArmLH.RotationAngle.X:=30;//上方向が正
FArmLH:=TMesh.Create(nil);
FArmLH.Parent:=FDArmLH;
FArmLH.Data.Points:=PointsStr;
FArmLH.Data.TriangleIndices:=TriangleIndicesStr;
FArmLH.Data.CalcSmoothNormals();
FArmLH.WrapMode:=TMeshWrapMode.Original;
FArmLH.Height:=0.6;
FArmLH.Width:=0.3;
FArmLH.Depth:=0.3;
FArmLH.HitTest:=False;
FArmLH.MaterialSource:=FMate;
FArmLH.Position.Y:=0.6;
FDArmRE:=TDummy.Create(nil);//右肩
FDArmRE.Parent:=FBody;
FDArmRE.Position.X:=1.3;
FDArmRE.Position.Y:=-0.8;
FDArmRE.RotationAngle.Z:=-8;//左方向が正
FDArmRE.RotationAngle.X:=-0;//上方向が正に向く
FArmRE:=TMesh.Create(nil);
FArmRE.Parent:=FDArmRE;
FArmRE.Data.Points:=PointsStr;
FArmRE.Data.TriangleIndices:=TriangleIndicesStr;
FArmRE.Data.CalcSmoothNormals();
FArmRE.WrapMode:=TMeshWrapMode.Original;
FArmRE.Height:=0.6;
FArmRE.Width:=0.3;
FArmRE.Depth:=0.3;
FArmRE.HitTest:=False;
FArmRE.MaterialSource:=FMate;
FArmRE.Position.Y:=0.6;
FDArmRH:=TDummy.Create(nil);//右ひじ
FDArmRH.Parent:=FArmRE;
FDArmRH.Position.Y:=0.6;
FDArmRH.RotationAngle.X:=30;//上方向が正
FArmRH:=TMesh.Create(nil);
FArmRH.Parent:=FDArmRH;
FArmRH.Data.Points:=PointsStr;
FArmRH.Data.TriangleIndices:=TriangleIndicesStr;
FArmRH.Data.CalcSmoothNormals();
FArmRH.WrapMode:=TMeshWrapMode.Original;
FArmRH.Height:=0.6;
FArmRH.Width:=0.3;
FArmRH.Depth:=0.3;
FArmRH.HitTest:=False;
FArmRH.MaterialSource:=FMate;
FArmRH.Position.Y:=0.6;
FDLegLN:=TDummy.Create(nil);
FDLegLN.Parent:=FBody;
FDLegLN.Position.X:=-0.5;
FDLegLN.Position.Y:=1.0;
FDLegLN.RotationAngle.X:=-0;
FDLegLN.RotationAngle.Z:=8;//外(左)に
FLegLN:=TMesh.Create(nil);
FLegLN.Parent:=FDLegLN;
FLegLN.Data.Points:=PointsStr;
FLegLN.Data.TriangleIndices:=TriangleIndicesStr;
FLegLN.Data.CalcSmoothNormals();
FLegLN.WrapMode:=TMeshWrapMode.Original;
FLegLN.Height:=0.6;
FLegLN.Width:=0.4;
FLegLN.Depth:=0.4;
FLegLN.HitTest:=False;
FLegLN.MaterialSource:=FMate;
FLegLN.Position.Y:=0.6;
FDLegLT:=TDummy.Create(nil);
FDLegLT.Parent:=FLegLN;
FDLegLT.Position.X:=0;
FDLegLT.Position.Y:=0.6;
FDLegLT.RotationAngle.X:=-0; //マイナス方向のみ
FLegLT:=TMesh.Create(nil);
FLegLT.Parent:=FDLegLT;
FLegLT.Data.Points:=PointsStr;
FLegLT.Data.TriangleIndices:=TriangleIndicesStr;
FLegLT.Data.CalcSmoothNormals();
FLegLT.WrapMode:=TMeshWrapMode.Original;
FLegLT.Height:=0.8;
FLegLT.Width:=0.4;
FLegLT.Depth:=0.4;
FLegLT.HitTest:=False;
FLegLT.MaterialSource:=FMate;
FLegLT.Position.Y:=0.8;
FDLegRN:=TDummy.Create(nil);
FDLegRN.Parent:=FBody;
FDLegRN.Position.X:=0.5;
FDLegRN.Position.Y:=1.0;
FDLegRN.RotationAngle.X:=0;
FDLegRN.RotationAngle.Z:=-8;//外(右)に
FLegRN:=TMesh.Create(nil);
FLegRN.Parent:=FDLegRN;
FLegRN.Data.Points:=PointsStr;
FLegRN.Data.TriangleIndices:=TriangleIndicesStr;
FLegRN.Data.CalcSmoothNormals();
FLegRN.WrapMode:=TMeshWrapMode.Original;
FLegRN.Height:=0.6;
FLegRN.Width:=0.4;
FLegRN.Depth:=0.4;
FLegRN.HitTest:=False;
FLegRN.MaterialSource:=FMate;
FLegRN.Position.Y:=0.6;
FDLegRT:=TDummy.Create(nil);
FDLegRT.Parent:=FLegRN;
FDLegRT.Position.X:=0;
FDLegRT.Position.Y:=0.6;
FDLegRT.RotationAngle.X:=-0; //マイナス方向のみ
FLegRT:=TMesh.Create(nil);
FLegRT.Parent:=FDLegRT;
FLegRT.Data.Points:=PointsStr;
FLegRT.Data.TriangleIndices:=TriangleIndicesStr;
FLegRT.Data.CalcSmoothNormals();
FLegRT.WrapMode:=TMeshWrapMode.Original;
FLegRT.Height:=0.8;
FLegRT.Width:=0.4;
FLegRT.Depth:=0.4;
FLegRT.HitTest:=False;
FLegRT.MaterialSource:=FMate;
FLegRT.Position.Y:=0.8;
SetLength(FNormal,1);
FNormal[0].Step:=0.5;
FNormal[0].LegLN:=0;
FNormal[0].LegRN:=0;
FNormal[0].LegLT:=0;
FNormal[0].LegRT:=0;
FNormal[0].ArmLE:=0;
FNormal[0].ArmRE:=0;
FNormal[0].ArmLH:=0;
FNormal[0].ArmRH:=0;
FNormal[0].BodyY:=BodyStandY;
SetLength(FWalk,5);
FWalk[0]:=FNormal[0];
FWalk[0].Step:=0.001;
FWalk[1].Step:=0.5;
FWalk[1].LegLN:=-30;
FWalk[1].LegRN:=30;
FWalk[1].LegLT:=0;
FWalk[1].LegRT:=-30;
FWalk[1].ArmLE:=30;
FWalk[1].ArmRE:=-30;
FWalk[1].ArmLH:=60;
FWalk[1].ArmRH:=20;
FWalk[1].BodyY:=BodyStandY+0.1;
FWalk[2]:=FNormal[0];
FWalk[2].Step:=1.0;
FWalk[3].Step:=1.5;
FWalk[3].LegLN:=30;
FWalk[3].LegRN:=-30;
FWalk[3].LegLT:=-30;
FWalk[3].LegRT:=0;
FWalk[3].ArmLE:=-30;
FWalk[3].ArmRE:=30;
FWalk[3].ArmLH:=20;
FWalk[3].ArmRH:=60;
FWalk[3].BodyY:=BodyStandY+0.1;
FWalk[4]:=FNormal[0];
FWalk[4].Step:=2.0;
SetLength(FJump,5);
FJump[0].Step:=0.4;
FJump[0].LegLN:=45;
FJump[0].LegRN:=45;
FJump[0].LegLT:=-90;
FJump[0].LegRT:=-90;
FJump[0].ArmLE:=45;
FJump[0].ArmRE:=45;
FJump[0].ArmLH:=110;
FJump[0].ArmRH:=110;
FJump[0].BodyY:=BodyStandY+0.6;
FJump[1]:=FNormal[0];
FJump[1].Step:=FJump[0].Step+0.3;
FJump[1].BodyY:=BodyStandY;
FJump[2]:=FNormal[0];
FJump[2].Step:=FJump[1].Step+0.8;
FJump[2].BodyY:=BodyStandY-2;
FJump[3]:=FNormal[0];
FJump[3].Step:=FJump[2].Step+0.8;
FJump[3].BodyY:=BodyStandY;
FJump[4]:=FJump[0];
FJump[4].Step:=FJump[3].Step+0.3;
FNowAct.Step:=0;
FNowAct.LegLN:=0;
FNowAct.LegRN:=0;
FNowAct.LegLT:=0;
FNowAct.LegRT:=0;
FNowAct.ArmLE:=0;
FNowAct.ArmRE:=0;
FNowAct.ArmLH:=0;
FNowAct.ArmRH:=0;
FNowAct.BodyY:=BodyStandY;//FBodyのY
FNowAct.x:=0;//xz平面上の座標
FNowAct.z:=0;//xz平面上の座標
FNowAct.Angle:=0;
FOldAct:=FNowAct;
end;
destructor TBase3D.Destroy;
begin
FLegLT.Free;
FLegRT.Free;
FDLegLT.Free;
FDLegRT.Free;
FLegLN.Free;
FLegRN.Free;
FDLegLN.Free;
FDLegRN.Free;
FArmLH.Free;
FArmRH.Free;
FDArmLH.Free;
FDArmRH.Free;
FArmLE.Free;
FArmRE.Free;
FDArmLE.Free;
FDArmRE.Free;
FHead.Free;
FBody.Free;
FDisk.Free;
FMate.Free;
FCamera.Free;
FDBase.Free;
end;
procedure TBase3D.OriginalRender(Sender: TObject; Context: TContext3D);
var i:Integer;
col:TColorMaterialSource;
begin
Context.BeginScene;
{
Context.DrawLines(
TMesh(Sender).Data.VertexBuffer,
TMesh(Sender).Data.IndexBuffer,
FMate.Material, 1
);
}
//線を描画
for i:=0 to 35 do
begin
Context.DrawLine(
Point3D(
cos((i+0)/18*Pi)/TMesh(Sender).Width*2,
0,
sin((i+0)/18*Pi)/TMesh(Sender).Depth*2
),
Point3D(
cos((i+1)/18*Pi)/TMesh(Sender).Width*2,
0,
sin((i+1)/18*Pi)/TMesh(Sender).Depth*2
),
0.2,$FFFF0000
);
end;
{
//点を描画
Context.DrawPoints(
TMesh(Sender).Data.VertexBuffer,
TMesh(Sender).Data.IndexBuffer,
FMate.Material, 1
);
}
Context.EndScene;
end;
procedure TBase3D.SetAction;
begin
FDLegLN.RotationAngle.X:=FNowAct.LegLN;
FDLegRN.RotationAngle.X:=FNowAct.LegRN;
FDLegLT.RotationAngle.X:=FNowAct.LegLT;
FDLegRT.RotationAngle.X:=FNowAct.LegRT;
FDArmLE.RotationAngle.X:=FNowAct.ArmLE;
FDArmRE.RotationAngle.X:=FNowAct.ArmRE;
FDArmLH.RotationAngle.X:=FNowAct.ArmLH;
FDArmRH.RotationAngle.X:=FNowAct.ArmRH;
FBody.Position.Y:=FNowAct.BodyY;
FDBase.RotationAngle.Y:=FNowAct.Angle;
FDBase.Position.X:=FNowAct.x;
FDBase.Position.Z:=FNowAct.z;
end;
initialization
begin
//True:ハードウェアDirectXを使う、False:GDIを使う
FMX.Types.GlobalUseDX:=True;
//True:ソフトフェアDirectXを使用する、False:ソフトウェアDirextXを使わない
//FMX.Types.GlobalUseDXSoftware:=True;
//True:Direct2Dを利用する、False:GDI+を使う
FMX.Types.GlobalUseDirect2D:=True;
//GDI+を使う場合のみ
//True:クリアタイプレンダリングが有効、False:グレースケールアンチエイリアス処理
FMX.Types.GlobalUseGDIPlusClearType:=false;
//Trueだと処理が早くなるがフォーカス効果が無効になる
FMX.Types.GlobalDisableFocusEffect:=True;
//GPUキャンバスを使うかどうか
FMX.Types.GlobalUseGPUCanvas:=True;
end;
end.
Unit1のソースコードを記述する
Unit1に切り替えて、以下のソースコードを記述します。
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes,
FMX.Types, FMX.Controls, FMX.Forms, FMX.StdCtrls,
FMX.Viewport3D, FMX.Controls.Presentation ,FMX.Objects3D,
FMX.Controls3D, FMX.Types3D,
Winapi.Windows
,Unit2;
type
TForm1 = class(TForm)
Panel1: TPanel;
Viewport3D1: TViewport3D;
Switch1: TSwitch;
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Switch1Switch(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure Viewport3D1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
procedure Viewport3D1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Single);
procedure Viewport3D1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
private
{ private 宣言 }
HumanMesh:TBase3D;
Dummy:TDummy;
Camera:TCamera;
Grid3D:TGrid3D;
Light:TLight;
mf:Boolean;//マウス左ボタンを押しているか
mp:TPointF;//マウス座標
public
{ public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.FormCreate(Sender: TObject);
begin
Viewport3D1.Multisample:=TMultisample.None;
HumanMesh:=TBase3d.Create(Viewport3D1);
Grid3D:=TGrid3D.Create(self);
Grid3D.Parent:=Viewport3D1;
Grid3D.Width:=100;
Grid3D.Height:=100;
Grid3D.RotationAngle.X:=90;
Grid3D.Frequency:=4;
Grid3D.Marks:=8;
Grid3D.HitTest:=False;//マウスクリックに応答しない
Grid3D.LineColor:=$CC777777;
Dummy:=TDummy.Create(self);
Dummy.Parent:=Viewport3D1;
//カメラの作成
Camera:=TCamera.Create(self);
Camera.Parent:=Dummy;
Camera.Position.X:=0;
Camera.Position.Y:=-20;
Camera.Position.Z:=-50;
Camera.RotationAngle.X:=-30;
Light:=TLight.Create(self);
Light.Parent:=Viewport3D1;
Light.RotationAngle.X:=340;
Light.RotationAngle.Y:=330;
Switch1.IsChecked:=True;
Viewport3D1.Camera:=HumanMesh.Camera;
Viewport3D1.UsingDesignCamera:=False;
Timer1.Interval:=33;
Timer1.Enabled:=True;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if Assigned(HumanMesh) then
FreeAndNil(HumanMesh);
end;
procedure TForm1.Switch1Switch(Sender: TObject);
begin
if Switch1.IsChecked then
//メッシュに追従するカメラに切り替える
Viewport3D1.Camera:=HumanMesh.Camera
else
//フツーのカメラに切り替える
Viewport3D1.Camera:=Camera;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var k:SmallInt; //キーボード入力用
ra:Single; //進行方向
s:T3DState; //情愛
stride:Single;//歩行速度
begin
ra:=0;
s:=T3DState.Normal;
stride:=0;
if HumanMesh.State=T3DState.Jump then
begin
//跳躍中だったら操作は受け付けない
s:=T3DState.Normal;
end
else
begin
//跳躍
k:=GetKeyState(VK_SPACE);
if k<0 then
begin
s:=T3DState.Jump;
end
else
begin
//後退
k:=GetKeyState(VK_DOWN);
if k<0 then
begin
s:=T3DState.Walk;
stride:=-0.1;
end;
//前進
k:=GetKeyState(VK_UP);
if k<0 then
begin
s:=T3DState.Walk;
stride:=0.2;
k:=GetKeyState(VK_CONTROL);
if k<0 then
stride:=stride+0.2;
end;
//左回転
k:=GetKeyState(VK_LEFT);
if k<0 then
begin
s:=T3DState.Walk;
ra:=ra-5;
end;
//右回転
k:=GetKeyState(VK_RIGHT);
if k<0 then
begin
s:=T3DState.Walk;
ra:=ra+5;
end;
end;
end;
HumanMesh.Action(s,ra,stride);
end;
procedure TForm1.Viewport3D1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
//マウスの左ボタンを押した
mf:=True;
mp.X:=X;
mp.Y:=y;
end;
procedure TForm1.Viewport3D1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Single);
begin
if mf=false then exit;
Dummy.RotationAngle.Y:=
Dummy.RotationAngle.Y+(mp.X-x);
mp.x:=x;
mp.Y:=y;
end;
procedure TForm1.Viewport3D1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
mf:=False;
end;
end.
実行する
実行ボタンを押して実行します。(デバッグ実行でもOKです。)キーボードの上矢印↑キーで前進、左矢印←キーで左回転、右矢印→キーで左回転、スペースキーでジャンプします。
Switch1のオン・オフでカメラを切り替えることが出来ます。
