勘誤:昨天的loadMap函數內有一部份寫錯了,需做更改,請大家見諒!
fopen函數的第一個參數,原本為TITLE_MAP,這邊需要請大家將其改為mapPath,否則地圖會永遠load到TITLE_MAP... 筆者在自己嘗試下發現了這個大Bug,若大家發現了換不了地圖的問題,大概出自於此orz
void loadMap(char *mapPath)
{
int i = 0, j = 0;
FILE *map_file = NULL;
if((map_file = fopen(mapPath, "r")) != NULL) // 請大家將此處的TITLE_MAP改成mapPath!
{
for(i = 0; i < MAP_HEIGHT; i++)
{
for(j = 0; j < MAP_WIDTH; j++)
{
fscanf(map_file, "%c", &game_map[i][j]);
}
}
fclose(map_file);
}
else
{
printf("地圖檔不存在或損壞...\n");
system("pause");
exit(1);
}
}
引言
昨天我們完成了遊戲的開頭,再來就是要進入遊戲內容的部分。
這邊的計畫是這樣的,
在使用者按了任意鍵進入遊戲後,
我們會先load第一張地圖,此時玩家會出現在地圖中,
因此我們第一步要來處理玩家的走動,也就是人物控制的部分!
規格表
以下是今天的規格表,大家可以先下載下來:
項目 | 內容 |
---|---|
地圖文字檔 | level0.txt |
開門音效 | ドア03 |
勝利音效 | ジングル01 |
下載後,將level0.txt直接放到maps資料夾中,並在loadMap.h中加上:
/* File: loadMap.h */
#define LEVEL0_MAP "maps/level0.txt"
開門音效先取名為"unlock.wav"、勝利音效取名為"victory.wav"(請先轉成.wav檔,再重新命名!),
都放入musics資料夾,並在SystemSetting.h中加上:
/* File: SystemSetting.h */
#define UNLOCK "musics/unlock.wav"
#define VICTORY "musics/victory.wav"
進入遊戲音效
這部分昨天沒有說明到,稍微補充一下:
/* File: main.c */
.
..
...
loadMap(TITLE_MAP);
showMap();
system("pause");
// 寫在system("pause");底下
// ------------新增部分-------------------
playMusic(MUSIC_STOP, BGM_MODE); // 當玩家按下任意鍵,先停止BGM播放
playMusic(SYSTEM_OK, SE_MODE); // 然後以音效模式(播放完才繼續執行)播放SYSTEM_OK的音效
// --------------------------------------
遊玩廣場PlayGround
在由開頭進入遊戲後,預計會執行的動作是:
如以上流程,每一關卡都會重複這樣的步驟,
關卡結束時會將"isPlaying"狀態解除,跳出迴圈,進入下一關。
我們可以選擇將這個流程包裝成一個函數,我們稱它為PlayGround,
如同遊戲主要遊玩的廣場。
以下我們先建立一組PlayGround檔案:
/* File: PlayGround.h */
#ifndef __PLAYGROUND_H__
#define __PLAYGROUND_H__
#include "LoadMap.h" // 需要用到LoadMap.h,先引入
#endif // __PLAYGROUND_H__
/* File: PlayGround.c */
#include "PlayGround.h"
玩家Player 定義
流程圖中,提到要定義玩家,我們首先來定義玩家(Player)是什麼樣子:
先構思一下,玩家需要哪些東西:
OK,暫時先定義這樣就行,之後有需要什麼新功能我們再補上。
這樣子的一組資料,我想用struct(結構)來實作最適合了~
/* File: PlayGround.h */
#ifndef __PLAYGROUND_H__
#define __PLAYGROUND_H__
#include "LoadMap.h"
// ------------新增部分-------------------
typedef struct player
{
char skin;
int x;
int y;
}Player; // 定義結構Player,包含skin, x, y等資訊
Player p; // 宣告一Player型態的變數p,之後對於player的操作都會從這裡取用
// (ex: p.x = 10; 將玩家x座標設為10)
// ---------------------------------------
#endif // __PLAYGROUND_H__
初始化玩家
老話一句,別忘了初始化~
我們宣告一個initPlayer函數來做初始化,並定義它。
/* File: PlayGround.h */
// 寫在Player p;底下
void initPlayer(char skin, int x, int y); // 設定玩家skin,並給定一x, y座標當作初始位置
/* File: PlayGround.c */
#include "PlayGround.h"
void initPlayer(char skin, int x, int y)
{
p.skin = skin; // 玩家的skin會設為傳進來的skin
p.x = x; // 玩家的x位置會設為傳進來的x
p.y = y; // 玩家的y位置會設為傳進來的y
game_map[y][x] = skin; // 在地圖上該位置寫入玩家skin,相當於將玩家擺到地圖中
isPlaying = 1; // 將isPlaying狀態設為1,方可進入迴圈(見以下介紹)
}
PlayGround函數
裝入半無窮迴圈的函數,PlayGround,
而所謂「半」無窮迴圈就是因為有isPlaying狀態判斷是否繼續迴圈,
因此我們也要定義isPlaying變數。
/* File: PlayGround.h */
#ifndef __PLAYGROUND_H__
#define __PLAYGROUND_H__
#include "LoadMap.h"
typedef struct player
{
char skin;
int x;
int y;
}Player;
Player p;
// ------------新增部分-------------------
int isPlaying; // 設為1時PlayGround的迴圈繼續,設為0時跳出迴圈,進入下一關或遊戲結束
// ---------------------------------------
void initPlayer(char skin, int x, int y);
// ------------新增部分-------------------
void playGround(char *mapPath, int initX, int initY); // PlayGround傳入要遊玩的地圖,
// 以及玩家初始位置
// ---------------------------------------
#endif // __PLAYGROUND_H__
/* File: PlayGround.c */
//寫在initPlayer底下
void playGround(char *mapPath, int initX, int initY)
{
system("cls"); // 清空前一幕留下的所有訊息
loadMap(mapPath); // load mapPath中的地圖檔
initPlayer('@', initX, initY); // 玩家skin設為"@",初始位置在initX, initY
while(isPlaying) // 由isPlaying判斷迴圈是否繼續
{
// 迴圈內容稍後分析
}
system("cls"); // 跳出迴圈後「完全」清空畫面,
// 因為selfCls只能清空地圖部分,這個則會整個視窗都清空,方便進入下一關
}
移動玩家 movePlayer
由流程得知迴圈中需要:
其中讀取按鍵一部分是為了移動玩家,
我們把移動玩家這部分包成一個函數,叫做movePlayer:
/* File: PlayGround.h */
// 寫在playGround底下
void movePlayer(int x, int y); // 簡單明瞭,想移動到哪個位置就傳入該座標
/* File: PlayGround.c */
// 寫在playGround底下
void movePlayer(int x, int y) // 先分兩情況
{
if(game_map[y][x] == ' ') // 若打算移動的位置是空的,則可以移動
{
game_map[p.y][p.x] = ' '; // 把移動前位置清空
game_map[y][x] = p.skin; // 把玩家放到新的位置
p.x = x; // 更新玩家現在的位置
p.y = y;
}
if(game_map[y][x] == 'E') // 若打算移動的位置是大寫E(終點),
{ // 播放音效、isPlaying設為0
playMusic(VICTORY, SE_MODE); // 以音效模式播放VICTORY
printf("抵達終點!\n"); // 提示玩家已抵達終點
system("pause");
isPlaying = 0; // 離開這個PlayGround
}
}
再來就可以補全PlayGround了!
/* File: PlayGround.c */
// 補全 while(isPlaying) 中的內容
while(isPlaying)
{
selfCls(); //
showMap(); // 更新畫面
// 按下各個方向鍵,移動玩家
if(GetAsyncKeyState(VK_RIGHT) != 0)
{
movePlayer(p.x + 1, p.y); // 例如p.x加一,就是往右移動一格
}
if(GetAsyncKeyState(VK_LEFT) != 0)
{
movePlayer(p.x - 1, p.y);
}
if(GetAsyncKeyState(VK_UP) != 0)
{
movePlayer(p.x, p.y - 1);
}
if(GetAsyncKeyState(VK_DOWN) != 0)
{
movePlayer(p.x, p.y + 1);
}
}
主函數呼叫
/* File: main.c */
#include "SystemSetting.h" // 設定標題、視窗、音效、錯誤處理、遊戲初始化等
#include "LoadMap.h" // 載入地圖、頁面
// ------------新增部分-------------------
#include "PlayGround.h" // 遊戲遊玩頁面、玩家設定
// ---------------------------------------
int main(int argc, char *argv[])
{
playMusic(BGM, BGM_MODE);
setGameTitle(GAME_TITLE);
setGameWindow(GAME_WIDTH, GAME_HEIGHT);
initMap();
loadMap(TITLE_MAP);
showMap();
system("pause");
playMusic(MUSIC_STOP, BGM_MODE);
playMusic(SYSTEM_OK, SE_MODE);
// ------------新增部分-------------------
playGround(LEVEL0_MAP, 28, 4); // 把level0.txt地圖讀取進來成為PlayGround
// ---------------------------------------
return 0;
}
執行
都ok了就執行看看吧!
用方向鍵將玩家"@"移動到"E"的位置,
就會聽到勝利的音效囉~然後按下任意鍵遊戲結束。
當然不只那麼簡單啦~ 明天我們會繼續加功能的!