・アニメーション(選択1)

簡易的なゴルフゲームを作成した。
マウスの左クリックで方向と強弱を決定し、カップインを目指す。
打ち直しボタンで再開でき、カップインまでの打数を記録する。

DynamicModule[{
phase="aim",
angle=Pi/4,
strength=1.0,
aimTheta=0,
powerPhase=0,
ball={0.1,0.1},
trajectory={},
t=0,
velocity={0,0},
g=0.002,
flag={1.7,0.1},
swingFrame=0,
onGround=False,
rollingVelocity=0,
swingDuration=40,
followDuration=20,
humanHeight=0.08,
clubLength=0.05,
cupSize=0.015,
shotCount=0,
ballInCup=False,
finalShotCount=0,
distThreshold=0.005,
speedThreshold=0.0003},

Column[{Dynamic[EventHandler[Graphics[Module[{aimArrow={},powerBar={},shotBall={},swingMan={},armAngle,
restitution=0.4,
friction=0.85,
humanBase,skyElements,clubEnd,shoulder,speed},humanBase={0.1,0.1};

shoulder=humanBase+{0,humanHeight*0.8};

(*クラブスイング動作*)
armAngle=Switch[phase,"aim"|"power"|"shot",-Pi/3,"swing",If[swingFrame<=swingDuration,-Pi/3-(2 Pi/3) (swingFrame/swingDuration),-Pi/3-(2 Pi/3)+(Pi/3) Min[(swingFrame-swingDuration)/followDuration,1]],_,-Pi/3];

clubEnd=shoulder+clubLength {Cos[armAngle],Sin[armAngle]};

If[phase==="done",swingFrame=0;
velocity={0,0};
rollingVelocity=0;
onGround=True;];

If[phase==="swing",swingFrame++;

If[swingFrame==swingDuration,velocity=0.025*strength {Cos[angle],Sin[angle]};
trajectory={};
ball=humanBase;
onGround=False;
rollingVelocity=0;
ballInCup=False;
shotCount++;
phase="shot";
t=0;];

If[swingFrame>swingDuration+followDuration,swingFrame=0;];,If[swingFrame!=0,swingFrame=0];];
Switch[phase,"aim",aimTheta+=0.05;
angle=Pi/4+0.3 Sin[aimTheta];
aimArrow={Red,Arrow[{humanBase+{0,humanHeight/4},humanBase+{0.25 Cos[angle],humanHeight/4+0.25 Sin[angle]}}]};,"power",powerPhase+=0.08;
strength=1+Sin[powerPhase];

powerBar={LightGray,Rectangle[{0.3,0.35},{0.7,0.38}],Red,Rectangle[{0.3,0.35},{0.3+0.4 strength/2,0.38}]};,"shot",t++;

(*ショット後のボールの運動*)
If[!onGround,ball=ball+velocity;
velocity=velocity+{0,-g};
If[ball[[2]]<=0.1,ball[[2]]=0.1;
velocity[[2]]=-velocity[[2]] restitution;
If[Abs[velocity[[2]]]<0.005,onGround=True;
rollingVelocity=velocity[[1]] friction;
velocity={0,0};];],rollingVelocity=rollingVelocity friction;
ball={ball[[1]]+rollingVelocity,0.1};

If[Abs[rollingVelocity]<0.001,rollingVelocity=0];];

AppendTo[trajectory,ball];
shotBall={Orange,PointSize[Medium],Point[ball]};
speed=Norm[velocity];

(*カップイン判定*)
If[!ballInCup&&Norm[ball-flag]<distThreshold&&speed<speedThreshold,ballInCup=True;
finalShotCount=shotCount;
shotCount=0;
phase="done";];,"done",Null];

(*ゲーム背景・描画*)
swingMan={RGBColor[0.2,0.4,0.8],Thick,Line[{humanBase,humanBase+{0,humanHeight}}],Line[{shoulder,clubEnd}],FaceForm[White],EdgeForm[Black],Disk[humanBase+{0,humanHeight*1.15},humanHeight/6],Red,Rectangle[humanBase+{-0.015,humanHeight*1.20},humanBase+{0.015,humanHeight*1.23}],Gray,Polygon[{clubEnd,clubEnd+0.01 RotationTransform[-Pi/4][{1,0}],clubEnd+0.01 RotationTransform[-Pi/4][{1,-0.2}],clubEnd+0.005 RotationTransform[-Pi/4][{0,-1}]}]};

skyElements={LightBlue,Rectangle[{0,0.1},{2,0.7}],Yellow,Disk[{1.8,0.6},0.05],White,Opacity[0.8],Disk[{0.5,0.6},0.03],Disk[{0.53,0.62},0.035],Disk[{0.57,0.6},0.03],Disk[{1.0,0.5},0.025],Disk[{1.03,0.52},0.03],Disk[{1.06,0.5},0.025]};

skyElements~Join~{Rectangle[{0,0.7},{2,1.2}],White,Table[Disk[{x,0.95+0.02 Sin[x*10]},0.03],{x,0.2,1.8,0.2}],Disk[{1.8,1.05},0.05],Green,Rectangle[{0,0},{2,0.1}],swingMan,Black,Disk[flag,cupSize],Line[{flag,flag+{0,cupSize*8}}],Red,Polygon[{flag+{0,cupSize*8},flag+{cupSize*2.5,cupSize*6.5},flag+{0,cupSize*5}}],Orange,PointSize[Medium],Point[ball],Orange,PointSize[Small],Point[trajectory],aimArrow,powerBar,shotBall,If[phase==="done",Style[Text["⛳イン！\n打数: "<>ToString[finalShotCount],{1,0.4}],20,Bold,Darker[Green]],{}]}],

PlotRange->{{0,2},{0,0.7}},ImageSize->700,PlotLabel->Style[Switch[phase,"aim","🧭クリックして方向を決定","power","💪クリックして強さを決定","swing","🏌️‍♂️スイング中！","shot","⛳ボールが飛んでいます","done","カップイン！'打ち直す'で再プレイ",_,""],16,Bold]],{"MouseDown":>(If[!ballInCup&&phase=!="done",Switch[phase,"aim",phase="power","power",phase="swing"]])}]],Row[{If[phase==="done",Button["🎮もう一度遊ぶ",(phase="aim";
shotCount=0;
t=0;
aimTheta=0;
powerPhase=0;
ball={0.1,0.1};
trajectory={};
onGround=False;
rollingVelocity=0;
swingFrame=0;
ballInCup=False;
finalShotCount=0;),ImageSize->Medium],Null],Button["🔁打ち直す",(phase="aim";
t=0;
aimTheta=0;
powerPhase=0;
ball={0.1,0.1};
trajectory={};
onGround=False;
rollingVelocity=0;
swingFrame=0;
ballInCup=False;),ImageSize->Medium],Spacer[30],Dynamic[Style["打数: "<>ToString[If[phase==="done",finalShotCount,shotCount]],16,Bold]]}]}]]