用一次函數來實現瞄準線的反射

摘要

瞄準線分三種:無效果直射、遇牆反射、遇牆與球體反射。今天 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~~

源碼在我的微信公衆號回覆關鍵詞【瞄準線】即可獲得

微信公衆號

發佈了126 篇原創文章 · 獲贊 138 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章