摘要
瞄准线分三种:无效果直射、遇墙反射、遇墙与球体反射。今天 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~~
源码在我的微信公众号回复关键词【瞄准线】即可获得