摘要:謝耳朵愛玩的游戲,石頭剪子布的升級版。擁有最高點數的玩家獲勝,其點數必須等于或低于點。在編寫這個游戲的過程中第一次引入了類概念。宇宙空間中微小的摩擦力和隕石撞擊后受到的力,都要考慮并且編入游戲中。
人人都應該學編程嗎?隨著每個人的工作與電腦連結愈發緊密,也許這是真的。
我是游戲設計師,在分工細致的國內網游業界,不需要研發或美術背景也能擔當游戲設計重任的角色多了起來。有時候他們甚至只需負責撰寫劇情文檔或游戲文案,一切涉及程序的工作內容都有開發同學代為解決。不離開自己的 comfort zone 也能很好地完成任務。
但在本職之外,了解程序如何工作,能帶來許多好處:日常工作中重復的工序可以自行使用程序解決;易犯的錯誤可以通過程序避免;更不用提編寫腳本的能力,能夠讓你直接控制你所設計的內容的每個細節,了解設計的邊界及內部空間。
畢竟,太初之時,只有程序。程序員想:「專職美術、策劃、設計、產品經理是好的。」便有了他們一干人等。
「每周一游」:每個星期快速開發一個游戲,連續進行數個星期。這是許多開發者們磨練自己想法和技巧的方式。
我沒有計算機背景或美術基礎,但乘中國游戲行業大發展,卻也幸運入行成為一名游戲策劃。我希望在日常工作之余,用一個辦法來鍛煉自己對游戲系統設計和開發過程的理解。因此,我參加了 Coursera 上的幾個課程,并且用課程提供的方便工具來實現設想中的功能。
一開始的成果非常基本、非常簡單,但到后面挑戰等級逐漸上升,到最后已經能獨立完成 600 行左右的程序。
接下來我就給各位看看我在這近四個月中的成果,以及我從中學習和體會到的內容。我盡量省略比較枯燥的實現細節,一來可以避免無聊,二來需要下功夫的東西還是親手實踐比較有幫助。如有興趣可來我的微博交流。
第一周:包剪錘蜥史波克(Rock-paper-scissors-lizard-Spock)
Sheldon 喜歡的游戲。
謝耳朵愛玩的游戲,石頭剪子布的升級版。內容最最基本,只要在控制臺里輸入命令,命令通過 if-elif-else 轉化成數字(0-4,分別代表出的5個東西)。
電腦則會隨機生成一個數字,轉換成字符串。再根據雙方數字,用 if-else 判斷勝負即可。
對我來說這是自己親手編寫的第一個游戲。它雖然簡單,但包含了一個游戲必須的全部要素:它有著固定的開始和結束,以及勝負的規則。
第二周:猜數字猜數字游戲就是由電腦隨機生成指定范圍內的一個數字,由你來猜,電腦告訴你是高還是低,一定次數后未猜中則輸掉的游戲。
在這個游戲中第一次引入全局變量的概念。初始化時,上下限以及允許你猜測的次數都是讀取全局變量。這樣一來,我們可以在游戲核心的方法之外,使用別的方法來修改全局變量,讓玩家可以自己選擇數字范圍和猜測次數。游戲本身則依然是 if-elif-else 這樣寫成的。
這是我親手編寫的第一個可以由玩家調整游戲設置的游戲。
第三周:秒表游戲秒表游戲是個考反應的游戲。點擊開始后秒表開始向前走,若你按停秒表時,秒表的時間恰巧停在整數(小數點后為0),則你得1分。游戲會記錄你按停的總次數和得分數。
這個游戲中涉及到為每個功能編寫多帶帶的方法。如玩家控制的按鈕start()、stop()、reset();游戲本身時間前進的tick()等。同時,為了讓時間正確地顯示在屏幕上,還有一個將時間轉化為「A:BC:D」這種形式的方法。
我們計時的方法是定義一個叫 time 的變量。由于這個游戲中最小的計時單位是 0.1 秒,所以每經過 100 毫秒我們就讓這個數字 +1。與此同時,編寫一個 format() 方法經過一系列計算將這個數字轉化為分、秒和0.1秒,顯示在屏幕上即可。判斷玩家是否得分仍然使用 if-else 結構。
這是第一次涉及到玩家進行的復雜操作,也是第一次認識到,在游戲畫面的表象之下究竟應該有些什么機制在運行。
第四周:乒乓(Pong)終于我們從小朋友玩的游戲進入了街機時代!
傳說 Pong 是世界上第一個電子游戲。在那個游戲機只有滾軸操作的年代,這個有著極簡單畫面的游戲啟發了無限后來者。看著它在手下形成還有些小感動呢。
這個游戲也是我制作的第一個不模擬現實中的「邏輯」,而是模擬「物理」的游戲。它的核心部分是球的速度變化、板子的速度變化,以及球與邊界和板子的碰撞。
為了讓這個游戲不至于無限地進行下去,我讓球的速度隨著每一次板子碰撞上升。但上升的公式寫成了指數函數,于是這球就啪啪啪越打越快每一回合很快就結束了。若改為對數函數,則會緩慢地趨近一個上限,令每一回合后期的雙人對局非常緊張、充滿變數。
這是我第一次體會到游戲的「手感」到底是怎么回事。每一次對參數的細微調整對手感帶來的變化,可以讓設計者與游戲本身有著更深刻的接觸。這是在目前分工充分的網游公司的日常工作中體會不到的感覺。
除此之外,很快地你就能從一個簡單原型中看出未來變化的可能。是否可以加入:
「球擊打在板子的不同部位,會彈向不同方向」?
「當板子擊球時,板子本身的速度會令球曲線飛出」?
或者「連續擊中球數次后玩家可以發出大招」?
等等諸如此類。想到這里,這個游戲能成為數十年游戲業的起點,也是有其道理的。
第五周:記憶游戲記憶游戲就是將多對牌打亂順序朝下放置,玩家一次翻開兩張,若相同則原樣留著,若不同則翻回去。所有牌都翻開后玩家勝利。
在這個游戲中,暫且用數字來代替撲克牌。我們用了一個 list (我有點搞不太清 list, array, tuple, set 幾個詞的中文翻譯,不亂講了……)來以 Boolean 值(True 和 False)記錄每張牌是否翻開的狀態。當設為翻開時,露出數字,否則在相應位置繪制一張牌背。
這個游戲的邏輯方面比較 tricky 的地方就是整個游戲實際上有三種狀態,需要分別處理:
新游戲,一張牌都沒翻開
翻開了(本回合內)第一張牌,等待翻開第二張
翻開了(本回合內)第二張牌,等待判斷是否相同
于是我使用一個叫做 state 的變量,分別以 0, 1, 2 代表三種狀態。在核心方法中利用 state 的值來決定接下來要做什么。
第六周:21點(Blackjack)啊,21 點。我人生中接觸的第一個撲克游戲。是的,在我會打「拖拉機」之前,7歲的我就在DOS下的初代大航海時代的酒館里學會了 21 點。這是年幼的我在那個游戲里玩懂的唯一一個系統……
這是個賭博游戲。簡單來說規則是:莊家給自己和玩家各發(deal)一張暗牌、一張明牌,玩家決定是否繼續加牌(hit);玩家加牌結束(stand)后莊家自行加牌,接著雙方攤牌。擁有最高點數的玩家獲勝,其點數必須等于或低于21點。
在編寫這個游戲的過程中第一次引入了類(class)概念。因為在游戲中許多物件都會重復出現,使用類可以很方便地重復制造它們:
每一張牌是 Class Card;
方法 get_suit() 可以獲取它的花色;
方法 get_rank() 可以獲取它的數字;
還有一個方法來把它繪制出來。
手牌是 Class Hand;
方法 add_card() 可以在手牌中增加一張牌;
方法 get_value() 可以算出手牌的分數。
牌庫則是 Class Deck。
方法 shuffle() 可以洗牌庫;
方法 deal_card() 用來發牌。
規定好這些基礎方法以后,重發牌、加牌、攤牌都可以通過這些功能的組合來實現。例如開局就是洗牌庫,向雙方發牌;雙方手牌加上兩張發出來的牌。等等。
此外這個游戲還第一次涉及到怎樣在畫面上繪制固定的圖形。整張牌表是一張大圖,怎么樣根據牌的值定位到對應的牌面也是要好好算一下。
第七周:小行星(Asteroid)經典街機游戲的復刻版!大制作來臨了!
這回的游戲涉及的內容比以前多,除了控制小飛船打來打去之外,動畫、音效、UI 等也都引入了游戲中。但每一部分的實現都可以通過之前嘗試的小功能疊加實現。簡單地了解游戲圖像和聲音到底怎么運作后,并無特別的困難。只是這一次我學著一個模塊一個模塊漸次開發和測試,一個功能調通無誤,再進行下一個。
反而是在游戲設計方面,制作這個游戲的過程給我帶來很多思考。在這個游戲中可供調整的變量太多了:飛船需要推進和旋轉;但推進是給飛船一個向前的加速度,而飛船本身還會有向著其他方向的速度。宇宙空間中微小的摩擦力、和隕石撞擊后受到的力,都要考慮并且編入游戲中。
這時你會發現,同樣的一些參數,經過調整會讓整個游戲變得徹底不同。這艘飛船到底是笨重、轉向慢、射速慢、射程遠的戰列艦,還是輕盈、轉向快、射速快、射程近的戰斗機?你要躲閃的是從一個方向襲來的流星群(隕石都從一邊來,而且向一個方向阻力特別大),還是四面八方出現的亂石?每一種選擇,好像都挺好玩的……
到這時我才了解到一個游戲設計者腦中「指揮意圖」清晰的重要性。你到底要做一個什么樣的游戲,給玩家帶來什么樣的情感?只有一個大概的「我要爽」是不夠的:究竟是控制巨大戰艦緩慢機動將將擦過一塊流星的那種屏氣凝神的爽,還是控制戰斗機高速穿梭在流星群中那種險象環生的爽?有時候自己也會猶豫。只有記住一開始你要提供的是怎么樣的情感,并且在全程中反復回看,才不會偏離目標。
一個人制作尚且如此,當需要團隊合作的時候,若不把一個確定的思路貫徹到底,怎么行呢?
第八周:2048啊,HTML 小游戲。在這個星期,2048 游戲突然流行了起來。于是我也跟風復刻了一個。看似簡單的游戲,真的要做出來,對于新手來說還是挺費腦筋的。
第一個問題就是,這個網格怎么做呢?我采用的轉化方法是使用一個二維的list。看起來就是:
[[0, 1, 2, 3] [0, 1, 2, 3] [0, 1, 2, 3] [0, 1, 2, 3]]
這樣一來,如果我要定位到第三(2)行第一(0)個格子,我就讀取這個 list 中的 List[2][0] 即可。這樣一來看起來頗為直觀,又能解決問題。
接下來又有好幾個問題需要一一解決。首先,當你按下一個方向鍵以后,所有行(列)的數字都會向著那個方向合并。這件事怎么辦呢?
首先我多帶帶寫了一個 merge() 方法。只要傳來一個 list,就逐個 iterate 并將合并后的值返回去。然后在主要 Class 中間的移動方法 move() 中規定,向哪個方向移動,就以那個方向的四個格子為排頭建立四個 list,傳去 merge() 那邊再替換回來。這樣一來這個游戲的核心規則就實現完成,剩下的邊邊角角多測試修繕即可。當測試成功的那一刻真是有一種爆棚的成就感——很少有解謎游戲的謎題能這樣讓你研究琢磨幾個小時。
當你把游戲的每個部分分入不同的 Class 和方法中后,可以感覺到效率提升不少。例如你在制作模塊 B ,此時要用到模塊 A 中的功能,你可以完全不管模塊 A 怎么實現的,只要把指定的數據傳進去,等著它傳出結果來就好了。
第九周:Cookie Clicker(點擊-放置游戲)這是個挺有病(誤)的游戲。你只要點這塊餅干就可以加餅干數,餅干可以買幫你加餅干的道具,越高級的道具加餅干越快,子子孫孫無窮匱也。聽說最近這種放置類游戲在一些小圈子里挺流行的……
游戲本身的設計相對簡單。加餅干數,加加餅干速度,獲取各種升級和冷卻的時間,購買道具等等,并不復雜。
但我們不想自己玩,我們想要電腦自動玩,算出最快速的策略,看看到底能獲得多少餅干。
為了這樣,我們專門做了一個叫 simulator_clicker() 的方法,它會根據輸入的策略,在合適的時間購買固定道具;而每個策略都可以另外定義。這樣一來,這個方法里引用的方法又引用了別的方法,復雜性上了一個臺階。
至于「策略」,就進入了 AI 的范疇。此時我們雖然只能使用最基本的條件判斷,但反復計算應該讓 AI 怎樣動作,還是挺有挑戰性的。只不過,發現讓 AI 采用「純隨機策略」亂買道具出來的結果比你辛苦計算的結果還好,就有點蛋疼……
第十周:Yahtzee這是個投骰的游戲,同樣涉及自己的「手」概念。大家自己玩一玩這個就明白了。 這一次制作的只涉及分數表上半區的部分。
Yahtzee游戲打印出的策略
為這個游戲編寫 AI 最有趣的地方是涉及到了概率和期望。我手上還有這么些骰子,那么接下來可能出現的所有手我都要算一遍,列成一個樹,然后找到概率最大的一種。我把列出所有可能手、為一手計算期望值、為一手計算分數和 AI 策略分別寫在 4 個方法里。
第十一周:僵尸末日
一群人類(綠點)被僵尸(紅點)包圍在破敗廢墟中的場景。請自行腦補。
啊,僵尸。也不知道誰規定的,僵尸及其變種的怪物成了無數影視游戲中人類可以毫無道德顧慮地擊殺的游戲怪物。
這個游戲的畫面如上圖所示:
黑色是障礙物。可以理解為房子、籬笆、爛掉的車什么的;任何單位不能通過。
紅色是僵尸。它們可以向上下左右四個方向移動,會自動前往最近的人類。
綠色是人類。他們可以向8個方向移動,會自動遠離僵尸。請不要吐槽為什么顏色好像應該反過來。
紫色是感染者。被僵尸抓到的人類就會這樣,不會動。可以理解為啃翻在地上,過一會兒就要變成僵尸起來。
所有的格子都是可以由玩家自行布置的。因此這是個樂趣在于 YY 的沙盒游戲。
點擊 "humans flee" 按鈕則人類移動一回合,點擊 "zombies stalk" 按鈕則僵尸移動一回合。它們采取的尋路策略都是廣度優先搜索。游戲不會結束,你可以在這個沙盒中給自己安排勝利條件。布置各種各樣的場面看著它們行動,也還能支撐個半小時的樂趣,是到目前為止制作的可玩性最強的游戲……
同樣的,這個游戲也是一個具有充分擴展性的游戲。感染者會不會轉化成僵尸?人類能不能拿到武器反擊僵尸?僵尸中間會不會有特殊感染者,能夠范圍攻擊、遠程拉住人類、能跳來跳去或者會爆炸?玩家這個上帝的力量有多大?跳出「玩家扮演游戲中的某個角色」的框框,會發現沙盒類游戲的樂趣所在。
第十二周:猜詞游戲猜詞游戲就是這樣:你指定一個詞,電腦會搜索詞庫,將這個詞的字母能組成的所有詞以星號遮住,你逐個嘗試將他們列出來的游戲。
這個游戲中第一次涉及到讀取文件。
為了成功的讀取到輸入的詞匯并且匹配所有可能組成的詞,我們需要使用一個 merge_sort() 方法來將一個打亂的列表變成有序的。這時我第一次接觸到「遞歸(recursion)」。
要理解遞歸,首先要理解遞歸(誤)。也就是說這個方法自己不斷引用自己。看起來就像
merge_sort(something): ... merge_sort(something_again) ...
這樣。
設計一個遞歸方法前,首先要明確停止遞歸的條件(base case)。在這個基礎上推算每一步應該怎么辦。可以拿一個簡單的例子在紙上演示,無誤后寫出來看看效果。
我的設想中,當給出一個 list 后,首先應當將其分成兩半,當字母的個數小于等于 1 就應該停止遞歸。
最后寫成的方法看起來像這樣:
def merge_sort(list1): """ Sort the elements of list1. Return a new sorted list with the same elements as list1. This function should be recursive. """ new_list = list(list1) # base case: when length is 1 or 0 if len(new_list) <= 1: return new_list # recurrences: if len(new_list) > 1: # split in half mid = len(new_list) / 2 half_list1 = new_list[0:mid] half_list2 = new_list[mid:] # call merge_sort on each half list1_sorted = merge_sort(half_list1) list2_sorted = merge_sort(half_list2) # and merge each sorted <- 在這個方法中會對2個元素進行排序 return merge(list1_sorted, list2_sorted)
對我來說遞歸還是挺復雜的。一個簡單的遞歸就要想很久,不過想清楚了之后的效果還是不錯的。不少復雜的游戲設計中都會出現類似的規則。
當然,你也可以不使用遞歸,而是設定一些條件重復地調用一個方法。但那樣的話代碼量就變得很大,執行效率可能也會變慢。你是要犧牲易理解性換取效率,還是犧牲效率換取易理解性呢?很多時候玩家也會試圖來理解你游戲的內在邏輯,能不能讓他們輕松辦到呢?
第十三周:九宮格(tic-tac-toe)九宮格,世界各地的小朋友可能都玩過的經典游戲。放大到5連就是五子棋。
為這個游戲編寫電腦對手采用的是所謂的「蒙特卡羅方法」。也就是從目前這一步開始,推算出每一個可能的游戲結果。勝則加分,負則扣分,和則不加不減;最后選定分數最高的一步落子。這種算法在棋盤復雜的的情況下很難實用,但應付九宮格是綽綽有余。
然后,為了測試這個對手到底強不強,我把游戲規則反了一下變成「逆九宮格」。也就是誰先連到 3 個就算輸。這種模式下,沒有下中間那個位置的不敗手,更能看出電腦的實力。第一盤我還沒反應過來,結果輸掉了。
逆九宮格:先達成三個一線者負
到這里,我編寫的 AI 就擺脫了特別直覺的 if-else 或者廣度優先搜索規則,進入了一個發揮其強大計算力的時代。假如把棋盤擴大幾倍,勝利條件相應放大,人類就很難戰勝電腦了。
第十四周:數字推盤游戲(n-Puzzle)一開始的游戲是15個方格,數字錯亂了,需要你來把它們移動回正確的位置。有一種改進型就是拼圖,首先你要找出圖片的正確順序,然后還要推回正確位置。
游戲本身的規則不難,但要做一個自動解 Puzzle 的 AI 就有點意思了,根據反復試玩觀察,一個盤面可以分為幾個區域,各自有固定解法:
第二行以下第一列右側的的
第二行以下最左邊一列的
第一行的
第二行的
最末階段左上角的4個
大家可以觀察動畫里面解開的過程,研究一下在這些區域我讓電腦怎么動作的……
一個個模塊分別編寫和測試,在內部再分情況討論,真是件體力活!但只要測試無誤,無論這個 puzzle 擴展到多大,解開它也就是時間問題。以后誰再拿這種東西為難你,只要把題目輸入進去,就能看著電腦瞬間自動解開并且給你一個操作順序了。
結語在整個的 14 周過程中間,我從能寫簡單的幾十行程序,逐漸進步到能完成較復雜的600行程序(不含UI部分)。在此過程中,我逐步學到和應用的知識有:
python 基礎語法
變量
list
方法(function)
類(class)
各種算法
遞歸
編程的 style 要求
……等等,族繁不及備載。這些知識以及應用的方法有可能忘卻,但在此過程中有著更多東西是令我體會深刻,很難忘記的:
將「手感好」、「手感不好」等感覺分析成多個具體部分,進行調整。
評估各種實現某個功能的手段,依據其復雜程度或者實現效率。
分步計劃并實現你期望的功能,最后組合成完整的游戲。
這些是在布魯姆教育目標分類法被列為比較高級的認知類型。知識可以被忘記,理解和應用的過程會讓你有一些印象,而分析、評估、合成的過程則可以逐步內化成你自己的能力。你從別人那里聽來的經驗是知識,也許你在自己行事的過程中能夠理解一些、應用一些,但更高級的認知,則非親手實踐不能取得。
如果你在游戲或者互聯網行業,但你并不知道程序同學們怎么工作、想些什么;或者總覺得自己的設想與實現之間有著一道障壁。也許自己親手實現(implement)自己設想的過程會帶給你啟發。
至少我在這 14 周每周做一個游戲的過程中,確實有這樣的體會。除此之外,親手實現設計的快感,掌握自己作品的快感,也是無可比擬的。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/37395.html
摘要:大家好,我是冰河有句話叫做投資啥都不如投資自己的回報率高。馬上就十一國慶假期了,給小伙伴們分享下,從小白程序員到大廠高級技術專家我看過哪些技術類書籍。 大家好,我是...
摘要:正好,最近又有幾位不同身份的初學者來咨詢,要我推薦幾本入門書籍,而我們薦書系列已經停更了兩個多月,所以,本期薦書就來推薦一些入門書籍吧。為了準備這期薦書,我專門搜集了本入門書籍,現在全部加入到了一份豆瓣豆列里,方便大家查看。 showImg(https://segmentfault.com/img/remote/1460000019299066?w=4790&h=3193); 本文原創...
摘要:如果你仍然無法抉擇,那請選擇,畢竟這是未來的趨勢,參考知乎回答還是編輯器該如何選我推薦社區版,配置簡單功能強大使用起來省時省心,對初學者友好。 這是一篇 Python 入門指南,針對那些沒有任何編程經驗,從零開始學習 Python 的同學。不管你學習的出發點是興趣驅動、拓展思維,還是工作需要、想要轉行,都可以此文作為一個參考。 在這個信息爆炸的時代,以 Python入門 為關鍵字搜索出...
閱讀 1308·2019-08-30 15:44
閱讀 1979·2019-08-30 13:49
閱讀 1651·2019-08-26 13:54
閱讀 3484·2019-08-26 10:20
閱讀 3239·2019-08-23 17:18
閱讀 3294·2019-08-23 17:05
閱讀 2130·2019-08-23 15:38
閱讀 1012·2019-08-23 14:35