版本:2.3.4
参考:
【持续更新】Cocos Creator源码分享——针对游戏中的各种功能
和原文的区别
1. 监听事件不使用字符串。例如"touchstart",使用cc.Node.EventType.TOUCH_START。因为使用字符串容易拼错。
2. 增加触摸响应区域。因为常规游戏中,虚拟摇杆可响应范围不仅仅是虚拟摇杆图片范围,而是一个可根据策划需求调整的范围,例如今天500x400,明天觉得600x400,只需要修改代码,不需要重新制作图片了。
3. 防止多点触摸。增加了touchID的判断,防止多个手指触摸导致的问题。例如一个手指在操作摇杆,另一个手指不小心在触摸区域点击了一下,导致触发了touch_end,使摇杆失效。
4. 增加了小圆移动范围设置。原来文章用大圆图片的高宽限制小圆的移动范围。但是大圆图片可能有透明区域,所以这里小圆的移动范围在代码里手动设置。
UI如下图,为了方便area用绿色显示,实际使用去掉就行了。
虚拟摇杆代码
// Learn TypeScript:
// - https://docs.cocos.com/creator/manual/en/scripting/typescript.html
// Learn Attribute:
// - https://docs.cocos.com/creator/manual/en/scripting/reference/attributes.html
// Learn life-cycle callbacks:
// - https://docs.cocos.com/creator/manual/en/scripting/life-cycle-callbacks.html
const {ccclass, property} = cc._decorator;
@ccclass
export default class JoyStick extends cc.Component {
@property(cc.Node)
panel:cc.Node = null; //大圆
@property(cc.Node)
btn:cc.Node = null; //小圆
@property(cc.Integer)
private panelWidth:number = 130; //去掉透明区域的大圆宽度
private panelInitPos:cc.Vec2; //大圆初始位置
private touchID:number; //触摸ID
public dir:cc.Vec3 = new cc.Vec3(0,0,0); //移动方向
public angle:number = 0; //弧度(角度)
public moving:boolean = false; //是否正在移动
onLoad(){
this.panelInitPos = new cc.Vec2(this.panel.x, this.panel.y);
}
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);
this.moving = false;
}
private onTouchStart(e:cc.Touch){
console.log("start");
//触摸点世界坐标转成局部坐标
let pos = this.node.convertToNodeSpaceAR(e.getLocation());
this.panel.setPosition(pos);
this.btn.setPosition(0,0);
this.touchID = e.getID();
}
private count = 1;
private onTouchMove(e:cc.Touch){
console.log("move");
if(this.touchID != e.getID()){
return;
}
//小圆移动
let posDelta = e.getDelta();
this.btn.x += posDelta.x;
this.btn.y += posDelta.y;
//将小圆限制大圆范围内
let ratio = this.btn.position.mag() / this.panelWidth;
if (ratio > 1) {
this.btn.setPosition(this.btn.position.div(ratio));
}
//获取向量归一化
this.dir = this.btn.position.normalizeSelf();
//获取弧度
this.angle = Math.atan2(this.btn.y, this.btn.x);
//正在移动
this.moving = true;
}
private onTouchEnd(e:cc.Touch){
console.log("end");
if(this.touchID != e.getID()){
return;
}
this.panel.setPosition(this.panelInitPos);
this.btn.setPosition(0,0);
this.moving = false;
}
private onTouchCancel(e:cc.Touch){
console.log("cancel");
if(this.touchID != e.getID()){
return;
}
this.panel.setPosition(this.panelInitPos);
this.btn.setPosition(0,0);
this.moving = false;
}
onDestroy(){
this.stop();
}
}
实际操作
@ccclass
export default class Helloworld extends cc.Component {
//虚拟摇杆Area
@property(cc.Node)
joyStickArea:cc.Node = null;
//虚拟摇杆代码
joyStick:JoyStick;
//角色
@property(cc.Node)
role:cc.Node = null;
//速度
speed:cc.Vec2 = new cc.Vec2(5,5);
onLoad(){
this.joyStick = this.joyStickArea.getComponent(JoyStick);
}
start() {
}
update(){
if(this.joyStick.moving){
//根据角度移动
// this.role.x += Math.cos(this.joyStick.angle)*this.speed.x;
// this.role.y += Math.sin(this.joyStick.angle)*this.speed.y;
//根据向量移动
this.role.x += this.joyStick.dir.x*this.speed.x;
this.role.y += this.joyStick.dir.y*this.speed.y;
}
}
}
演示效果