向量判断划动方向
1. 背景
在当下手机游戏开发过程中.面对的不在是物理按键,一切操作都在一个可触摸的手机屏幕上.从用户按下屏幕开始,到移动手指,到结束.这段操作我们称其为滑动操作.单从用户的角度,一眼就知道当前滑动的方向.可程序还是要通过数据才能判断.大体上需要如下要素
- 笛卡尔坐标系,右手坐标系,逆时针方向夹角为正
- 触摸开始点 p1(x1,y1)
- 触摸结束点 p2(x2,y2)
在Cocos creator 中使用节点的TOUCH_START
和TOUCH_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 imes vec p_2 =x_1y_2-x_2y_1$
假设向量p1与p2之间的夹角为$ heta$ 则
$vec p_1 imes vec p_2 =|vec p_1||vec p_2|sin heta$
叉乘的结果也是一个向量,在二维坐标系中,它的结果等两个向量构成平行四边形的面积.在三维坐标系中,表示同时垂直于这两个向量的向量.也叫做,这两个向量构成平面的法向量.
所以通过以上两个公式结合,就可以得到两个向量夹角的$sin( heta)$值,结合sin函数图像,再结合下图可知
当p2在p1逆时针方向,夹角$ hetain[0,pi],则 sin( heta)>0$,所以有$vec p_1 imes vec p_2gt0$
当p2在p1顺时针方向,夹角$ hetain[-pi,0],则 sin( heta)<0$,所以有$vec p_1 imes vec p_2lt0$
当p2 p1共线时,夹角$ heta=0^circ或者 heta=pi或者 heta=-pi$,所以有$vec p_1 imes 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,形成如下图所示
所以最终判断,转换成夹角范围的判断.所有有
- 左划夹角范围 $[0,pi/4]或者[-pi/4,0]$
- 上划夹角范围 $(pi/4,3pi/4)$
- 右划夹角范围 $[3pi/4,pi]或者[-pi,-3pi/4]$
- 下划夹角范围 $(-3pi/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注定是不平凡的一年.加油!!
欢迎感兴趣的朋友关注我的订阅号“小院不小”,或点击下方二维码关注。我将多年开发中遇到的难点,以及一些有意思的功能,体会都会一一发布到我的订阅号中