转载URL:https://www.jianshu.com/p/d76dc3cf5aa7
[关键词]小游戏开发,cocos,数学,相似三角形,单位向量,弧度角度转换,向量转换为夹度,产生不重复的随机序列,切水果游戏,抛物线
一、相似三角形知识的应用
在摇杆控制物体运动的游戏中,摇杆的手柄(下图黄色圆饼),不能移出摇杆所在的套(下图灰色圆环),也就是说摇杆偏离中心点的最大距离为max_R。一旦触摸移动过程中移动的点超出此最大距离,我们需要将摇杆手柄拖回到最大距离处。
如何将摇杆拖回到灰色圆环处,无非是要求出在C点的坐标。根据△ODC和△OBA为相似三角形,有OD:OB = OC:OA,也就是?:pos.x = max_R:len。
其中pos为A点的坐标(x,y),len为向量OA的模。
?表示C点的x坐标,也就是但是我们求出来,要作为新的pos,去设置摇杆的位置。
二、单位向量
在摇杆控制物体的运动时,如果摇杆只控制物体的方向,也就是说不管摇杆偏离原点多远,都不会影响物体运动的速度,而只影响物体运动的方向,也就是实质上我们要获取一个方向的值(比如向量与x轴正方向的夹角),这时候,我们就需要使用单位向量。
单位向量是指向量的模为1的向量,也就是以一个单位长度为半径画一个圆,从圆心指向圆周上任意一点的向量都是单位向量。
假设单位向量OA与x轴正向的夹角为α,则向量OA可以用(cosα,sinα)表示,也就是A点的坐标。如下图所示:
所以不管任何向量,我们要转换为单位向量,只需要计算出这个向量的余弦和正弦值就可以了。例如向量OC我们将其转换为单位向量就是OC’,因为OC’的长度为1,所以向量OC’的坐标就是(cos(c),sin(c)),c为OC和x轴正方向的夹角。而OC’和OC的方向是重合的,他们与x轴正向夹角相等,也就是我们只需要计算向量OC的cos(c)、sin(c)即可。
假设得到的C点坐标为pos(x,y),则在Cocos游戏开发中,会有下列代码:
var len = pos.mag(); // 求向量pos的模(内部算法是(x2+y2)开根号)。
pos.x = pos.x / len; // 得到单位向量的x坐标
pos.y = pos.y/ len; // 得到单位向量的y坐标
三、弧度角度转换
角度指从原点发出的射线与X轴正向的角度。而x轴正向绕原点在水平平面内旋转一周为360°。而弧度是指圆弧长度与半径的比值。假设有个半径r为1的圆,则其一周为360°。圆周对应的弧度长为2πr=2π。也就是说在角度转换为弧度是360°等价于2π,180°等价于π。
(1)弧度转换为角度:根据degree : 180 = rad:π
var rad = Math.atan2(y, x); // 反正切函数,得到弧度
var degree = 180 * rad / Math.PI ; // 将弧度rad转换为角度
(2)角度转换为弧度:根据rad:π=degree:180
var degree = 90;
var rad = Math.PI * degree / 180; // 角度转换为弧度
四、向量转换为夹角
在游戏中,有时候我们需要计算物体旋转了多少度,比如我们要模拟一个KTV里面的游戏转盘,手指触摸的时候,要获取触摸的点的位置,触摸点的位置与转盘中心点之间构成一个向量,我们要获取转动了多少度,就需要有一个参考向量。
如下图所示,假如蓝色的圆就是转盘的边缘,我们最开始触摸A点,然后转动到B点,这时候转动的角度就是∠AOB,这个角度在Cocos中如何求呢?
代码pos就表示B点的坐标,pos和原点(0,0)相减,得到向量OB,也即代码中的dirVec,弧AB为radian,转换为角度degree。
// 向量转换为角度
vectorsToDegree(dirVec) {
// 水平向右的对比向量
let comVec = cc.v2(1, 0);
// 求方向向量与对比向量间的弧度
let radian = dirVec.signAngle(comVec);
// 将弧度转换为角度
let degree = cc.misc.radiansToDegrees(radian);
return degree;
},
调用示例(具体可以参考我们的《KTV酒令转盘虚拟仿真实现》):
// 获取触摸点与原点连线的射线与X轴正向的角度
getTouchAngle(e){
var screen_pos = e.getLocation(); // 触摸点世界坐标
// 转换为相对于当前节点的锚点的坐标
var pos = this.node.convertToNodeSpaceAR(screen_pos);
// 获取触摸点距离轮盘中心点的向量
var dirVec = pos.sub(cc.v2(0,0));
// 将向量转换为基于参考方向(v2(0,1))的角度
return this.vectorsToDegree(dirVec);
},
五、切水果游戏中的抛物线
重力加速g:
Vy= V0+ g * t;
h = V0 t + 0.5 g t t;
斜抛运动(如下图所示):
分解到水平上:匀速直线运动;
分解到竖直方向:匀变速直线运动(加速度)。
具体在Cocos中如何实现,限于篇幅,在此不做赘述,可以参考我们的公开课《切水果》核心技术:抛物线物理仿真。
六、如何产生不重复的随机序列
应用场景:例如洗牌,发牌,例如扫雷游戏中从100个格子中随机抽取10个埋雷。
凡是需要打乱顺序,或者从某个数组中抽取一定数量的不重复的元素,都用得着。
// 数组工具类
var ArrayUtils = function(){};
// 【1】初始化得到有序元素数组:[start,end)的自然数序列
ArrayUtils.initOrderArray = function(start, end){
let sortArray = [];
for(let i= start; i sortArray.push(i); } return sortArray; }; // 【2】从数组arr中随机抽取count个元素,返回数组 ArrayUtils.randChoiseFromArr = function(arr, count){ let result = arr; // 随机排序,打乱顺序 result.sort(function(){ return 0.5 - Math.random(); }); // 返回打乱顺序后的数组中的前count个元素 return result.slice(0,count); }; // 【3】从从start到end中的连续整数中随机抽取count个数字 ArrayUtils.randChoiseFromTo = function(start, end, count){ let arr = this.initOrderArray(start,end); return this.randChoiseFromArr(arr, count); }; // 【2】-【方式二】从数组arr中随机抽取count个元素,返回数组 ArrayUtils.getRandomArrayElements = function(arr, count) { // 从0位置取到结束位置存入shffled数组 let shuffled = arr.slice(0); let i = arr.length; let min = i - count; let temp = 0; let index = 0; // 随机一个位置的元素和最后一个元素交换 // 随机一个位置元素和倒数第二个元素交换 // 假设i=8,count=3,则min=5, // 循环体中[i]=7,6,5,也就是说最后三个元素要从数组中随机取 // 循环结束后,从min=5的位置取到结束,即取3个元素。 while(i-- > min) { index = Math.floor((i + 1) * Math.random()); temp = shuffled[index]; shuffled[index] = shuffled[i]; shuffled[i] = temp; } return shuffled.slice(min); }; module.exports= ArrayUtils; 原理解析:如上述代码,方式一randChoiseFromArr方法,采用随机排序打乱数组中所有元素的顺序,然后从数组第一个元素开始抽取需要的count个元素。 方式二getRandomArrayElements方法,需要取几个元素,就随机几个位置和倒数最后几个元素交换,最后取倒数最后几个元素即可。注意:index = Math.floor((i+ 1) * Math.random());i+1才能保证最后一个元素可能是自身和自身交换,也就是保证这个位置的元素可能是原有这个位置的元素。 上面所有代码我们已经全部测试通过,感兴趣的朋友可以自己动手去实践。
————————————————————————————————————
作者:游戏程序猿
链接:https://www.jianshu.com/p/d76dc3cf5aa7
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。