這周來寫如何讓母球撞擊牆壁反彈
先畫出一個視窗與兩顆球(紅色和藍色),兩球之間會有線相連,滑鼠鼠標與紅色球之間也有線相連(皆是指向圓心),並且紅色球可以透過滑鼠點擊而推進,大部分解釋都有註解在程式碼後
//想要做出撞球/彈珠台
//球互相撞
PVector ball,ballv1;//藍球的向量與速度
PVector ball2,ballv2;//紅球的向量與速度
void setup(){
size(500,300);//size(1200,900);視窗大小
ball = new PVector(100,150);//紅球的xy座標(100,150)
ball2 = new PVector(400,150);//藍球的xy座標(400,150)
ballv1 = new PVector(0,0);//紅球的速度初始化
ballv2 = new PVector(0,0);//藍球的速度初始化
}
void draw(){
background(#FFFFF2);//將背景清空才不會有殘影
ball();//執行函式ball();
ball2();//執行函式ball2();
ball.x+=ballv1.x;//紅球X方向的速度慢慢增加
ball.y+=ballv1.y;//紅球Y方向的速度慢慢增加
ball2.x+=ballv2.x;//藍球X的速度慢慢增加
ball2.y+=ballv2.y;//藍球Y方向的速度慢慢增加
ballv1.x*=0.99;//紅球X方向的速度增加摩擦力
ballv1.y*=0.99;//紅球Y方向的速度增加摩擦力
ballv2.x *=0.99;//藍球X方向的速度增加摩擦力
ballv2.y *=0.99;//藍球Y方向的速度增加摩擦力
if(abs(ballv2.x)<0.001) ballv2.x=0;//如果藍球X方向速度(絕對值)小於0.001,則直接歸0
if(abs(ballv2.y)<0.001) ballv2.y=0;//如果藍球Y方向速度(絕對值)小於0.001,則直接歸0
if(abs(ballv1.x)<0.001) ballv1.x=0;//如果紅球X方向速度(絕對值)小於0.001,則直接歸0
if(abs(ballv1.y)<0.001) ballv1.y=0;//如果紅球Y方向速度(絕對值)小於0.001,則直接歸0
line(ball.x,ball.y,mouseX,mouseY);//畫出一條線,連接紅球與鼠標的(X,Y)座標
line(ball.x,ball.y,ball2.x,ball2.y);//畫出一條線,連接紅球與藍球的(X,Y)座標
}
void ball(){//紅球
fill(#FA9090);//粉紅色
ellipse(ball.x, ball.y, 30,30);//大小30*30的球
if(ball.x+ballv1.x>500-15 || ball.x+ballv1.x<15){
//球互相撞
PVector ball,ballv1;//藍球的向量與速度
PVector ball2,ballv2;//紅球的向量與速度
void setup(){
size(500,300);//size(1200,900);視窗大小
ball = new PVector(100,150);//紅球的xy座標(100,150)
ball2 = new PVector(400,150);//藍球的xy座標(400,150)
ballv1 = new PVector(0,0);//紅球的速度初始化
ballv2 = new PVector(0,0);//藍球的速度初始化
}
void draw(){
background(#FFFFF2);//將背景清空才不會有殘影
ball();//執行函式ball();
ball2();//執行函式ball2();
ball.x+=ballv1.x;//紅球X方向的速度慢慢增加
ball.y+=ballv1.y;//紅球Y方向的速度慢慢增加
ball2.x+=ballv2.x;//藍球X的速度慢慢增加
ball2.y+=ballv2.y;//藍球Y方向的速度慢慢增加
ballv1.x*=0.99;//紅球X方向的速度增加摩擦力
ballv1.y*=0.99;//紅球Y方向的速度增加摩擦力
ballv2.x *=0.99;//藍球X方向的速度增加摩擦力
ballv2.y *=0.99;//藍球Y方向的速度增加摩擦力
if(abs(ballv2.x)<0.001) ballv2.x=0;//如果藍球X方向速度(絕對值)小於0.001,則直接歸0
if(abs(ballv2.y)<0.001) ballv2.y=0;//如果藍球Y方向速度(絕對值)小於0.001,則直接歸0
if(abs(ballv1.x)<0.001) ballv1.x=0;//如果紅球X方向速度(絕對值)小於0.001,則直接歸0
if(abs(ballv1.y)<0.001) ballv1.y=0;//如果紅球Y方向速度(絕對值)小於0.001,則直接歸0
line(ball.x,ball.y,mouseX,mouseY);//畫出一條線,連接紅球與鼠標的(X,Y)座標
line(ball.x,ball.y,ball2.x,ball2.y);//畫出一條線,連接紅球與藍球的(X,Y)座標
}
void ball(){//紅球
fill(#FA9090);//粉紅色
ellipse(ball.x, ball.y, 30,30);//大小30*30的球
if(ball.x+ballv1.x>500-15 || ball.x+ballv1.x<15){
//如果X座標+X座標速度>500-15 且 X座標+X座標速度<15(總之就是不能超過X方向的視窗邊界
ballv1.x = -ballv1.x;//就反彈
}
if(ball.y+ballv1.y>300-15 || ball.y+ballv1.y<15){
}
if(ball.y+ballv1.y>300-15 || ball.y+ballv1.y<15){
//如果Y座標+Y座標速度>300-15 且 Y座標+Y座標速度<15(總之就是不能超過Y方向的視窗邊界
ballv1.y = -ballv1.y;//就反彈
}
}
void ball2(){//藍球
fill(#9CD0FC);//藍色的球
ellipse(ball2.x, ball2.y, 30,30);//大小30*30的球
if(ball2.x+ballv2.x>500-15 || ball2.x+ballv2.x<15){
ballv1.y = -ballv1.y;//就反彈
}
}
void ball2(){//藍球
fill(#9CD0FC);//藍色的球
ellipse(ball2.x, ball2.y, 30,30);//大小30*30的球
if(ball2.x+ballv2.x>500-15 || ball2.x+ballv2.x<15){
//如果X座標+X座標速度>500-15 且 X座標+X座標速度<15(總之就是不能超過X方向的視窗邊界
ballv2.x = - ballv2.x;//就反彈
}
if(ball2.y+ballv2.y>300-15 || ball2.y+ballv2.y<15){
ballv2.x = - ballv2.x;//就反彈
}
if(ball2.y+ballv2.y>300-15 || ball2.y+ballv2.y<15){
//如果Y座標+Y座標速度>300-15 且 Y座標+Y座標速度<15(總之就是不能超過Y方向的視窗邊界
ballv2.y = - ballv2.y;//就反彈
}
}
void mousePressed(){//滑鼠點擊時
ballv1.x = (ball.x-mouseX)/10.0;//X方向的速度會等於(紅球X座標-滑鼠X座標)/10
ballv1.y = (ball.y-mouseY)/10.0;//Y方向的速度會等於(紅球Y座標-滑鼠Y座標)/10
}
ballv2.y = - ballv2.y;//就反彈
}
}
void mousePressed(){//滑鼠點擊時
ballv1.x = (ball.x-mouseX)/10.0;//X方向的速度會等於(紅球X座標-滑鼠X座標)/10
ballv1.y = (ball.y-mouseY)/10.0;//Y方向的速度會等於(紅球Y座標-滑鼠Y座標)/10
}
目前可以看出藍色球是沒有任何反應的,所以我們接下來要寫碰撞偵測,當紅色的球碰到藍色的球,兩顆就會各自反彈移動
//想要做出撞球/彈珠台
//球互相撞
PVector ball,ballv1;//紅球的向量與速度
PVector ball2,ballv2;//藍球的像亮與速度
void setup(){
size(500,300);//視窗大小
ball = new PVector(100,150);//紅球的xy座標(100,150)
ball2 = new PVector(400,150);//藍球的xy座標(400,150)
ballv1 = new PVector(0,0);//紅球的速度初始化
ballv2 = new PVector(0,0);//藍球的速度初始化
}
void draw(){
background(#FFFFF2);//將背景清空才不會有殘影
ball();//執行函式ball();
ball2();//執行函式ball2();
if( collision() ){//碰撞偵測
}else{
}
ball.x+=ballv1.x;//紅球X方向的速度慢慢增加
ball.y+=ballv1.y;//紅球Y方向的速度慢慢增加
ball2.x+=ballv2.x;//藍球X的速度慢慢增加
ball2.y+=ballv2.y;//藍球Y方向的速度慢慢增加
ballv1.x*=0.99;//紅球X方向的速度增加摩擦力
ballv1.y*=0.99;//紅球Y方向的速度增加摩擦力
ballv2.x *=0.99;//藍球X方向的速度增加摩擦力
ballv2.y *=0.99;//藍球Y方向的速度增加摩擦力
if(abs(ballv2.x)<0.001) ballv2.x=0;//如果藍球X方向速度(絕對值)小於0.001,則直接歸0
if(abs(ballv2.y)<0.001) ballv2.y=0;//如果藍球Y方向速度(絕對值)小於0.001,則直接歸0
if(abs(ballv1.x)<0.001) ballv1.x=0;//如果紅球X方向速度(絕對值)小於0.001,則直接歸0
if(abs(ballv1.y)<0.001) ballv1.y=0;//如果紅球Y方向速度(絕對值)小於0.001,則直接歸0
line(ball.x,ball.y,mouseX,mouseY);//畫出一條線,連接紅球與鼠標的(X,Y)座標
line(ball.x,ball.y,ball2.x,ball2.y);//畫出一條線,連接紅球與藍球的(X,Y)座標
}
void ball(){//紅球
fill(#FA9090);//粉色
ellipse(ball.x, ball.y, 30,30);//大小30*30的球
if(ball.x+ballv1.x>500-15 || ball.x+ballv1.x<15){
//如果X座標+X座標速度>500-15 且 X座標+X座標速度<15(總之就是不能超過X方向的視窗邊界
ballv1.x = -ballv1.x;//就反彈
}
if(ball.y+ballv1.y>300-15 || ball.y+ballv1.y<15){
//如果Y座標+Y座標速度>300-15 且 Y座標+Y座標速度<15(總之就是不能超過Y方向的視窗邊界
ballv1.y = -ballv1.y;//就反彈
}
}
void ball2(){//藍球
fill(#9CD0FC);//藍色的球
ellipse(ball2.x, ball2.y, 30,30);//大小30*30的球
if(ball2.x+ballv2.x>500-15 || ball2.x+ballv2.x<15){
//如果X座標+X座標速度>500-15 且 X座標+X座標速度<15(總之就是不能超過X方向的視窗邊界
ballv2.x = - ballv2.x;//就反彈
}
if(ball2.y+ballv2.y>300-15 || ball2.y+ballv2.y<15){
//如果Y座標+Y座標速度>300-15 且 Y座標+Y座標速度<15(總之就是不能超過Y方向的視窗邊界
ballv2.y = - ballv2.y;//就反彈
}
}
void mousePressed(){//滑鼠點擊時
ballv1.x = (ball.x-mouseX)/10.0;//X方向的速度會等於(紅球X座標-滑鼠X座標)/10
ballv1.y = (ball.y-mouseY)/10.0;//Y方向的速度會等於(紅球Y座標-滑鼠Y座標)/10
}
boolean collision(){//碰撞
if( dist(ball.x+ballv1.x , ball.y+ballv1.y , ball2.x+ballv2.x , ball2.y+ballv2.y)<30 ){
//計算兩點的距離,如果X和Y方向都小於30
drawTangent();//執行切線程式
drawInput();//執行輸出
drawOutput();//執行輸入
//ballv1.x=0; ballv1.y=0; ballv2.x=0; ballv2.y=0;
return true;//有撞到
}else return false;//沒有撞到
}
void drawTangent(){//計算切線
float cx = (ball.x+ball2.x)/2;
float cy = (ball.y+ball2.y)/2;
ellipse(cx,cy,3,3);
float dx = (ball.x-ball2.x);
float dy = (ball.y-ball2.y);
line(cx-dy*10, cy+dx*10, cx+dy*10, cy-dx*10);
line(cx, cy, cx-ballv1.x*10, cy-ballv1.y*10);
{
float ddx=ballv1.x, ddy=ballv1.y;
nx=ball.x-ball2.x;
ny=ball.y-ball2.y;
float len = sqrt(nx*nx+ny*ny);//計算向量len,sqrt是開根號
nx /= len;
ny /= len;
float inner = nx*ddx+ny*ddy;//內積
nextX=ddx-inner*nx*2;
nextY=ddy-inner*ny*2;
solveMV();
ballv2.x = nx*scale2;
ballv2.y = ny*scale2;
ballv1.x=nextX*scale;
ballv1.y=nextY*scale;
line(cx, cy, cx+nextX, cy+nextY);
}
}
float nx, ny;
float scale, scale2;
float nextX, nextY;
void solveMV(){
float m1=1, m2=1;
float vx1=ballv1.x, vy1=ballv1.y;
float vx2=ballv2.x, vy2=ballv2.y;
// m1*vx1+m2*vx2 == m1*nextX*scale + m2*nx*scale2 (*ny)
// m1*vy1+m2*vy2 == m1*nextY*scale + m2*ny*scale2 (*nx)
// ny*(m1*vx1+m2*vx2)-nx*(m1*vy1+m2*vy2) == (ny*m1*nextX-nx*m1*nextY) *scale
scale = (ny*(m1*vx1+m2*vx2)-nx*(m1*vy1+m2*vy2) ) / (ny*m1*nextX-nx*m1*nextY);
scale2 = (m1*vx1+m2*vx2-m1*nextX*scale)/(m2*nx);
println(scale, scale2);//為了確認計算結果有無錯誤,要輸出看看
println("eq1", m1*vx1+m2*vx2, m1*nextX*scale + m2*nx*scale2);//為了確認計算結果有無錯誤,要輸出看看
println("eq2", m1*vy1+m2*vy2, m1*nextY*scale + m2*ny*scale2);//為了確認計算結果有無錯誤,要輸出看看
}
void drawInput(){
line(ball.x,ball.y, ball.x+ballv1.x*10, ball.y+ballv1.y*10);//畫出切線
}
void drawOutput(){
float dx=ballv1.x*10, dy=ballv1.y*10;
float nx=ball.x-ball2.x, ny=ball.y-ball2.y;
float len = sqrt(nx*nx+ny*ny);//計算向量
nx /= len;
ny /= len;
float inner = nx*dx+ny*dy;
nextX=dx-inner*nx*2;
nextY=dy-inner*ny*2;
line(ball.x,ball.y, ball.x+nextX, ball.y+nextY);
}
//球互相撞
PVector ball,ballv1;//紅球的向量與速度
PVector ball2,ballv2;//藍球的像亮與速度
void setup(){
size(500,300);//視窗大小
ball = new PVector(100,150);//紅球的xy座標(100,150)
ball2 = new PVector(400,150);//藍球的xy座標(400,150)
ballv1 = new PVector(0,0);//紅球的速度初始化
ballv2 = new PVector(0,0);//藍球的速度初始化
}
void draw(){
background(#FFFFF2);//將背景清空才不會有殘影
ball();//執行函式ball();
ball2();//執行函式ball2();
if( collision() ){//碰撞偵測
}else{
}
ball.x+=ballv1.x;//紅球X方向的速度慢慢增加
ball.y+=ballv1.y;//紅球Y方向的速度慢慢增加
ball2.x+=ballv2.x;//藍球X的速度慢慢增加
ball2.y+=ballv2.y;//藍球Y方向的速度慢慢增加
ballv1.x*=0.99;//紅球X方向的速度增加摩擦力
ballv1.y*=0.99;//紅球Y方向的速度增加摩擦力
ballv2.x *=0.99;//藍球X方向的速度增加摩擦力
ballv2.y *=0.99;//藍球Y方向的速度增加摩擦力
if(abs(ballv2.x)<0.001) ballv2.x=0;//如果藍球X方向速度(絕對值)小於0.001,則直接歸0
if(abs(ballv2.y)<0.001) ballv2.y=0;//如果藍球Y方向速度(絕對值)小於0.001,則直接歸0
if(abs(ballv1.x)<0.001) ballv1.x=0;//如果紅球X方向速度(絕對值)小於0.001,則直接歸0
if(abs(ballv1.y)<0.001) ballv1.y=0;//如果紅球Y方向速度(絕對值)小於0.001,則直接歸0
line(ball.x,ball.y,mouseX,mouseY);//畫出一條線,連接紅球與鼠標的(X,Y)座標
line(ball.x,ball.y,ball2.x,ball2.y);//畫出一條線,連接紅球與藍球的(X,Y)座標
}
void ball(){//紅球
fill(#FA9090);//粉色
ellipse(ball.x, ball.y, 30,30);//大小30*30的球
if(ball.x+ballv1.x>500-15 || ball.x+ballv1.x<15){
//如果X座標+X座標速度>500-15 且 X座標+X座標速度<15(總之就是不能超過X方向的視窗邊界
ballv1.x = -ballv1.x;//就反彈
}
if(ball.y+ballv1.y>300-15 || ball.y+ballv1.y<15){
//如果Y座標+Y座標速度>300-15 且 Y座標+Y座標速度<15(總之就是不能超過Y方向的視窗邊界
ballv1.y = -ballv1.y;//就反彈
}
}
void ball2(){//藍球
fill(#9CD0FC);//藍色的球
ellipse(ball2.x, ball2.y, 30,30);//大小30*30的球
if(ball2.x+ballv2.x>500-15 || ball2.x+ballv2.x<15){
//如果X座標+X座標速度>500-15 且 X座標+X座標速度<15(總之就是不能超過X方向的視窗邊界
ballv2.x = - ballv2.x;//就反彈
}
if(ball2.y+ballv2.y>300-15 || ball2.y+ballv2.y<15){
//如果Y座標+Y座標速度>300-15 且 Y座標+Y座標速度<15(總之就是不能超過Y方向的視窗邊界
ballv2.y = - ballv2.y;//就反彈
}
}
void mousePressed(){//滑鼠點擊時
ballv1.x = (ball.x-mouseX)/10.0;//X方向的速度會等於(紅球X座標-滑鼠X座標)/10
ballv1.y = (ball.y-mouseY)/10.0;//Y方向的速度會等於(紅球Y座標-滑鼠Y座標)/10
}
boolean collision(){//碰撞
if( dist(ball.x+ballv1.x , ball.y+ballv1.y , ball2.x+ballv2.x , ball2.y+ballv2.y)<30 ){
//計算兩點的距離,如果X和Y方向都小於30
drawTangent();//執行切線程式
drawInput();//執行輸出
drawOutput();//執行輸入
//ballv1.x=0; ballv1.y=0; ballv2.x=0; ballv2.y=0;
return true;//有撞到
}else return false;//沒有撞到
}
void drawTangent(){//計算切線
float cx = (ball.x+ball2.x)/2;
float cy = (ball.y+ball2.y)/2;
ellipse(cx,cy,3,3);
float dx = (ball.x-ball2.x);
float dy = (ball.y-ball2.y);
line(cx-dy*10, cy+dx*10, cx+dy*10, cy-dx*10);
line(cx, cy, cx-ballv1.x*10, cy-ballv1.y*10);
{
float ddx=ballv1.x, ddy=ballv1.y;
nx=ball.x-ball2.x;
ny=ball.y-ball2.y;
float len = sqrt(nx*nx+ny*ny);//計算向量len,sqrt是開根號
nx /= len;
ny /= len;
float inner = nx*ddx+ny*ddy;//內積
nextX=ddx-inner*nx*2;
nextY=ddy-inner*ny*2;
solveMV();
ballv2.x = nx*scale2;
ballv2.y = ny*scale2;
ballv1.x=nextX*scale;
ballv1.y=nextY*scale;
line(cx, cy, cx+nextX, cy+nextY);
}
}
float nx, ny;
float scale, scale2;
float nextX, nextY;
void solveMV(){
float m1=1, m2=1;
float vx1=ballv1.x, vy1=ballv1.y;
float vx2=ballv2.x, vy2=ballv2.y;
// m1*vx1+m2*vx2 == m1*nextX*scale + m2*nx*scale2 (*ny)
// m1*vy1+m2*vy2 == m1*nextY*scale + m2*ny*scale2 (*nx)
// ny*(m1*vx1+m2*vx2)-nx*(m1*vy1+m2*vy2) == (ny*m1*nextX-nx*m1*nextY) *scale
scale = (ny*(m1*vx1+m2*vx2)-nx*(m1*vy1+m2*vy2) ) / (ny*m1*nextX-nx*m1*nextY);
scale2 = (m1*vx1+m2*vx2-m1*nextX*scale)/(m2*nx);
println(scale, scale2);//為了確認計算結果有無錯誤,要輸出看看
println("eq1", m1*vx1+m2*vx2, m1*nextX*scale + m2*nx*scale2);//為了確認計算結果有無錯誤,要輸出看看
println("eq2", m1*vy1+m2*vy2, m1*nextY*scale + m2*ny*scale2);//為了確認計算結果有無錯誤,要輸出看看
}
void drawInput(){
line(ball.x,ball.y, ball.x+ballv1.x*10, ball.y+ballv1.y*10);//畫出切線
}
void drawOutput(){
float dx=ballv1.x*10, dy=ballv1.y*10;
float nx=ball.x-ball2.x, ny=ball.y-ball2.y;
float len = sqrt(nx*nx+ny*ny);//計算向量
nx /= len;
ny /= len;
float inner = nx*dx+ny*dy;
nextX=dx-inner*nx*2;
nextY=dy-inner*ny*2;
line(ball.x,ball.y, ball.x+nextX, ball.y+nextY);
}
這部分的程式是運用入射角等於反射角的概念去寫,但是入射角並不等於反射角!!
因為兩顆球都會動,所以並不適用於這個定律
而是要利用動量守恆來讓兩顆球去動作
上面雖然兩顆球可以彈來彈去,不過移動軌跡就會是錯誤的


沒有留言:
張貼留言