2012年11月22日 星期四

期末Project 第10組


主題:接水流小遊戲

遊戲方式與規則:
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了,天啊......





沒有留言:

張貼留言