アニメーション

DynamicModule[{radius=0.3,pocketRadius=0.6,tableSize=10,friction=0.99,pos1={3,5},vel1={0.,0.},pos2={7,5},vel2={0.,0.},shotVec={1,0},pockets={{0,0},{10,0},{0,10},{10,10}},ball1In=False,ball2In=False},Manipulate[If[!ball1In,pos1+=vel1;vel1*=friction];
If[!ball2In,pos2+=vel2;vel2*=friction];
(*壁反射*)If[!ball1In,If[pos1[[1]]-radius<=0||pos1[[1]]+radius>=tableSize,vel1[[1]]=-vel1[[1]]];
If[pos1[[2]]-radius<=0||pos1[[2]]+radius>=tableSize,vel1[[2]]=-vel1[[2]]];];
If[!ball2In,If[pos2[[1]]-radius<=0||pos2[[1]]+radius>=tableSize,vel2[[1]]=-vel2[[1]]];
If[pos2[[2]]-radius<=0||pos2[[2]]+radius>=tableSize,vel2[[2]]=-vel2[[2]]];];
(*衝突処理*)Module[{d=pos1-pos2,dist=Norm[pos1-pos2]},If[dist<=2 radius&&!ball1In&&!ball2In,Module[{n=Normalize[d],vRel=vel1-vel2,vDotN,impulse},vDotN=vRel.n;
If[vDotN<0,impulse=n vDotN;
vel1-=impulse;
vel2+=impulse;
pos1=pos2+2 radius Normalize[pos1-pos2];]]];];
(*ポケット判定*)If[!ball1In&&AnyTrue[pockets,Norm[pos1-#]<pocketRadius&],ball1In=True;vel1={0.,0.};];
If[!ball2In&&AnyTrue[pockets,Norm[pos2-#]<pocketRadius&],ball2In=True;vel2={0.,0.};];
Graphics[{(*テーブルとポケット*)LightGreen,Rectangle[{0,0},{tableSize,tableSize}],Black,Disk[#,pocketRadius]&/@pockets,(*ショット矢印*)If[vel1=={0.,0.}&&!ball1In,{Red,Arrow[{pos1,pos1+shotVec}]},{}],(*球描画*)If[!ball1In,{Blue,Disk[pos1,radius]},{}],If[!ball2In,{Orange,Disk[pos2,radius]},{}]},PlotRange->{{-1,tableSize+1},{-1,tableSize+1}},ImageSize->400],{{shotVec,{1,0}},Locator,Appearance->"Arrow"},Button["Shoot",If[vel1=={0.,0.}&&!ball1In,vel1=0.5 Normalize[shotVec]]],Button["Reset",pos1={3,5};pos2={7,5};
vel1={0.,0.};vel2={0.,0.};
shotVec={1,0};
ball1In=False;ball2In=False;]]]