配置完成基本DT物體項后,在這個DT物體項中開始添加這個玩家的動畫信息,如下所示。UseAnim設置是否使用動畫功能,這里開啟。AnimTypeN設置總共的動畫類型數,當前只有一個待機動畫,因此設置為1。AnimType1FrameN設置1號動畫的幀數,在這里即為待機動畫的幀數,共13幀(0.obj~12.obj共13個幀物體)。AnimType1Frame1~AnimType1Frame13依次設置這13個幀物體的obj模型路徑。
...
[DTMod3]
texnum = 1
mtl=model\LearnW3D\Chapter1\DT\Anim\standby\mod.mtl
mod=model\LearnW3D\Chapter1\DT\Anim\standby\mod.obj
tex1=model\LearnW3D\Chapter1\DT\Anim\standby\mod.bmp
UseAnim=1
AnimTypeN=1
AnimType1FrameN=13
AnimType1Frame1=model\LearnW3D\Chapter1\DT\Anim\standby\0.obj
AnimType1Frame2=model\LearnW3D\Chapter1\DT\Anim\standby\1.obj
AnimType1Frame3=model\LearnW3D\Chapter1\DT\Anim\standby\2.obj
AnimType1Frame4=model\LearnW3D\Chapter1\DT\Anim\standby\3.obj
AnimType1Frame5=model\LearnW3D\Chapter1\DT\Anim\standby\4.obj
AnimType1Frame6=model\LearnW3D\Chapter1\DT\Anim\standby\5.obj
AnimType1Frame7=model\LearnW3D\Chapter1\DT\Anim\standby\6.obj
AnimType1Frame8=model\LearnW3D\Chapter1\DT\Anim\standby\7.obj
AnimType1Frame9=model\LearnW3D\Chapter1\DT\Anim\standby\8.obj
AnimType1Frame10=model\LearnW3D\Chapter1\DT\Anim\standby\9.obj
AnimType1Frame11=model\LearnW3D\Chapter1\DT\Anim\standby\10.obj
AnimType1Frame12=model\LearnW3D\Chapter1\DT\Anim\standby\11.obj
AnimType1Frame13=model\LearnW3D\Chapter1\DT\Anim\standby\12.obj
[DTMod]
num = 3
...
設置完DT物體后,開始設置此DT物體派生出的PT物體。然后在[ScenePTMod1]字段中添加第3個PT物體信息如下:
...
3x=0.0
3y=0.0
3z=0.0
3sx=0.38089
3sy=0.38089
3sz=0.38089
3AbsPos=1
3ShowTip=0
3type=3
3SelMode=0 ??
3DyPhyCtrlMod=1
3DyPhyCtrlModID=2
num = 3
...
之前我們講過,用戶控制玩家,是控制玩家的包絡模型,因為如果直接控制玩家的動畫模型,玩家動畫模型的面數過多、是否為封閉物體這些因素都是不可控因素,都將導致玩家的碰撞檢測可能出現問題,因此我們直接控制玩家包絡模型。那么如何讓玩家的動畫模型跟隨著包絡模型?這里只需要設置此玩家的動畫模型受物理模型控制即可。
此字段中,DyPhyCtrlMod設置此動畫模型是一個受物理物體控制的物體,DyPhyCtrlModID設置這個物體具體受哪個物理物體控制,為物體的ID即序號,這里為2即玩家的包絡模型。type為此物體的DT物體類型(序號),即為3號。sx、sy、sz設置此模型的縮放系數x分量、y分量和z分量,2.5.1節中已經把玩家的動畫模型縮放入包絡模型中,縮放比例為38.089%,因此這里設置縮放為0.38089。
AbsPos設置這個物體的位置為絕對位置,那么這個物體的初始位置即為x、y、z位置所表示,若不設置為絕對位置,那么這個物體的初始位置即為x+場景偏移x、y+場景偏移y、z+場景偏移z的位置所表示(場景偏移見應用篇)。
因為此物體完全由玩家包絡模型控制,那么此物體的姿態將與玩家包絡模型完全一致,此時x、y、z將表示此物體相對于被控物理模型的位置偏移,這里x、y、z設置為0表示沒有偏移,與包絡模型完全一致。但偏移還取決于是否設置絕對位置(AbsPos),當設置了則偏移為x、y、z表示,若不設置則偏移為x+場景偏移x、y+場景偏移y、z+場景偏移。
隨后在組中添加此3號PT物體,如下所示:
...
[Scene1Group]
Group1ID1=1
Group1ID2=2
Group1ID3=3
Group1Num=3
...
到此用戶控制玩家包絡的運動時,將同時控制玩家動畫模型,但是玩家是不應該看到包絡模型的,因此需要隱藏包絡模型,我們為2號包絡PT物體添加如下配置項:
...
2x=-114.234
2y=39.009
2z=127.017
2ShowTip=0
2type=2
2SelMode=0 ?
2DyPhyMod=1
2ModCanColldSelfRot=0 ?
2HideMod=1
...
HideMod設置此物體隱藏(并非不繪制),這時包絡不可見,只有玩家動畫模型。運行測試如下圖所示,圖2.11顯示了待機動畫模型的第一幀動畫。
在這一部分,我們將在場景中添加按鈕,玩家接近此按鈕時此按鈕發出提示音,同時在屏幕中央顯示操作提示,按照提示操作后一個不停旋轉的圓石盤將緩慢上升,托著玩家到達另一處高臺。因為此按鈕是固定位置,因為按鈕物體可以是場景物體。在[SceneMod1]中添加按鈕場景物體,添加的配置信息如下,那么按鈕的場景物體序號為2。
...
2mod_lv1=model\LearnW3D\Chapter1\anniu.obj
2tex=model\LearnW3D\Chapter1\anniu.bmp
2mtl=model\LearnW3D\Chapter1\anniu.mtl
2GenShadow=0
num = 2
...
在插件代碼中,我們新建一個線程,用于控制游戲的劇情,在W3DCustomEntry()中創建一個劇情控制線程,同時添加一個全局變量dwStage作為游戲的當前劇情號,初始化為0表示此時游戲劇情從頭開始。
代碼如下:
...
//劇情從頭開始
dwStage=0;
//劇情線程
_h = CreateThread(0,0,StageCtrlThread,0,0,0);
CloseHandle(_h);
...
建立劇情控制線程框架,代碼如下:
...
//劇情控制線程
DWORD WINAPI StageCtrlThread(LPVOID p)
{ ?
while(on)
{
DWORD _dwStatus;
DWORD _dwInfo;
//等待加載完畢
W3DGetSystemStatus(&_dwStatus,&_dwInfo);
if(_dwStatus == 1 && _dwInfo == 2)
{
break;
} ?
Sleep(10);
} ??
//開始工作
while(on)
{
//此處添加劇情處理代碼
//...
Sleep(10);
}
return TRUE;
}
...
要完成玩家接近此按鈕時此按鈕發出提示音這一個工作,我們需要知道此按鈕模型當前的位置和玩家當前的位置。按鈕模型是場景物體,可以使用W3DGetSceneModPos函數獲得位置,函數原型如下:
函數定義
BOOL W3DGetSceneModPos(DWORD dwModID,vec3* pvPos)
功能描述
獲得場景物體當前的中心點位置。dwModID為查詢的場景物體序號,pvPos返回此場景物體的中心點位置。
提示:pvPos[0],pvPos[1],pvPos[2]分別為返回的x,y,z坐標。
返回值
函數執行成功為true,失敗為false,調用W3DGetLastError了解更多錯誤信息
需要注意的是,場景物體的中心點指的是此物體的邊界盒中心點,場景物體邊界框和中心點位置如下圖所示,圖中場景物體為一個茶壺,長方體為此茶壺的編輯框。
當到達觸發半徑后,將發出提示音。W3D引擎聲音播放方法主要有三種:背景音樂、場景物體聲音、PT物體聲音。
背景音樂:不是場景中某個物體發出的聲音,而是一種泛在的聲音,因此不存在音量隨距離的增大減少而發生改變。
場景物體聲音:場景中某個場景物體發出的聲音,可以設置音量隨距離此物體的增大減少而發生改變。
PT物體聲音:場景中某個PT物體發出的聲音,可以設置音量隨距離此物體的增大減少而發生改變。
在這里,因為按鈕是場景物體,因此需要添加一個場景物體聲音,可以使用函數W3DAddModSoundByPlayPos為一個場景物體增加一個聲音播放請求,此函數原型如下:
函數定義
BOOL W3DAddModSoundByPlayPos(DWORD dwModID,
DWORD dwModType,
DWORD dwSoundVolume,
char* pszSoundFileName,
BOOL bPlayByDis,
DWORD dwPlayPosID,
DWORD dwPlayDeviceID,
BOOL bAddExist,
DWORD dwDisMode)
功能描述
為場景或PT物體添加一個播放聲音請求。
dwModID為物體序號。
dwModType為物體類型,場景物體為W3D_SCENE_MOD,PT物體為W3D_PT_MOD(此枚舉類型定義在W3DEngShell.h中)。
dwSoundVolume為聲音的初始音量,最小音量為0。
pszSoundFileName為需要播放的聲音文件路徑(路徑為ascii字符格式),聲音文件格式可以為wav或mp3等格式。
bPlayByDis指定此聲音的音量是否隨距離此物體的距離變化,true為隨距離變化,距離此物體越遠聲音越小,越近聲音越大,false不隨距離變化,一直保持為初始音量。
dwPlayDeviceID指定一個聲音播放設備ID用于播放此聲音,引擎內置了20個播放器,因此編號ID可以為1~20。
dwPlayPosID指定此聲音播放請求在播放隊列中的位置,引擎內置了一個500大小的播放隊列,任何一個聲音需要播放時,需要將此播放請求填寫到這個隊列的其中一個位置。參數設置范圍為0~499。
bAddExist指定當dwPlayPosID位置上已經存在了聲音播放請求時,是否仍然要添加播放請求。
dwDisMode指定隨聲音隨距離的衰減模式,
當為0時,聲音隨距離非線性衰減(衰減較快),當前音量=初始音量/距離
當為1時,聲音隨距離線性衰減(衰減較慢),當前音量=初始音量-距離。
當為2時,聲音隨距離非線性衰減(衰減較快),當前音量=初始音量-距離2。
返回值
函數執行成功為true,失敗為false,調用W3DGetLastError了解更多錯誤信息
注意此函數僅向引擎申請一次播放請求,引擎是否播放此聲音取決于以下情況是否滿足:
dwPlayDeviceID指定了正確的播放器ID
dwPlayPosID指定了正確的播放隊列位置
dwPlayPosID位置處沒有正在播放的聲音
bAddExist可以設置當此播放位置已經有聲音請求時,是否仍然添加聲音請求。當此播放位置有聲音請求但未播放時,如果仍然添加聲音將覆蓋原先的播放請求,那么當播放到此播放位置時,將播放覆蓋后的那個聲音;當此播放位置有聲音請求且正在播放時,強行添加聲音將導致聲音播放出現錯誤。
引擎同樣支持播放隊列位置的聲音狀態的查詢,調用函數W3DInquireSoundStatus,函數定義原型如下所示:
函數定義
BOOL W3DInquireSoundStatus(DWORD dwPlayPosID, DWORD* dwStatus)
功能描述
查詢播放隊列位置處的聲音播放狀態。
dwPlayPosID指定需要查詢的播放隊列位置。dwStatus返回狀態,狀態可以是:W3D_SOUND_STATUS_PLAY(正在播放),W3D_SOUND_STATUS_END(播放結束)(此枚舉類型定義在W3DEngShell.h中)
返回值
函數執行成功為true,失敗為false,調用W3DGetLastError了解更多錯誤信息
可以調用W3DStopModSound函數立即停止指定播放隊列位置處的聲音,函數定義原型如下所示:
函數定義
BOOL W3DStopModSound(DWORD dwPlayPosID)
功能描述
停止播放隊列位置處的聲音。dwPlayPosID指定需要停止的播放隊列位置。
返回值
函數執行成功為true,失敗為false,調用W3DGetLastError了解更多錯誤信息
在距離觸發劇情處添加代碼實現播放提示音,添加的代碼如下所示:
? ?...
//如果小于指定距離觸發劇情
if(_dis < 30)
{
W3DAddModSoundByPlayPos(2,
? ?W3D_SCENE_MOD,
?1000,
?"sound\\1.mp3",
?1,
?0,
?1,
?0,
?1);
dwStage=1;
}
...
代碼為按鈕場景物體(序號為2)添加一個聲音(聲音文件路徑為sound\1.mp3),使用最大音量(即此聲音文件的默認音量),設置聲音隨距離衰減,使用1號播放器,0號播放隊列位置,存在聲音不添加,聲音播放模式為線性衰減。代碼最后設置劇情號為1,表示聲音播放后進入1號劇情,即開始下一階段的游戲劇情。運行游戲進行測試,如下圖所示,包含聲音的視頻演示資料在本書配套學習資源包中。
在2號劇情中,我們需要將提示標中的文字改為“請按下E鍵”,可以使用函數W3DSetFMModWord完成,函數原型如下:
函數定義
BOOL W3DSetFMModWord(DWORD dwModID,
BOOL bChangeWord,
WCHAR* word,
? BOOL bChangeWordCol,
? float fWordR,float fWordG,float fWordB,
? BOOL bChangeWordTran,
? float fTranX,float fTranY,float fTranZ,
BOOL bChangeWordRot,
float fRotRa,float fRotX,
float fRotY,float fRotZ,
? BOOL bChangeWordScale,
? float fSclX,float fSclY,float fSclZ)
功能描述
設置封面物體的文字信息。dwModID為封面物體序號ID。
bChangeWord設置是否改變文字內容,設置為true則word有效。
word設置文字內容,unicode字符串形式。
bChangeWordCol設置是否改變文字顏色,設置為true則fWordR,fWordG,fWordB有效。
fWordR,fWordG,fWord設置文字的顏色紅,綠,藍分量,每一個分量取值范圍為[0.0,1.0]。
bChangeWordTran設置是否改變文字的位置信息,fTranX、fTranY和fTranZ分別是x、y、z位置分量。
bChangeWordRot設置是否改變文字的旋轉信息,fRotRa、fRotX、fRotY和fRotZ是旋轉角度、旋轉x軸分量、y軸分量、z軸分量。
bChangeWordScale設置是否改變文字的縮放信息,fSclX、fSclY和fSclZ是縮放x分量、y分量、z分量。
(文字位置、旋轉、縮放信息含義見3.2節封面物體配置信息中相關描述)
返回值
函數執行成功為true,失敗為false,調用W3DGetLastError了解更多錯誤信息
添加2號劇情代碼如下所示,首先設置文字內容為“請按下E鍵”同時將文字顏色設置為紅色,位置不改變。代碼最后進入3號劇情。
...
else if(dwStage == 2)
{
W3DSetFMModWord(1,
? ? ? ? ? ? ? ? ? ? ? 1,TEXT("請按下E鍵"),//改變文字內容
? ? ? ? 1,1.0f,0.0f,0.0f,//初始設置為紅色
? ?0,0,0,0,0,0,0,0,0,0,0,0,0);
dwStage = 3;
}
...
4.5 無阻塞延時方法
在進入3號劇情后,需要不斷紅白閃爍文字,同時文字緩慢飛入屏幕指定位置。閃爍文字需要間隔一個時間將文字顏色變換為紅色和白色,如果間隔時間使用阻塞型函數,如sleep等函數,那么劇情線程將阻塞,無法執行飛入代碼,因此我們需要無阻塞的延時方法。無阻塞的延時方法可以使用多種第三方庫或自行編寫,也可以使用引擎自帶無阻塞延遲函數完成,函數原型如下:
函數定義
BOOL W3DEventDelay(BOOL bReset,DWORD dwIndex, DWORD dwTime)
功能描述
無阻塞延時。dwIndex指定事件序號,引擎支持5000種事件的獨立超時檢查,因此可設置為0~4999。
dwTime設置此事件的超時時間(ms為單位)。
bReset重置此事件,那么將強制記錄當前時間為開始時間。
返回值
當前時間超時則返回true,否則返回false
當第一次調用此函數時,引擎將記錄當前時間為此事件的開始時間并返回false。隨后對此事件的函數調用將檢測當前時間距離記錄的上一次開始時間是否超過指定的超時時間,未超過則返回false,若超過返回true且記錄當前時間為此事件的當前時間。當重置此事件后,將結束此事件的無阻塞延時內部事件記錄并重置內部參數并返回true,即相當于清除了此事件的超時時間,下次對此事件的函數調用可以像第一次調用那樣設置新的超時時間。
那么使用這樣的無阻塞延遲函數,我們可以完成不阻塞其他代碼的延時。在3號劇情中添加如下代碼。代碼中使用0號事件,延時100ms將顏色設置變量反轉一次,然后調用W3DSetFMModWord只設置文字顏色。
...
else if(dwStage == 3)
{
static bool _ys=0;
if(W3DEventDelay(0,0,100))
{
_ys = !_ys;
}
if(_ys)
{
W3DSetFMModWord(1,
? ?0,0,
? ?1,1.0f,0.0f,0.0f,//設置為紅色
? ?0,0,0,0,0,0,0,0,0,0,0,0,0);
}
else
{
W3DSetFMModWord(1,
? ?0,0,
? ? ? ? ? ?1,1.0f,0.0f,0.0f,//設置為白色
? ?0,0,0,0,0,0,0,0,0,0,0,0,0);
}
}
...
4.6 封面物體飛入動畫實現
封面物體的文字閃爍開始后,我們讓封面物體從屏幕外飛入屏幕內指定位置,即從左側(-0.093,0.032,-0.11)移動到右側(-0.06,0.032,-0.11)。在劇情3中繼續添加代碼如下所示:
...
//提示標慢慢進入屏幕指定位置
static float _cswz=-0.093f;
W3DSetFMModPos(1,
? ? ? ? ? ? ? ? ?0,
? ?vec3(_cswz,0.032,-0.11),
? ?vec4(0.0f,0.0f,0.0f,0.0f),
? ?vec3(1.0f,1.0f,1.0f));
//位置自增
_cswz+=0.0005f;
//到指定位置
if(_cswz >= -0.06f)
{
W3DSetFMModWord(1,
? ? 0,0,
? ? 1,1.0f,0.0f,0.0f,//設置為紅色
? ? 0,0,0,0,0,0,0,0,0,0,0,0,0);
dwStage = 4;
}
...
代碼不斷調用W3DSetFMModPos設置提示標位置,讀者可以修改參數實現不同速度的飛入效果,當到達指定位置后,將提示標文字顏色設置為紅色,同時進入4號劇情。
4.7 場景物體移動開發方法
進入4號劇情后,等待用戶按下’E’鍵,按鈕消失,這時從地面下方升上來一個圓盤拖著玩家上升到一個高臺,在上升過程中地面不斷震動。
對按鍵的檢測,可以使用函數W3DGetSingleKeyInfo完成(2.5.4節中描述了函數原型),在4號劇情中添加代碼如下:
...
else if(dwStage == 4)
{
bool _eResult;
//獲得E鍵的狀態
W3DGetSingleKeyInfo('E',&_eResult);
//如果按下
if(_eResult)
{
//...
}
}
...
檢測到按下E鍵后,讓按鈕消失。讓按鈕消失可以用兩種方法實現:按鈕模型不繪制和按鈕模型隱藏。模型不繪制與隱藏有一定的區別:
按鈕模型不繪制:引擎不會為此模型分配渲染和物理資源,系統開銷最小。
按鈕模型隱藏:引擎會為此模型分配物理和其他資源,只是最終不進行圖像渲染,有一定系統資源開銷。
設置一個場景物體或PT物體是否繪制可以使用函數W3DSetSceneModShow或W3DSetPTModShow,函數原型如下:
函數定義
BOOL W3DSetSceneModShow(DWORD dwModID,DWORD dwMode)
功能描述
設置場景物體顯示狀態。dwModID指定場景物體序號ID。dwMode設置此物體的顯示狀態,true為繪制,false為不繪制。
返回值
函數執行成功為true,失敗為false,調用W3DGetLastError了解更多錯誤信息
函數定義
BOOL W3DSetPTModShow(DWORD dwModID,DWORD dwMode)
功能描述
設置PT物體顯示狀態。dwModID指定PT物體序號ID。dwMode設置此物體的顯示狀態,true為繪制,false為不繪制。
返回值
函數執行成功為true,失敗為false,調用W3DGetLastError了解更多錯誤信息
設置一個場景物體或PT物體是否隱藏可以使用函數W3DSetSceneModHide或W3DSetPTModHide,函數原型如下:
函數定義
BOOL W3DSetSceneModHide(DWORD dwModID,DWORD dwMode)
功能描述
設置場景物體隱藏狀態。dwModID指定場景物體序號ID。dwMode設置此物體的隱藏狀態,true為隱藏,false為不隱藏。
返回值
函數執行成功為true,失敗為false,調用W3DGetLastError了解更多錯誤信息
函數定義
BOOL W3DSetPTModHide(DWORD dwModID,DWORD dwMode)
功能描述
設置PT物體隱藏狀態。dwModID指定PT物體序號ID。dwMode設置此物體的隱藏狀態,true為隱藏,false為不隱藏。
返回值
函數執行成功為true,失敗為false,調用W3DGetLastError了解更多錯誤信息
因為此按鈕僅顯示一個模型,不進行物理運算,因此我們不繪制此按鈕模型。添加代碼如下所示,按下E鍵后不繪制2號場景物體(即為按鈕物體),完成后進行5號劇情。
...
//如果按下
if(_eResult)
{
W3DSetSceneModShow(2,0);
dwStage = 5;
}
...
在5號劇情中,開始將地面下方的圓盤升上來拖著玩家上升到一個高臺,同時地面震動。一個物體托著或推著一個物體移動,一般可以采用動畫或物理運算方法。
動畫方法:實時計算兩個物體的距離,當距離為接觸時,一個物體跟著另一個物體移動,這種方法程序實現比較復雜,且移動靈活性低、效果不自然。
物理運算方法:將兩個物體設置為剛性物體物體,一個物理物體對另一個物體物體的碰撞、擠壓等作用所發生的形變、位置移動由物理引擎實時計算。這種方法對游戲開發者來說較為簡單,且移動靈活性高、效果更好。
這里我們使用物理運算方法實現。玩家包絡已經是動態物理物體,那么我們只需要將此圓盤模型也設置為物理物體即可,但設置為靜態還是動態物理物體?由于在此游戲中,我們可以認為此圓臺的托舉是對于玩家是個不可抗力,即玩家的力量無法讓圓臺發生位置改變,那么我們可以將此圓臺設置為一個靜態物理模型。制作圓臺與高臺模型如下圖所示:
通過網盤分享的文件:W3D引擎游戲開發學習資料
鏈接: https://pan.baidu.com/s/1mm5f9nDzkT3KlFYQscJBoQ?pwd=1234 提取碼: 1234