打算写一个Cocos Creator 3D组件系列的文章
记录下平时常用的组件,减少开发时间,如果有更好的实现方法,欢迎交流
这篇是实现一个「操纵杆」,先上效果
1 实现了什么
- 支持跟随手指操纵杆出现位置
- 支持4向、8向、任意方向移动
2 跟随实现
首先监听屏幕上的触摸事件,分别为
- 触摸开始
- 触摸移动
- 触摸结束
- 触摸取消
如下:
this.node.on(Node.EventType.TOUCH_START, this.callbackTOUCH_START, this);
this.node.on(Node.EventType.TOUCH_MOVE, this.callbackTOUCH_MOVE, this);
this.node.on(Node.EventType.TOUCH_END, this.callbackTOUCH_END, this);
this.node.on(Node.EventType.TOUCH_CANCEL, this.callbackTOUCH_CANCEL, this);
当用户在屏幕上触摸开始时,就刚点击屏幕的一瞬间,获取到手指触摸屏幕的位置,设置操纵杆的位置为手指触摸的位置,实现如下:
let UITC: UITransformComponent = this.node.getComponent(UITransformComponent);//ui坐标转化组件
let localPosition: Vec3 = new Vec3();
let touch: Touch = event.touch;//触摸事件,存储触摸的位置坐标等
UITC.convertToNodeSpaceAR(new Vec3(touch.getUILocation().x, touch.getUILocation().y, 0), localPosition);//转化坐标
this.node.getChildByName("joystickBg").setPosition(localPosition);
this.node.getChildByName("joystickBar").setPosition(localPosition);
❝在Cocos3D中坐标转化需要拿到一个「UITransformComponent」组件,通过获取到手指触摸的「ui坐标」转化为操纵杆需要的「本地坐标」
❞
上述就简单实现了跟随功能,难点是在「坐标转化」
3 方向移动实现
3.1 方向移动
我们之前监听了触摸移动事件,这时候需要用到了,思路如下
- 拿到手指移动时候的触摸点的位置坐标,判断手机的触摸点是在操控杆的圆内,返回操纵点的最后位置,并设置操纵杆的点的位置
- 判断触摸点方向,四向或者八向或者任意方向
3.1.1 设置操纵杆点的位置
获取手指触摸点离操纵杆的距离,判断是否在圆内
在园内就直接设置触摸点的坐标为操纵杆圆点的坐标
不在园内就获取该触摸点和操纵杆圆点所在直线和圆的交点,通过向量,取一方向的交点为设置操纵杆圆点的坐标
/**
* 获取两点间的距离
* @param p1 点1
* @param p2 点2
*/
private getDistance(p1: Vec2, p2: Vec2): number {
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
}
let dis = this.getDistance(nodePositionV2, cameraPt);//获取两点之间的距离,判断是否在园内
//距离和半径做比较
if (dis < rx) { //在园内
result = cameraPt;//手指触摸点位置为操纵杆圆点位置
} else {
let intersectionPosition = this.getInsertPointBetweenCircleAndLine(nodePositionV2, cameraPt, rx);//获取到圆外的点和圆之间的交点
let sub = cameraPt.subtract(nodePositionV2);//向量减法,判断方向
if (sub.x > 0) {
result = intersectionPosition[0];
} else {
result = intersectionPosition[1];
}
}
3.2 判断触摸点方向
拿到触摸点移动的位置和操纵杆中心的位置
3.2.1 四方向
触摸点和操纵杆原点相减
let x = touchPosition.x - localPosition.x;//x方向的位移
let y = touchPosition.y - localPosition.y;//y方向的位移
判断x方向的位移是否大于y方向的位移,如果大于,则向左或者向右移动,反之亦然。再判断x是否大于0来判断向左或者向右位移
if (Math.abs(x) > Math.abs(y)) {
if (x > 0) {
this.direction = Dir.RIGHT;
} else {
this.direction = Dir.LEFT;
}
} else {
if (y > 0) {
this.direction = Dir.UP;
} else {
his.direction = Dir.DOWN;
}
}
3.2.2 八方向
我们把360度分成了8份,一个方向为45度,正右边为0度,所以-22.5到22.5为右方向,依次列出8个方向
我们通过点获取弧度,通过弧度获取角度,实现如下:
//弧度
let radian = Math.atan2(y, x);
//角度 = 弧度 * 180 / Math.PI;
let angel = radian * 180 / Math.PI;
//angel: +x:0 +y:90 -x:(180||-180) -y:-90
❝atan2函数返回的是原点至点(x,y)的方位角,即与 x 轴的夹角。也可以理解为复数 x+yi 的辐角。返回值的单位为弧度
❞
设置方向
if (angel >= -22.5 && angel < 22.5) {
this.direction = Dir.RIGHT;
} else if (angel >= 22.5 && angel < 67.5) {
this.direction = Dir.UPRIGHT;
} else if (angel >= 67.5 && angel < 112.5) {
this.direction = Dir.UP;
} else if (angel >= 112.5 && angel < 157.5) {
this.direction = Dir.UPLEFT;
} else if ((angel >= 157.5 && angel <= 180) || (angel >= -180 && angel < -157.5)) {//特殊处理临界点
this.direction = Dir.LEFT;
} else if (angel >= -157.5 && angel < -112.5) {
this.direction = Dir.DOWNLEFT;
} else if (angel >= -112.5 && angel < -67.5) {
this.direction = Dir.DOWN;
} else if (angel >= -67.5 && angel < -22.5) {
this.direction = Dir.DOWNRIGHT;
}
3.2.3 任意方向
和八方向一致,先拿到角度,在转化为人物的角度,
let radian = Math.atan2(y, x);
let angelLocal = radian * 180 / Math.PI;
//angelLocal: +x:0 +y:90 -x:(180||-180) -y:-90
if (angelLocal >= -90 && angelLocal <= 180) {//坐标转化
this.angel = angelLocal + 90;
} else if (angelLocal >= -180 && angelLocal < -90) {
this.angel = angelLocal + 450;
}
//angel: +x:90 +y:180 -x:270 -y:360
❝人物的旋转角度为0~360,点的角度为0~180,需要再次转化坐标
❞
在任意角度中,除了需要转方向,还需要求出在x方向和y方向的占比,赋予x方向速度多少和y方向速度多少
x = Math.sin(Math.PI/180*this.angel);
y = Math.cos(Math.PI/180*this.angel);
❝在四方向和八方向中操纵杆默认是1的速度,人物可以设置自己的速度
任意方向中需要获取x或者y向的速度
❞
最后
完整代码已经上传至github https://github.com/keien411/eat_food_3d,
欢迎来收藏订阅
不定期更新cocos creator3D 组件代码。
本文使用 mdnice 排版