人型の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のオン・オフでカメラを切り替えることが出来ます。