利用Python模擬谷歌的小恐龍游戲
前言
本文的文字及圖片來(lái)源于網(wǎng)絡(luò),僅供學(xué)習(xí)、交流使用,不具有任何商業(yè)用途,如有問(wèn)題請(qǐng)及時(shí)聯(lián)系我們以作處理。
PS:如有需要Python學(xué)習(xí)資料的小伙伴可以加點(diǎn)擊下方鏈接自行獲取
谷歌流量器中有個(gè)很有名的彩蛋:當(dāng)你網(wǎng)絡(luò)出現(xiàn)問(wèn)題時(shí),就會(huì)出現(xiàn)一個(gè)“小恐龍游戲”。
(如果想要直接進(jìn)行游戲,可以在地址欄輸入:chrome://dino)
今天我們就來(lái)給大家演示下,用Python來(lái)自己做一個(gè)仿制的“小恐龍游戲”!
廢話不多說(shuō),讓我們愉快地開始吧~
開發(fā)工具
Python版本:3.6.4
相關(guān)模塊:
pygame模塊;以及一些python自帶的模塊。
環(huán)境搭建
安裝Python并添加到環(huán)境變量,pip安裝需要的相關(guān)模塊即可。
先睹為快
在終端運(yùn)行如下命令即可:
python Game7.py
效果如下:
代碼介紹
這里介紹一下游戲的實(shí)現(xiàn)原理。
首先,我們對(duì)游戲進(jìn)行一些必要的初始化工作:
# 游戲初始化 pygame.init() screen = pygame.display.set_mode(cfg.SCREENSIZE) pygame.display.set_caption('T-Rex Rush —— Charles的皮卡丘') # 導(dǎo)入所有聲音文件 sounds = {} for key, value in cfg.AUDIO_PATHS.items(): sounds[key] = pygame.mixer.Sound(value)
接著,我們來(lái)考慮一下,游戲中有哪些游戲元素:
小恐龍:由玩家控制以躲避路上的障礙物;
路面:游戲的背景;
云:游戲的背景;
飛龍:路上的障礙物之一,小恐龍碰上就會(huì)死掉;
仙人掌:路上的障礙物之一,小恐龍碰上就會(huì)死掉;
記分板:記錄當(dāng)前的分?jǐn)?shù)和歷史最高分。
讓我們來(lái)依次定義一下這些游戲元素類。對(duì)于云,路面以及仙人掌來(lái)說(shuō),定義起來(lái)很簡(jiǎn)單,我們只需要加載對(duì)應(yīng)的游戲元素圖片:
然后寫兩個(gè)類內(nèi)部方法update和draw就ok了。兩個(gè)方法分別用于將場(chǎng)景不斷向左移動(dòng)以實(shí)現(xiàn)小恐龍不斷向前移動(dòng)的動(dòng)畫效果和將場(chǎng)景顯示在游戲界面的對(duì)應(yīng)位置上。具體而言,代碼實(shí)現(xiàn)如下:
'''地板''' class Ground(pygame.sprite.Sprite): def __init__(self, imagepath, position, **kwargs): pygame.sprite.Sprite.__init__(self) # 導(dǎo)入圖片 self.image_0 = pygame.image.load(imagepath) self.rect_0 = self.image_0.get_rect() self.rect_0.left, self.rect_0.bottom = position self.image_1 = pygame.image.load(imagepath) self.rect_1 = self.image_1.get_rect() self.rect_1.left, self.rect_1.bottom = self.rect_0.right, self.rect_0.bottom # 定義一些必要的參數(shù) self.speed = -10 '''更新地板''' def update(self): self.rect_0.left += self.speed self.rect_1.left += self.speed if self.rect_0.right < 0: self.rect_0.left = self.rect_1.right if self.rect_1.right < 0: self.rect_1.left = self.rect_0.right '''將地板畫到屏幕''' def draw(self, screen): screen.blit(self.image_0, self.rect_0) screen.blit(self.image_1, self.rect_1) '''云''' class Cloud(pygame.sprite.Sprite): def __init__(self, imagepath, position, **kwargs): pygame.sprite.Sprite.__init__(self) # 導(dǎo)入圖片 self.image = pygame.image.load(imagepath) self.rect = self.image.get_rect() self.rect.left, self.rect.top = position # 定義一些必要的參數(shù) self.speed = -1 '''將云畫到屏幕上''' def draw(self, screen): screen.blit(self.image, self.rect) '''更新云''' def update(self): self.rect = self.rect.move([self.speed, 0]) if self.rect.right < 0: self.kill() '''仙人掌''' class Cactus(pygame.sprite.Sprite): def __init__(self, imagepaths, position=(600, 147), sizes=[(40, 40), (40, 40)], **kwargs): pygame.sprite.Sprite.__init__(self) # 導(dǎo)入圖片 self.images = [] image = pygame.image.load(imagepaths[0]) for i in range(3): self.images.append(pygame.transform.scale(image.subsurface((i*101, 0), (101, 101)), sizes[0])) image = pygame.image.load(imagepaths[1]) for i in range(3): self.images.append(pygame.transform.scale(image.subsurface((i*68, 0), (68, 70)), sizes[1])) self.image = random.choice(self.images) self.rect = self.image.get_rect() self.rect.left, self.rect.bottom = position self.mask = pygame.mask.from_surface(self.image) # 定義一些必要的變量 self.speed = -10 '''畫到屏幕上''' def draw(self, screen): screen.blit(self.image, self.rect) '''更新''' def update(self): self.rect = self.rect.move([self.speed, 0]) if self.rect.right < 0: self.kill()
記分板的定義也類似,只不過(guò)它不需要移動(dòng),但是需要實(shí)時(shí)地更新當(dāng)前 的分?jǐn)?shù):
'''記分板''' class Scoreboard(pygame.sprite.Sprite): def __init__(self, imagepath, position, size=(11, 13), is_highest=False, bg_color=None, **kwargs): pygame.sprite.Sprite.__init__(self) # 導(dǎo)入圖片 self.images = [] image = pygame.image.load(imagepath) for i in range(12): self.images.append(pygame.transform.scale(image.subsurface((i*20, 0), (20, 24)), size)) if is_highest: self.image = pygame.Surface((size[0]*8, size[1])) else: self.image = pygame.Surface((size[0]*5, size[1])) self.rect = self.image.get_rect() self.rect.left, self.rect.top = position # 一些必要的變量 self.is_highest = is_highest self.bg_color = bg_color self.score = '00000' '''設(shè)置得分''' def set(self, score): self.score = str(score).zfill(5) '''畫到屏幕上''' def draw(self, screen): self.image.fill(self.bg_color) for idx, digital in enumerate(list(self.score)): digital_image = self.images[int(digital)] if self.is_highest: self.image.blit(digital_image, ((idx+3)*digital_image.get_rect().width, 0)) else: self.image.blit(digital_image, (idx*digital_image.get_rect().width, 0)) if self.is_highest: self.image.blit(self.images[-2], (0, 0)) self.image.blit(self.images[-1], (digital_image.get_rect().width, 0)) screen.blit(self.image, self.rect)
上面代碼用is_highest變量來(lái)區(qū)分該記分板是否用于記錄游戲最高分,還是只是記錄當(dāng)前的分?jǐn)?shù),做該區(qū)分的原因是游戲最高分前面有HI標(biāo)識(shí),所以占的空間更大:
飛龍的定義就稍微復(fù)雜一些了,因?yàn)樗粌H需要向左移動(dòng),還需要做出不停扇動(dòng)翅膀的效果。具體而言,飛龍有兩張圖:
你需要做的就是每隔一段時(shí)間就切換一次當(dāng)前的飛龍圖片,以實(shí)現(xiàn)飛龍扇動(dòng)翅膀的效果:
'''飛龍''' class Ptera(pygame.sprite.Sprite): def __init__(self, imagepath, position, size=(46, 40), **kwargs): pygame.sprite.Sprite.__init__(self) # 導(dǎo)入圖片 self.images = [] image = pygame.image.load(imagepath) for i in range(2): self.images.append(pygame.transform.scale(image.subsurface((i*92, 0), (92, 81)), size)) self.image_idx = 0 self.image = self.images[self.image_idx] self.rect = self.image.get_rect() self.rect.left, self.rect.centery = position self.mask = pygame.mask.from_surface(self.image) # 定義一些必要的變量 self.speed = -10 self.refresh_rate = 10 self.refresh_counter = 0 '''畫到屏幕上''' def draw(self, screen): screen.blit(self.image, self.rect) '''更新''' def update(self): if self.refresh_counter % self.refresh_rate == 0: self.refresh_counter = 0 self.image_idx = (self.image_idx + 1) % len(self.images) self.loadImage() self.rect = self.rect.move([self.speed, 0]) if self.rect.right < 0: self.kill() self.refresh_counter += 1 '''載入當(dāng)前狀態(tài)的圖片''' def loadImage(self): self.image = self.images[self.image_idx] rect = self.image.get_rect() rect.left, rect.top = self.rect.left, self.rect.top self.rect = rect self.mask = pygame.mask.from_surface(self.image)
最后,我們需要定義一下小恐龍類,也就是最復(fù)雜的一個(gè)游戲精靈類。它有低頭,跳躍,普通前進(jìn)三種狀態(tài)。對(duì)于低頭來(lái)說(shuō):
你只需要和飛龍扇動(dòng)翅膀一樣,不斷切換兩張低頭的圖片以實(shí)現(xiàn)小恐龍跑動(dòng)的效果就可以了。對(duì)于普通狀態(tài)也是類似的:
對(duì)于跳躍狀態(tài),我們則可以通過(guò)初中學(xué)的上拋和自由落體運(yùn)動(dòng)公式來(lái)建模,從而計(jì)算小恐龍?jiān)谪Q直方向上的位置。具體而言,代碼實(shí)現(xiàn)如下:
'''小恐龍''' class Dinosaur(pygame.sprite.Sprite): def __init__(self, imagepaths, position=(40, 147), size=[(44, 47), (59, 47)], **kwargs): pygame.sprite.Sprite.__init__(self) # 導(dǎo)入所有圖片 self.images = [] image = pygame.image.load(imagepaths[0]) for i in range(5): self.images.append(pygame.transform.scale(image.subsurface((i*88, 0), (88, 95)), size[0])) image = pygame.image.load(imagepaths[1]) for i in range(2): self.images.append(pygame.transform.scale(image.subsurface((i*118, 0), (118, 95)), size[1])) self.image_idx = 0 self.image = self.images[self.image_idx] self.rect = self.image.get_rect() self.rect.left, self.rect.bottom = position self.mask = pygame.mask.from_surface(self.image) # 定義一些必要的變量 self.init_position = position self.refresh_rate = 5 self.refresh_counter = 0 self.speed = 11.5 self.gravity = 0.6 self.is_jumping = False self.is_ducking = False self.is_dead = False self.movement = [0, 0] '''跳躍''' def jump(self, sounds): if self.is_dead or self.is_jumping: return sounds['jump'].play() self.is_jumping = True self.movement[1] = -1 * self.speed '''低頭''' def duck(self): if self.is_jumping or self.is_dead: return self.is_ducking = True '''不低頭''' def unduck(self): self.is_ducking = False '''死掉了''' def die(self, sounds): if self.is_dead: return sounds['die'].play() self.is_dead = True '''將恐龍畫到屏幕''' def draw(self, screen): screen.blit(self.image, self.rect) '''載入當(dāng)前狀態(tài)的圖片''' def loadImage(self): self.image = self.images[self.image_idx] rect = self.image.get_rect() rect.left, rect.top = self.rect.left, self.rect.top self.rect = rect self.mask = pygame.mask.from_surface(self.image) '''更新小恐龍''' def update(self): if self.is_dead: self.image_idx = 4 self.loadImage() return if self.is_jumping: self.movement[1] += self.gravity self.image_idx = 0 self.loadImage() self.rect = self.rect.move(self.movement) if self.rect.bottom >= self.init_position[1]: self.rect.bottom = self.init_position[1] self.is_jumping = False elif self.is_ducking: if self.refresh_counter % self.refresh_rate == 0: self.refresh_counter = 0 self.image_idx = 5 if self.image_idx == 6 else 6 self.loadImage() else: if self.refresh_counter % self.refresh_rate == 0: self.refresh_counter = 0 if self.image_idx == 1: self.image_idx = 2 elif self.image_idx == 2: self.image_idx = 3 else: self.image_idx = 1 self.loadImage() self.refresh_counter += 1
定義完游戲精靈類,我們就可以實(shí)例化他們:
# 定義一些游戲中必要的元素和變量 score = 0 score_board = Scoreboard(cfg.IMAGE_PATHS['numbers'], position=(534, 15), bg_color=cfg.BACKGROUND_COLOR) highest_score = highest_score highest_score_board = Scoreboard(cfg.IMAGE_PATHS['numbers'], position=(435, 15), bg_color=cfg.BACKGROUND_COLOR, is_highest=True) dino = Dinosaur(cfg.IMAGE_PATHS['dino']) ground = Ground(cfg.IMAGE_PATHS['ground'], position=(0, cfg.SCREENSIZE[1])) cloud_sprites_group = pygame.sprite.Group() cactus_sprites_group = pygame.sprite.Group() ptera_sprites_group = pygame.sprite.Group() add_obstacle_timer = 0 score_timer = 0
然后寫游戲主循環(huán)啦:
# 游戲主循環(huán) clock = pygame.time.Clock() while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE or event.key == pygame.K_UP: dino.jump(sounds) elif event.key == pygame.K_DOWN: dino.duck() elif event.type == pygame.KEYUP and event.key == pygame.K_DOWN: dino.unduck() screen.fill(cfg.BACKGROUND_COLOR) # --隨機(jī)添加云 if len(cloud_sprites_group) < 5 and random.randrange(0, 300) == 10: cloud_sprites_group.add(Cloud(cfg.IMAGE_PATHS['cloud'], position=(cfg.SCREENSIZE[0], random.randrange(30, 75)))) # --隨機(jī)添加仙人掌/飛龍 add_obstacle_timer += 1 if add_obstacle_timer > random.randrange(50, 150): add_obstacle_timer = 0 random_value = random.randrange(0, 10) if random_value >= 5 and random_value <= 7: cactus_sprites_group.add(Cactus(cfg.IMAGE_PATHS['cacti'])) else: position_ys = [cfg.SCREENSIZE[1]*0.82, cfg.SCREENSIZE[1]*0.75, cfg.SCREENSIZE[1]*0.60, cfg.SCREENSIZE[1]*0.20] ptera_sprites_group.add(Ptera(cfg.IMAGE_PATHS['ptera'], position=(600, random.choice(position_ys)))) # --更新游戲元素 dino.update() ground.update() cloud_sprites_group.update() cactus_sprites_group.update() ptera_sprites_group.update() score_timer += 1 if score_timer > (cfg.FPS//12): score_timer = 0 score += 1 score = min(score, 99999) if score > highest_score: highest_score = score if score % 100 == 0: sounds['point'].play() if score % 1000 == 0: ground.speed -= 1 for item in cloud_sprites_group: item.speed -= 1 for item in cactus_sprites_group: item.speed -= 1 for item in ptera_sprites_group: item.speed -= 1 # --碰撞檢測(cè) for item in cactus_sprites_group: if pygame.sprite.collide_mask(dino, item): dino.die(sounds) for item in ptera_sprites_group: if pygame.sprite.collide_mask(dino, item): dino.die(sounds) # --將游戲元素畫到屏幕上 dino.draw(screen) ground.draw(screen) cloud_sprites_group.draw(screen) cactus_sprites_group.draw(screen) ptera_sprites_group.draw(screen) score_board.set(score) highest_score_board.set(highest_score) score_board.draw(screen) highest_score_board.draw(screen) # --更新屏幕 pygame.display.update() clock.tick(cfg.FPS) # --游戲是否結(jié)束 if dino.is_dead: break
游戲主循環(huán)的邏輯很簡(jiǎn)單,即每幀游戲畫面,我們都需要檢測(cè)一下玩家的操作,如果玩家按下了空格鍵或者↑鍵,則小恐龍?zhí)S,如果玩家按下了↓鍵,則小恐龍低頭,否則小恐龍正常向前沖。
然后在游戲中,我們隨機(jī)產(chǎn)生云,飛龍和仙人掌這些游戲場(chǎng)景和障礙物,并且和路面一起以相同的速度向左移動(dòng),從而實(shí)現(xiàn)小恐龍向右移動(dòng)的視覺(jué)效果。在移動(dòng)的過(guò)程中,我們需要對(duì)小恐龍和仙人掌,小恐龍和飛龍進(jìn)行碰撞檢測(cè),當(dāng)小恐龍碰到這些障礙物時(shí),小恐龍就死掉了,本局游戲也隨之結(jié)束。
需要注意的是我們應(yīng)該使用collide_mask函數(shù)來(lái)進(jìn)行更為精確的碰撞檢測(cè),而不是之前的collide_rect函數(shù):
即當(dāng)兩個(gè)目標(biāo)的最小外接矩形有重疊時(shí),collide_rect就會(huì)判定兩個(gè)目標(biāo)有碰撞,這顯然是不合理的,會(huì)給玩家?guī)?lái)較差的游戲體驗(yàn)。
另外,當(dāng)分?jǐn)?shù)每提高一千分,我們就和原版的游戲一樣增加一點(diǎn)場(chǎng)景和障礙物向左移動(dòng)的速度(也就是增加小恐龍向右移動(dòng)的速度)。
最后,把當(dāng)前所有的游戲元素綁定到屏幕上并更新當(dāng)前的屏幕就ok了。
大概就是這樣,大功告成完整源代碼詳見相關(guān)文件唄
以上就是利用Python模擬谷歌的小恐龍游戲的詳細(xì)內(nèi)容,更多關(guān)于Python 游戲的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
pycharm 如何取消連按兩下shift出現(xiàn)的全局搜索
這篇文章主要介紹了pycharm 如何取消連按兩下shift出現(xiàn)的全局搜索?下面小編就為大家介紹一下解決方法,還等什么?一起跟隨小編過(guò)來(lái)看看吧2021-01-01Python實(shí)現(xiàn)字符串逆序輸出功能示例
這篇文章主要介紹了Python實(shí)現(xiàn)字符串逆序輸出功能,結(jié)合具體實(shí)例形式分析了Python針對(duì)字符串的遍歷、翻轉(zhuǎn)、排序等相關(guān)操作技巧,需要的朋友可以參考下2017-06-06利用python實(shí)現(xiàn)平穩(wěn)時(shí)間序列的建模方式
這篇文章主要介紹了利用python實(shí)現(xiàn)平穩(wěn)時(shí)間序列的建模方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-06-06Python自動(dòng)化辦公之Word文件內(nèi)容的讀取
word、excel、PPT,雖然說(shuō)是特殊文件,其實(shí)也是實(shí)際工作中我們經(jīng)常會(huì)用到的文件類型。本文將為大家詳解Python讀取Word文件和文件內(nèi)容的方法,感興趣的可以了解一下2022-05-05Blender Python編程實(shí)現(xiàn)程序化建模生成超形示例詳解
這篇文章主要為大家介紹了Blender Python編程實(shí)現(xiàn)程序化建模生成超形示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08Python腳本實(shí)現(xiàn)集群檢測(cè)和管理功能
這篇文章主要介紹了Python腳本實(shí)現(xiàn)集群檢測(cè)和管理功能,本文講解了實(shí)現(xiàn)想法、開發(fā)工具選擇、經(jīng)驗(yàn)分享、代碼示例等內(nèi)容,需要的朋友可以參考下2015-03-03使用python的chardet庫(kù)獲得文件編碼并修改編碼
windows和linux采用了不同的編碼,這讓很多人傷透了腦經(jīng),這里我采用了Python的chardet庫(kù)獲得代碼的編碼,然后修改編碼2014-01-01Pandas 類型轉(zhuǎn)換astype()的實(shí)現(xiàn)
本文主要介紹了Pandas 類型轉(zhuǎn)換astype()的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07