一 , 數學準備
Ⅰ, 三角函數
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsAjMfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsQTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5yM0cjM3YjM0QzMxATOxYTMvwVMyQDMxIDMy8CXzV2Zh1WavwVbvNmLvR3YxUjL3M3Lc9CX6MHc0RHaiojIsJye.png)
ox1/ox = oy1/oy (相似三角形)
二 , 關于弧度 Math.atan2 在-PI (3,4象限)到 PI(1,2象限) 之間 . 如果要在3,4象限得到正值, 則需要使用 2PI - X的方式. 角度也是一樣, 代碼如下:
/**
* 擷取方向弧度
*/
public getRadian(): number {
if( this.dir.x == 0 && this.dir.y == 0){
return 0;
}
let radian: number = Math.atan2( this.dir.y ,this.dir.x );
if( radian >= 0 ) return radian;
return 2*Math.PI + radian;
}
/**
* 擷取方向角度
*/
public getAngle(): number{
let angle: number = this.getRadian()/ Math.PI * 180;
if( angle >= 0 ) return angle;
return 360 + angle;
}
三, 層級管理器如下
場景設計如下圖, 可以看到要把stick限制在bg内需要用到相似三角形的原理, 部分代碼如下:
let screenPos: Vec2 = e.getLocation();//擷取螢幕坐标(stick所在的)
let pos: Vec3 = (this.node.getComponent(UITransform) as UITransform).convertToNodeSpaceAR( v3( screenPos.x, screenPos.y, 0 ));//轉joystick和bg一樣
let len: number = Vec2.len( v2(pos.x,pos.y) );//求模
this.dir.x = pos.x / len;//cos x
this.dir.y = pos.y / len;//sin y
if( len > this.maxR ){
pos.x = pos.x * this.maxR/len;
pos.y = pos.y * this.maxR/len;
}else if( len < this.minR && this.minR > 0){
pos.x = pos.x * this.minR/len;
pos.y = pos.y * this.minR/len;
}
this.stick.setPosition( pos.x,pos.y);
四, 全部代碼
import { _decorator, Component, Node, log, EventTouch, Vec2, UITransform, Vec3, v3, v2 } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('JoyStickCtr')
export class JoyStickCtr extends Component {
@property( { type: Node } )
// @ts-ignore
private stick: Node;
@property
private maxR: number = 128;//最大移動半徑
@property
private minR: number = 20;//最小移動半徑
//@ts-ignore
private dir: Vec2;
onEnable(){
this.listener( true );
}
onDisable(){
this.listener( false );
}
private listener( isAdd: boolean ): void {
if( isAdd ) {
this.stick.on( Node.EventType.TOUCH_START, this.onTouchHandler, this);
this.stick.on( Node.EventType.TOUCH_MOVE, this.onTouchHandler, this);
this.stick.on( Node.EventType.TOUCH_END, this.onTouchHandler, this);
this.stick.on( Node.EventType.TOUCH_CANCEL, this.onTouchHandler, this);
}else{
this.stick.off( Node.EventType.TOUCH_START, this.onTouchHandler, this);
this.stick.off( Node.EventType.TOUCH_MOVE, this.onTouchHandler, this);
this.stick.off( Node.EventType.TOUCH_END, this.onTouchHandler, this);
this.stick.off( Node.EventType.TOUCH_CANCEL, this.onTouchHandler, this);
}
}
private onTouchHandler( e : EventTouch): void{
switch (e.type){
case Node.EventType.TOUCH_START:
break;
case Node.EventType.TOUCH_MOVE:
let screenPos: Vec2 = e.getLocation();//擷取螢幕坐标(stick所在的)
let pos: Vec3 = (this.node.getComponent(UITransform) as UITransform).convertToNodeSpaceAR( v3( screenPos.x, screenPos.y, 0 ));//轉joystick和bg一樣
let len: number = Vec2.len( v2(pos.x,pos.y) );//求模
this.dir.x = pos.x / len;//cos x
this.dir.y = pos.y / len;//sin y
if( len > this.maxR ){
pos.x = pos.x * this.maxR/len;
pos.y = pos.y * this.maxR/len;
}else if( len < this.minR && this.minR > 0){
pos.x = pos.x * this.minR/len;
pos.y = pos.y * this.minR/len;
}
this.stick.setPosition( pos.x,pos.y);
break;
case Node.EventType.TOUCH_END:
case Node.EventType.TOUCH_CANCEL:
this.stick.setPosition(0,0);
this.dir.x = this.dir.y = 0;
break;
}
}
/**
* 擷取方向向量
*/
public getDir(): Vec2{
return this.dir;
}
/**
* 擷取方向弧度(0~2PI)
*/
public getRadian(): number {
if( this.dir.x == 0 && this.dir.y == 0){
return 0;
}
let radian: number = Math.atan2( this.dir.y ,this.dir.x );
if( radian >= 0 ) return radian;
return 2*Math.PI + radian;
}
/**
* 擷取方向角度(0~360)
*/
public getAngle(): number{
let angle: number = this.getRadian()/ Math.PI * 180;
if( angle >= 0 ) return angle;
return 360 + angle;
}
start () {
this.dir = v2(0,0);
// this.listener( true );
}
// update (deltaTime: number) {
// // [4]
// }
}