Joystick.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import CMath from "./CMath";
  2. /**
  3. * 摇杆
  4. */
  5. const { ccclass, property } = cc._decorator;
  6. const TouchType = cc.Enum({
  7. DEFAULT: 0,//摇杆固定位置
  8. FOLLOW: 1//摇杆跟随
  9. });
  10. const DirectionType = cc.Enum({
  11. FOUR: 4,
  12. EIGHT: 8,
  13. ALL: 0,
  14. });
  15. @ccclass
  16. export default class Joystick extends cc.Component {
  17. @property({
  18. type: TouchType,
  19. displayName: '触摸类型',
  20. })
  21. mTouchType = TouchType.DEFAULT;
  22. @property({
  23. type: DirectionType,
  24. displayName: '方向类型',
  25. })
  26. mDirectionType = TouchType.DEFAULT;
  27. @property(cc.Node)
  28. mDot: cc.Node = null;//摇杆
  29. @property(cc.Node)
  30. mRing: cc.Node = null;//摇杆底
  31. @property(cc.Node)
  32. mDir: cc.Node = null;//方向指示图
  33. public isDisable = false;
  34. private _radius: number;//半径
  35. private _angle: number;//当前触摸的角度
  36. private _distance: number = 0;//点击与中心的距离
  37. private tmpDir = { x: 0, y: 0 }
  38. /**
  39. * @runing 行走
  40. * @angle 角度
  41. * @distance 距离[0-1]
  42. */
  43. private callback: (move: boolean, v2: any, angle: number) => void;
  44. public onLoad() {
  45. this._radius = (this.mRing.width - this.mDot.width) / 2;
  46. this.mDir.opacity = 0;
  47. this.mDot.opacity = 100;
  48. this._initTouchEvent();
  49. }
  50. public stop() {
  51. // this.isDisable = true;
  52. this._touchEndEvent();
  53. }
  54. public setCallback(callback: (move: boolean, any, angle: number) => void) {
  55. this.callback = callback;
  56. }
  57. //对圆圈的触摸监听
  58. private _initTouchEvent() {
  59. this.node.on(cc.Node.EventType.TOUCH_START, this._touchStartEvent, this);
  60. this.node.on(cc.Node.EventType.TOUCH_MOVE, this._touchMoveEvent, this);
  61. // // 触摸在圆圈内离开或在圆圈外离开后,摇杆归位,player速度为0
  62. this.node.on(cc.Node.EventType.TOUCH_END, this._touchEndEvent, this);
  63. this.node.on(cc.Node.EventType.TOUCH_CANCEL, this._touchEndEvent, this);
  64. cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyPressed, this);
  65. cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyReleased, this);
  66. }
  67. private _touchStartEvent(event) {
  68. if (this.isDisable) {
  69. return;
  70. }
  71. // 获取触摸位置的世界坐标转换成圆圈的相对坐标(以圆圈的锚点为基准)
  72. let touchPos = this.node.convertToNodeSpaceAR(event.getLocation());
  73. // let ringPos = this.mRing.getPosition();
  74. // //触摸点与圆圈中心的距离
  75. // let distance = this._getDistance(touchPos, ringPos);
  76. // //手指在圆圈内触摸,控杆跟随触摸点
  77. // if (this._radius > distance) {
  78. // this.mDot.setPosition(touchPos);
  79. // this.updateAngle();
  80. // return true;
  81. // }else{
  82. // if(this.mTouchType == TouchType.FOLLOW){
  83. // this.mRing.setPosition(touchPos);
  84. // this.mDot.setPosition(touchPos);
  85. // return true;
  86. // }
  87. // }
  88. this.mDir.opacity = 255;
  89. this.mDot.opacity = 255;
  90. if (this.mTouchType == TouchType.FOLLOW) {
  91. this.mRing.setPosition(touchPos);
  92. this.mDot.setPosition(touchPos);
  93. this.mDir.setPosition(touchPos);
  94. return true;
  95. }
  96. return false;
  97. }
  98. private _touchMoveEvent(event) {
  99. if (this.isDisable) {
  100. return;
  101. }
  102. var touchPos = this.node.convertToNodeSpaceAR(event.getLocation());
  103. let ringPos = this.mRing.getPosition();
  104. var distance = this._getDistance(touchPos, ringPos);
  105. this._distance = distance;
  106. if (this._radius > distance) {
  107. this.mDot.setPosition(touchPos);
  108. } else {
  109. //控杆永远保持在圈内,并在圈内跟随触摸更新角度
  110. let radian = CMath.getAngle(ringPos, touchPos);
  111. let x = ringPos.x + Math.cos(radian) * this._radius;
  112. let y = ringPos.y + Math.sin(radian) * this._radius;
  113. this.mDot.setPosition(cc.v2(x, y));
  114. }
  115. //更新角度
  116. this.updateAngle();
  117. }
  118. private getDisTance(): number {
  119. if (this._distance > this._radius) {
  120. return 1;
  121. } else {
  122. return this._distance / this._radius;
  123. }
  124. }
  125. private _touchEndEvent() {
  126. this.mDot.setPosition(this.mRing.getPosition());
  127. this._angle = 0;
  128. if (this.callback) {
  129. this.callback(false, { x: 0, y: 0 }, 0);
  130. }
  131. this.mDir.opacity = 0;
  132. this.mDot.opacity = 100;
  133. }
  134. private onKeyPressed(event) {
  135. let keyCode = event.keyCode;
  136. switch (keyCode) {
  137. case cc.macro.KEY.w:
  138. case cc.macro.KEY.up:
  139. this.tmpDir.y = 1;
  140. break;
  141. case cc.macro.KEY.s:
  142. case cc.macro.KEY.down:
  143. this.tmpDir.y = -1;
  144. break;
  145. case cc.macro.KEY.a:
  146. case cc.macro.KEY.left:
  147. this.tmpDir.x = -1;
  148. break;
  149. case cc.macro.KEY.d:
  150. case cc.macro.KEY.right:
  151. this.tmpDir.x = 1;
  152. break;
  153. }
  154. this.reviseValue();
  155. }
  156. private onKeyReleased(event) {
  157. let keyCode = event.keyCode;
  158. switch (keyCode) {
  159. case cc.macro.KEY.w:
  160. case cc.macro.KEY.up:
  161. this.tmpDir.y = 0;
  162. break;
  163. case cc.macro.KEY.s:
  164. case cc.macro.KEY.down:
  165. this.tmpDir.y = 0;
  166. break;
  167. case cc.macro.KEY.a:
  168. case cc.macro.KEY.left:
  169. this.tmpDir.x = 0;
  170. break;
  171. case cc.macro.KEY.d:
  172. case cc.macro.KEY.right:
  173. this.tmpDir.x = 0;
  174. break;
  175. }
  176. this.reviseValue();
  177. }
  178. private reviseValue() {
  179. if (this.callback) {
  180. if (this.tmpDir.x == 0 && this.tmpDir.y == 0) {
  181. this.callback(false, { x: 0, y: 0 }, 0);
  182. } else {
  183. let angle = Math.atan2(this.tmpDir.y, this.tmpDir.x);
  184. this.callback(true, { x: this.tmpDir.x, y: this.tmpDir.y }, angle);
  185. }
  186. }
  187. }
  188. private updateAngle() {
  189. let p1 = this.mRing.getPosition();
  190. let p2 = this.mDot.getPosition();
  191. let angle = Math.atan2(p2.y - p1.y, p2.x - p1.x);
  192. this.mDir.angle = angle * 180 / Math.PI;
  193. this._angle = angle;
  194. let xx = this.getDir(this.mDir.angle)
  195. if (this.callback) {
  196. this.callback(true, xx, angle);
  197. }
  198. return this._angle;
  199. }
  200. //计算两点间的距离并返回
  201. private _getDistance(pos1: cc.Vec2, pos2: cc.Vec2) {
  202. return Math.sqrt(Math.pow(pos1.x - pos2.x, 2) + Math.pow(pos1.y - pos2.y, 2));
  203. }
  204. /**
  205. * 根据弧度获取方向
  206. * @param angle
  207. */
  208. private getDir(angle: number) {
  209. if (angle >= -30 && angle < 30) {
  210. return { x: 1.4, y: 0 }
  211. } else if (angle >= 30 && angle < 60) {
  212. return { x: 1, y: 1 }
  213. } else if (angle >= 60 && angle < 120) {
  214. return { x: 0, y: 1.4 }
  215. } else if (angle >= 120 && angle < 150) {
  216. return { x: -1, y: 1 }
  217. } else if (angle >= 150 && angle < 180) {
  218. return { x: -1.4, y: 0 }
  219. } else if (angle >= -180 && angle < -150) {
  220. return { x: -1.4, y: 0 }
  221. } else if (angle >= -150 && angle < -120) {
  222. return { x: -1, y: -1 }
  223. } else if (angle >= -120 && angle < -60) {
  224. return { x: 0, y: -1.4 }
  225. } else if (angle >= -60 && angle < -30) {
  226. return { x: 1, y: -1 }
  227. } else {
  228. return { x: 0, y: 0 }
  229. }
  230. }
  231. }