Cocos Creator制作一个虚拟摇杆
1. 演示版本:v2.4.3语言:TS演示GIF
2. 实现过程素材期望效果过程(1)摇杆跟随触摸(2)摇杆自动归位(3)限制摇杆不出界原理:
(4)添加箭头原理:
主脚本编写(1)主脚本部分实现功能(2)演示(3)实现摇杆脚本部分主脚本部分
第一次发文章,若有错误望大佬们指正。源码
1. 演示
版本:v2.4.3
语言:TS
演示GIF
![在这里插入图片描述](https://img-blog.csdnimg.cn/fdb8c604fb594e48b86b0b959d1fd6ba.gif#pic_center)
2. 实现过程
素材
![在这里插入图片描述](https://img-blog.csdnimg.cn/ddff1f65aa084577bd19c532ad535dc6.png)
期望效果
类似于王者荣耀的那种小摇杆摇杆中心位置为屏幕点击的位置摇杆点击部分不会出界
过程
(1)摇杆跟随触摸
![在这里插入图片描述](https://img-blog.csdnimg.cn/113b056ba3bc42fb99c8da51c5d13ba7.jpg)
this.Joystick = this.node.getChildByName("Joystick");
// 此处监听的joystick为摇杆
this.Joystick.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
onTouchMove(e: cc.Event.EventTouch) {
this.JoystickMove(e)
}
JoystickMove(e: cc.Event.EventTouch) {
// 移动
let delta = e.getDelta();
let moveDistance = cc.v3(delta.x / this.node.scale, delta.y / this.node.scale)
// 此处增加缩放参数为了方便,使用时可以直接缩放大小
this.Joystick.setPosition(this.Joystick.position.add(moveDistance))
}
此时可以实现触摸点跟随手指或者鼠标移动。getDelta函数是获取触点距离上一次事件移动的距离对象,返回的是一个Vec2。但此时摇杆不会自动归位。
(2)摇杆自动归位
this.Joystick.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
this.Joystick.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchCancel, this);
onTouchEnd(e: cc.Event.EventTouch) {
this.JoystickReset()
}
onTouchCancel(e: cc.Event.EventTouch) {
this.JoystickReset()
}
JoystickReset() {
cc.tween(this.Joystick)
.to(0.05, {x: 0, y: 0})
.start()
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/fc6d21b7d65d463f9cc45055d3262853.gif#pic_center)
TOUCH_END, TOUCH_CANCEL代表的状态为当手指在目标节点区域内离开屏幕时,当手指在目标节点区域外离开屏幕时。使用缓动使动画更流畅。
(3)限制摇杆不出界
原理:
此处思路来源于CSDN章鱼仔,通过三角形的相似等比。 完善JoystickMove代码
JoystickMove(e: cc.Event.EventTouch) {
// 移动
let delta = e.getDelta();
let moveDistance = cc.v3(delta.x / this.node.scale, delta.y / this.node.scale)
// 加上缩放参数,更加方便实用
this.Joystick.setPosition(this.Joystick.position.add(moveDistance))
// 转换坐标
let touchPos = e.getLocation(); // 以当前屏幕左下角为坐标系原点所获得的的位置
let touchPosInNode = this.node.convertToNodeSpaceAR(touchPos)
let distanceBetweenTouchPosToJoystick = touchPosInNode.mag() * this.node.scale; // 此处缩放参数作用与前边同理
// 限制移动 < 半径
if (distanceBetweenTouchPosToJoystick > this.radius) {
let lengthScale = this.radius / distanceBetweenTouchPosToJoystick;
this.Joystick.x = touchPosInNode.x * lengthScale;
this.Joystick.y = touchPosInNode.y * lengthScale;
}
}
此时移动不会出界,而且可以任意调整缩放倍数都可以保持不出界状态! 注意getLocation()函数返回的坐标是以屏幕左下角为坐标中心的坐标,并不是世界坐标! 节点.convertToNodeSpaceAR(位置)返回的是这个位置在这个节点下的位置。
(4)添加箭头
完善代码
onTouchMove(e: cc.Event.EventTouch) {
this.JoystickMove(e)
this.arrowDirection()
}
arrowDirection() {
// 设置箭头大小
this.setArrowLength()
// 计算夹角
this.setArrow(cc.v2(this.Joystick.position))
}
setArrowLength() {
let arrowParamScale = this.Joystick.position.mag() * this.node.scale / this.radius;
this.arrow.width = this.arrowMaxLenth * arrowParamScale; // 箭头长度
this.arrow.opacity = 255 * arrowParamScale; // 箭头透明度
}
/**
*
* @param JoystickPos 摇杆节点坐标
*/
setArrow(JoystickPos: cc.Vec2) {
let dir = JoystickPos.sub(cc.v2(0, 0))
let vec = cc.v2(0, 1); // 水平向右的对比向量
let radian = dir.signAngle(vec); // 求方向向量与对比向量间的弧度
let rotate = cc.misc.radiansToDegrees(radian); // 将弧度转换为角度
this.arrow.angle = -rotate - 90; // ***此处rotate正负值以及减去的角度根据自己的图片去修改***
}
再完善一下摇杆回弹时的动画,让箭头可以复原!
JoystickReset() {
let time: number = 0.05;
let arrowReset = cc.tween(this.arrow).to(time, {width: 0, opacity: 0})
cc.tween(this.Joystick)
.call(() => {
arrowReset.start()
})
.to(time, {x: 0, y: 0})
.start()
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/63ce48c035464e53886abeb3b461342e.gif#pic_center)
原理:
首先dir是摇杆的向量与摇杆中心的一个带方向的向量差,若是求向量夹角必须有另外一个参考向量,此处定义为(0, 1),求出的值为弧度所以需要将弧度转换为我们要的角度。
弧度转角度公式: 角度 = 弧度 * 180 / PI cocos中可以直接使用cc.misc.radiansToDegrees()
singAngle()函数源码部分如下,如果对向量的叉乘、点乘的几何意义不熟悉可以看一下:
/*
* 带方向的夹角的弧度。该方法仅用做兼容 2D 计算。
*/
signAngle (vector) {
cc.warnID(1408, 'vec3.signAngle', 'v2.1', 'cc.v2(selfVector).signAngle(vector)');
let vec1 = new Vec2(this.x, this.y);
let vec2 = new Vec2(vector.x, vector.y);
return vec1.signAngle(vec2);
}
/*
* 带方向的夹角的弧度。
*/
signAngle (vector: Vec2): number {
let angle = this.angle(vector);
return this.cross(vector) max_inclusive) {
var temp = min_inclusive;
min_inclusive = max_inclusive;
max_inclusive = temp;
}
return value |