W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
在這節(jié)練習,我想給你介紹一個使用python創(chuàng)建某類東西的過程,也就是“面向?qū)ο缶幊獭保∣OP)。我把它叫做一個過程,是因為我將給出一系列按順序進行的步驟,但是你也不應該死板的遵循這個步驟,企圖用它解決所有難題。它們對于許多編程問題只是一個良好的開頭,而不應該被認為是解決這些問題的唯一方法。這個過程只是一個你可以遵循的方法:
- 寫出或畫出你的問題
- 從1中提煉關鍵問題并搜索相關資料
- 為2中的問題創(chuàng)建一個有層次結構的類和對象映射
- 編寫類和測試代碼,并保證他們運行
- 重復并精煉
按照這個順序執(zhí)行流程,叫做“自頂向下”的方式,意思是說,它從非常抽象寬松的想法開始,然后慢慢提煉,直到想法是堅實的東西,然后你再開始編碼。
首先我只是寫出這個問題,并試圖想出任何我想要的東西就可以了。也許我會畫一兩個圖表,或者某種可能的地圖,甚至給自己寫了一系列的電子郵件描述這個問題。這給了我表達了問題的關鍵概念的方法,并探測出我對于這個游戲有什么具體的想法和了解。
接下來,我瀏覽這些筆記,圖表以及描述,通過這些記錄我找到我需要的關鍵問題點。有一個簡單的技巧:簡單地列出你的筆記和圖表中所有的名詞和動詞,然后寫出他們是如何相關的。這一步其實也我為我下一步要寫的類、對象、以及函數(shù)等提供了命名列表。我利用這個概念列表,研究任何我不明白的地方,如果我需要,我還可以進一步完善它們。
一旦我完成這個列表,我可以創(chuàng)建的一個簡單的輪廓/樹用來說明這些概念之間的關系。你還可以對著你的名詞列表并詢問“這個名詞和其他的是一個概念嗎?或者它們有一個通用的父類嗎,那它們的父類是什么?”持續(xù)檢查,直到你得到一個有層次結構的類,它應該像一棵簡單的樹或者圖表。然后檢查你的動詞列表,看它們是否可以作為函數(shù)的名字添加到你的類樹種。
隨著這棵類樹的生成,我坐下來寫一些基本的框架代碼,代碼中只包括剛才提到的類,類中包含的函數(shù)。然后我再寫一個測試用例,用來檢驗我剛才寫的類是正確的。有時候我可能只需要在開始寫一段測試代碼,但是更多的時候,我需要寫一段測試,再寫一段代碼,再寫一段測試...直到整個項目完成。
最后,在我完成更多的工作之前,我周期性的重復和精煉這個流程,是他變得清楚和易于理解。如果我在一個沒有預料到的地方被一些概念或問題卡住,我會停下來在這一小部分上投入更大的精力來分析解決,直到我解決了這問題,再繼續(xù)下去。
這節(jié)練習中,我將通過建造一個游戲引擎帶大家學習這一流程。
我打算制作一個叫做 "Gothons from Planet Percal #25"的游戲,這個一個小型的太空冒險游戲。
我為這個游戲?qū)懥艘恍《蚊枋觯?/p>
“外星人乘坐一個宇宙飛船入侵,我們的英雄需要通過一個迷宮似的房間擊敗他們,然后他才能逃入一個逃生艙到達下面的行星。游戲?qū)⒏褚粋€有著文本輸出和有趣的死法的Zork或冒險類型游戲。游戲?qū)ㄒ粋€引擎運行充滿房間或場景的地圖。 當玩家進入房間,每個房間將打印自己的描述,然后告訴引擎運行下一個地圖?!?/p>
我先描述每個場景:
Death: 這是玩家死亡的場景,應該是有趣的。
Central Corridor: 這是游戲的起點,在這里已經(jīng)有一個外星人等待英雄的到來,它們需要被一個玩笑打敗。
Laser Weapon Armory: 這是英雄得到了中子彈獲得的逃生艙之前要炸毀的船。它有一個需要英雄猜數(shù)的鍵盤。
The Bridge: 和外星人戰(zhàn)斗的另一個場景,英雄防止炸彈的地方。
Escape Pod: 英雄逃脫的場景,但是需要英雄找對正確的逃生艙
現(xiàn)在,我可以繪制出它們的地圖,或者對每個房間再多寫一些描述,或者其他一些我腦中出現(xiàn)的想法。
現(xiàn)在我已經(jīng)有足夠多的信息還提取出名詞,并分析它們的類結構。首先我列出所有的名詞:
- Alien
- Player
- Ship
- Maze
- Room
- Scene
- Gothon
- Escape Pod
- Planet
- Map
- Engine
- Death
- Central Corridor
- Laser Weapon Armory
- The Bridge
我也可能要列出所有的動詞,看它們能否作為函數(shù)的名字,不過現(xiàn)在我要先跳過這一步。
你需要研究所有你現(xiàn)在還不知道的概念。例如,我可能會玩幾個這種類型的游戲,并確保知道他們是如何工作的。我可能會研究船舶和炸彈的設計和工作原理。也許我會研究怎么樣將游戲中的數(shù)據(jù)或狀態(tài)存儲在數(shù)據(jù)庫等一些技術上的問題。我做完這個研究之后,我可能會回到第1步重新開始,根據(jù)新的信息,重寫描述和提取新的概念。
當我完成上面的步驟,我通過思考“哪些是類似于其他東西的”,把名詞列表轉(zhuǎn)換成一個有層次結構的類樹,同樣,我也會思考,哪些單詞是其他東西的基礎呢?
馬上我發(fā)現(xiàn),"Room" 和 "Scene"對于我要做的事情來說基本上是一樣的事情。在這個游戲中,我選擇使用"Scene" 。接下來我發(fā)現(xiàn),所有的特殊房間比如"Central Corridor"基本上也跟"Scene"是一樣的。我發(fā)現(xiàn)"Death"也是一個"Scene", 由于我選擇了"Scene"而不是"Room",你可以有一個“死亡場景”,而是不一個很奇怪的“死亡房間”。"Maze"和"Map"基本一樣,所以我選擇使用"Map",因為我更多的時候都用它。我不想做一個戰(zhàn)斗系統(tǒng),所以我會先忽略"Alien"和 "Player",但是會把他們保存下來以供以后使用。"Planet"也可能僅僅是另一個場景,而不是什么具體的事情。
在此之后,我開始創(chuàng)建一個層次結構的類:
- Map
- Engine
Scene
- Death
- Central Corridor
- Laser Weapon Armory
- The Bridge
- Escape Pod
然后,我會找出說明中每個動詞都需要什么行動。比如,我從說明中得知,我需要一個方法來"run"這個引擎,通過地圖獲得下一個場景"get the next scene",獲得"opening scene",或者"enter"一個場景。我把這些加到類樹里:
- Map- next_scene- opening_scene
- Engine- play
- Scene- enter Death Central Corridor Laser Weapon Armory The Bridge* Escape Pod
注意一下,我只是把-enter
放到Scene
下面,因為我知道所有的場景都會繼承它并重寫它。
當我有了這個類樹和一些功能之后,我在編輯器中打開源文件,并嘗試編寫代碼。通常,我會復制粘貼這棵類樹到源文件中,然后編輯它。下面是一個如何編碼的小例子,文件末尾包含一個簡單的測試用例:
class Scene(object):
def enter(self):
pass
class Engine(object):
def __init__(self, scene_map):
pass
def play(self):
pass
class Death(Scene):
def enter(self):
pass
class CentralCorridor(Scene):
def enter(self):
pass
class LaserWeaponArmory(Scene):
def enter(self):
pass
class TheBridge(Scene):
def enter(self):
pass
class EscapePod(Scene):
def enter(self):
pass
class Map(object):
def __init__(self, start_scene):
pass
def next_scene(self, scene_name):
pass
def opening_scene(self):
pass
a_map = Map('central_corridor')
a_game = Engine(a_map)
a_game.play()
在這個文件中,你可以看到我只是復制了我想要的層次結構,然后一點點的補齊代碼再運行它,看看它在這個基本結構中是否運行順利。 在這節(jié)練習后面的部分,你會填補這段代碼的其余部分,使其正常工作,以配合練習開頭的游戲描述。
在我提供的流程中,最后一步并不是實際意義上的一步,而是要做一個循環(huán)。在編程的世界里,你永遠做不到一次通過,相反,你退回整個過程,并再次根據(jù)你從后面的步驟中了解到的信息完善它。有時候我已經(jīng)到了第3步,但是我發(fā)現(xiàn)我還需要在第1、2步做更多工作,我會停下來并返回去完善它。有時候我也會突然靈光一閃,跳到最后,用我腦子里更好的解決方案編碼實現(xiàn),但是之后,我仍然會回去完成前面的步驟,以確保我的工作覆蓋了所有的可能性。
在這一過程的另一個觀點是,你不是僅在一個層面上使用這個流程,當你遇到某些特定問題的時候,你可以在任意一個層級上使用該流程。比方說,我不知道如何寫Engine.play
方法,我可以靜下心來用這個流程只做這一種功能,直到弄清楚這個方法怎么寫。
因為這個流程在最抽象的概念(頂部)開始,然后再下降到實際執(zhí)行過程中,因此這一流程通常標示為“自上而下”。我希望你使用這一流程,但你也應該知道,還有另一種方式來解決程序中的問題,這種方式是從代碼開始,再“上升”到抽象的概念問題。這種方式被稱為“自下而上”。下面是自下而下方式所遵循的步驟:
- 取一小塊問題,編寫一些代碼,并讓他勉強運行
- 完善代碼,將其轉(zhuǎn)換成一些更正式的包含類和自動化測試的代碼。
- 提取其中的關鍵概念,并嘗試找出研究他們。
- 寫出到底發(fā)生了什么的描述。
- 繼續(xù)完善代碼,也可能是把它扔掉,并重新開始。
- 移動到其他問題上,重復步驟。
當你需要更優(yōu)質(zhì)的代碼,并在代碼中更自然的思考你要解決的問題時,這種方式更好一些。尤其是當你知道小塊的難題,但沒有足夠的信息把握整個概念的時候,這個方式是一個解決問題很好的辦法。將問題分解成碎片并探索代碼,直到你解決這個問題。然而,你解決問題的途徑可能是緩慢而曲折的,所以,我的這一流程中也包含后退并反復研究問題,直到你通過自己所為解決所有難題。
我要告訴你我解決上述問題的最終辦法,但我不希望你只是直接跳到這里并輸入代碼。我希望你能通過我的描述,完成我寫的那個簡單粗糙的代碼框架,并讓他運行起來。當你有了自己的解決方案時,你可以回來看看我是怎么解決的。
我準備把文件ex43.py
拆成小塊,再一一解釋,而不是一次性提供完整的代碼。
from sys import exit
from random import randint
這只是我們游戲需要導入的包,沒什么新奇的。
class Scene(object):
def enter(self):
print "This scene is not yet configured. Subclass it and implement enter()."
exit(1)
正如你在框架代碼里看到的,我創(chuàng)建了基礎類Scene
,它將包含所有scene要做的所有的事。在這段代碼中,它們并沒有做什么,所以這只是給你的一個如果創(chuàng)建基類的示范。
class Engine(object):
def __init__(self, scene_map):
self.scene_map = scene_map
def play(self):
current_scene = self.scene_map.opening_scene()
last_scene = self.scene_map.next_scene('finished')
while current_scene != last_scene:
next_scene_name = current_scene.enter()
current_scene = self.scene_map.next_scene(next_scene_name)
# be sure to print out the last scene
current_scene.enter()
我同樣創(chuàng)建了類Engine
,并且你能看到我已經(jīng)使用了方法Map.opening_scene
和 Map.next_scene
。因為在我寫Map
類之前,做了一點計劃,我可以假設我后面會寫這些,然后提前使用它們。
class Death(Scene):
quips = [
"You died. You kinda suck at this.",
"Your mom would be proud...if she were smarter.",
"Such a luser.",
"I have a small puppy that's better at this."
]
def enter(self):
print Death.quips[randint(0, len(self.quips)-1)]
exit(1)
我的第一個場景是一個叫做Death
的場景,它給你展現(xiàn)了你能寫的最簡單的場景。
class CentralCorridor(Scene):
def enter(self):
print "The Gothons of Planet Percal #25 have invaded your ship and destroyed"
print "your entire crew. You are the last surviving member and your last"
print "mission is to get the neutron destruct bomb from the Weapons Armory,"
print "put it in the bridge, and blow the ship up after getting into an "
print "escape pod."
print "\n"
print "You're running down the central corridor to the Weapons Armory when"
print "a Gothon jumps out, red scaly skin, dark grimy teeth, and evil clown costume"
print "flowing around his hate filled body. He's blocking the door to the"
print "Armory and about to pull a weapon to blast you."
action = raw_input("> ")
if action == "shoot!":
print "Quick on the draw you yank out your blaster and fire it at the Gothon."
print "His clown costume is flowing and moving around his body, which throws"
print "off your aim. Your laser hits his costume but misses him entirely. This"
print "completely ruins his brand new costume his mother bought him, which"
print "makes him fly into an insane rage and blast you repeatedly in the face until"
print "you are dead. Then he eats you."
return 'death'
elif action == "dodge!":
print "Like a world class boxer you dodge, weave, slip and slide right"
print "as the Gothon's blaster cranks a laser past your head."
print "In the middle of your artful dodge your foot slips and you"
print "bang your head on the metal wall and pass out."
print "You wake up shortly after only to die as the Gothon stomps on"
print "your head and eats you."
return 'death'
elif action == "tell a joke":
print "Lucky for you they made you learn Gothon insults in the academy."
print "You tell the one Gothon joke you know:"
print "Lbhe zbgure vf fb sng, jura fur fvgf nebhaq gur ubhfr, fur fvgf nebhaq gur ubhfr."
print "The Gothon stops, tries not to laugh, then busts out laughing and can't move."
print "While he's laughing you run up and shoot him square in the head"
print "putting him down, then jump through the Weapon Armory door."
return 'laser_weapon_armory'
else:
print "DOES NOT COMPUTE!"
return 'central_corridor'
接下來,我創(chuàng)建了CentralCorridor
,這是游戲的開始。我在寫Map
之前,為游戲制作了這個場景,是因為我后面要引用它們。
class LaserWeaponArmory(Scene):
def enter(self):
print "You do a dive roll into the Weapon Armory, crouch and scan the room"
print "for more Gothons that might be hiding. It's dead quiet, too quiet."
print "You stand up and run to the far side of the room and find the"
print "neutron bomb in its container. There's a keypad lock on the box"
print "and you need the code to get the bomb out. If you get the code"
print "wrong 10 times then the lock closes forever and you can't"
print "get the bomb. The code is 3 digits."
code = "%d%d%d" % (randint(1,9), randint(1,9), randint(1,9))
guess = raw_input("[keypad]> ")
guesses = 0
while guess != code and guesses < 10:
print "BZZZZEDDD!"
guesses += 1
guess = raw_input("[keypad]> ")
if guess == code:
print "The container clicks open and the seal breaks, letting gas out."
print "You grab the neutron bomb and run as fast as you can to the"
print "bridge where you must place it in the right spot."
return 'the_bridge'
else:
print "The lock buzzes one last time and then you hear a sickening"
print "melting sound as the mechanism is fused together."
print "You decide to sit there, and finally the Gothons blow up the"
print "ship from their ship and you die."
return 'death'
class TheBridge(Scene):
def enter(self):
print "You burst onto the Bridge with the netron destruct bomb"
print "under your arm and surprise 5 Gothons who are trying to"
print "take control of the ship. Each of them has an even uglier"
print "clown costume than the last. They haven't pulled their"
print "weapons out yet, as they see the active bomb under your"
print "arm and don't want to set it off."
action = raw_input("> ")
if action == "throw the bomb":
print "In a panic you throw the bomb at the group of Gothons"
print "and make a leap for the door. Right as you drop it a"
print "Gothon shoots you right in the back killing you."
print "As you die you see another Gothon frantically try to disarm"
print "the bomb. You die knowing they will probably blow up when"
print "it goes off."
return 'death'
elif action == "slowly place the bomb":
print "You point your blaster at the bomb under your arm"
print "and the Gothons put their hands up and start to sweat."
print "You inch backward to the door, open it, and then carefully"
print "place the bomb on the floor, pointing your blaster at it."
print "You then jump back through the door, punch the close button"
print "and blast the lock so the Gothons can't get out."
print "Now that the bomb is placed you run to the escape pod to"
print "get off this tin can."
return 'escape_pod'
else:
print "DOES NOT COMPUTE!"
return "the_bridge"
class EscapePod(Scene):
def enter(self):
print "You rush through the ship desperately trying to make it to"
print "the escape pod before the whole ship explodes. It seems like"
print "hardly any Gothons are on the ship, so your run is clear of"
print "interference. You get to the chamber with the escape pods, and"
print "now need to pick one to take. Some of them could be damaged"
print "but you don't have time to look. There's 5 pods, which one"
print "do you take?"
good_pod = randint(1,5)
guess = raw_input("[pod #]> ")
if int(guess) != good_pod:
print "You jump into pod %s and hit the eject button." % guess
print "The pod escapes out into the void of space, then"
print "implodes as the hull ruptures, crushing your body"
print "into jam jelly."
return 'death'
else:
print "You jump into pod %s and hit the eject button." % guess
print "The pod easily slides out into space heading to"
print "the planet below. As it flies to the planet, you look"
print "back and see your ship implode then explode like a"
print "bright star, taking out the Gothon ship at the same"
print "time. You won!"
return 'finished'
class Finished(Scene):
def enter(self):
print "You won! Good job."
return 'finished'
這是游戲剩下的場景。因為我知道我需要他們,并且已經(jīng)想好他們應該如何交織在一起,所以我可以直接編碼。
順便說一下,我不是直接輸入這些代碼。記得我說過的嗎,用增量的方法嘗試構建所有的代碼,一次只寫一小部分。我只是給你看了最終的結果。
class Map(object):
scenes = {
'central_corridor': CentralCorridor(),
'laser_weapon_armory': LaserWeaponArmory(),
'the_bridge': TheBridge(),
'escape_pod': EscapePod(),
'death': Death(),
'finished': Finished(),
}
def __init__(self, start_scene):
self.start_scene = start_scene
def next_scene(self, scene_name):
val = Map.scenes.get(scene_name)
return val
def opening_scene(self):
return self.next_scene(self.start_scene)
在這之后,我寫了Map
類,你可以看到它通過名字把所有的場景存在了字典中。我把這個字典叫做Map.scenes
。這也是為什么map是在scens之后才寫的原因,因為這個字典需要包含所有已存在的場景。
a_map = Map('central_corridor')
a_game = Engine(a_map)
a_game.play()
最后,我讓我的代碼通過創(chuàng)建一個Map
,并在調(diào)用play
之前把map交給Engine
,把游戲運行起來,
確保你明白這個游戲,并且首先嘗試自己解決它。如果你被難住了,可以閱讀一小部分我的代碼,然后再嘗試自己搞定它。
當我運行我的游戲的時候,我可以看到:
$ python ex43.py
The Gothons of Planet Percal #25 have invaded your ship and destroyed
your entire crew. You are the last surviving member and your last
mission is to get the neutron destruct bomb from the Weapons Armory,
put it in the bridge, and blow the ship up after getting into an
escape pod.
You're running down the central corridor to the Weapons Armory when
a Gothon jumps out, red scaly skin, dark grimy teeth, and evil clown costume
flowing around his hate filled body. He's blocking the door to the
Armory and about to pull a weapon to blast you.
> dodge!
Like a world class boxer you dodge, weave, slip and slide right
as the Gothon's blaster cranks a laser past your head.
In the middle of your artful dodge your foot slips and you
bang your head on the metal wall and pass out.
You wake up shortly after only to die as the Gothon stomps on
your head and eats you.
Your mom would be proud...if she were smarter.
- 修改這個游戲!你可能不喜歡這個游戲。讓游戲運行起來,然后按照你的喜好修改它。這是你的電腦,你可以用它做你想做的事情
- 代碼中有一個bug,為什么門鎖了11次?
- 解釋一下返回至下一個房間的工作原理。
- 增加作弊代碼,這樣你能通過一些更難的房間。我能只在一行上加兩個單詞做到這些。
- 回到我的描述和分析,嘗試為英雄和他遇見的各種哥頓人創(chuàng)建一個小型作戰(zhàn)系統(tǒng)。
- 這其實是一個小版本的“有限狀態(tài)機(finite state machine)”,找資料閱讀了解一下,雖然你可能看不懂,但還是找來看看吧。
你可以就像給朋友講述一個故事一樣,來創(chuàng)建游戲?;蛘吣憧梢圆扇『唵蔚哪阆矚g的書或電影場景。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: