YAS's VB.NET Tips
 
Pythonista活用
Pythonista活用
2019/01/18

バウンドするボール

Tweet ThisSend to Facebook | by:YAS
 Pythonista3の練習作1つ目。
「円が衝突したときの動き」「当たり判定の後に,円が接するところまで戻す」を使って,円が衝突しながら画面上を動き回るサンプルコード。
 各円にはランダムに初期座標と初期速度ベクトルを与える。
 各円は同じ質量で,摩擦はなく,反発係数を1とする。
 反発係数が1なので,ずっと動き続ける。

 昔のBio_100%のモグラーとか,ポイみたいなゲームが比較的簡単に作れそう。確かにPythonって短いコードでいろいろできますね。デバッグ時に,ほんの数行追加するだけで,matplotlibで座標の変化をグラフ化して見たり,sympyで二次方程式の解があっているか確認したりできました。タプルも最初は戸惑ったけれど,コードがシンプルになり,とても便利です。

 以下のコードをPythonista3にコピー&ペーストすれば動作します。


23:59 | 投票する | 投票数(1) | コメント(0)
2019/01/18

速い平方根の求め方

Tweet ThisSend to Facebook | by:YAS
 Pythonで実数n(n≧0)の平方根を求めるには,n**(1/2),math.sqrt(n),numpy.sqrt(n),pow(n,1/2)などがある。答えの有効桁数や,nを負数にした場合の挙動などに違いが見られるが,基本的には同じ答えが得られる。
import time
import math
import numpy as np

for n in range(10):
  
print(n+1,'回戦')
  t1
=time.time()
  
for i in range(1000000):
    p
=i**(1/2)
  t2
=time.time()
  
print(t2-t1)

  t1
=time.time()
  
for i in range(1000000):
    p
=math.sqrt(i)
  t2
=time.time()
  
print(t2-t1)

  t1
=time.time()
  
for i in range(1000000):
    p
=pow(i,1/2)
  t2
=time.time()
  
print(t2-t1)

  t1
=time.time()
  
for i in range(1000000):
    p
=np.sqrt(i)
  t2
=time.time()
  
print(t2-t1)

 上のコードをPythonista3とJupyter Notebookで実行したところ,n**(1/2)math.sqrt(n)がほぼ同じで速く,pow(n,1/2)が少し遅く,numpy.sqrt(n)がかなり遅いという結果になりました。(numpy.sqrt()は配列やリストなど複数の数をまとめて処理させると速いそうです。)
 Pythonista3(iPad 2017モデル) Jupyter Notebook
1 回戦
0.25328826904296875  ← n**(1/2)
0.2289888858795166   ← math.sqrt(n)
0.3298521041870117   ← pow(n,1/2)
5.7376227378845215   ← numpy.sqrt(n)
2 回戦
0.2213280200958252
0.22719120979309082
0.3298790454864502
5.751811981201172
3 回戦
0.22010588645935059
0.2246711254119873
0.3332500457763672
5.7477171421051025
4 回戦
0.2227330207824707
0.22210407257080078
0.3330090045928955
5.739731788635254
5 回戦
0.22470712661743164
0.2236800193786621
0.33375072479248047
5.742916107177734
6 回戦
0.22667884826660156
0.2206406593322754
0.33280515670776367
5.731882095336914
7 回戦
0.22341084480285645
0.2242288589477539
0.33399105072021484
5.738736867904663
8 回戦
0.22586584091186523
0.22838401794433594
0.3287699222564697
5.748663902282715
9 回戦
0.21982312202453613
0.22762703895568848
0.330125093460083
5.741748094558716
10 回戦
0.2216320037841797
0.2248692512512207
0.3309180736541748
5.735345840454102
 1 回戦
0.30855536460876465
0.38390588760375977
0.6192374229431152
1.7245447635650635
2 回戦
0.28778696060180664
0.243882417678833
0.48995542526245117
1.57002854347229
3 回戦
0.2936825752258301
0.31377220153808594
0.49370288848876953
1.7043194770812988
4 回戦
0.33944153785705566
0.3863201141357422
0.5154023170471191
1.5431039333343506
5 回戦
0.42851996421813965
0.2959136962890625
0.49349379539489746
1.9792490005493164
6 回戦
0.36693477630615234
0.24079442024230957
0.7523927688598633
2.1007657051086426
7 回戦
0.39217042922973633
0.3354625701904297
0.5430314540863037
1.8233575820922852
8 回戦
0.3186681270599365
0.29424381256103516
0.5318617820739746
1.803809642791748
9 回戦
0.28631591796875
0.24852752685546875
0.4139130115509033
1.4854552745819092
10 回戦
0.44863057136535645
0.24718928337097168
0.4330742359161377
1.3051936626434326

23:58 | 投票する | 投票数(0) | コメント(0)
2019/01/18

当たり判定の後に,円が接するところまで戻す

Tweet ThisSend to Facebook | by:YAS
 下の図のように,中心の座標が  (x_{1},\;y_{1})  で半径が  r_{1}  の円O1と,中心の座標が  (x_{2},\;y_{2})  で半径が  r_{2}の円O2があるとすると,この2つの当たり判定は,三平方の定理から,(1)のような式になります。

   (x_{2}-x_{1})^2+(y_{2}-y_{1})^2  \raisebox{3}{\relstack{<}{=}}  (r_{1}+r_{2})^2\;\cdots\;\text{(1)}

   

 ①で右辺と左辺が等しい場合には問題ありませんが,多くの場合左辺が右辺よりも小さい状態で当たりが判定されます。これは2つの円が重なっている状態なので,衝突によって円の進む方向が変わっても,またすぐに当たりとして判定されてしまいます。そこで,2つの円が接した瞬間の円の中心の座標を計算で求めることにします。

「チャート式基本事項14直線のベクトル方程式」より

   \vec{OA}=\vec{a}\vec{OE}=\vec{e} とすると,

 点Aを通り, \vec{OE}  に平行な直線は,次のようなベクトル方程式で表される。

   \vec{x}=\vec{a}+t\vec{e} ( t は実数)
 

 当たり判定時の原点から円の中心へのベクトルをそれぞれ  (a_{1},\;b_{1})(a_{2},\;b_{2})  とし,円の速度ベクトルをそれぞれ (c_{1},\;d_{1})(c_{2},\;d_{2}),2つの円が接した瞬間の原点から円の中心へのベクトルをそれぞれ  (x_{1},\;y_{1})(x_{2},\;y_{2})  とすると,下のようなベクトル方程式になる。

  \begin{eqnarray}&(x_{1},\;y_{1})&=&(a_{1},\;b_{1})&+&t&(c_{1},\;d_{1})&\;\cdots\;\text{(2)}\\&(x_{2},\;y_{2})&=&(a_{2},\;b_{2})&+&t&(c_{2},\;d_{2})&\;\cdots\;\text{(3)}\end{eqnarray}
   ( t は実数)

   (2)と(3)を展開すると,
  \begin{eqnarray}&(x_{1},\;y_{1})&=&(a_{1}&+&c_{1}t,\;b_{1}&+&d_{1}t)&\;\cdots\;\text{(4)}\\&(x_{2},\;y_{2})&=&(a_{2}&+&c_{2}t,\;b_{2}&+&d_{2}t)&\;\cdots\;\text{(5)}\end{eqnaray}
   
   (4)と(5)を(1)に代入すると,
   (a_{2}+c_{2}t-a_{1}-c_{1}t)^2+(b_{2}+d_{2}t-b_{1}-d_{1}t)^2=(r_{1}+r_{2})^2\;\cdots\;\text{(6)}

   (6)で次のように置き換えると,
   A=a_{2}-a_{1}B=b_{2}-b_{1}C=c_{2}-c_{1}D=d_{2}-d_{1}R=r_{1}+r_{2}
   として代入すると,以下のようになる。

   (A+Ct)^2+(B+Dt)^2=R^2\;\cdots\;\text{(7)}

   これを展開して,
   A^2+2ACt+C^2t^2+B^2+2BDt+D^2t^2-R^2=0

   整理すると,次のような二次方程式になる。
   (C^2+D^2)t^2+2(AC+BD)t+(A^2+B^2-R^2)=0\;\cdots\;\text{(8)}

   (8)を二次方程式の解の公式  x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}  に当てはめると,

   t=\frac{-2(AC+BD)\pm\sqrt{\left{2(AC+BD)\right}^2-4(C^2+D^2)(A^2+B^2-R^2)}}{2(C^2+D^2)}

   円が接した時点(過去)の t が知りたいので,2つの解の内,t≦0の解をとる。
    
23:46 | 投票する | 投票数(0) | コメント(0)
2019/01/18

円が衝突したときの動き

Tweet ThisSend to Facebook | by:YAS
 Pythonista3で簡単なゲームをつくるために,ビリヤードのように平面上で円がぶつかる際の円の動きについて,高校時代のチャート式代数・幾何をひっぱりだしてきて考えてみた。奥付を見ると,昭和61年の改訂新版第2刷とあるが,まぁこんな基本的なことは30年やそこらでは変わらないであろう。

 下の図のように,\vec{v_{1}}で移動する円O1と静止している円O2が衝突するとする。また,衝突した瞬間の円O1の中心の座標は(x1, y1),円O2の中心を座標を(x2, y2)とする。

 
 「チャート式基本事項6ベクトルの表し方」より

 任意のベクトルは,ただ一通りに,下の式の形で表される。

   \vec{p}=k\vec{a}+l\vec{b} (klは実数)

 円の中心と中心を結んだ向きのベクトルを\vec{a}\vec{a}に垂直なベクトルを \vec{b} とすると,下の式で表すことができる。

   \vec{v_{1}}=k\vec{a}+l\vec{b} 

「チャート式基本事項10垂直条件」より


   \vec{a}\perp\vec{b}\;\Longleftrightarrow\;\vec{a}\cdot\vec{b}=0

 よって,\vec{a}=(a_{1},\;b_{1})\vec{b}=(a_{2},\;b_{2}) とすると,

   a_{1}b_{1}+a_{2}b_{2}=0


 上の垂直条件は, b_{1}=a_{2}b_{2}=-a_{1}のときにも成り立つ。よって,下の図のように,以下の式が成り立つ。


   \vec{v_{1}}=k\vec{a}+l\vec{b}
   \vec{a}=(x_{2}-x_{1},\;y_{2}-y_{1})
   \vec{b}=(y_{2}-y_{1},\;-(x_{2}-x_{1}))

 \vec{v_{1}}=(a,\;b)とすると,以下の式が成り立つ。
   
   \begin{eqnarray}a&=&k(x_{2}&-&x_{1})&+&l(y_{2}&-&y_{1})\\b&=&k(y_{2}&-&y_{1})&-&l(x_{2}&-&x_{1})\end{eqnarray}

 X=x_{2}-x_{1}Y=y_{2}-y_{1}とすると,

   \begin{eqnarray}a&=&kX&+&lY\;\cdots\;\text(1)\\b&=&kY&-&lX\;\cdots\;\text(2)\end{eqnarray}

 これらをklについて解くと,下のようになる。

   (1)の両辺にXをかけると,
   aX=kX^2+lXY\;\cdots\;\text(3)
   (2)の両辺にYをかけると,
   bY=kY^2-lXY\;\cdots\;\text{(4)}

   (3)と(4)の両辺を足すと,
   aX+bY=kX^2+kY^2

   よって,
   k=\frac{aX+bY}{X^2+Y^2}

 kがもとめられれば,l\vec{b}=\vec{v_{1}}-k\vec{a} で l\vec{b} も簡単に求められるため,わざわざ l を求める必要はないが,一応続きを書いてみる。

   同様に,(1)の両辺にYをかけると,
   aY=kXY+lY^2\;\cdots\;\text(5)
   (2)の両辺にXをかけると,
   bX=kXY-lX^2\;\cdots\;\text(6)

   (5)から(6)の両辺を引くと,
   aY-bX=lY^2+lX^2

   よって,
   l=\frac{aY-bX}{X^2+Y^2}

 円がぶつかったときに,静止している円O2に k\vec{a} の速度ベクトルが加わり,移動している円O1から k\vec{a} の速度ベクトルが失われる。
 両方の円が移動している場合は,入れ替えて考え,速度ベクトルを合成する。
20:00 | 投票する | 投票数(0) | コメント(0)