Cocos2d-x 3.5 入门(二)怎样设置参数,让游戏更接近实际物理效果

这次需要做一个以跑跳为主题的游戏demo,想法先不说,首先创建工程:

cocos new Jump -p com.ziqiang.cocos.game -l cpp -d /Users/...(最后一项是位置,即工程存放的路径)


Tips:

1 还有一个tip写在这里,在游戏中创建一个字符label用如下的语句来创建,程序中一定会用到:

</pre></p><p style="font-size: 11px; margin-top: 0px; margin-bottom: 0px; font-family: Menlo;"><pre name="code" class="cpp" style="color: rgb(255, 255, 255);"><span style="background-color: rgb(0, 0, 0);">auto jumpItem = MenuItemLabel::create( Label::createWithSystemFont( "Jump", "" ,30), CC_CALLBACK_1(HelloWorld::menuCloseCallback,this) );</span>

或者分两行:

auto  labelup = Label::createWithSystemFont("UP", "Arial", 100);
auto upMenuItem = MenuItemLabel::create(labelup, CC_CALLBACK_1(HelloMap::menuCallback, this));

2 关于schedule函数来实时更新物体位置

schedule这个函数经常用到,它用来描述节点对象上定时执行的方法,物体跳起与落下的动作是通过渲染场景中该物体的位置变化实现的。

要描述位置的变化,需要给物体设置初始位置并指定位移,

其中,初始位置是一个二维的点,当然也可以用向量描述.

位移就是位置的变化,即物体由初始位置运动到终位置的一段向量.

所以t时刻的位置就是初始位置与位移之和。这三个量都是向量:

初始位置是我们设置的,想要获得终位置,位移的获知是关键。

位移怎么变,取决于作者想写一个什么运动,匀速运动,匀变速运动,还是变加速运动等等

如果是实际的物理世界中,

物体做匀速运动,速度为V,每一微小的时间段dt里面的位移是:

ds=V*dt

物体做匀变速运动,加速度位a,每一微小的时间段dt里面的速度变化是:

V=a*dt

对应位移就是:

ds=(V0+dv)*dt   注:V0是这段位移起始点上物体的速度。

注:变加速运动不说,对于有重力作用的一些运动,这些就够了。


因为对于游戏中世界呈现是通过渲染实现的,每渲染一次就会做相应的运算一次,所以游戏中的物理世界的时间轴是离散的。

如果我的schedule函数如下句所写:

this->schedule( schedule_selector( HelloWorld::s_sche), 1.0f );
执行这个语句就意味着我1秒钟执行一次s_sche。而s_sche函数就是用来更新物体位置的,即物体的位移信息包含在这个函数中。

但是如果参数设置不合适,虚拟世界的运动就会很假。重点就是设置加速度,初速度等参数。

所以问题来了,怎样设置这些物理参数,我们的运动就会看起来更像真的呢?

我们如果想“模拟”真实的现实世界,可以先想象这样一个现实世界,然后假定我们的模拟是现实世界的等时间段抽样即可,相当于隔一段时间为现实的物理世界拍一次照片。这个时间段就是上面schedule函数中的1s。如果设置成1.0f/60.0f就代表1/60s拍一次照。

在程序中每拍一次照片的时间,需要按照程序计算一次新的位置,我们把这个算法写进s_sche函数中就能在等时间间隔的执行了。

另外,把手机屏幕看作现实世界的缩小画面的话,就需要进行虚拟世界和物理世界两域中距离的换算了,我们一般游戏中精灵跳跃的高度一般都会很高,往往比自身的身高还高,但在玩家的游戏体验上,这点是能够接受的。所以针对精灵跳起的高度比自己身高还高的情况,与其说跳起,不如说被抛起。

所以现在假设一个直径是40像素(40p 注:这里一单位p代表一单位的像素宽度)的圆球,那么通常来看,这个40像素的圆球在现实世界中可以假设成直径20cm的一个圆球,后面我们要将这个球抛起,所以起码假设它和我们双手的尺寸差不多大。根据这个能接受的假设,我们就有换算关系:

40p=20cm=0.2m

即 1m=200p

通过百度,我们可以查到抛起一个物体,物体离开手的初速度大概是30m/s左右,当然这个值取决于托起物体的距离,你手作用于物体的力及离开手的时间,这里就先不详述。那么根据30m/s的这个初速度假设,游戏世界中的初速度就可以是:

30m/s=6000p/s

现实物理世界加速度是大概取-10m/(s^2),换算成游戏中,就是:

-10m/(s^2)=-2000p/(s^2)

假设初始高度为0,所以向上抛起这个物体,1s后物体的位置是:

当然也可以带高中公式:

即(30*1-0.5*10*1)m=25m=5000p

5000p已经远远超出游戏的屏幕高度750p了,上面公式中可以看到,a,t都没有修改的余地,唯一可以修改的就是初速度v(0),可见30m/s太大了,我们想个办法来倒推多少比较合适。

假设物体向上运动的顶点是一半的屏幕高度,即375p处,就是1.875m,物理世界中顶点处的速度是0,可以根据下面两公式列方程:

v(t)-v(0)=at

s(t)=v(0)t+0.5at^2

其中v(t)=0,s(t)=1.875m,a=-10m/(s^2)

解得v(0)大概是6.1237m/s (注:(6^(1/2))/2)

即在游戏中是:1224.74p/s

验证一下这个数据是否合理:

用6.1237m/s的速度向上抛物,1s后物体的位置是:

(6.1237*1-0.5*10*1)m=1.1237m=224.74p

是个可能看起来舒服的结果了。

那么1/60s在哪儿呢?

(6.1237*(1/60)-0.5*10*(1/60)^2)m=0.100672m=20.135p

因为1/60s是cocos2d的渲染时间间隔,如果按照这个计算,就是第一次渲染时发现这个物体移动了20像素的位置。

同样可以算出以下数据:

2/60s时:39.71

3/60s时:58.73

4     :77.20

5     :95.11

6     :112.47

7     :129.28

根据方程

做图,直观的看到:(注:s-t图)

大概一秒多,物体就落回地面了。

这是比较舒服的方式。

可以直接把上面的公式写成代码用在程序里面

void HelloWorld::jump_up()
{
    m_pRole->setPositionY((6.1237*(count*0.016666667)-5*(count*0.016666667)*(count*0.016666667))*200);
}<span style="color:#ffffff;">
</span>
这段代码中的count就是方程中的x,先在p_sche中写count++,就可以用了。一开始我写的是count/60,因为往往计算结果是无理数,所以结果是double型,达不到每秒60次,效果会有很强的顿挫感,即便如上面写成count*0.016666667,也会有顿挫感。索性简化成下面这样:

m_pRole->setPositionY((6.1237*(count*0.02)-5*(count*0.02)*(count*0.02))*200);
实践证实,还是会有轻微的卡顿。再简化:

m_pRole->setPositionY((6*(count*0.02)-5*(count*0.02)*(count*0.02))*200);
还是不理想,继续简化:

m_pRole->setPositionY(20*count-0.3*count*count);

效果还是不理想。所以问题应该出在乘法上。

相比较物理世界而言,游戏中的时间是离散的,那么下一时刻的速度v2=v1+at

下一时刻的位置就是s2=s1+v2t,在s-t图上看,相当于用一条一条的来拟合这条抛物线。时间间隔取得越窄越接近,顿挫感越差。这个顿挫感和之前那个顿挫感不是一回事,之前那个是运算速度跟不上渲染速度造成的,这个顿挫感是时间间隔设置太大造成的。当然在这种方法里,你可以设置感觉舒服的时间间隔,来有效的平衡顿挫感和计算量。

把这个思路运用到该想法中,设置t是(1/60)s,a=-10m/(s^2)=-2000p/(s^2)则

v2=v1-33

s2=s1+0.02*v2
同时设置初速度是1225p/s,写下面代码:

    f_ySpeed = f_ySpeed-33;
    m_pRole->setPositionY(m_pRole->getPositionY()+0.02*f_ySpeed);
还是有顿挫感,因为还有乘法。这里面的关键点是消除0.02*f_ySpeed。

要知道,匀变速运动中速度的变化是线性的,如果吧0.02这个系数提前给第一个方程乘过,后面就可以直接加了,所以初速度修正为:

0.02*1225p/s = 25p/s (从这里可以看出 现实世界速度 = 虚拟世界速度*抽样时间)

33修正为0.02*33=0.66,反观因为33=2000/60,此时可以将2000/60这个整体定义为我们虚拟世界中的加速度大小。

所以一般的,如果我们计算出虚拟世界的加速度应该是2000p/s^2,那么经过1/60s的抽样后,可以将2000/60理解为加速度大小。

这样就是:

f_ySpeed = f_ySpeed-0.66;
m_pRole->setPositionY(m_pRole->getPositionY()+f_ySpeed);
卡顿感没有了。

根据上面的思路,我们可以逆过来讨论:

如果我们写成如下代码:设置初速度是10,加速度是1,抽样时间是1/60s,

f_ySpeed = f_ySpeed-1;
m_pRole->setPositionY(m_pRole->getPositionY()+f_ySpeed);
那么这段代码描写的现实世界中的初速度,加速度分别是多少呢?大概多高的时候停止运动呢?

根据上面的思路,1=虚拟世界的加速度*抽样时间,即虚拟世界的加速度是60,

而  虚拟世界的加速度=现实世界的加速度*抽样时间,即现实世界的加速度时3600p/s^2

假设200p=1m,那么现实世界加速度就是18m/s^2 (注:距离的换算需要通过精灵的大小和场景中的一些图片尺寸综合考虑)。

又因为

虚拟世界速度=现实世界速度*抽样时间

则现实世界速度是600p/s,即3m/s

所以该物体是以3m/s初始速度向上抛,运动时的加速度大概是18m/s^2向下(话说所以空气阻力造成的加速度是8m/s^2向下,还挺大呢)

如果我们的初速度不改,还是25p/s,而速度的增量改为1,则
对应现实世界加速度是18m/s^2
对应现实世界速度是(25*60)p/s=1500p/s=7.5m/s

再反观一下,因为人眼的最大反应时间间隔是0.02s即1/50s,所以schedule也不用非得到1/60s调用一次制定函数,所以我们设置成1/50试试看。
这里,抽样间隔就是1/50s
那么如果初速度设置为6.1237m/s=1224.74p/s,那么对应
虚拟世界速度 = 现实世界速度*抽样时间 = 24.49p/s
如果加速度还是10m/s=2000p/s
虚拟世界加速度 = 现实世界加速度*抽样时间 = 40p/s^2
速度增量 = 虚拟世界加速度 * 抽样时间 = 0.8p/s
f_ySpeed = f_ySpeed-0.8;
m_pRole->setPositionY(m_pRole->getPositionY()+f_ySpeed);
运行一下,也没有卡顿的感觉。看起来还是很舒服。如果我们把抽样时间也写做一个变量,通过改动抽样时间来看运动何时卡顿:
float count = 0.03;
构造函数中写:
f_ySpeed( 1225*count ),
f_deltaySpeed( 2000*count*count),

p_sche中写:
f_ySpeed = f_ySpeed-f_deltaySpeed;
m_pRole->setPositionY(m_pRole->getPositionY()+f_ySpeed);
根据测试,0.03s即1/30s时运动还是显得比较光滑的,大于这个数值开始就有顿挫感了(用的iphone6模拟器测试)



好了,如果是要做一个跑跳为主题的游戏,首先想到的就是酷跑的主题。但是我还没有接触过地图滚动等知识,用现有的知识能否做一个相对也有一些娱乐价值的游戏呢?

下一篇文章再说吧,这篇内容太多了,就这样

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值