一、
相似三角形知識的應用在搖桿控制物體運動的遊戲中,搖桿的手柄(下圖黃色圓餅),不能移出搖桿所在的套(下圖灰色圓環),也就是說搖桿偏離中心點的最大距離為max_R。一旦觸摸移動過程中移動的點超出此最大距離,我們需要將搖桿手柄拖回到最大距離處。
如何將搖桿拖回到灰色圓環處,無非是要求出在C點的座標。根據△ODC和△OBA為相似三角形,有OD:OB = OC:OA,也就是?:pos.x = max_R:len。
其中pos為A點的座標(x,y),len為向量OA的模。
?表示C點的x座標,也就是但是我們求出來,要作為新的pos,去設置搖桿的位置。
所以有:
二、 單位向量
在搖桿控制物體的運動時,如果搖桿只控制物體的方向,也就是說不管搖桿偏離原點多遠,都不會影響物體運動的速度,而隻影響物體運動的方向,也就是實質上我們要獲取一個方向的值(比如向量與x軸正方向的夾角),這時候,我們就需要使用單位向量。
單位向量是指向量的模為1的向量,也就是以一個單位長度為半徑畫一個圓,從圓心指向圓周上任意一點的向量都是單位向量。
假設單位向量OA與x軸正向的夾角為α,則向量OA可以用(cosα,sinα)表示,也就是A點的座標。如下圖所示:
所以不管任何向量,我們要轉換為單位向量,只需要計算出這個向量的餘弦和正弦值就可以了。例如向量OC我們將其轉換為單位向量就是OC’,因為OC’的長度為1,所以向量OC’的座標就是(cos(c),sin(c)),c為OC和x軸正方向的夾角。而OC’和OC的方向是重合的,他們與x軸正向夾角相等,也就是我們只需要計算向量OC的cos(c)、sin(c)即可。
假設得到的C點座標為pos(x,y),則在Cocos遊戲開發中,會有下列代碼:
var len = pos.mag(); // 求向量pos的模(內部算法是(x2+y2)開根號)。
pos.x = pos.x / len; // 得到單位向量的x座標
pos.y = pos.y/ len; // 得到單位向量的y座標
三、 弧度角度轉換
角度指從原點發出的射線與X軸正向的角度。而x軸正向繞原點在水平平面內旋轉一週為360°。而弧度是指圓弧長度與半徑的比值。假設有個半徑r為1的圓,則其一週為360°。圓周對應的弧度長為2πr=2π。也就是說在角度轉換為弧度是360°等價於2π,180°等價於π。
(1)弧度轉換為角度:根據 degree : 180 = rad:π
var rad = Math.atan2(y, x); // 反正切函數,得到弧度
var degree = 180 * rad / Math.PI ; // 將弧度rad轉換為角度
(2)角度轉換為弧度:根據rad:π=degree:180
var degree = 90;
var rad = Math.PI * degree / 180; // 角度轉換為弧度
四、 向量轉換為夾角
在遊戲中,有時候我們需要計算物體旋轉了多少度,比如我們要模擬一個KTV裡面的遊戲轉盤,手指觸摸的時候,要獲取觸摸的點的位置,觸摸點的位置與轉盤中心點之間構成一個向量,我們要獲取轉動了多少度,就需要有一個參考向量。
如下圖所示,假如藍色的圓就是轉盤的邊緣,我們最開始觸摸A點,然後轉動到B點,這時候轉動的角度就是∠AOB,這個角度在Cocos中如何求呢?
代碼pos就表示B點的座標,pos和原點(0,0)相減,得到向量OB,也即代碼中的dirVec,弧AB為radian,轉換為角度degree。
// 向量轉換為角度
vectorsToDegree(dirVec) {
// 水平向右的對比向量
let comVec = cc.v2(1, 0);
// 求方向向量與對比向量間的弧度
let radian = dirVec.signAngle(comVec);
// 將弧度轉換為角度
let degree = cc.misc.radiansToDegrees(radian);
return degree;
},
調用示例(具體可以參考我們的《KTV酒令轉盤虛擬仿真實現》):
// 獲取觸摸點與原點連線的射線與X軸正向的角度
getTouchAngle(e){
var screen_pos = e.getLocation(); // 觸摸點世界座標
// 轉換為相對於當前節點的錨點的座標
var pos = this.node.convertToNodeSpaceAR(screen_pos);
// 獲取觸摸點距離輪盤中心點的向量
var dirVec = pos.sub(cc.v2(0,0));
// 將向量轉換為基於參考方向(v2(0,1))的角度
return this.vectorsToDegree(dirVec);
},
五、 切水果遊戲中的拋物線
重力加速g:
Vy = V0 + g * t;
h = V0 * t + 0.5 * g * t * t;
斜拋運動(如下圖所示):
分解到水平上: 勻速直線運動;
分解到豎直方向: 勻變速直線運動 (加速度)。
具體在Cocos中如何實現,限於篇幅,在此不做贅述,可以參考我們的公開課《切水果》核心技術:拋物線物理仿真。
六、 如何產生不重複的隨機序列
應用場景:例如洗牌,發牌,例如掃雷遊戲中從100個格子中隨機抽取10個埋雷。
凡是需要打亂順序,或者從某個數組中抽取一定數量的不重複的元素,都用得著。
// 數組工具類
var ArrayUtils = function(){};
// 【1】初始化得到有序元素數組:[start,end)的自然數序列
ArrayUtils.initOrderArray = function(start, end){
let sortArray = [];
for(let i= start; i sortArray.push(i); } return sortArray; }; // 【2】從數組arr中隨機抽取count個元素,返回數組
ArrayUtils.randChoiseFromArr = function(arr, count){
let result = arr;
// 隨機排序,打亂順序
result.sort(function(){
return 0.5 - Math.random();
});
// 返回打亂順序後的數組中的前count個元素
return result.slice(0,count);
};
// 【3】從從start到end中的連續整數中隨機抽取count個數字
ArrayUtils.randChoiseFromTo = function(start, end, count){
let arr = this.initOrderArray(start,end);
return this.randChoiseFromArr(arr, count);
};
// 【2】-【方式二】從數組arr中隨機抽取count個元素,返回數組
ArrayUtils.getRandomArrayElements = function(arr, count) {
// 從0位置取到結束位置存入shffled數組
let shuffled = arr.slice(0);
let i = arr.length;
let min = i - count;
let temp = 0;
let index = 0;
// 隨機一個位置的元素和最後一個元素交換
// 隨機一個位置元素和倒數第二個元素交換
// 假設i=8,count=3,則min=5,
// 循環體中[i]=7,6,5,也就是說最後三個元素要從數組中隨機取
// 循環結束後,從min=5的位置取到結束,即取3個元素。
while(i-- > min) {
index = Math.floor((i + 1) * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;
}
return shuffled.slice(min);
};
module.exports = ArrayUtils;
原理解析:如上述代碼,方式一randChoiseFromArr 方法,採用隨機排序打亂數組中所有元素的順序,然後從數組第一個元素開始抽取需要的count個元素。
方式二getRandomArrayElements方法,需要取幾個元素,就隨機幾個位置和倒數最後幾個元素交換,最後取倒數最後幾個元素即可。注意:index = Math.floor((i
+ 1) * Math.random()); i+1才能保證最後一個元素可能是自身和自身交換,也就是保證這個位置的元素可能是原有這個位置的元素。上面所有代碼我們已經全部測試通過,感興趣的朋友可以自己動手去實踐。
相似三角形、單位向量轉換、弧度角度轉換,大家可以看我們的公開課《Cocos Creator坦克大戰遊戲》。向量轉換為夾角在我們公開課《KTV酒令轉盤虛擬仿真實現》中講過。如何產生不重複的隨機序列,在掃雷遊戲中應用。需要這方面資料視頻的朋友,可以點擊下面鏈接加群瞭解點擊鏈接加入群聊【cocos/unity交流群】
閱讀更多 遊戲開發達人 的文章