这里需要注意的是,配置项中HideMod设置为1表示此场景物体模型被隐藏,因为包络只负责物理运算并不真实显示,因此隐藏。之前有讲到过,无论是在配置项中隐藏还是用函数隐藏物体,引擎仍然需要分配物理资源(前提是这个物体是物理物体),因此才可以执行物理运算,如果设置为不绘制,虽然也是不可见,但不会执行任何物理运算,在这里显然是不合适的(玩家会掉下去)。
配置项中设置DisableLight为1表示此物体不接受光照运算,那么此物体将以基础颜色显示,场景任何光照变化对此物体无效,不接受光照运算将大幅提升此物体的渲染速度。通常情况下,那些始终不可见物体、隐藏物体、发光物体或光源可以设置为不接受光照,因为光照在这些物体上没有意义。
要实现圆盘托着玩家上升,只需要移动圆盘的包络模型和圆盘模型即可,引擎会自动对圆盘的包络模型与玩家包络模型进行碰撞测试并执行物理运算,其结果就会将玩家托举上去。因为圆盘包络与圆盘模型都是场景物体,可以使用函数W3DSetSceneModPos或W3DSetSceneModPosIndirectly设置场景物体的位置信息,两个函数的原型如下所示:
函数定义 | BOOL W3DSetSceneModPos(DWORD dwModID, BOOL bChangeTrans, vec3 vTrans, BOOL bChangeRot, vec4 vRot, BOOL bChangeScl, vec3 vScl) |
功能描述 | 立即设置场景物体的位置信息,设置后场景物体位置的改变将立即生效。dwModID指定场景物体序号ID。bChangeTrans设置是否改变位置信息,true则vTrans有效。vTrans设置此场景物体的位置偏移值(注意这里是位置偏移,不是绝对位置)。bChangeRot设置是否改变旋转信息,true则vRot有效。vRot设置此场景物体的旋转信息。bChangeScl设置是否改变缩放信息,true则vScl有效。vScl设置此场景物体的缩放信息。 |
返回值 | 函数执行成功为true,失败为false,调用W3DGetLastError了解更多错误信息 |
函数定义 | BOOL W3DSetSceneModPosIndirectly(DWORD dwModID, DWORD dwChangeTransMode, DWORD dwChangeRotMode, DWORD dwChangeSclMode, vec3 vTrans,vec4 vRot,vec3 vScl) |
功能描述 | 向引擎请求设置场景物体的位置信息。 dwModID指定场景物体序号ID。 dwChangeTransMode设置改变物体偏移的模式,模式1为绝对位置模式,此时vTrans设置此物体的绝对偏移,模式2为增量位置模式,此时vTrans设置此物体的偏移增量(此时最终偏移为:模型当前偏移x分量+vTrans[0],当前偏移y分量+vTrans[1],当前偏移z分量+vTrans[2])。 dwChangeRotMode设置改变物体旋转的模式,模式1为绝对旋转模式,此时vRot设置此物体的绝对旋转信息,模式2为增量旋转模式,此时vRot设置此物体的旋转增量(此时最终旋转为:模型当前旋转角度+vRot[0],当前旋转轴x分量+vRot[1],当前旋转轴y分量+vRot[2],当前旋转轴z分量+vRot[3])。 dwChangeSclMode设置改变物体缩放的模式,模式1为绝对缩放模式,此时vScl设置此物体的绝对缩放信息,模式2为增量缩放模式,此时vScl设置此物体的缩放增量(此时最终缩放为:模型当前缩放x分量+vScl[0],当前缩放y分量+vScl[1],当前缩放z分量+vScl[2])。 |
返回值 | 函数执行成功为true,失败为false,调用W3DGetLastError了解更多错误信息 |
函数W3DSetSceneModPos与W3DSetSceneModPosIndirectly都可以设置场景物体的位置,但这两个函数是有区别的:
W3DSetSceneModPos直接设置此场景物体的位置并立即生效,无论此物体当前处在哪个渲染阶段、处在哪个物理计算阶段,因此这个函数适合需要立即改变物体位置或不关心物理计算结果是否正常的场合。
W3DSetSceneModPosIndirectly向引擎发出改变场景物体位置的请求,由引擎在内部的正确时间设置此物体的位置,因此这个函数虽不会立即让物体的位置设置生效,但可以保证物体的物理计算结果正确。因为是由引擎设置物体的位置,因此当上一次设置未完成时,此次设置会返回失败,以确保物体的位置、物理计算的正确性。
本次我们使用圆台包络这样一个场景静态物理物体托着玩家包络这个一个PT动态物理物体,可以使用W3DSetSceneModPos直接设置圆台物理物体的位置让其一直上升,但物理计算并没有经过引擎内部的纠正,因为会表现出玩家的抖动,所以这里不建议使用此函数。这里我们应使用W3DSetSceneModPosIndirectly让引擎内部设置圆台包络位置。在5号剧情中添加如下代码:
...
else if(dwStage == 5)
{
//抬升圆台
W3DSetSceneModPosIndirectly(3,
2,0,0,
vec3(0.0f,1.0f,0.0f),
vec4(0),vec3(0));
//抬升圆台包络
W3DSetSceneModPosIndirectly(4,
2,0,0,
vec3(0.0f,1.0f,0.0f),
vec4(0),vec3(0));
}
...
这里我们使用2号增量模式让圆台和圆台包络模型一直上升,向量vec3(0.0f,1.0f,0.0f)表示每次以Y轴增加1.0的速度向上移动模型。
4.8 高精度动画延时
可能有读者注意到了,在剧情控制线程中我们加了sleep(10)这样简单的不精准的延时,这样做的目的首先确保不会占用过多CPU资源,第二在之前的剧情控制中,我们没有涉及连续移动一个物体这样的“动画”功能,因此不精准的延时不影响功能。
当涉及到连续移动圆台这样的“动画”功能时,这样不精准的延时就不行了。首先,不精准的延时加入到这里的剧情控制线程中,将导致W3DSetSceneModPosIndirectly函数调用速度不均匀,进而将导致圆台上升速度不均匀。第二,过大的延时时间将导致此“动画”看起来卡顿,更为糟糕的是,如果这个物体作用于玩家身上时,并且当此物体的移动速度较快时,还会引起玩家的抖动。
由于圆台需要快速上升托起玩家,因此我们需要较小的、精准的延时才能实现稳定的托起玩家的效果。读者可能会想到将延时时间设置为sleep(0)或直接去掉延时,但sleep(0)仍然不精准且由于每台计算机的主频不同,去掉延时后的线程执行速度也是不可控的,这样将导致做出来的“动画”播放速度和流畅度与玩家计算机主频相关,这是游戏制作不可接受的(你不能接受你的游戏在配置高的计算机上动画过快)。为了在任何主频计算机上都能稳定播放“动画”,我们需要用主频无关的高精度延时技术。引擎自带函数W3DHighPrecisionDelay帮助我们实现了这个功能,函数原型如下:
函数定义 | BOOL W3DHighPrecisionDelay(LONGLONG llDelayTime) |
功能描述 | 高精度延时。llDelayTime指定延时时间(纳秒ns为单位)。 |
返回值 | 延时结束返回true,发生错误返回false,调用W3DGetLastError了解更多错误信息 |
此函数的延时时间以纳秒为单位(1秒=1000*1000*1000纳秒)。引擎在初始化时会自动检测用户计算机硬件是否支持高精度计数器,如果不支持,后续对W3DHighPrecisionDelay的调用会直接返回失败false。在插件中添加一个全局变量,用于随时设置当前的延时模式,同时插件初始化时默认使用sleep(10)作为延时(因为大部分剧情不涉及动画),添加的代码如下所示:
...
BOOL bHgProDelay;//全局变量
...
bHgProDelay=0;//默认不需要高精度延时
...
在5号剧情中添加代码,切换到高精度延时模型,代码如下:
...
else if(dwStage == 5)
{
//此时切换到高精度延时模式
bHgProDelay=1;
//抬升圆台
W3DSetSceneModPosIndirectly(3,
2,0,0,
vec3(0.0f,1.0f,0.0f),
vec4(0),vec3(0));
//抬升圆台包络
W3DSetSceneModPosIndirectly(4,
2,0,0,
vec3(0.0f,1.0f,0.0f),
vec4(0),vec3(0));
}
//一般延时
if(bHgProDelay == 0)
{
Sleep(10);
}
//精准延时
else
{
W3DHighPrecisionDelay(100*1000);//100微秒(100*1000纳秒)
}
...
在高精度延时代码中,我们设置延时100*1000纳秒即100微秒,读者可以设置更小的延时,那么“动画”将更加流畅,但速度也将更快,为保持原速度,可以将向量vec3(0.0f,1.0f,0.0f)中的1.0适当减小。
总之,如果你需要物体移动速度非常慢,不精准的延时效果可以接受。但如果你需要物体移动速度较快,那么应保证控制此物体移动的线程延时足够小,而足够小的延时就必须使用高精度延时,且要调整移动物体的位移量增量,这样才能确保“动画”流程、稳定。需要注意的是,高精度延时将导致调用线程的CPU占用率轻微上升。
4.9 场景物体位置监控
我们已经实现了圆台平顺地将玩家托起,但这个托起将一直持续,我们需要圆台上升到达高台后停止上升,这样玩家才能进入高台。首先我们需要在3dsmax中查看圆台到达高台时自身的高度,如图4.10所示(注意这里需要将圆台抬升到与高台对齐或向上一点,否则玩家上高台时有可能卡住)。图4.10左图中红框中数值(1090.713)表示圆台上升的y轴偏移距离,右图中红框中数值(588.497)表示圆台此时的绝对y坐标(请注意在3dsmax中,z轴是图形学中的y轴),那么我们可以通过两种方法检测圆台是否上升到高台为止:
① 实时检测圆台的y偏移是否大于等于(1090.713)
② 实时检测圆台的绝对y坐标否大于等于(588.497)
以下我们将分别讲解如果使用这两种方法进行检测。
图4.10 圆台向上抬升的距离与圆台Y轴绝对坐标
4.9.1 偏移检测法
可以通过使用函数W3DGetSceneModPosInfo获取场景物体的实时位置信息,函数原型如下:
函数定义 | BOOL W3DGetSceneModPosInfo(DWORD dwModID,vec3* pvTrans,vec4* pvRot,vec3* pvScl) |
功能描述 | 获得场景物体的位置信息。dwModID指定场景物体ID。pvTrans返回此物体的位置偏移信息,pvRot返回此物体的旋转信息,pvScl返回此物体的缩放信息。 |
返回值 | 调用成功返回true,失败返回false,调用W3DGetLastError了解更多错误信息 |
随后在5号剧情中添加如下代码:
...
//实时获得偏移位置
vec3 _ytTans,_temp3;
vec4 _temp4;
W3DGetSceneModPosInfo(4,&_ytTans,&_temp4,&_temp3);
if(_ytTans[1] >= 1090.713f)
{
//圆台到达位置...
}
...
代码中实时获得4号场景物体(圆台包络)的位置偏移,当y轴偏移(_ytTans[1])大于等于指定偏移后,圆台到达位置。注意,也可以实时获得3号场景物体(圆台模型)的位置偏移,同样可以达到效果。
4.9.3 绝对位置检测法
可以通过使用函数W3DGetSceneModPos获取场景物体的实时中心点位置(函数原型定义见4.1节),在5号剧情中添加如下代码:
...
//实时获得中心点位置
vec3 _ytPos;
W3DGetSceneModPos(3,&_ytPos);
if(_ytPos[1] >= 588.497f)
{
//圆台到达位置...
}
...
代码中实时获得3号场景物体(圆台模型)的中心点位置,当中心点y轴绝对坐标(_ytTans[1])大于等于指定位置后,圆台到达位置。注意,也可以实时获得4号场景物体(圆台包络)的中心点位置,同样可以达到效果。可以看到,检查场景物体偏移与绝对位置(中心点)是否到达指定位置都可以实现场景物体的位置监控,但我们应在不同的场景使用合适的方法。
偏移检测法适用情况:使用偏移位置的改变来设置物体位置时,适合使用偏移检测法,偏移检测法在任何三维编辑器辅助设计时,都能给用户提供最精准的位置检测方法。
绝对位置检测法适用情况:使用绝对位置设置物体位置时,或不方便得到偏移位置时,可以使用绝对位置检测法。这里需要注意的是:W3D引擎中对物体中心点的定义与3dsmax中的中心点定义不同,因此会有轻微数值偏差,如果需要极为精准的位置检测,在未使用W3D引擎场景编辑器进行中心点获取之前,请使用偏移检测法。W3D引擎场景编辑器使用反复,详见高级篇。
由于抬升圆台和圆台包络我们使用的是偏移改变方法,因此在5号剧情中使用偏移检测方法进行。
4.10 视点震动效果
读者可能见过在一些游戏中,大型物体移动或出现时,往往伴随着场景震动,在游戏中实现此效果通常不会真实抖动整个场景,因为可能导致场景物理物体重新计算,进而导致不希望的位移发生(因为我们仅仅是让画面发生震动,不希望物体因震动移动)。引擎提供了视线震动的方法,可以很方便模拟出场景震动效果,使用函数W3DUserViewShakeMode,函数原型如下所示,振幅越大,整个画面的晃动越大。
函数定义 | BOOL W3DUserViewShakeMode(float fRange) |
功能描述 | 设置用户视点震动模式。fRange设置振幅,取值范围[0.0,无穷大]。 |
返回值 | 调用成功返回true,失败返回false,调用W3DGetLastError了解更多错误信息 |
在5号剧情中添加如下代码:
...
//默认视点震动加剧
static bool _zd=0;
//5秒后震动逐渐停止
if(W3DEventDelay(0,1,5000))
{
_zd=1;
}
if(_zd==0)
{
//视点震动逐渐加剧
static float _sdzd=0.0;
W3DUserViewShakeMode(_sdzd);
if(_sdzd < 5.0)
{
_sdzd +=0.0001f;
}
}
else
{
//视点震动逐渐停止
static float _sdzd=5.0;
W3DUserViewShakeMode(_sdzd);
if(_sdzd >0.0)
{
_sdzd -=0.0001f;
}
}
...
代码中我们使用默认震动是不断加剧的,在W3DEventDelay函数中设置了1号事件,延时时间为5秒,5秒后震动开始减弱。因为整个剧情中,0号事件已经使用(在3号剧情中用于闪烁提示标文字颜色),因此我们这里使用1号事件。注意,这里仍然可以使用0号事件,但需要在使用前重置0号事件,可以在4号剧情中添加重置代码W3DEventDelay(1,0,0)。
4.11 添加巨石震动音效及背景声音
在5号剧情中添加如下代码,这里为3号场景物体(圆台模型)添加巨石震动声音,使用了1号播放器和0号播放位置。注意,这样设置的前提是1号播放器已经播放完了0号播放位置的声音。如果不能确定某个播放位置的声音已经播放完毕,可以使用W3DInquireSoundStatus查询此播放位置的播放状态,如果正在播放可以使用W3DStopModSound停止此播放位置的声音,停止了以后才可以使用此播放位置添加新的声音,如果还没有播放,可以强制覆盖此播放位置的声音请求。
...
//播放巨石声音
W3DAddModSoundByPlayPos(3,
W3D_SCENE_MOD,
1000,
"sound\\smss.mp3",
1,
0,
1,
0,
1);
...
背景声音是一种泛在的声音,不是某个物体发出的声音,因此不会随距离变化音量发生改变。添加背景声音使用函数W3DPlayBGSoundImmly,函数原型如下:
函数定义 | BOOL W3DPlayBGSound(char* pszSoundFileName, DWORD dwSoundVolume |
功能描述 | 添加背景声音播放请求。pszSoundFileName设置背景声音的文件路径(ascii格式),可以支持的声音格式为wav或mp3。dwSoundVolume设置初始播放音量大小,最小音量为0,最大音量为1000。 |
返回值 | 调用成功返回true,失败返回false,调用W3DGetLastError了解更多错误信息 |
此函数只添加背景声音的播放请求,当已经在播放背景声音时,添加无效。在剧情控制线程入口处添加播放背景声音请求代码如下,初始音量设置为100。
...
//添加背景声音
W3DPlayBGSound("sound\\bgm\\1.mp3",100);
...
最后在剧情5中圆台到达位置后,进入剧情6,同时将延时模式设置回一般延时模式,因为此时没有动画因此不需要高精度延时,而且可以进一步降低CPU使用率,添加的代码如下所示:
...
//圆台到达位置...
bHgProDelay=0;
dwStage=6;
...
本节演示视频
学习资料下载:
通过网盘分享的文件:W3D引擎游戏开发学习资料
链接: https://pan.baidu.com/s/1Wrs6uWqwOr8Wk-ImNa8Fvg?pwd=1234 提取码: 1234
公众号:W3D引擎