PHP 是一種易於使用、易於學習且能夠普遍獲取的編程語言。它很是適合開發在各類遊戲中可使用的簡單腳本。不管是一我的玩簡單的使用紙和筆的遊戲,仍是同一羣人玩複雜的桌面角色扮演遊戲,或者任意類型的聯機遊戲,本系列都提供了適合您的內容。「用 PHP 能夠編寫的 30 個遊戲腳本」 系列中的每篇文章都將分別用不到 300 詞的文字介紹 10 個腳本(3d10 表示 「擲三個 10 面的骰子」),這些介紹性文字甚至對於開發新手來講都十分簡單,並且對於經驗豐富的遊戲玩家來講也十分有用。本系列的目的在於爲您提供能夠修改的內容來知足自身的需求,以便您能夠在下一次遊戲交流會上經過展現您的筆記原本給朋友和玩家們留下深入印象。 開始以前 做爲一名遊戲專家/設計者和開發人員,我常常發現本身在運行、規劃和玩遊戲時,不多編寫有用的實用程序和腳本。有時我須要快速想出創意。其餘時候,我只須要編出一大堆非玩家角色(Non-Player Character,NPC)的名稱。偶爾,我還須要處理數字、處理一些異常或者將一些文字遊戲集成到遊戲中。只需事先完成一點腳本工做,就能夠更好地管理這些任務。 本文將探究在各類遊戲中可使用的 10 個基本腳本。代碼壓縮包包含所討論的每一個腳本的完整源代碼,而且能夠在 chaoticneutral 查看腳本實際運行狀況。 咱們將快速地介紹這些腳本。有關如何查找主機或設置服務器的內容將不作介紹。有不少 Web 託管公司提供 PHP,而且若是須要安裝本身的 PHP,XAMPP 安裝程序使用起來也十分簡單。咱們將不會花費大量時間談論 PHP 最佳實踐或遊戲設計技術。本文介紹的腳本易於理解、使用簡單並能夠快速掌握。 簡單的擲骰器 許多遊戲和遊戲系統都須要骰子。讓咱們先從簡單的部分入手:擲一個六面骰子。實際上,滾動一個六面骰子就是從 1 到 6 之間選擇一個隨機數字。在 PHP 中,這十分簡單:echo rand(1,6);。 在許多狀況下,這基本上很簡單。可是在處理機率遊戲時,咱們須要一些更好的實現。PHP 提供了更好的隨機數字生成器:mt_rand()。在不深刻研究二者差異的狀況下,能夠認爲 mt_rand 是一個更快、更好的隨機數字生成器:echo mt_rand(1,6);。若是把該隨機數字生成器放入函數中,則效果會更好。 清單 1. 使用 mt_rand() 隨機數字生成器函數 function roll () { return mt_rand(1,6); } echo roll(); 而後能夠把須要滾動的骰子類型做爲參數傳遞給函數。 清單 2. 將骰子類型做爲參數傳遞 function roll ($sides) { return mt_rand(1,$sides); } echo roll(6); // roll a six-sided die echo roll(10); // roll a ten-sided die echo roll(20); // roll a twenty-sided die 從這裏開始,咱們能夠繼續根據須要一次滾動多個骰子,返回結果數組;也能夠一次性滾動多個不一樣類型的骰子。可是大多數任務均可以使用這個簡單的腳本。 隨機名稱生成器 若是正在運行遊戲、編寫故事或者一次性建立大批字符,有時會疲於應付不斷出現的新名字。讓咱們看一看可用於解決此問題的一個簡單隨機名稱生成器。首先,讓咱們建立兩個簡單數組 — 一個用於名字,一個用於姓氏。 清單 3. 名字和姓氏的兩個簡單數組 $male = array( "William", "Henry", "Filbert", "John", "Pat", ); $last = array( "Smith", "Jones", "Winkler", "Cooper", "Cline", ); 而後就能夠從每一個數組中選擇一個隨機元素:echo $male[array_rand($male)] . ' ' . $last[array_rand($last)];。要一次性提取多個名稱,只需混合數組並根據須要提取。 清單 4. 混合名稱數組 shuffle($male); shuffle($last); for ($i = 0; $i <= 3; $i++) { echo $male[$i] . ' ' . $last[$i]; } 基於此基本概念,咱們能夠建立保存名字和姓氏的文本文件。若是在文本文件的每一行中存放一個名字,則能夠輕鬆地用換行符分隔文件內容以構建源代碼數組。 清單 5. 建立名稱的文本文件 $male = explode('\n', file_get_contents('names.female.txt')); $last = explode('\n', file_get_contents('names.last.txt')); 構建或查找一些好的名字文件(代碼歸檔 中附帶了一些文件),此後咱們毫不再須要爲名字煩惱。 場景生成器 利用構建名字生成器使用的相同基本原理,咱們能夠構建場景生成器。今生成器不但在角色扮演遊戲中十分有用,並且在須要用到僞隨機環境集合(可用於角色扮演、即興創做、寫做等狀況)的狀況下也十分有用。我最喜歡的遊戲之一,Paranoia 在其 GM Pack 中包括了 「任務混合器(mission blender)」。任務混合器可用於在快速滾動骰子時整合完整任務。讓咱們整合本身的場景生成器。 考慮如下場景:您醒來後發現本身迷失於叢林中。您知道本身必須趕去紐約,可是不知道緣由。您能夠聽到附近的狗叫聲及清晰的敵方搜尋者的聲音。您渾身發冷、不住顫抖,並且沒有武器。該場景中的每一句話都介紹場景的特定方面: 「您醒來後發現本身迷失於叢林中」 — 這句話將創建設置。 「您知道本身必須趕去紐約」 — 這句話將描述目標。 「您能夠聽到狗叫聲」 — 這句話將介紹敵人。 「您渾身發冷、不住顫抖,並且沒有武器」 — 這句話將添加複雜度。 就像建立名字和姓氏的文本文件同樣,首先分別建立設置、目標、敵人和複雜度的文本文件。代碼歸檔中附帶了樣例文件。在擁有這些文件後,生成場景的代碼與生成名稱的代碼基本相同。 清單 6. 生成場景 $settings = explode("\n", file_get_contents('scenario.settings.txt')); $objectives = explode("\n", file_get_contents('scenario.objectives.txt')); $antagonists = explode("\n", file_get_contents('scenario.antagonists.txt')); $complicati**** = explode("\n", file_get_contents('scenario.complicati****.txt')); shuffle($settings); shuffle($objectives); shuffle($antagonists); shuffle($complicati****); echo $settings[0] . ' ' . $objectives[0] . ' ' . $antagonists[0] . ' ' . $complicati****[0] . " \n"; 咱們能夠經過添加新文本文件向場景中添加元素,也可能但願添加多重複雜度。添加到基本文本文件中的內容越多,場景隨時間的變化就越多。 牌組建立器(Deck builder)和裝備(shuffler) 若是您要玩紙牌而且要處理與紙牌相關的腳本,咱們須要用裝備中的工具整合一副牌組構建器。首先,讓咱們構建一副標準紙牌。須要構建兩個數組 — 一個用於保存同花色的組牌,而另外一個用於保存牌面。若是稍後須要添加新組牌或牌類型,則這樣作將得到很好的靈活性。 清單 7. 構建一副標準撲克牌 $suits = array ( "Spades", "Hearts", "Clubs", "Diamonds" ); $faces = array ( "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King", "Ace" ); 而後構建一副牌數組來保存全部紙牌值。只需使用一對 foreach 循環便可完成此操做。 清單 8. 構建一副牌數組 $deck = array(); foreach ($suits as $suit) { foreach ($faces as $face) { $deck[] = array ("face"=>$face, "suit"=>$suit); } } 在構建了一副撲克牌數組後,咱們能夠輕鬆地洗牌並隨機抽出一張牌。 清單 9. 洗牌並隨機抽出一張牌 shuffle($deck); $card = array_shift($deck); echo $card['face'] . ' of ' . $card['suit']; 如今,咱們就得到了抽取多副牌或構建多層牌盒(multideck shoe)的捷徑。 勝率計算器:發牌 因爲構建撲克牌時會分別跟蹤每張牌的牌面和花色,所以能夠經過編程方式利用這副牌來計算獲得特定牌的概率。首先每隻手分別抽出五張牌。 清單 10. 每隻手抽出五張牌 $hands = array(1 => array(), 2=>array()); for ($i = 0; $i < 5; $i++) { $hands[1][] = implode(" of ", array_shift($deck)); $hands[2][] = implode(" of ", array_shift($deck)); } 而後能夠查看這副牌,看看剩餘多少張牌以及抽到特定牌的機率是多少。查看剩餘的牌數十分簡單。只須要計算 $deck 數組中包含的元素數。要得到抽到特定牌的機率,咱們須要一個函數來遍歷整副牌並估算其他牌以查看是否匹配。 清單 11. 計算抽到特定牌的概率 function calculate_odds($draw, $deck) { $remaining = count($deck); $odds = 0; foreach ($deck as $card) { if ( ($draw['face'] == $card['face'] && $draw['suit'] == $card['suit'] ) || ($draw['face'] == '' && $draw['suit'] == $card['suit'] ) || ($draw['face'] == $card['face'] && $draw['suit'] == '' ) ) { $odds++; } } return $odds . ' in ' $remaining; } 如今能夠選出嘗試抽出的牌。爲了簡單起見,傳入看上去相似某張牌的數組。咱們能夠查找特定的一張牌。 清單 12. 查找指定的一張牌 $draw = array('face' => 'Ace', 'suit' => 'Spades'); echo implode(" of ", $draw) . ' : ' . calculate_odds($draw, $deck); 或者能夠查找指定牌面或花色的牌。 清單 13. 查找指定牌面或花色的牌 $draw = array('face' => '', 'suit' => 'Spades'); $draw = array('face' => 'Ace', 'suit' => ''); 簡單的撲克發牌器 如今已經獲得牌組構建器和一些工具,能夠幫助計算出抽出特定卡的機率,咱們能夠整合一個真正簡單的發牌器來進行發牌。出於本例的目的,咱們將構建一個能夠抽出五張牌的發牌器。發牌器將從整副牌中提供五張牌。使用數字指定須要放棄哪些牌,而且發牌器將用一副牌中的其餘牌替換這些牌。咱們無需指定發牌限制或特殊規則,可是您可能會發現這些是很是有益的我的經驗。 如上一節所示,生成並洗牌,而後每隻手五張牌。按數組索引顯示這些牌,以即可以指定返回哪些牌。您可使用表示要替換哪些牌的複選框來完成此操做。 清單 14. 使用複選框表示要替換的牌 foreach ($hand as $index =>$card) { echo " " . $card['face'] . ' of ' . $card['suit'] . " "; } 而後,計算輸入 array $_POST['card'],查看哪些牌已被選擇用於替換。 清單 15. 計算輸入 $i = 0; while ($i < 5) { if (isset($_POST['card'][$i])) { $hand[$i] = array_shift($deck); } } 使用此腳本,您能夠嘗試找處處理特定一組牌的最佳方法。 Hangman 遊戲 Hangman 實質上是一款猜字遊戲。給定單詞的長度,咱們使用有限的幾回機會猜這個單詞。若是猜出了出如今該單詞中的一個字母,則填充該字母出現的全部位置。在猜錯若干次(一般爲六次)後,您就輸了比賽。要構建一個簡陋的 hangman 遊戲,咱們須要從單詞列表開始。如今,讓咱們把單詞列表製做成一個簡單的數組。 清單 16. 建立單詞列表 $words = array ( "giants", "triangle", "particle", "birdhouse", "minimum", "flood" ); 使用前面介紹的技術,咱們能夠把這些單詞移動到外部單詞列表文本文件中,而後根據須要導入。 在獲得單詞列表後,須要隨機選出一個單詞,將每一個字母顯示爲空,而後開始猜想。咱們須要在每次進行猜想時跟蹤正確和錯誤的猜想。只需序列化猜想數組並在每次猜想時傳遞它們,就可實現跟蹤目的。若是須要阻止人們經過查看頁面源代碼僥倖猜對,則須要執行一些更安全的操做。 構建數組以保存字母和正確/錯誤的猜想。對於正確的猜想,咱們將用字母做爲鍵並用句點做爲值填充數組。 清單 17. 構建保存字母和猜想結果的數組 $letters = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o', 'p','q','r','s','t','u','v','w','x','y','z'); $right = array_fill_keys($letters, '.'); $wrong = array(); 如今須要一些代碼來評估猜想並在完成猜字遊戲的過程當中顯示該單詞。 清單 18. 評估猜想並顯示進度 if (stristr($word, $guess)) { $show = ''; $right[$guess] = $guess; $wordletters = str_split($word); foreach ($wordletters as $letter) { $show .= $right[$letter]; } } else { $show = ''; $wrong[$guess] = $guess; if (count($wrong) == 6) { $show = $word; } else { foreach ($wordletters as $letter) { $show .= $right[$letter]; } } } 在 源代碼歸檔 中,能夠看到如何序列化猜想數組並將該數組從一次猜想傳遞到另外一次猜想中。 縱橫字謎助手 我知道這樣作不合適,可是有時在玩縱橫拼字謎時,您不得不費勁地找出以 C 開頭並以 T 結尾、包含五個字母的單詞。使用爲 Hangman 遊戲構建的相同單詞列表,咱們能夠輕鬆地搜索符合某個模式的單詞。首先,找到一種傳輸單詞的方法。爲了簡單起見,用句點替換缺乏的字母:$guess = "c...t";。因爲正則表達式將把句點處理爲單個字符,所以咱們能夠輕鬆地遍歷單詞列表以查找匹配。 清單 19. 遍歷單詞列表 foreach ($words as $word) { if (preg_match("/^" . $_POST['guess'] . "$/",$word)) { echo $word . " \n"; } } 根據單詞列表的質量及猜想的準確度,咱們應當可以獲得合理的單詞列表以用於可能的匹配。您必須本身決定 「表示 ‘不按規則玩’ 的由五個字母組成的單詞」 的謎底是 「chest」 仍是 「cheat」。 米德里比斯 米德里比斯是一款文字遊戲,玩家在遊戲中獲得一個簡短的故事並用同一類型的不一樣單詞替換主要類型的單詞,從而建立同一個故事的更無聊的新版本。閱讀如下文本:「I was walking in the park when I found a lake. I jumped in and swallowed too much water. I had to go to the hospital.」 開始用其餘單詞標記替換單詞類型。開始和結束標記帶有下劃線用於阻止意外的字符串匹配。 清單 20. 用單詞標記替換單詞類型 $text = "I was _VERB_ing in the _PLACE_ when I found a _NOUN_. I _VERB_ed in, and _VERB_ed too much _NOUN_. I had to go to the _PLACE_."; 接下來,建立幾個基本單詞列表。對於本例,咱們也不會作得太複雜。 清單 21. 建立幾個基本單詞列表 $verbs = array('pump', 'jump', 'walk', 'swallow', 'crawl', 'wail', 'roll'); $places = array('park', 'hospital', 'arctic', 'ocean', 'grocery', 'basement', 'attic', 'sewer'); $nouns = array('water', 'lake', 'spit', 'foot', 'worm', 'dirt', 'river', 'wankel rotary engine'); 如今能夠重複地評估文原本根據須要替換標記。 清單 22. 評估文本 while (preg_match("/(_VERB_)|(_PLACE_)|(_NOUN_)/", $text, $matches)) { switch ($matches[0]) { case '_VERB_' : shuffle($verbs); $text = preg_replace($matches[0], current($verbs), $text, 1); break; case '_PLACE_' : shuffle($places); $text = preg_replace($matches[0], current($places), $text, 1); break; case '_NOUN_' : shuffle($nouns); $text = preg_replace($matches[0], current($nouns), $text, 1); break; } } echo $text; 很明顯,這是一個簡單而粗糙的示例。單詞列表越精確,而且花在基本文本上的時間越多,結果就越好。咱們已經使用了文本文件建立名稱列表及基本單詞列表。使用相同原則,咱們能夠建立按類型劃分的單詞列表並使用這些單詞列表建立更加變化無窮的米德里比斯遊戲。 ×××機 所有選中×××的六個正確號碼 —— 退一步說 —— 在統計學上是不可能的。不過,許多人仍然花錢去玩,並且若是您喜歡號碼,則查看趨勢圖可能頗有趣。讓咱們構建一個腳本,該腳本將容許跟蹤贏獎號碼並在列表中提供選擇次數最少的 6 個號碼。 (免責聲明:這不會幫助您中×××獎,所以請不要花錢購買獎券。這只是爲了娛樂)。 把贏獎的×××選擇保存到文本文件中。用逗號分隔各個號碼並把每組號碼放在單獨一行中。使用換行符分隔文件內容並使用逗號分隔行後,能夠獲得相似清單 23 的內容。 清單 23. 把選擇的贏獎×××保存到文本文件中 $picks = array( array('6', '10', '18', '21', '34', '40'), array('2', '8', '13', '22', '30', '39'), array('3', '9', '14', '25', '31', '35'), array('11', '12', '16', '24', '36', '37'), array('4', '7', '17', '26', '32', '33') ); 很明顯,這不足以成爲繪製統計數據的基本文件。可是它是一個開端,而且足以演示基本原理。 設置一個基本數組以保存選擇範圍。例如,若是選擇 1 到 40 之間(例如,$numbers = array_fill(1,40,0);)的號碼,則遍歷咱們的選擇,遞增相應的匹配值。 清單 24. 遍歷選擇 foreach ($picks as $pick) { foreach ($pick as $number) { $numbers[$number]++; } } 最後,根據值將號碼排序。此操做應當會把最少選擇的號碼放在數組的前部。 清單 25. 根據值將號碼排序 asort($numbers); $pick = array_slice($numbers,0,6,true); echo implode(',', array_keys($pick)); 經過有規律地向包含中獎號碼列表的文本文件添加實際的×××中獎號碼,能夠發現選號的長期趨勢。查看某些號碼的出現頻率十分有趣。