主題:接水流小遊戲
遊戲方式與規則:
1.空中會有水龍頭在特定時間噴射固定量的水柱,畫面中有許多黑色的方塊會讓水流改變方向
2.以雙手去補足黑色方塊的位置,讓水可以正確地流到灰色的終點
3.根據所接到的水量來決定得分以及過關判定條件。
如下圖所示,藍色的水流噴到黑色物體之後往錯誤的方向移動,所以錯過了灰色的終點
必須放上自己的手去調整位置,才可以導引水流到正確的終點
可能會用到的技術:
1.手的偵測,考慮是否戴上特定顏色的手套以區分手跟臉
2.水流的判定,考慮將水流做成大量的小球,每一個小球物件都有他的生命值。
3.物件的碰撞判定,考慮套用openGL的繪圖功能
11/29老師有提出黑色方塊與水滴的軌跡要如何運算
因此想了一些目前可行且算得出來的方法和想法
首先是畫面中的藍色軌跡,雖然說是水流,但實際上應該是會作一個迴圈去製造1000個小圓形,並且讓這些小圓形不間斷地連續發射,製造出水珠連在一起的效果。
每一個圓形是一個class作出來的,有幾個重要的值如下:
起始的XY速度 int speedX,speedY;
基本上就是給定一個初始速度之後,會讓球朝著XY的方向前進,即向量。
目前的位置XY值 int localX,localY;
用來紀錄位置,偵測碰撞是否會發生,即座標
因為水球有初始速度,所以最後一定會跑到畫面之外,除非被障礙物擋住,那就會反射往另一個方向繼續前進,而且每個水球都有重力(Gravity)的概念,最後一定會往下掉。
為了省去不必要的因素與麻煩,遊戲中的摩擦力以及反彈力都預設為無耗損,完全反彈。也就是說每一顆球(小圓形)碰撞到任何物體,都是會完全鏡射的方式反彈。從發射器出發之後,也不會因為有任何的空氣阻力而減緩到前進或下墜的速度。
再來是畫面中黑色障礙物的部份,是先繪製矩形,這些矩形的大小(width,height)和旋轉角度(ankle)會先設計好,在水珠碰撞到這些障礙物的時候作反射的計算。
最後是終點的水杯,設定一個範圍,只要水球進入這個範圍就可以得分,而且水球也會因此消滅,理論上比反彈的判定容易一些。
int Xs,Ys,Xd,Yd;
//發射器的起始點座標和向量初始值
int gravity = 5;
//重力調成9.8太重了,直接對半切,4.9大約等於5
struct wBall
{
int id;
int nowX = Xs;
int nowY = Ys; //現在的位置就是發射器的初始位置
int DirectX = Xd;
int DirectY = Yd; //套用發射器的起始向量
boolean life = 0; //一開始是死掉的(還沒出現)
}
障礙物的設定
如果一條在座標上的線段為兩個點(X1,Y1),(X2,Y2)
那麼該線段的方程式可寫作
Y = m X + b
其中斜率m = (Y1-Y2)/(X1-X2), 參數b = X2Y1-X2Y1
可推得 Y = X[(Y1-Y2)/(X1-X2)]+ (X2Y1-X2Y1)
且方向向量為Vd = (X2-X1,Y2-Y1),法向量為 Vn = (Y2-Y1,X1-X2)
如果有一個向量V1(Xa,Ya)接觸到這條線上,其入射角Ain會等於(V1‧Vd)/|V1|*|Vd|
將Vd反向之後就可以反推得反射角Aout = Ain
特別要注意到的是,視訊上的座標跟一般XY座標上下是顛倒的
預設一個水球從被產生出來到它消失的流程
create Emitter:
Xs = 320;
Ys = 20; //座標(320,20)
Xd = 3;
Yd = 0; //方向向右,速度為3個pixels
for(int i = 0; i <= 100; i++) //總共有100顆球
{
wBall ball[i] = new wBall();
ball[i].life = 1; //球的出現
ball[i].nowX += Xs+ball[i].directX;
ball[i].nowY += Ys+ball[i].directY+gravity;
//每次座標都會更新
if(nowX<0 || nowX >Width || nowY<0 || nowY > Height)
{
//判斷是否還在畫面以內
ball[i].life = 0; //球就消失了
delete ball[i];
lossPointCount++; //計算失分
}
//假設有個線段放在(350,70)~(300,120)
//其線段的方程式為Y = X - 280 : 300<x<350,70<y<120
if((directX>0 && nowX >300) || nowY<0 || nowY > Height)
{
//判斷是否碰到障礙物
//計算失分
}
}
第16週新增方法:
旋轉矩陣R
如果遇到以下四種線段
1.水平左右
斜率為0,那麼球碰到線的時候X方向不變,Y方向直接反彈。
2.左上到右下
斜率為正的,球碰到線的時候,旋轉R度把線轉為水平的,反彈之後再把結果旋轉-R度以求得正確的反彈角度。
3.右上到左下
斜率為負的,方法同上。
4.垂直上下
斜率不存在,所以直接做X方向的反彈,Y方向則不變。
如圖所示,兩百顆球的碰撞情形,接下來就是設定更多條的線段了
而線段的畫法,是以迴圈去繪製許多小球,而小球的座標連在一起看起來就是一條直線。
如上圖的粉紅色直線,其座標為(0,160)到(640,320),其方程式可以寫成Y=0.25*X+160,可以得到該方向向量為(4,1),所以可以寫個迴圈
Int X = 0,Y = 160;
for(int j = 0;j<160;j++)
{
X
= X+4*j;
Y
= Y+j;
//在此畫160顆球,就可以畫出一條直線
}
運用以上方法,可以畫出許多條線
判定旋轉的函式
void rotate(double vectorProcessX, double vectorProcessY,double nVectorX,double nVectorY,int i);
裡面下了5個參數
vectorProcessX,vectorProcess
分別代表要處理的球向量XY
nVectorX,nVectorY
分別代表碰撞到的這條線的方向向量是多少(以方便計算該斜率)
i
區分是哪一顆球去撞到
裡面的判定是當球碰撞瞬間,計算出該線段的斜率,就可以抓到角度A的cosA和sinA值
利用旋轉矩陣[(cosA,-sinA),(sinA,cosA)]可以逆時針旋轉A度的特性
把整個碰撞系統的球向量也一起旋轉,變成水平反射之後再使用順時針旋轉將線段和向量轉回原本的斜度
測試之後發現誤差極小,大約只會有小數點後第6位的誤差
最後放上DEMO影片連結
http://www.youtube.com/watch?v=CaMN3eSc6tI
呃.....我不小心錯過了moodle的上傳時間,還有沒有救啊.....?
我們的報告在DEMO當天就已經完成了,只有DEMO影片是星期五晚上錄的
可是剛才我想起來我忘記上傳moodle了,天啊......
沒有留言:
張貼留言