版本:2.4.10
之前用Egret时写过一个虚拟摇杆 Egret虚拟摇杆 ,这里用Cocos实现。
一 演示效果
二 摇杆原理
和Egret的虚拟摇杆实现原理是一样的,用正切函数Math.atan2来获取触摸点和原点的角度。
三 虚拟摇杆实现
1. 使用Math.atan2正切函数获取触摸点距离原点的弧度,弧度=Math.atan2(触摸点Y-原点Y,触摸点X-原点X),再把弧度转成角度,角度 = 弧度*180/Math.PI 。
2. 触摸移动时,小圆移动到触摸位置,小圆不能超出大圆范围,用变量 circleRadius 设置小圆移动范围,这个值一般是大圆半径,但是大小圆图片可能留白较多,所以还是要自己设置。
3. 触摸开始、滑动、结束都会抛出事件,用于处理其它事件。
4. 为了防止多点触摸,需要比较 cc.Event.EventTouch的getID()。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | const { ccclass, property, menu } = cc._decorator; /** * 虚拟摇杆 * @author chenkai 2022.9.5 */ @ccclass @menu( "framework/Joystick" ) export default class Joystick extends cc.Component { /**触摸开始 */ public static START: string = "JoystickEvent_TouchStart" ; /**触摸移动 */ public static MOVE: string = "JoystickEvent_TouchMove" ; /**触摸结束 */ public static END: string = "JoystickEvent_TouchEnd" ; /**大圆 */ @property({ type: cc.Node, tooltip: "大圆" }) bigCircle: cc.Node = null ; /**小圆 */ @property({ type: cc.Node, tooltip: "小圆" }) smallCircle: cc.Node = null ; /**小圆移动范围半径 (小圆移动范围在大圆以内,所以小圆移动范围半径约等于大圆的半径)*/ @property({ type: cc.Integer, tooltip: "小圆移动半径" }) circleRadius: number = 0; /**大圆初始位置 */ private _bigCircleInitPos: cc.Vec2 = new cc.Vec2(0, 0); /**触摸ID,防多点触摸 */ private _touchID: number = 0; onLoad() { this ._bigCircleInitPos = new cc.Vec2( this .bigCircle.x, this .bigCircle.y); } onDestroy() { } /**启动虚拟键盘 */ public start() { this .node. on (cc.Node.EventType.TOUCH_START, this .onTouchStart, this ); this .node. on (cc.Node.EventType.TOUCH_MOVE, this .onTouchMove, this ); this .node. on (cc.Node.EventType.TOUCH_END, this .onTouchEnd, this ); this .node. on (cc.Node.EventType.TOUCH_CANCEL, this .onTouchCancel, this ); } /**停止虚拟键盘 */ public stop() { this .node.off(cc.Node.EventType.TOUCH_START, this .onTouchStart, this ); this .node.off(cc.Node.EventType.TOUCH_MOVE, this .onTouchMove, this ); this .node.off(cc.Node.EventType.TOUCH_END, this .onTouchEnd, this ); this .node.off(cc.Node.EventType.TOUCH_CANCEL, this .onTouchCancel, this ); } /**触摸开始 */ private onTouchStart(e: cc.Event.EventTouch) { //触摸点世界坐标转成局部坐标 let pos = this .node.convertToNodeSpaceAR(e.getLocation()); this .bigCircle.setPosition(pos); this .smallCircle.setPosition(0, 0); this ._touchID = e.getID(); this .node.emit(Joystick.START); } /**触摸移动 */ private onTouchMove(e: cc.Event.EventTouch) { //防多点触摸 if ( this ._touchID != e.getID()) { return ; } //获取触摸点和虚拟摇杆的距离、弧度 let location = e.getLocation(); //触摸点当前世界坐标 let startLocation = e.getStartLocation(); //触摸起始点世界坐标 let dist = cc.Vec2.distance(location, startLocation); //触摸点和大圆距离 (大圆坐标 = 触摸起始点坐标) let radian = Math.atan2(location.y - startLocation.y, location.x - startLocation.x); //触摸点和大圆夹角弧度 //触摸点在大圆范围内,则小圆位置移动到当前触摸点 if (dist <= this .circleRadius) { let smallPos = this .bigCircle.convertToNodeSpaceAR(location); this .smallCircle.setPosition(smallPos); //触摸点在圆环范围外,如果小圆移动到当前触摸点就会跑出大圆了,所以小圆位置移动到大圆边缘 } else { this .smallCircle.x = Math.cos(radian) * this .circleRadius; this .smallCircle.y = Math.sin(radian) * this .circleRadius; } //派发移动事件 this .node.emit(Joystick.MOVE, radian * 180 / Math.PI); } /**触摸结束 */ private onTouchEnd(e: cc.Event.EventTouch) { //防多点触摸 if ( this ._touchID != e.getID()) { return ; } //大圆回到起始位置 this .bigCircle.setPosition( this ._bigCircleInitPos); //小圆回到原点 this .smallCircle.setPosition(0, 0); //派发触摸结束事件 this .node.emit(Joystick.END); } /**触摸结束,在虚拟摇杆之外的位置松开手指 */ private onTouchCancel(e: cc.Event.EventTouch) { this .onTouchEnd(e); } } |
四 实际使用
joystick是挂组件Joystick.ts的节点,这个节点的高宽也是触摸范围,如果需要全屏触摸都能使用摇杆,那么节点高宽就要设置为全屏大小。
bigCircle是大圆
smallCircle是小圆
angleLab是角度文本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | const { ccclass, property } = cc._decorator; @ccclass export default class MainScene extends cc.Component { @property({ type: Joystick, tooltip: "虚拟摇杆" }) joystick: Joystick = null ; @property({ type: cc.Label, tooltip: "角度文本" }) angleLab: cc.Label = null ; onLoad() { //触摸开始 this .joystick.node. on (Joystick.START, () => { console.log( "触摸开始" ); }); //触摸移动 this .joystick.node. on (Joystick.MOVE, (angle: number) => { this .angleLab. string = "角度:" + Math.round(angle); }); //触摸结束 this .joystick.node. on (Joystick.END, () => { console.log( "触摸结束" ); this .angleLab. string = "角度:" + 0; }); //摇杆启动 this .joystick.start(); } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律