用一次函数来实现瞄准线的反射

摘要

瞄准线分三种:无效果直射、遇墙反射、遇墙与球体反射。今天 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~~

源码在我的微信公众号回复关键词【瞄准线】即可获得

微信公众号

发布了120 篇原创文章 · 获赞 133 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/kuokuo666/article/details/103438506