摘要
瞄準線分三種:無效果直射、遇牆反射、遇牆與球體反射。今天 KUOKUO 用一次函數與繪圖組件來實現第二種:遇牆反射。
正文
版本說明
使用 CocosCreator 的 2.2.1 版本演示。
一次函數
相信大家都知道一次函數 y = k·x + b,如下圖,它就是條直線。
只要確認了 k 與 b,就確認了這條直線。b 的值是如何求的呢?x = 0 時對應的 y 值。所以 b 的值體現在 y 軸上。那麼它如何應用於瞄準線呢?
層級
如下層級,有一個帶有繪圖組件的節點 Draw Mgr。(畫布是 720 * 1280)。
實現原理
我們以繪圖節點爲中心建立 X-Y 座標系。左邊界是 -360,右邊界是 360。
/** 左右邊界及總寬 */
let POS = cc.Enum({
LEFT: -360,
RIGHT: 360,
WIDTH: 720,
});
如圖,我們先通過第一次與邊界相交來求得 b 的增量。觀察中心的點位與邊界點位,b 的值總是邊界 y 值加上增量值。
// 算一下 b 的增長值
let d_b = (k > 0 ? POS.RIGHT : POS.LEFT) * k;
b = y + d_b;
長度削減
我們先指定長度。然後在一個死循環裏,不斷的判斷預計達到的邊界長度是否是小於剩餘長度的。如果是夠長的,進行削減。如果不夠長了,我們要判斷兩種情況,是一開始就不夠長還是反彈到最後不夠長,用一個 isReBound 標誌判斷。
drawLine (pos) {
this.draw.clear();
let lineLength = 1200;
let k = pos.y / pos.x;
// 預計到達的邊界點
let point = cc.v2(0, 0);
// 畫筆到起始點
this.draw.moveTo(0, 0);
let b = 0;
let x, y;
// 算一下 b 的增長值
let d_b = (k > 0 ? POS.RIGHT : POS.LEFT) * k;
// 起始標誌
let isRebound = false;
while (true) {
// 如果到牆,求與牆的交點
x = k > 0 ? POS.RIGHT : POS.LEFT;
// 一元函數 y = k·x + b
y = k * x + b;
// 到達牆壁所需長度
let l = cc.v2(x, y).sub(point).mag();
// 判斷能否到牆
if (l < lineLength) {
isRebound = true;
// 扣去已經過長度
lineLength -= l;
this.draw.lineTo(x, y);
// 更改下一輪循環起始點
point.x = x;
point.y = y;
b = y + d_b;
k *= -1;
} else {
// 如果不能到牆,分爲兩種情況,需要一個標誌
if (isRebound) {
let l_k = lineLength / l;
let r_x = POS.WIDTH * l_k;
x = k > 0 ? POS.LEFT + r_x : POS.RIGHT - r_x;
y = k * x + b;
} else {
let l_k = lineLength / l;
let r_x = POS.WIDTH / 2 * l_k;
x = k > 0 ? r_x : -r_x;
y = k * x;
// 中心處理
if (x > -0.05 && x < 0.05);
y = lineLength;
}
this.draw.lineTo(x, y);
break;
}
}
this.draw.stroke();
},
在最中心時,由於過於接近 0 會導致瞄準線不可見,所以限制了 -0.05 到 0.05。
清除線
clearLine () {
this.draw.clear();
},
觸摸監聽與座標轉化
腳本綁定於 Canvas,手指觸摸時基於 Canvas 節點轉化座標,但是 Draw Mgr 節點的座標不是 0,0 所以要做差。
start () {
this.node.on(cc.Node.EventType.TOUCH_START, (e) => {
let pos = this.node.convertToNodeSpaceAR(e.getLocation());
pos.x -= this.draw.node.x;
pos.y -= this.draw.node.y;
this.drawLine(pos);
}, this);
this.node.on(cc.Node.EventType.TOUCH_MOVE, (e) => {
let pos = this.node.convertToNodeSpaceAR(e.getLocation());
pos.x -= this.draw.node.x;
pos.y -= this.draw.node.y;
this.drawLine(pos);
}, this);
this.node.on(cc.Node.EventType.TOUCH_END, (e) => {
this.clearLine();
}, this);
},
最後效果
結語
其實不難,學會了吧!
O(∩_∩)O~~
源碼在我的微信公衆號回覆關鍵詞【瞄準線】即可獲得