摘要瞄准线分三种:无效果直射、遇墙反射、遇墙与球体反射。今天 KUOKUO 用一次函数与绘图组件来实现第二种:遇墙反射。

正文

版本说明

使用 CocosCreator 的 2.2.1 版本演示。用一次函数来实现瞄准线的反射_小游戏

一次函数

相信大家都知道一次函数 y = k·x + b,如下图,它就是条直线。用一次函数来实现瞄准线的反射_死循环_02只要确认了 k 与 b,就确认了这条直线。b 的值是如何求的呢?x = 0 时对应的 y 值。所以 b 的值体现在 y 轴上。那么它如何应用于瞄准线呢?

层级

如下层级,有一个带有绘图组件的节点 Draw Mgr。(画布是 720 * 1280)。用一次函数来实现瞄准线的反射_死循环_03

实现原理

我们以绘图节点为中心建立 X-Y 坐标系。左边界是 -360,右边界是 360。
  1. /** 左右边界及总宽 */

  2. let POS = cc.Enum({

  3. LEFT: -360,

  4. RIGHT: 360,

  5. WIDTH: 720,

  6. });

用一次函数来实现瞄准线的反射_微信_04如图,我们先通过第一次与边界相交来求得 b 的增量。观察中心的点位与边界点位,b 的值总是边界 y 值加上增量值。
  1. // 算一下 b 的增长值

  2. let d_b = (k > 0? POS.RIGHT : POS.LEFT) * k;

  1. b = y + d_b;

长度削减

我们先指定长度。然后在一个死循环里,不断的判断预计达到的边界长度是否是小于剩余长度的。如果是够长的,进行削减。如果不够长了,我们要判断两种情况,是一开始就不够长还是反弹到最后不够长,用一个 isReBound 标志判断。
  1. drawLine (pos) {

  2. this.draw.clear();

  3. let lineLength = 1200;

  4. let k = pos.y / pos.x;

  5. // 预计到达的边界点

  6. let point = cc.v2(0, 0);

  7. // 画笔到起始点

  8. this.draw.moveTo(0, 0);

  9. let b = 0;

  10. let x, y;

  11. // 算一下 b 的增长值

  12. let d_b = (k > 0? POS.RIGHT : POS.LEFT) * k;

  13. // 起始标志

  14. let isRebound = false;

  15. while(true) {

  16. // 如果到墙,求与墙的交点

  17. x = k > 0? POS.RIGHT : POS.LEFT;

  18. // 一元函数 y = k·x + b

  19. y = k * x + b;

  20. // 到达墙壁所需长度

  21. let l = cc.v2(x, y).sub(point).mag();

  22. // 判断能否到墙

  23. if(l < lineLength) {

  24. isRebound = true;

  25. // 扣去已经过长度

  26. lineLength -= l;

  27. this.draw.lineTo(x, y);

  28. // 更改下一轮循环起始点

  29. point.x = x;

  30. point.y = y;

  31. b = y + d_b;

  32. k *= -1;

  33. } else{

  34. // 如果不能到墙,分为两种情况,需要一个标志

  35. if(isRebound) {

  36. let l_k = lineLength / l;

  37. let r_x = POS.WIDTH * l_k;

  38. x = k > 0? POS.LEFT + r_x : POS.RIGHT - r_x;

  39. y = k * x + b;

  40. } else{

  41. let l_k = lineLength / l;

  42. let r_x = POS.WIDTH / 2* l_k;

  43. x = k > 0? r_x : -r_x;

  44. y = k * x;

  45. // 中心处理

  46. if(x > -0.05&& x < 0.05);

  47. y = lineLength;

  48. }

  49. this.draw.lineTo(x, y);

  50. break;

  51. }

  52. }

  53. this.draw.stroke();

  54. },

在最中心时,由于过于接近 0 会导致瞄准线不可见,所以限制了 -0.05 到 0.05。

清除线

  1. clearLine () {

  2. this.draw.clear();

  3. },

触摸监听与坐标转化

脚本绑定于 Canvas,手指触摸时基于 Canvas 节点转化坐标,但是 Draw Mgr 节点的坐标不是 0,0 所以要做差。
  1. start () {

  2. this.node.on(cc.Node.EventType.TOUCH_START, (e) => {

  3. let pos = this.node.convertToNodeSpaceAR(e.getLocation());

  4. pos.x -= this.draw.node.x;

  5. pos.y -= this.draw.node.y;

  6. this.drawLine(pos);

  7. }, this);

  8. this.node.on(cc.Node.EventType.TOUCH_MOVE, (e) => {

  9. let pos = this.node.convertToNodeSpaceAR(e.getLocation());

  10. pos.x -= this.draw.node.x;

  11. pos.y -= this.draw.node.y;

  12. this.drawLine(pos);

  13. }, this);

  14. this.node.on(cc.Node.EventType.TOUCH_END, (e) => {

  15. this.clearLine();

  16. }, this);

  17. },

最后效果

用一次函数来实现瞄准线的反射_小游戏

结语

其实不难,学会了吧!