電腦遊戲製作開發設計論壇 首頁 電腦遊戲製作開發設計論壇
任何可以在PC上跑的遊戲都可以討論,主要以遊戲之製作開發為主軸,希望讓台灣的遊戲人有個討論、交流、教學、經驗傳承的園地
 
 常見問題常見問題   搜尋搜尋   會員列表會員列表   會員群組會員群組   會員註冊會員註冊 
 個人資料個人資料   登入檢查您的私人訊息登入檢查您的私人訊息   登入登入 

Google
[C++][10*]習題解答

 
發表新主題   回覆主題    電腦遊戲製作開發設計論壇 首頁 -> 遊戲程式初級班:語法及基礎概念
上一篇主題 :: 下一篇主題  
發表人 內容
yag
Site Admin


註冊時間: 2007-05-02
文章: 688

2673.35 果凍幣

發表發表於: 2007-8-6, PM 1:53 星期一    文章主題: [C++][10*]習題解答 引言回覆

嗯...我回來了...
那麼,先來個習題解答吧
首先是初級的...很簡單,把
代碼:
else if( Move == "get coins" )   // 撿錢

改成
代碼:
else if( Move == "get coins" || Move == "g" )   // 撿錢

就行了

中級的,我是改成了如下:
代碼:
   char death = NULL;   // 判斷是否結束遊戲的旗標

   Init( MapDesc );
   
   while( 1 )
   {
      fflush( stdin );   // 消除下面cin >> death;時,使用者輸入的過多資訊

      Desc( MapDesc, X, Y );
      InputCmd( X, Y );      // 因為主角剛出現時要預設為無事狀態,所以先在迴圈外呼叫一次

      while( status != 99 )   // 遊戲未結束前
      {
         Desc( MapDesc, X, Y );      // 顯示場景說明
         CoinOrDingDing();   // 顯示出特殊情況說明
         InputCmd( X, Y );   // 等待接收輸入
      }

ASK:
      cout << "你已經死了!三途之川的船在前方等著你,要上船嗎?(y/n)" << endl;
      cin >> death;

      if( death == 'y' || death == 'Y' )
      {
         cout << "擺渡人拉拉:你好!你好!" << endl;
         cout << "擺渡人拉拉:抱抱!" << endl;
         cout << "擺渡人拉拉:說.再.見!" << endl;
         cout << "擺渡人拉拉:再見!" << endl;
         system( "pause" );
         break;   // 跳離while(1),遊戲結束
      }
      else if( death == 'n' || death == 'N' )
      {
         cout << "Yag大神以巫師的神力使你原地復活!" << endl;   // 原地復活,所以X跟Y不變
         cout << "你心懷感激地將身上的金幣全部交出當成治療費。" << endl;
         MyMoney = 0;   // 將錢交出
         system( "pause" );
         system( "cls" );
         status = 0;   // 因為復活,將狀態改回正常狀態
      }
      else
         goto ASK;   // 隨便輸入就重新發問
   }

   return 0;

你們會發現,我把CoinOrDingDing();給抽了出來,那是因為我為了寫高級習題,把一些函式做了修改的關係,底下會進行說明。

話說,我當初出高級習題時,沒有仔細想清楚就出題了,呵呵,所以真的有去思考的人,應該會覺得有些微的彆扭吧,因為程式的一些細微架構並不適合直接寫一個get函式。
比如說,地上隨機出現的money是個區域變數,由CoinOrDingDing();傳給InputCmd,要是又寫了個get函式,那麼勢必要再傳給get才能進行處理,但我當初出題時沒想到這點,所以並沒有把money當成參數之一。
不過如果把money當成參數之一,又會有其他問題,像是之後再擴展時,如果地上有屍體、有裝備的話呢?再一一增加參數一個一個傳?這恐怕不是什麼好方法。
何況如果地上有裝備的話,勢必要在地圖上增加一個掉落物的陣列來儲存目前地面上有的物品,不然只要一離開地圖再回來,物品就消失了,感覺會比較差,另外,如果有機會把它改成可連線的遊戲,那麼不記錄掉落物,就會造成別人丟在地上的東西你看不到的情形。
所以,雖然目前我還沒把掉落物陣列加進來,不過為了未來的擴展性,我將money改成了全域變數,也因此CoinOrDingDing不再需要傳參數出來,InputCmd也不再需要讀個money參數進來,所以CoinOrDingDing理所當然的就被抽出來了。

那麼,底下是寫get函式時主要變動到的部份:
代碼:
void get( string things = "coins" );   // 撿起東西,預設為撿金幣

代碼:
int moneyOnGround = 0;   // 隨機出現在地上的金幣數量

上面兩個都是寫在main之前的宣告
代碼:
      else if( Move.substr( 0, 4 ) == "get " )   // 撿東西
      {
         for( i = 4; i < Move.length(); i++ )   // 去掉前面的多餘空白
         {
            if( Move.substr( i, 1 ).compare( " " ) != 0 )
               break;
         }
         if( i == Move.length() )   // 除了一堆空白外沒別的東西
            get();
         else
            get( Move.substr( i, Move.find_last_not_of( " " ) - i + 1 ) );   // 去掉後面的多餘空白,傳入get()

         Flag = true;   // 因為撿東西並沒有離開場景,所以繼續迴圈
      }
      else if( Move.substr( 0, 2 ) == "g " )      // 撿東西
      {
         for( i = 2; i < Move.length(); i++ )   // 去掉前面的多餘空白
         {
            if( Move.substr( i, 1 ).compare( " " ) != 0 )
               break;
         }
         if( i == Move.length() )   // 除了一堆空白外沒別的東西
            get();
         else
            get( Move.substr( i, Move.find_last_not_of( " " ) - i + 1 ) );   // 去掉後面的多餘空白,傳入get()

         Flag = true;   // 因為撿東西並沒有離開場景,所以繼續迴圈
      }
      else if( Move == "get" || Move == "g" )      // 撿東西
      {
         get();
         Flag = true;   // 因為撿錢並沒有離開場景,所以繼續迴圈
      }

上面是InputCmd中處理的部份,大家可以看到我把它分成了三個if,這是為了讓g跟get具有同等效力,至於substr、compare、find_last_not_of之類的,是為了讓玩家的輸入格式可以自由一點,像是以往只接受:
"get coins"
改了初級的還接受:
"g"
現在的話,可以接受以下各種格式:
代碼:
"g    "
"g    coins    "
"get    "
"get"
"get    coins     "
"g  coins"
"get    coins"

換句話說,開頭可以用g可以用get,而此指令跟撿起的目標物之間的空白會盡數忽略,撿起的目標物之後的空白也會盡數忽略,這在之前是辦不到的,之前只要多打了一個空白字元就會造成輸入錯誤。

那麼,來講講substr,這個函式可以取得目前字串的子字串,第一個參數是起始索引,第二個參數是要取得的長度,例:
"get coins" → substr( 0, 1 ) == "g"
"get coins" → substr( 2, 4 ) == "t co"
"get coins" → substr( 5, 7 ) == "oins"

應該可以了解到,索引是從0開始計算,而長度是從1開始計算,第三個例子中,雖然長度為7,但是字串已結束,所以只會抓到字串尾巴就停止。

至於length函式,當然是取得字串的長度,長度是由1開始計算,所以字串的最大索引會是length - 1,因為索引在C++中總是由0開始的。

接著的compare函式,會對字串做比較的動作,唯一的一個參數,就是做比較的目標字串,在程式中我這樣寫:
代碼:
Move.substr( i, 1 ).compare( " " )

就是從Move中取得一個字元長度的子字串,並將它跟空白字元做比較,如果兩者相同,會回傳一個數值為0,如果Move比空白字元"大",那會回傳一個正整數(在我的compile下,它是1),如果Move比空白字元"小",那會回傳一個負整數(在我的compile下,它是-1)。
那麼,字串怎麼分大小呢?以英文字母來說,大寫字母比較小,小寫字母比較大,然後從第一個字元開始比大小,如果第一個字元一樣,那就從第二個字元開始比,以下是一些例子:
"abc" > "ABC"
"abcd" > "abc"
"abc" > "aaa"
"h" > "abc"
"game" > "abcdefg"
"game" > "gal"
"gun" > "game"


最後是find_last_not_of函式,它的功能就是從字串的最尾巴開始往回找,找出第一個不屬於其唯一參數(字元集)的字元,然後回傳它的索引,接著後面的索引減掉前面的索引再加上一,就是我要的子字串的長度囉。
關於字元集,大家可能不太了解其意思,底下是個例子:
代碼:
string test = "What a nice day!!";
cout << test.find_last_not_of( "AaEeIiOoUu" ) << endl;
cout << test.find_last_of( "AaEeIiOoUu" );

這段程式碼的輸出會是:
16
13
"What a nice day!!"總共17個字,索引從0(W)到16(!),而"AaEeIiOoUu"就是我們的字元集,find_last_not_of,會從最後面找尋第一個不屬於這字元集的字元,那也就是第二個"!",所以回傳索引為16。
接著find_last_of應該不用多講解,跟find_last_not_of只差了個not,所以就是從後找尋第一個屬於其字元集的字元,也就是day當中的"a",因此其回傳索引為13。
這樣應該有了解了吧?習題解答就到此為止,下面是整個程式碼(cpp檔)的載點以及其執行檔(exe檔)的載點,希望對你們有用。

main.cpp(13KB)
example10-2.exe(288KB)
回頂端
檢視會員個人資料 發送私人訊息 發送電子郵件
從之前的文章開始顯示:   
發表新主題   回覆主題    電腦遊戲製作開發設計論壇 首頁 -> 遊戲程式初級班:語法及基礎概念 所有的時間均為 台灣時間 (GMT + 8 小時)
1頁(共1頁)

 
前往:  
無法 在這個版面發表文章
無法 在這個版面回覆文章
無法 在這個版面編輯文章
無法 在這個版面刪除文章
無法 在這個版面進行投票
可以 在這個版面附加檔案
可以 在這個版面下載檔案


Powered by phpBB © 2001, 2005 phpBB Group
正體中文語系由 phpbb-tw 維護製作