撞球遊戲 billiards

Posted on Sat, Sep 25, 2021 python

線上執行

GlowScript IDE

GlowScript is an easy-to-use, powerful environment for creating 3D animations and publishing them on the web. Here at glowscript.org, you can write and run GlowScript programs right in your browser, store them in the cloud for free, and easily share them with others. The Help provides full documentation.

程式碼

GlowScript 3.1 VPython

# 1 參數設定, 設定變數及初始值
size = 1.5 # 小球半徑
g = 9.8 # 地球重力加速度 9.8 m/s^2
t = 0 # 計算時間用參數
dt = 0.01 # 時間間隔
floorfric = 0.01
wallbounce = 0.01
batangle=pi/2
batlength = 30
batforce = 5


#設定球,及球初速、加速
ballinhole=[]
ballA=[]   # 利用列表儲存球
N=16 
balld=2*size*sin(2/3)
ballpos=[vector(0,-50,0),vector(0,40,0),
        vector(size,40+balld,0),vector(-size,40+balld,0),
        vector(2*size,40+2*balld,0),vector(0,40+2*balld,0),vector(-2*size,40+2*balld,0),
        vector(3*size,40+3*balld,0),vector(size,40+3*balld,0),vector(-size,40+3*balld,0),vector(-3*size,40+3*balld,0),
        vector(4*size,40+4*balld,0),vector(2*size,40+4*balld,0),vector(0,40+4*balld,0),vector(-2*size,40+4*balld,0),vector(-4*size,40+4*balld,0)]
for  i in range(0,N): # 所有球之位置
    ball = sphere(pos = ballpos[i], radius = size, color = color.red , make_trail=False) 
    ballA.append(ball)
    ballA[i].v = vector(0,0,0)
    ballinhole.append(0)
ballA[0].color=color.white


holeA=[]#球洞
holepos=[vector(-40,80,-2),vector(-40,0,-2),vector(-40,-80,-2),
         vector(40,80,-2),vector(40,0,-2),vector(40,-80,-2)]
for i in range(0,6) :
  hole =sphere(pos = holepos[i] ,radius = 3*size, color = color.black)
  holeA.append(hole)


#設定球桌
table = box(pos = vector(0, 0, -2), length = 80, height = 160+balld,width = 2, color = color.blue)#桌
quan = box(pos = vector(0, 0, -2), length = 90, height = 170, width= 1, color = color.cyan)#框
bat = arrow(pos=vector(0,-80,0), axis=vector(0,batlength,0), shaftwidth=1)#球桿


#箭頭(球桿)
bat.pos=vector(ballA[0].pos.x-(batlength+batforce)*cos(batangle),ballA[0].pos.y-(batlength+batforce)*sin(batangle),0)
bat.axis=vector(ballA[0].pos.x-bat.pos.x,ballA[0].pos.y-bat.pos.y,0)

#3 小球運動部分,小球撞牆時反彈=> 無窮迴圈
while True : 
  stop = 0#所有球靜止
  bat.opacity=0
  while (stop==0):
    stop=1
    for k in range(0,N,1):
    #球速度、位置的變化
        ballA[k].v=ballA[k].v*(1-floorfric) # v=v0+a*t 物理錯誤
        ballA[k].pos=ballA[k].pos+ballA[k].v*dt # S=s0+v*t
        rate(200) #動畫更新時間間隔 0.2秒

    #撞地板的反彈
        if(ballA[k].pos.y <= -80 ) :
          ballA[k].pos.y = -80
          ballA[k].v.y = -ballA[k].v.y*(1-wallbounce)

    #撞天花板的反彈
        if(ballA[k].pos.y >= 80 ) :
          ballA[k].pos.y = 80
          ballA[k].v.y = -ballA[k].v.y*(1-wallbounce)

    #撞右牆的反彈
        if( ballA[k].pos.x >= 40 ) :
          ballA[k].pos.x = 40
          ballA[k].v.x = -ballA[k].v.x*(1-wallbounce)
    
    #撞左牆的反彈
        if( ballA[k].pos.x <= -40 ) :
          ballA[k].pos.x = -40  
          ballA[k].v.x = -ballA[k].v.x*(1-wallbounce)
          
    #進洞
        for i in range (0,6):
            d=mag(ballA[k].pos-holeA[i].pos)
            if (d<=4*size):
              ballA[k].opacity=0
              ballinhole[k]=1
              ballA[k].pos.z=(-10)
              ballA[k].v=vector(0,0,0)
              
    #二維彈性碰撞
        for h in range(k+1,N,1):
            bd = ballA[k].pos-ballA[h].pos
            if (mag(bd)<=2*size):
              projK=proj( ballA[k].v ,bd )#向兩球連線向量投影速度向量
              projH=proj( ballA[h].v ,bd )
              ballA[k].v+=projH-projK
              ballA[h].v+=projK-projH
              ballA[h].pos=ballA[h].pos - bd*(1- mag(bd)/(2*size))

    #是否停止
        if(ballA[k].v.mag>=10):
            stop=0
        else:
            ballA[k].v=vector(0,0,0)
      #目前時間+時間間格<== 設定新時間為下一個時間
        t=t+dt
  #如果白球進洞跳回中間
  if (ballinhole[0]==1):
    ballA[0].opacity=100
    ballA[0].pos=vector(0,-50,0)
    ballinhole[0]=0
  #箭頭(球桿)
  bat.pos=vector(ballA[0].pos.x-(batlength+batforce)*cos(batangle),ballA[0].pos.y-(batlength+batforce)*sin(batangle),0)
  bat.axis=vector(batlength*cos(batangle),batlength*sin(batangle),0)
  batforce = 5
  batangle=pi/2 #單位 弳
  while True:
    rate(100) 
    bat.opacity=100
    k=keysdown()
    if 'left' in k:
      batangle=batangle-0.01
    if 'right' in k:
      batangle=batangle+0.01
    if 'up' in k:
      batforce=batforce+1
    if 'down' in k:
      batforce=batforce-1
    #print(batangle,batforce, k) 
    if batforce<5:  #球棒最近距離
        batforce=5
    if batforce>20: #球棒最遠距離
        batforce=20
    bat.pos=vector(ballA[0].pos.x-(batlength+batforce)*cos(batangle),ballA[0].pos.y-(batlength+batforce)*sin(batangle),0)
    bat.axis=vector(batlength*cos(batangle),batlength*sin(batangle),0)
    if ' ' in k:
      ballA[0].v=vector(10*batforce*cos(batangle),10*batforce*sin(batangle),0)
      for q in range (batforce,0,-1):
          rate(200)
          bat.pos=vector(ballA[0].pos.x-(batlength+batforce)*cos(batangle),ballA[0].pos.y-(batlength+batforce)*sin(batangle),0)
          batforce-=1
      break