向量判断划动方向

向量判断划动方向

1. 背景

在当下手机游戏开发过程中.面对的不在是物理按键,一切操作都在一个可触摸的手机屏幕上.从用户按下屏幕开始,到移动手指,到结束.这段操作我们称其为滑动操作.单从用户的角度,一眼就知道当前滑动的方向.可程序还是要通过数据才能判断.大体上需要如下要素

  • 笛卡尔坐标系,右手坐标系,逆时针方向夹角为正
  • 触摸开始点 p1(x1,y1)
  • 触摸结束点 p2(x2,y2)

在Cocos creator 中使用节点的TOUCH_STARTTOUCH_END收集p1和p2


//注册事件 
this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
this.node.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);

...

onTouchStart(event: cc.Event.EventTouch) {
    this.touchStartPoint = event.getLocation();
}
onTouchEnd(event: cc.Event.EventTouch) {
    this.touchEndPoint = event.getLocation();
    this.calcSlideDir();
}

2. 两点判断法

设 dx=x2-x1
如果 dx > 0 则p2在p1的左边,说明当前划动可能为左划
如果 dx = 0 则p2 p1 共线,说明当前为上划或者下划
如果 dx < 0 则p2在p1的右边,说明当前划动可能为右划

设 dy=y2-y1
如果 dy > 0 则p2在p1上方,说明当前划动可能为上划
如果 dy = 0 则p2 p1共线,说明当前为左划或者右划
如果 dy < 0 则p2在p1下方,说明当前滑动可能为下划

可见单纯从 x y 来比较都不能判断具体方向,所以我们还得让 dx dy竞争下,谁的模长,谁占优势,如果相等,方向以dy为准.当然,从有效性方面考虑,我们还得判断下是否滑动了足够长的距离,假设滑动2px有效.

如下代码:

 //author:herbert wx:464884492 
 //加群,请回复消息 cocos
enum Dir(LEFT,RIGHT,UP,DOWN,NONE);

calcDir(p1:cc.Vec2,p2:cc.Vec2):Dir{
   let dir:Dir=Dir.NONE;
   let dx=p2.x-p1.x;
   let dy=p2.y-p1.y;
   if(dx*dx<4&&dy*dy<4){
       return dir;
   }
   if(dx*dx > dy*dy){
      if(dx>0)dir=Dir.LEFT;
      if(dx<0)dir=Dir.RIGHT;
      if(dx==0){
          if(dy>0)dir=Dir.UP;
          if(dy<0)dir=Dir.DOWN;
      }
   }
   return dir;
}

3. 向量叉乘判断

在坐标系中,向量是一个有方向和大小的矢量.向量分为叉乘和点乘.在坐标系中,将点与原点连接,就构成一个向量.向量叉乘有如下公式

$\vec p_1 \times \vec p_2 =x_1y_2-x_2y_1$

假设向量p1与p2之间的夹角为$\theta$ 则

$\vec p_1 \times \vec p_2 =|\vec p_1||\vec p_2|sin\theta$

叉乘的结果也是一个向量,在二维坐标系中,它的结果等两个向量构成平行四边形的面积.在三维坐标系中,表示同时垂直于这两个向量的向量.也叫做,这两个向量构成平面的法向量.

所以通过以上两个公式结合,就可以得到两个向量夹角的$sin(\theta)$值,结合sin函数图像,再结合下图可知

坐标示意图

当p2在p1逆时针方向,夹角$\theta\in[0,\pi],则 sin(\theta)>0$,所以有$\vec p_1 \times \vec p_2\gt0$
当p2在p1顺时针方向,夹角$\theta\in[-\pi,0],则 sin(\theta)<0$,所以有$\vec p_1 \times \vec p_2\lt0$
当p2 p1共线时,夹角$\theta=0^\circ或者\theta=\pi或者\theta=-\pi$,所以有$\vec p_1 \times \vec p_2=0$

然后结合当前坐标系是左手系,还是右系.即可大概判断划动方向.在cocos creator中判断正角和负角就使用了叉乘

 //author:herbert wx:464884492 
 //加群,请回复消息 cocos
  /**
   * !#en Get angle in radian between this and vector with direction.
   * !#zh 带方向的夹角的弧度。
   * @method signAngle
   * @param {Vec2} vector
   * @return {number} from -MathPI to Math.PI
   */
  signAngle (vector: Vec2): number {
      let angle = this.angle(vector);
      return this.cross(vector) < 0 ? -angle : angle;
  }

可见用向量叉乘判断划动方向,并不方便.

4. 向量减法判断

其实滑动方向,就是向量p2减去向量p1.

向量算法

然后选择单位向量(1,0)作为参考向量,根据计算的向量与单位向量的夹角值判断,即可判定划动方向.在坐标系中分别画两条参考 y=x,以及y=-x,形成如下图所示

dot

所以最终判断,转换成夹角范围的判断.所有有

  • 左划夹角范围 $[0,\pi/4]或者[-\pi/4,0]$
  • 上划夹角范围 $(\pi/4,3\pi/4)$
  • 右划夹角范围 $[3\pi/4,\pi]或者[-\pi,-3\pi/4]$
  • 下划夹角范围 $(-3\pi/4,-\pi/4)$

在cocos中可以是向量方法signAngle得到计算两个带有方向的向量夹角,参考代码如下

  //author:herbert wx:464884492 
  //加群,请回复消息 cocos
   enum Dir(LEFT,RIGHT,UP,DOWN,NONE);
   calcDir(p1:cc.Vec2,p2:cc.Vec2):Dir{
        let dir:Dir=Dir.NONE;
        let subVec: cc.Vec2 = p2.sub(p1);
        if (subVec.mag() < 0.5) {
            return dir;
        }
        subVec.normalizeSelf();
        let ang = cc.v2(1, 0).signAngle(subVec);
        if (-Math.PI / 4 <= ang && ang <= Math.PI / 4) {
            dir = Dir.LEFT;
        }
        if (Math.PI / 4 < ang && ang < 3 * Math.PI / 4) {
            dir = Dir.UP;
        }
        if ((3 * Math.PI / 4 <= ang && ang <= Math.PI) || (-Math.PI <= ang && ang <= -3 * Math.PI / 4)) {
           dir = Dir.RIGHT;
        }
        if (-3 * Math.PI / 4 < ang && ang < -Math.PI / 4) {
           dir = Dir.DOWN;
        }
        return dir;
    }

5. 总结

对于夹角方向判断,需要确定当前坐标系是左手还是右手.然后手握旋转轴,其余四指弯曲方向,便是正角方向.

知识虽小,重在积累.2020注定是不平凡的一年.加油!!

欢迎感兴趣的朋友关注我的订阅号“小院不小”,或点击下方二维码关注。我将多年开发中遇到的难点,以及一些有意思的功能,体会都会一一发布到我的订阅号中
订阅号

posted @ 2020-03-31 10:52  _herbert  阅读(728)  评论(0编辑  收藏  举报