2022年11月21日 星期一

TC*(˙Ⱉ˙ฅ)​-week11

撞球遊戲製作

運用入射角=反射角的概念

將這個概念代入到程式裡面去
設定畫布大小後,在x=50,y=100的位置畫一個向著右下角移動的線

float vx=1,vy=1.3;//vx=1往右,vy=1.3往下
float x=50,y=100;//本來的位置
void setup(){//設定
  size(200,300);//視窗大小
}
void draw(){//畫圖
  ellipse(x,y,3,3);//畫一個3x3(線粗細)的x,y座標
  x += vx;//不斷更新的x座標
  y += vy;//不斷更新的y座標
}



上面那個程式讓我們學會如何畫一個向右下角奔跑的線,那麼接下來我們要學習如何讓線在碰到邊框之後會進行反彈
以y座標為例子,我們將視窗寬度設定為200,所以如果超出200的話就要進行反彈,才不會讓線超出視窗並一直跑
float vx=1,vy=1.3;//vx=1往右,vy=1.3往下
float x=50,y=100;//本來的位置
void setup(){//設定
  size(300,200);//視窗大小
}
void draw(){//畫圖
  ellipse(x,y,3,3);//畫一個3x3(線粗細)的x,y座標
  x += vx;
  y += vy;
  //要如何做出反彈效果?
  if(y>200){//當y座標大於我們所設定的視窗寬度200時
    vy = -vy;//y就會反彈
  }
}




float vx=1,vy=1.3;//vx=1往右,vy=1.3往下
float x=50,y=100;//本來的位置
void setup(){//設定
  size(100,200);//視窗大小
}
void draw(){//畫圖
  ellipse(x,y,3,3);//畫一個3x3(線粗細)的x,y座標
  x += vx;
  y += vy;
  //要如何做出反彈效果?
  if(x>100){//如果x座標>視窗長度100
    vx = -vx;//x就會反彈
  }
}


用內積將這兩個程式碼進行合體,稍微改寫一下,(我們看回圖1,綠色線的長度要拿去做內積,也就是速度x法向量),試著將v加上v2來看看會如何移動,會發現v會非常快速的向右邊移動,這並不是我們想要的結果
PVector v;//float vx=1往右,vy=1.3;往下(速度)
PVector p;//flaot x=50,y=100;(座標)
PVector n;//法向量
void setup(){//設定
  size(400,300);//視窗大小
  v = new PVector(1,1.3);//設定速度
  p = new PVector(50,100);//設定座標
  n = new PVector(-1,0);//設定法向量
}
void draw(){//畫圖
  ellipse(p.x,p.y,3,3);//畫一個3x3(線粗細)的x,y座標
  p.add(v);//p的座標要加上v的速度//x += vx; y += vy;
  //要如何做出反彈效果?
  if(p.x >= 100){
    //vx = -vx;
    float green = PVector.dot(v,n);//綠色線的長度(做功)=PVector做內積(把速度跟法向量做內積)
    PVector v2 = PVector.mult(n,green);//乘法,v2是綠色的長度(功)
    v.add(v2);//原來的v加上v2//v就會向右移動超快,所以是錯誤的
  }
}



配合這張圖來看
以PVector來改寫上上個y方向的程式會得到一樣的結果
PVector v;//float vx=1往右,vy=1.3;往下(速度)
PVector p;//flaot x=50,y=100;(座標)
PVector n;//法向量
void setup(){//設定
  size(300,200);//視窗大小
  v = new PVector(1,1.3);//設定速度
  p = new PVector(50,100);//設定座標
  n = new PVector(0,-1);//設定法向量,向上
}
void draw(){//畫圖
  ellipse(p.x,p.y,3,3);//畫一個3x3(線粗細)的x,y座標
  p.add(v);//p的座標要加上v的速度//x += vx; y += vy;
  //要如何做出反彈效果?
  if(p.y >= 200){//一旦碰撞發生
    //vx = -vx;
    float green = PVector.dot(v,n);//綠色線的長度(做功)=PVector做內積(把速度跟法向量做內積)
    PVector v2 = PVector.mult(n,-green*2);//乘法,v2是綠色長度(功)//原來的方向加上2倍的負方向
    v.add(v2);//v加上v2
  }
}


PVector v;//float vx=1往右,vy=1.3;往下(速度)
PVector p;//flaot x=50,y=100;(座標)
void setup(){//設定
  size(500,500);//視窗大小
  v = new PVector(1,1.3);//設定速度
  p = new PVector(50,60);//設定座標
}
void draw(){//畫圖
  ellipse(p.x,p.y,3,3);//畫一個3x3(線粗細)的x,y座標
  p.add(v);//p的座標要加上v的速度//x += vx; y += vy;
  line(0,500,mouseX,mouseY);
  ellipse(300,300,150,150);//畫圓
  //要如何做出反彈效果?
  if(dist(p.x,p.y,300,300)<75){//一旦碰撞發生
    //vx = -vx;
    PVector n = new PVector(-1,-1).normalize();//單位法向量
    float green = PVector.dot(v,n);//綠色線的長度(做功)=PVector做內積(把速度跟法向量做內積)
    PVector v2 = PVector.mult(n,-green*2);//乘法,v2是綠色的長度(功)//原來的方向加上2倍的負方向
    v.add(v2);//v加上v2
  }
}


延續上周繼續學習撞球遊戲的製作方式

但上次學習的那個程式反射角並不是很完美,那麼要如何做出完美的入射角反射角呢?

PVector v;//float vx=1往右,vy=1.3;往下(速度)
PVector p;//flaot x=50,y=100;(座標)
void setup(){//設定
  size(500,500);//視窗大小
  v = new PVector(1,1.3);//設定速度
  p = new PVector(50,60);//設定座標
}
void draw(){//畫圖
  ellipse(p.x,p.y,3,3);//畫一個3x3(線粗細)的x,y座標
  p.add(v);//p的座標要加上v的速度//x += vx; y += vy;
  line(0,500,mouseX,mouseY);
  ellipse(300,300,150,150);//畫圓
  //要如何做出反彈效果?
  if(dist(p.x,p.y,300,300)<75){//一旦碰撞發生
    //vx = -vx;
    PVector c = new PVector(300,300);//圓心
    PVector n = PVector.sub(p,c).normalize();//單位法向量,要初始化做減法,把p跟圓心做相減再取法向量
    //完美的入射角等於反射角
    float green = PVector.dot(v,n);//綠色線的長度(做功)=PVector做內積(把速度跟法向量做內積)
    PVector v2 = PVector.mult(n,-green*2);//乘法,v2是綠色的長度(功)//原來的方向加上2倍的負方向
    v.add(v2);//v加上v2
  }
}


試著用滑鼠操控大球,讓小球再觸碰到大球的時候進行反彈
PVector v;//float vx=1往右,vy=1.3;往下(速度)
PVector p;//flaot x=50,y=100;(座標)
void setup(){//設定
  size(500,500);//視窗大小
  v = new PVector(1,1.3);//設定速度
  p = new PVector(50,60);//設定座標
}
void draw(){//畫圖
  background(#FFFFF2);//將背景清空才不會有殘影
  ellipse(p.x,p.y,3,3);//畫一個3x3(線粗細)的x,y座標
  p.add(v);//p的座標要加上v的速度//x += vx; y += vy;
  line(0,500,mouseX,mouseY);
  ellipse(mouseX,mouseY,150,150);//畫圓
  //要如何做出反彈效果?
  if(dist(p.x,p.y,mouseX,mouseY)<75){//一旦碰撞發生,距離也要改成mouseX,mouseY(球才會在表面進行反彈)
    //vx = -vx;
    PVector c = new PVector(mouseX,mouseY);//圓心(可以用滑鼠操控圓,但是會有殘影,要先將背景清空)
    PVector n = PVector.sub(p,c).normalize();//單位法向量,要初始化做減法,把p跟圓心做相減再取法向量
    //完美的入射角等於反射角
    float green = PVector.dot(v,n);//綠色線的長度(做功)=PVector做內積(把速度跟法向量做內積)
    PVector v2 = PVector.mult(n,-green*2);//乘法,v2是綠色的長度(功)//原來的方向加上2倍的負方向
    v.add(v2);//v加上v2
  }
}



當小球觸碰到大球時會顯示法向量的線條
PVector v;//float vx=1往右,vy=1.3;往下(速度)
PVector p;//flaot x=50,y=100;(座標)
void setup(){//設定
  size(500,500);//視窗大小
  v = new PVector(1,1.3);//設定速度
  p = new PVector(50,60);//設定座標
  frameRate(10);//每秒刷新多少幀數,一秒鐘刷新10幀
}
void draw(){//畫圖
  background(#FFFFF2);//將背景清空才不會有殘影
  ellipse(p.x,p.y,3,3);//畫一個3x3(線粗細)的x,y座標
  p.add(v);//p的座標要加上v的速度//x += vx; y += vy;
  //line(0,500,mouseX,mouseY);(可省略,不用畫輔助線)
  ellipse(mouseX,mouseY,150,150);//畫圓
  //要如何做出反彈效果?
  if(dist(p.x,p.y,mouseX,mouseY)<75){//一旦碰撞發生,距離(dist)也要改成mouseX,mouseY(球才會在表面進行反彈)
    //vx = -vx;
    PVector c = new PVector(mouseX,mouseY);//圓心(可以用滑鼠操控圓,但是會有殘影,要先將背景清空)
    PVector n = PVector.sub(p,c).normalize();//單位法向量,要初始化做減法,把p跟圓心做相減再取法向量
    //完美的入射角等於反射角
    float green = PVector.dot(v,n);//綠色線的長度(做功)=PVector做內積(把速度跟法向量做內積)
    PVector v2 = PVector.mult(n,-green*2);//乘法,v2是綠色的長度(功)//原來的方向加上2倍的負方向
    v.add(v2);//v加上v2
    line(c.x,c.y,p.x,p.y);//(當小球碰到大球會閃過法向量)
  }
}

沒有留言:

張貼留言