ArcadePython 游戲框架入門

      網(wǎng)友投稿 1265 2022-05-29

      目錄

      背景和設置

      基本arcade程序

      arcade概念

      初始化

      窗口和坐標

      畫畫

      面向?qū)ο笤O計

      游戲循環(huán)

      Python 游戲設計基礎

      導入和常量

      窗口類

      精靈和精靈列表

      調(diào)度功能

      添加敵人

      移動精靈

      移除精靈

      添加云

      鍵盤輸入

      更新游戲?qū)ο?/p>

      在窗口上繪圖

      碰撞檢測

      附加功能

      聲音

      Python 游戲速度

      調(diào)整和增強

      關于來源的說明

      結論

      電腦游戲是向人們介紹編碼和計算機科學的好方法。由于我年輕時是一名玩家,編寫電子游戲的誘惑是我學習編碼的原因。當然,當我學習 Python 時,我的第一反應就是寫一個 Python 游戲。

      雖然 Python 使每個人都可以更輕松地學習編碼,但視頻游戲編寫的選擇可能有限,特別是如果您想編寫具有出色圖形和引人入勝的音效的街機游戲。多年來,Python 游戲程序員僅限于該pygame框架。現(xiàn)在,還有另一個選擇。

      該arcade庫是一個現(xiàn)代 Python 框架,用于制作具有引人注目的圖形和聲音的游戲。面向?qū)ο蟛?Python 3.6 及更高版本構建,arcade為程序員提供了一套現(xiàn)代工具來打造出色的 Python 游戲體驗。

      在本教程中,您將學習如何:

      安裝該arcade庫

      在屏幕上繪制項目

      工作與arcadePython的游戲循環(huán)

      管理屏幕上的圖形元素

      處理用戶輸入

      播放音效和音樂

      描述如何Python的游戲編程與arcade來自不同pygame

      背景和設置

      該arcade庫由美國愛荷華州辛普森學院的計算機科學教授Paul Vincent Craven編寫。由于它建立在窗口和多媒體庫之上,因此具有各種改進、現(xiàn)代化和增強功能:pygletarcadepygame

      擁有現(xiàn)代 OpenGL 圖形

      支持 Python 3類型提示

      更好地支持動畫精靈

      包含一致的命令、函數(shù)和參數(shù)名稱

      鼓勵將游戲邏輯與顯示代碼分離

      需要更少的樣板代碼

      維護更多文檔,包括完整的 Python 游戲示例

      具有用于平臺游戲的內(nèi)置物理引擎

      要安裝arcade及其依賴項,請使用適當?shù)膒ip命令:

      $ python -m pip install arcade

      在 Mac 上,您還需要安裝PyObjC:

      $ python -m pip install PyObjC arcade

      基于您的平臺的完整安裝說明可用于Windows、Mac、Linux甚至Raspberry Pi。如果您愿意,您甚至可以arcade直接從源代碼安裝。

      注意:最新版本的arcade利用數(shù)據(jù)類,它們僅包含在 Python 3.7 及更高版本中。

      但是,PyPI for Python 3.6 上提供了一個反向移植,您可以使用pip以下命令進行安裝:

      $ python -m pip install dataclasses

      有關更多信息,請參閱Python 3.7 中的數(shù)據(jù)類終極指南。

      本教程假設您arcade始終使用2.1 和 Python 3.7。

      基礎arcade課程

      在深入研究之前,讓我們先看看一個arcade程序,它會打開一個窗口,用白色填充它,并在中間畫一個藍色圓圈:

      1# Basic arcade program 2# Displays a white window with a blue circle in the middle 3 4# Imports 5import arcade 6 7# Constants 8SCREEN_WIDTH = 600 9SCREEN_HEIGHT = 800 10SCREEN_TITLE = "Welcome to Arcade" 11RADIUS = 150 12 13# Open the window 14arcade.open_window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) 15 16# Set the background color 17arcade.set_background_color(arcade.color.WHITE) 18 19# Clear the screen and start drawing 20arcade.start_render() 21 22# Draw a blue circle 23arcade.draw_circle_filled( 24 SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, RADIUS, arcade.color.BLUE 25) 26 27# Finish drawing 28arcade.finish_render() 29 30# Display everything 31arcade.run()

      當你運行這個程序時,你會看到一個看起來像這樣的窗口:

      讓我們逐行分解:

      第 5 行導入arcade庫。沒有這個,其他的都不起作用。

      為清楚起見,第 8 行到第 11 行定義了一些稍后將使用的常量。

      第 14 行打開主窗口。您提供寬度、高度和標題欄文本,然后arcade完成其余的工作。

      第 17 行使用arcade.color包中的常量設置背景顏色。您還可以使用列表或元組指定 RGB 顏色。

      第 20 行設置arcade為繪圖模式。在此線之后繪制的任何內(nèi)容都將顯示在屏幕上。

      第 23 到 25 行通過提供中心 X 和 Y 坐標、半徑和要使用的顏色來繪制圓。

      第 28 行結束繪圖模式。

      第 31 行顯示您的窗口供您查看。

      如果您熟悉pygame,那么您會注意到一些不同之處:

      沒有pygame.init()。當您運行import arcade.

      沒有明確定義的顯示循環(huán)。它在arcade.run().

      這里也沒有事件循環(huán)。同樣,arcade.run()處理事件并提供一些默認行為,例如關閉窗口的能力。

      您可以使用預定義的顏色進行繪圖,而不是自己定義所有顏色。

      您必須使用start_render()和開始和完成街機中的繪圖finish_render()。

      讓我們仔細看看arcade這個程序背后的基本概念。

      arcade?概念

      就像pygame,arcade代碼運行在幾乎所有支持 Python 的平臺上。這需要arcade處理這些平臺上各種硬件差異的抽象。理解這些概念和抽象將幫助你設計和開發(fā)你自己的游戲,同時理解它們之間的arcade不同pygame將幫助你適應其獨特的觀點。

      初始化

      由于它涉及多種平臺,因此arcade必須執(zhí)行初始化步驟才能使用它。此步驟是自動的,并且在您導入時發(fā)生arcade,因此您無需編寫額外的代碼。導入時,arcade執(zhí)行以下操作:

      驗證您是否在 Python 3.6 或更高版本上運行。

      導入pyglet_ffmeg2用于聲音處理的庫(如果可用)。

      導入pyglet用于窗口和多媒體處理的庫。

      為顏色和鍵映射設置常量。

      導入剩余的arcade庫。

      將此與 相比pygame,它需要為每個模塊進行單獨的初始化步驟。

      窗口和坐標

      中的所有內(nèi)容都arcade發(fā)生在一個窗口中,您可以使用open_window().?目前,arcade僅支持單個顯示窗口。您可以在打開窗口時調(diào)整其大小。

      arcade使用您可能在代數(shù)課中學到的相同笛卡爾坐標系。窗口位于象限 I 中,原點 (0, 0) 位于屏幕的左下角。向右移動 x 坐標增加,向上移動 y 坐標增加:

      需要注意的是,這種行為pygame與許多其他 Python 游戲框架相反。您可能需要一些時間來適應這種差異。

      畫畫

      開箱即用,arcade具有繪制各種幾何形狀的功能,包括:

      Arcs

      Circles

      Ellipses

      Lines

      Parabolas

      Points

      Polygons

      Rectangles

      Triangles

      所有繪圖功能draw_都以一致的命名和參數(shù)模式開始并遵循。繪制填充和輪廓形狀有不同的功能:

      因為矩形很常見,所以有三個獨立的函數(shù)可以以不同的方式繪制它們:

      draw_rectangle()?期望矩形中心的 x 和 y 坐標、寬度和高度。

      draw_lrtb_rectangle()?期望左右 x 坐標,然后是頂部和底部 y 坐標。

      draw_xywh_rectangle()?使用左下角的 x 和 y 坐標,然后是寬度和高度。

      請注意,每個函數(shù)都需要四個參數(shù)。您還可以使用緩沖繪圖函數(shù)繪制每個形狀,該函數(shù)利用頂點緩沖區(qū)將所有內(nèi)容直接推送到顯卡,以實現(xiàn)令人難以置信的性能改進。所有緩沖繪圖函數(shù)都以create_一致的命名和參數(shù)模式開始并遵循。

      面向?qū)ο笤O計

      它的核心arcade是一個面向?qū)ο蟮膸臁>拖駊ygame,您可以按arcade程序編寫代碼,就像在上面的示例中所做的那樣。然而,arcade當您創(chuàng)建完全面向?qū)ο蟮某绦驎r,它的真正威力就會顯現(xiàn)出來。

      當您arcade.open_window()在上面的示例中調(diào)用時,代碼會arcade.Window在幕后創(chuàng)建一個對象來管理該窗口。稍后,您將創(chuàng)建自己的類,arcade.Window以編寫完整的 Python 游戲。

      首先,看一下現(xiàn)在使用面向?qū)ο蟾拍畹脑际纠a,以突出主要區(qū)別:

      # Basic arcade program using objects # Displays a white window with a blue circle in the middle # Imports import arcade # Constants SCREEN_WIDTH = 600 SCREEN_HEIGHT = 800 SCREEN_TITLE = "Welcome to Arcade" RADIUS = 150 # Classes class Welcome(arcade.Window): """Main welcome window """ def __init__(self): """Initialize the window """ # Call the parent class constructor super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) # Set the background window arcade.set_background_color(arcade.color.WHITE) def on_draw(self): """Called whenever you need to draw your window """ # Clear the screen and start drawing arcade.start_render() # Draw a blue circle arcade.draw_circle_filled( SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, RADIUS, arcade.color.BLUE ) # Main code entry point if __name__ == "__main__": app = Welcome() arcade.run()

      讓我們一行一行地看一下這段代碼:

      第 1 到 11 行與前面的過程示例相同。

      第 15 行是差異開始的地方。您定義一個Welcome基于父類調(diào)用的類arcade.Window。這允許您根據(jù)需要覆蓋父類中的方法。

      第 18 到 26 行定義了該.__init__()方法。在調(diào)用用于設置窗口的父.__init__()方法之后super(),您可以像以前一樣設置其背景顏色。

      第 28 到 38 行定義了.on_draw().?這是Window您可以重寫以自定義arcade程序行為的幾種方法之一。每次arcade想要在窗口上繪制時都會調(diào)用此方法。它首先調(diào)用arcade.start_render(),然后是所有繪圖代碼。arcade.finish_render()但是,您不需要調(diào)用,因為arcade會在.on_draw()結束時隱式調(diào)用它。

      第 41 到 43 行是代碼的主要入口點。在您首先創(chuàng)建一個Welcome名為的新對象后app,您可以調(diào)用arcade.run()以顯示該窗口。

      這個面向?qū)ο蟮睦邮菑腶rcade.?您可能已經(jīng)注意到的一件事是.on_draw().?arcade每次要在窗口上繪制時都會調(diào)用它。那么,如何arcade知道何時繪制任何東西呢?讓我們來看看這意味著什么。

      游戲循環(huán)

      幾乎每場比賽中的所有動作都發(fā)生在一個中央游戲循環(huán)中。您甚至可以在跳棋、老女仆或棒球等實體游戲中看到游戲循環(huán)的示例。游戲循環(huán)在游戲設置和初始化后開始,并在游戲完成時結束。在這個循環(huán)中,有幾件事會依次發(fā)生。一個游戲循環(huán)至少需要執(zhí)行以下四個動作:

      該程序確定游戲是否結束。如果是,則循環(huán)結束。

      處理用戶輸入。

      游戲?qū)ο蟮臓顟B(tài)根據(jù)用戶輸入或時間等因素進行更新。

      游戲根據(jù)新狀態(tài)顯示視覺效果并播放聲音效果。

      在 中pygame,您必須明確設置和控制此循環(huán)。在 中arcade,為您提供了 Python 游戲循環(huán),封裝在arcade.run()調(diào)用中。

      在內(nèi)置游戲循環(huán)期間,arcade調(diào)用一組Window方法來實現(xiàn)上面列出的所有功能。這些方法的名稱都on_以task 或 event handlers開頭,可以將其視為任務或事件處理程序。當arcade游戲循環(huán)需要更新所有 Python 游戲?qū)ο蟮臓顟B(tài)時,它會調(diào)用.on_update().?當它需要檢查鼠標移動時,它會調(diào)用.on_mouse_motion().

      默認情況下,這些方法都沒有做任何有用的事情。當您基于 來創(chuàng)建您自己的類時arcade.Window,您可以根據(jù)需要覆蓋它們以提供您自己的游戲功能。提供的一些方法包括:

      鍵盤輸入:?.on_key_press()?,.on_key_release()

      鼠標輸入:?.on_mouse_press()?,?.on_mouse_release(),.on_mouse_motion()

      更新游戲?qū)ο螅?.on_update()

      畫畫:?.on_draw()

      您不需要覆蓋所有這些方法,只需覆蓋您想要為其提供不同行為的方法。您也無需擔心他們何時被調(diào)用,只需擔心他們被調(diào)用時該怎么做。接下來,您將探索如何將所有這些概念組合在一起來創(chuàng)建游戲。

      Python 游戲設計基礎

      在您開始編寫任何代碼之前,先做好設計總是一個好主意。由于您將在本教程中創(chuàng)建一個 Python 游戲,因此您還將為其設計一些游戲玩法:

      該游戲是一個橫向滾動的敵人回避游戲。

      播放器從屏幕左側(cè)開始。

      敵人以固定的間隔和右側(cè)的隨機位置進入。

      敵人沿直線向左移動,直到他們離開屏幕。

      玩家可以向左、向右、向上或向下移動來躲避敵人。

      玩家不能離開屏幕。

      當玩家被敵人擊中或用戶關閉窗口時游戲結束。

      當他描述軟件項目時,我的一位前同事曾經(jīng)說過:“直到你知道你不知道什么,你才知道你做了什么。”?考慮到這一點,以下是本教程中不會涉及的一些內(nèi)容:

      沒有多重生命

      沒有記分

      無玩家攻擊能力

      沒有進階

      沒有“老板”角色

      您可以隨意嘗試將這些和其他功能添加到您自己的程序中。

      導入和常量

      與任何arcade程序一樣,您將從導入庫開始:

      # Basic arcade shooter # Imports import arcade import random # Constants SCREEN_WIDTH = 800 SCREEN_HEIGHT = 600 SCREEN_TITLE = "Arcade Space Shooter" SCALING = 2.0

      除了arcade,您還導入random,因為稍后您將使用隨機數(shù)。常量設置窗口大小和標題,但什么是SCALING?此常量用于使窗口和其中的游戲?qū)ο笞兇笠匝a償高 DPI 屏幕。隨著教程的繼續(xù),您將看到它在兩個地方使用。您可以更改此值以適合您的屏幕大小。

      窗口類

      要充分利用arcadePython 游戲循環(huán)和事件處理程序,請基于以下內(nèi)容創(chuàng)建一個新類arcade.Window:

      35class SpaceShooter(arcade.Window): 36 """Space Shooter side scroller game 37 Player starts on the left, enemies appear on the right 38 Player can move anywhere, but not off screen 39 Enemies fly to the left at variable speed 40 Collisions end the game 41 """ 42 43 def __init__(self, width, height, title): 44 """Initialize the game 45 """ 46 super().__init__(width, height, title) 47 48 # Set up the empty sprite lists 49 self.enemies_list = arcade.SpriteList() 50 self.clouds_list = arcade.SpriteList() 51 self.all_sprites = arcade.SpriteList()

      您的新類就像上面的面向?qū)ο笫纠粯娱_始。在第 43 行,您定義了構造函數(shù),它獲取游戲窗口的寬度、高度和標題,并將super()它們傳遞給父級。然后在第 49 行到第 51 行初始化一些空的精靈列表。在下一節(jié)中,您將了解有關精靈和精靈列表的更多信息。

      精靈和精靈列表

      您的 Python 游戲設計要求從左側(cè)開始并可以在窗口中自由移動的單個玩家。它還需要隨機出現(xiàn)在右側(cè)并向左側(cè)移動的敵人(換句話說,不止一個)。雖然您可以使用draw_命令來繪制玩家和每個敵人,但很快就會很難保持筆直。

      相反,大多數(shù)現(xiàn)代游戲使用精靈來表示屏幕上的對象。從本質(zhì)上講,精靈是在屏幕上的特定位置繪制的具有定義大小的游戲?qū)ο蟮亩S圖片。在 中arcade,精靈是 class 的對象arcade.Sprite,您將使用它們來代表您的玩家和敵人。你甚至會加入一些云來使背景更有趣。

      管理所有這些精靈可能是一個挑戰(zhàn)。您將創(chuàng)建一個單人游戲精靈,但您還將創(chuàng)建大量敵人和云精靈。跟蹤所有這些是精靈列表的工作。如果您了解Python 列表的工作原理,那么您就有了使用arcade的精靈列表的工具。精靈列表的作用不僅僅是保存所有的精 靈。它們實現(xiàn)了三個重要的行為:

      您可以通過一次調(diào)用來更新列表中的所有精靈SpriteList.update()。

      您可以通過一次調(diào)用來繪制列表中的所有精靈SpriteList.draw()。

      您可以檢查單個精靈是否與列表中的任何精靈發(fā)生碰撞。

      如果您只需要管理多個敵人和云,您可能想知道為什么需要三個不同的精靈列表。原因是三個不同的精靈列表中的每一個都存在,因為您將它們用于三個不同的目的:

      你.enemies_list用來更新敵人的位置并檢查碰撞。

      您用于.clouds_list更新云位置。

      最后,你.all_sprites用來繪制一切。

      現(xiàn)在,列表僅與其包含的數(shù)據(jù)一樣有用。以下是填充精靈列表的方法:

      53def setup(self): 54 """Get the game ready to play 55 """ 56 57 # Set the background color 58 arcade.set_background_color(arcade.color.SKY_BLUE) 59 60 # Set up the player 61 self.player = arcade.Sprite("images/jet.png", SCALING) 62 self.player.center_y = self.height / 2 63 self.player.left = 10 64 self.all_sprites.append(self.player)

      您定義.setup()將游戲初始化為已知起點。雖然您可以在 中執(zhí)行此操作.__init__(),但使用單獨的.setup()方法很有用。

      想象一下,您希望您的 Python 游戲具有多個級別,或者您的玩家擁有多個生命。不是通過調(diào)用 重新啟動整個游戲.__init__(),而是調(diào)用.setup()將游戲重新初始化到已知起點或設置新級別。即使這個 Python 游戲沒有這些功能,設置結構可以讓以后更快地添加它們。

      在第 58 行設置背景顏色后,您可以定義玩家精靈:

      第 61 行arcade.Sprite通過指定要顯示的圖像和縮放因子來創(chuàng)建一個新對象。將圖像組織到單個子文件夾中是個好主意,尤其是在較大的項目中。

      第 62行將精靈的 y 位置設置為窗口高度的一半。

      第 63 行通過將左邊緣放置在距窗口左邊緣幾個像素的位置來設置精靈的 x 位置。

      第 64 行最后用于.append()將精靈添加到.all_sprites您將用于繪圖的列表中。

      第 62 行和第 63 行顯示了兩種不同的定位精靈的方法。讓我們仔細看看所有可用的精靈定位選項。

      所有精靈在arcade窗口中都有特定的大小和位置:

      由Sprite.width和指定的大小由Sprite.height創(chuàng)建精靈時使用的圖形決定。

      該位置最初設置為精靈的中心,由Sprite.center_x和指定Sprite.center_y,在窗口中的 (0,0) 處。

      一旦.center_x和.center_y坐標是已知的,arcade可以使用尺寸來計算Sprite.left,Sprite.right,Sprite.top,和Sprite.bottom邊緣,以及。

      這也適用于相反的情況。例如,如果您設置Sprite.left為給定值,那么arcade也會重新計算剩余的位置屬性。您可以使用它們中的任何一個來定位精靈或在窗口中移動它。這是arcade精靈的一個非常有用和強大的特性。如果您使用它們,那么您的 Python 游戲?qū)⑿枰纫韵赂俚拇apygame:

      現(xiàn)在您已經(jīng)定義了玩家精靈,您可以處理敵人的精靈。該設計要求您讓敵人的精靈定期出現(xiàn)。你怎么能這樣做?

      調(diào)度功能

      arcade.schedule()正是為此目的而設計的。它需要兩個參數(shù):

      要調(diào)用的函數(shù)的名稱

      每次調(diào)用之間等待的時間間隔,以秒為單位

      由于您希望在整個游戲中同時出現(xiàn)敵人和云,因此您設置了一個計劃函數(shù)來創(chuàng)建新的敵人,第二個函數(shù)來創(chuàng)建新的云。該代碼進入.setup().?下面是代碼的樣子:

      66# Spawn a new enemy every 0.25 seconds 67arcade.schedule(self.add_enemy, 0.25) 68 69# Spawn a new cloud every second 70arcade.schedule(self.add_cloud, 1.0)

      現(xiàn)在您所要做的就是定義self.add_enemy()和self.add_cloud()。

      添加敵人

      從您的 Python 游戲設計來看,敵人具有三個關鍵屬性:

      它們出現(xiàn)在窗口右側(cè)的隨機位置。

      它們沿直線向左移動。

      當它們離開屏幕時它們就會消失。

      創(chuàng)建敵人精靈的代碼與創(chuàng)建玩家精靈的代碼非常相似:

      93def add_enemy(self, delta_time: float): 94 """Adds a new enemy to the screen 95 96 Arguments: 97 delta_time {float} -- How much time has passed since the last call 98 """ 99 100 # First, create the new enemy sprite 101 enemy = arcade.Sprite("images/missile.png", SCALING) 102 103 # Set its position to a random height and off screen right 104 enemy.left = random.randint(self.width, self.width + 80) 105 enemy.top = random.randint(10, self.height - 10)

      .add_enemy()采用單個參數(shù) ,delta_time表示自上次調(diào)用以來已經(jīng)過去了多長時間。這是 需要的arcade.schedule(),雖然您不會在這里使用它,但它對于需要高級計時的應用程序很有用。

      與播放器精靈一樣,您首先創(chuàng)建一個arcade.Sprite帶有圖片和縮放因子的新精靈。您使用.left和.top將位置設置為屏幕右側(cè)某處的隨機位置:

      這使得敵人可以順利地移動到屏幕上,而不僅僅是出現(xiàn)在屏幕上。現(xiàn)在,你如何讓它移動?

      移動精靈

      移動精靈需要您在游戲循環(huán)的更新階段更改其位置。雖然您可以自己完成此操作,但arcade有一些內(nèi)置功能可以減少您的工作量。每個arcade.Sprite不僅有一組位置屬性,而且還有一組運動屬性。每次更新精靈時,arcade都會使用運動屬性來更新位置,從而為精靈賦予相對運動。

      該Sprite.velocity屬性是一個由 x 和 y 位置變化組成的元組。您也可以直接訪問Sprite.change_x和Sprite.change_y。如上所述,每次更新精靈時,它.position都會根據(jù).velocity.?您需要做的.add_enemy()就是設置速度:

      107# Set its speed to a random speed heading left 108enemy.velocity = (random.randint(-20, -5), 0) 109 110# Add it to the enemies list 111self.enemies_list.append(enemy) 112self.all_sprites.append(enemy)

      在第 108 行將速度設置為向左移動的隨機速度后,將新敵人添加到相應的列表中。當您稍后調(diào)用時sprite.update(),arcade將處理其余的:

      在您的 Python 游戲設計中,敵人從右到左沿直線移動。因為你的敵人總是向左移動,一旦他們離開屏幕,他們就不會回來。如果您可以擺脫屏幕外的敵人精靈以釋放內(nèi)存并加快更新速度,那就太好了。幸運的是,arcade你覆蓋了。

      移除精靈

      因為你的敵人總是向左移動,所以他們的 x 位置總是越來越小,而他們的 y 位置總是不變的。因此,當enemy.right小于零時,您可以確定敵人在屏幕外,即窗口的左邊緣。一旦你確定敵人不在屏幕上,你調(diào)用enemy.remove_from_sprite_lists()從它所屬的所有列表中刪除它并從內(nèi)存中釋放該對象:

      if enemy.right < 0: enemy.remove_from_sprite_lists()

      但是你什么時候執(zhí)行這個檢查?通常,這會在精靈移動后立即發(fā)生。但是,請記住之前所說的關于.all_enemies精靈列表的內(nèi)容:

      你.enemies_list用來更新敵人的位置并檢查碰撞。

      這意味著在 中SpaceShooter.on_update(),您將調(diào)用enemies_list.update()自動處理敵人的移動,這主要執(zhí)行以下操作:

      for enemy in enemies_list: enemy.update()

      如果您可以將屏幕外檢查直接添加到enemy.update()呼叫中,那就太好了,您可以!記住,arcade是一個面向?qū)ο蟮膸臁_@意味著您可以基于arcade類創(chuàng)建自己的類,并覆蓋要修改的方法。在這種情況下,您創(chuàng)建一個新類arcade.Sprite并.update()僅覆蓋:

      17class FlyingSprite(arcade.Sprite): 18 """Base class for all flying sprites 19 Flying sprites include enemies and clouds 20 """ 21 22 def update(self): 23 """Update the position of the sprite 24 When it moves off screen to the left, remove it 25 """ 26 27 # Move the sprite 28 super().update() 29 30 # Remove if off the screen 31 if self.right < 0: 32 self.remove_from_sprite_lists()

      您定義FlyingSprite為將在您的游戲中飛行的任何事物,例如敵人和云。然后您覆蓋.update(),首先調(diào)用super().update()以正確處理運動。然后,您執(zhí)行屏幕外檢查。

      由于您有一個新課程,您還需要對以下內(nèi)容進行一些小改動.add_enemy():

      def add_enemy(self, delta_time: float): """Adds a new enemy to the screen Arguments: delta_time {float} -- How much time as passed since the last call """ # First, create the new enemy sprite enemy = FlyingSprite("images/missile.png", SCALING)

      Sprite您不是創(chuàng)建新的,而是創(chuàng)建新的FlyingSprite以利用新的.update()。

      添加云

      為了使您的 Python 游戲在視覺上更具吸引力,您可以向天空添加云彩。云在天空中飛舞,就像您的敵人一樣,因此您可以以類似的方式創(chuàng)建和移動它們。

      .add_cloud()遵循與 相同的模式.add_enemy(),但隨機速度較慢:

      def add_cloud(self, delta_time: float): """Adds a new cloud to the screen Arguments: delta_time {float} -- How much time has passed since the last call """ # First, create the new cloud sprite cloud = FlyingSprite("images/cloud.png", SCALING) # Set its position to a random height and off screen right cloud.left = random.randint(self.width, self.width + 80) cloud.top = random.randint(10, self.height - 10) # Set its speed to a random speed heading left cloud.velocity = (random.randint(-5, -2), 0) # Add it to the enemies list self.clouds_list.append(cloud) self.all_sprites.append(cloud)

      云的移動速度比敵人慢,因此您在第 129 行計算出較低的隨機速度。

      現(xiàn)在你的 Python 游戲看起來更完整了:

      您的敵人和云現(xiàn)在已創(chuàng)建并自行移動。是時候讓玩家使用鍵盤移動了。

      鍵盤輸入

      本arcade.Window類有處理鍵盤輸入兩種功能。您的 Python 游戲?qū)?on_key_press()在按下按鍵和.on_key_release()松開按鍵時調(diào)用。這兩個函數(shù)都接受兩個整數(shù)參數(shù):

      symbol?代表按下或釋放的實際鍵。

      modifiers表示哪些修飾符被關閉。這些措施包括Shift,Ctrl,和Alt鍵。

      幸運的是,您不需要知道哪些整數(shù)代表哪些鍵。該arcade.key模塊包含您可能想要使用的所有鍵盤常量。傳統(tǒng)上,使用鍵盤移動演奏者使用三組不同鍵中的一組或多組:

      四個方向鍵Up,Down,Left,和Right

      鍵I,?J,?K, and?L,分別映射到 Up、Left、Down 和 Right

      對于左手控制,鍵W,?A,?S, 和D也映射到上、左、下和右

      對于此游戲,您將使用箭頭和I/?J/?K/?L。每當用戶按下移動鍵時,玩家精靈就會向那個方向移動。當用戶釋放移動鍵時,精靈將停止向該方向移動。您還提供了一種使用 退出游戲的方法Q,以及一種使用 暫停游戲的方法P。為此,您需要響應按鍵和釋放:

      當一個鍵被按下時,調(diào)用.on_key_press()。在該方法中,您檢查按下了哪個鍵:

      如果是Q,那么您只需退出游戲。

      如果是P,則您設置一個標志以指示游戲已暫停。

      如果它是一個移動鍵,那么您可以相應地設置播放器的.change_x或.change_y。

      如果它是任何其他鍵,那么您可以忽略它。

      當一個鍵被釋放時,調(diào)用.on_key_release()。再次檢查釋放了哪個鍵:

      如果是移動鍵,則相應地將玩家的.change_x或設置.change_y為 0。

      如果它是任何其他鍵,那么您可以忽略它。

      代碼如下所示:

      134def on_key_press(self, symbol, modifiers): 135 """Handle user keyboard input 136 Q: Quit the game 137 P: Pause/Unpause the game 138 I/J/K/L: Move Up, Left, Down, Right 139 Arrows: Move Up, Left, Down, Right 140 141 Arguments: 142 symbol {int} -- Which key was pressed 143 modifiers {int} -- Which modifiers were pressed 144 """ 145 if symbol == arcade.key.Q: 146 # Quit immediately 147 arcade.close_window() 148 149 if symbol == arcade.key.P: 150 self.paused = not self.paused 151 152 if symbol == arcade.key.I or symbol == arcade.key.UP: 153 self.player.change_y = 5 154 155 if symbol == arcade.key.K or symbol == arcade.key.DOWN: 156 self.player.change_y = -5 157 158 if symbol == arcade.key.J or symbol == arcade.key.LEFT: 159 self.player.change_x = -5 160 161 if symbol == arcade.key.L or symbol == arcade.key.RIGHT: 162 self.player.change_x = 5 163 164def on_key_release(self, symbol: int, modifiers: int): 165 """Undo movement vectors when movement keys are released 166 167 Arguments: 168 symbol {int} -- Which key was pressed 169 modifiers {int} -- Which modifiers were pressed 170 """ 171 if ( 172 symbol == arcade.key.I 173 or symbol == arcade.key.K 174 or symbol == arcade.key.UP 175 or symbol == arcade.key.DOWN 176 ): 177 self.player.change_y = 0 178 179 if ( 180 symbol == arcade.key.J 181 or symbol == arcade.key.L 182 or symbol == arcade.key.LEFT 183 or symbol == arcade.key.RIGHT 184 ): 185 self.player.change_x = 0

      在 中.on_key_release(),您只檢查會影響您的播放器精靈運動的鍵。無需檢查 Pause 或 Quit 鍵是否被釋放。

      現(xiàn)在您可以在屏幕上移動并立即退出游戲:

      您可能想知道暫停功能是如何工作的。要查看實際效果,您首先需要學習更新所有 Python 游戲?qū)ο蟆?/p>

      更新游戲?qū)ο?/p>

      僅僅因為您為所有精靈設置了速度并不意味著它們會移動。為了讓它們移動,你必須在游戲循環(huán)中一遍又一遍地更新它們。

      由于arcade控制 Python 游戲循環(huán),它還通過調(diào)用 來控制何時需要更新.on_update()。您可以重寫此方法來為您的游戲提供適當?shù)男袨椋ㄓ螒蛞苿雍推渌袨椤τ谶@個游戲,你需要做一些事情來正確更新一切:

      您檢查游戲是否已暫停。如果是這樣,那么您可以退出,因此不會發(fā)生進一步的更新。

      您更新所有精靈以使其移動。

      您檢查玩家精靈是否已移出屏幕。如果是這樣,那么只需將它們移回屏幕上即可。

      暫時就這樣了。下面是這段代碼的樣子:

      189def on_update(self, delta_time: float): 190 """Update the positions and statuses of all game objects 191 If paused, do nothing 192 193 Arguments: 194 delta_time {float} -- Time since the last update 195 """ 196 197 # If paused, don't update anything 198 if self.paused: 199 return 200 201 # Update everything 202 self.all_sprites.update() 203 204 # Keep the player on screen 205 if self.player.top > self.height: 206 self.player.top = self.height 207 if self.player.right > self.width: 208 self.player.right = self.width 209 if self.player.bottom < 0: 210 self.player.bottom = 0 211 if self.player.left < 0: 212 self.player.left = 0

      第 198 行是您檢查游戲是否暫停的地方,如果是,則簡單地返回。這會跳過所有剩余的代碼,因此不會有任何移動。所有精靈的移動都由第 202 行處理。這行代碼有以下三個原因:

      每個精靈都是self.all_sprites列表的成員。

      呼叫到self.all_sprites.update()在呼叫結果.update()列表中的每一個角色。

      列表中的每個精靈都有.velocity(由.change_x和.change_y屬性組成)并且在.update()調(diào)用它時將處理自己的運動。

      最后,您在第 205 到 212 行通過比較精靈的邊緣和窗口的邊緣來檢查玩家精靈是否在屏幕外。例如,在第 205 和 206 行,如果self.player.top超出屏幕頂部,則重置self.player.top到屏幕頂部。現(xiàn)在一切都已更新,您可以繪制所有內(nèi)容。

      在窗口上繪圖

      由于游戲?qū)ο蟮母掳l(fā)生在.on_update(),因此繪制游戲?qū)ο髮⒃诿麨?的方法中進行似乎是合適的.on_draw()。由于您已將所有內(nèi)容組織到精靈列表中,因此此方法的代碼非常簡短:

      231def on_draw(self): 232 """Draw all game objects 233 """ 234 arcade.start_render() 235 self.all_sprites.draw()

      所有的繪制都從調(diào)用arcade.start_render()第 234 行開始。就像更新一樣,您可以通過調(diào)用self.all_sprites.draw()第 235 行來一次性繪制所有精靈。現(xiàn)在 Python 游戲只有最后一部分需要處理,這是最后一部分初步設計:

      當玩家被障礙物擊中或用戶關閉窗口時,游戲結束。

      這是真正的游戲部分!現(xiàn)在,敵人會飛過你的玩家精靈,什么也不做。讓我們看看如何添加此功能。

      碰撞檢測

      游戲都是關于一種或另一種形式的碰撞,即使在非電腦游戲中也是如此。沒有真實或虛擬的碰撞,就沒有曲棍球的擊球目標,在西洋雙陸棋中沒有雙六,在國際象棋中也沒有辦法在騎士叉的末端捕獲對手的皇后。

      計算機游戲中的碰撞檢測要求程序員檢測兩個游戲?qū)ο笫欠癫糠终紦?jù)了屏幕上的相同空間。您使用碰撞檢測來射擊敵人,限制玩家在墻壁和地板上的移動,并提供障礙物來躲避。根據(jù)所涉及的游戲?qū)ο蠛退璧男袨椋鲎矙z測邏輯可能需要復雜的數(shù)學運算。

      但是,您不必使用arcade.?您可以使用三種不同Sprite方法之一來快速檢測碰撞:

      Sprite.collides_with_point((x,y))True如果給定點(x,y)在當前精靈的邊界內(nèi),則返回,F(xiàn)alse否則返回。

      Sprite.collides_with_sprite(Sprite)True如果給定的精靈與當前精靈重疊,則返回,F(xiàn)alse否則返回。

      Sprite.collides_with_list(SpriteList)返回一個包含SpriteList與當前精靈重疊的所有精靈的列表。如果沒有重疊的精靈,則列表將為空,這意味著它的長度為零。

      由于您對單人精靈是否與任何敵方精靈發(fā)生碰撞感興趣,因此最后一種方法正是您所需要的。您調(diào)用self.player.collides_with_list(self.enemies_list)并檢查它返回的列表是否包含任何精靈。如果是這樣,那么你結束游戲。

      那么,你在哪里打這個電話?最好的地方是.on_update(),就在你更新所有東西的位置之前:

      189def on_update(self, delta_time: float): 190 """Update the positions and statuses of all game objects 191 If paused, do nothing 192 193 Arguments: 194 delta_time {float} -- Time since the last update 195 """ 196 197 # If paused, don't update anything 198 if self.paused: 199 return 200 201 # Did you hit anything? If so, end the game 202 if self.player.collides_with_list(self.enemies_list): 203 arcade.close_window() 204 205 # Update everything 206 self.all_sprites.update()

      第 202 和 203 行檢查player和 中的任何精靈之間的碰撞.enemies_list。如果返回的列表包含任何精靈,則表示發(fā)生碰撞,您可以結束游戲。現(xiàn)在,為什么要在更新所有位置之前進行檢查?記住 Python 游戲循環(huán)中的動作順序:

      您更新游戲?qū)ο蟮臓顟B(tài)。你在.on_update().

      您將所有游戲?qū)ο罄L制在它們的新位置。你在.on_draw().

      如果在更新 中的所有內(nèi)容后檢查碰撞.on_update(),則在檢測到碰撞時不會繪制任何新位置。您實際上是根據(jù)尚未向用戶顯示的精靈位置檢查碰撞。在玩家看來,游戲似乎在真正發(fā)生碰撞之前就結束了!當您首先檢查時,您確保玩家可見的內(nèi)容與您正在檢查的游戲狀態(tài)相同。

      現(xiàn)在您有一個看起來不錯并提供挑戰(zhàn)的 Python 游戲!現(xiàn)在您可以添加一些額外的功能來幫助您的 Python 游戲脫穎而出。

      附加功能

      您可以將更多功能添加到 Python 游戲中以使其脫穎而出。除了游戲設計中提到的您沒有實現(xiàn)的功能之外,您可能還會想到其他功能。本節(jié)將介紹兩個功能,它們將通過添加聲音效果和控制游戲速度為您的 Python 游戲帶來一些額外的影響。

      聲音

      聲音是任何電腦游戲的重要組成部分。從爆炸到敵人的嘲諷再到背景音樂,您的 Python 游戲在沒有聲音的情況下有點單調(diào)。開箱即用,arcade提供對WAV文件的支持。如果ffmpeg 庫已安裝且可用,則arcade還支持Ogg和MP3格式的文件。您將添加三種不同的音效和一些背景音樂:

      當玩家向上移動時會播放第一個音效。

      當玩家向下移動時會播放第二個音效。

      發(fā)生碰撞時播放第三種音效。

      背景音樂是您添加的最后一件事。

      您將從音效開始。

      在您可以播放任何這些聲音之前,您必須加載它們。你這樣做.setup():

      66# Spawn a new enemy every 0.25 seconds 67arcade.schedule(self.add_enemy, 0.25) 68 69# Spawn a new cloud every second 70arcade.schedule(self.add_cloud, 1.0) 71 72# Load your sounds 73# Sound sources: Jon Fincher 74self.collision_sound = arcade.load_sound("sounds/Collision.wav") 75self.move_up_sound = arcade.load_sound("sounds/Rising_putter.wav") 76self.move_down_sound = arcade.load_sound("sounds/Falling_putter.wav")

      就像您的精靈圖像一樣,將所有聲音放在一個子文件夾中是一種很好的做法。

      加載聲音后,您可以在適當?shù)臅r間播放它們。對于.move_up_soundand?.move_down_sound,這發(fā)生在.on_key_press()處理程序期間:

      134def on_key_press(self, symbol, modifiers): 135 """Handle user keyboard input 136 Q: Quit the game 137 P: Pause the game 138 I/J/K/L: Move Up, Left, Down, Right 139 Arrows: Move Up, Left, Down, Right 140 141 Arguments: 142 symbol {int} -- Which key was pressed 143 modifiers {int} -- Which modifiers were pressed 144 """ 145 if symbol == arcade.key.Q: 146 # Quit immediately 147 arcade.close_window() 148 149 if symbol == arcade.key.P: 150 self.paused = not self.paused 151 152 if symbol == arcade.key.I or symbol == arcade.key.UP: 153 self.player.change_y = 5 154 arcade.play_sound(self.move_up_sound) 155 156 if symbol == arcade.key.K or symbol == arcade.key.DOWN: 157 self.player.change_y = -5 158 arcade.play_sound(self.move_down_sound)

      現(xiàn)在,每當玩家向上或向下移動時,您的 Python 游戲都會播放聲音。

      每當.on_update()檢測到碰撞時都會播放碰撞聲音:

      def on_update(self, delta_time: float): """Update the positions and statuses of all game objects If paused, do nothing Arguments: delta_time {float} -- Time since the last update """ # If paused, don't update anything if self.paused: return # Did you hit anything? If so, end the game if len(self.player.collides_with_list(self.enemies_list)) > 0: arcade.play_sound(self.collision_sound) arcade.close_window() # Update everything self.all_sprites.update()

      就在窗戶關閉之前,會播放碰撞聲。

      添加背景音樂遵循與添加音效相同的模式。唯一的區(qū)別是它何時開始播放。對于背景音樂,您通常在關卡開始時啟動它,因此在以下位置加載并啟動聲音.setup():

      66# Spawn a new enemy every 0.25 seconds 67arcade.schedule(self.add_enemy, 0.25) 68 69# Spawn a new cloud every second 70arcade.schedule(self.add_cloud, 1.0) 71 72# Load your background music 73# Sound source: http://ccmixter.org/files/Apoxode/59262 74# License: https://creativecommons.org/licenses/by/3.0/ 75self.background_music = arcade.load_sound( 76 "sounds/Apoxode_-_Electric_1.wav" 77) 78 79# Load your sounds 80# Sound sources: Jon Fincher 81self.collision_sound = arcade.load_sound("sounds/Collision.wav") 82self.move_up_sound = arcade.load_sound("sounds/Rising_putter.wav") 83self.move_down_sound = arcade.load_sound("sounds/Falling_putter.wav") 84 85# Start the background music 86arcade.play_sound(self.background_music)

      現(xiàn)在,您不僅有音效,還有一些漂亮的背景音樂!

      arcade目前對聲音的作用有一些限制:

      任何聲音都沒有音量控制。

      無法重復聲音,例如循環(huán)播放背景音樂。

      在您嘗試停止之前,無法判斷當前是否正在播放聲音。

      沒有ffmpeg,您只能使用 WAV 聲音,它可能很大。

      盡管有這些限制,但為您的arcadePython 游戲添加聲音是非常值得的。

      Arcade:Python 游戲框架入門

      Python 游戲速度

      任何游戲的速度都取決于其幀速率,即屏幕上圖形更新的頻率。較高的幀率通常會帶來更流暢的游戲體驗,而較低的幀率則讓您有更多時間來執(zhí)行復雜的計算。

      arcadePython 游戲的幀速率由arcade.run().?Python 游戲循環(huán)調(diào)用.on_update()和.on_draw()大約每秒 60 次。因此,游戲的幀速率為每秒 60 幀或60 FPS。

      請注意,上面的描述說幀速率大約為60 FPS。不保證此幀速率是準確的。它可能會根據(jù)許多因素上下波動,例如機器上的負載或比正常更新時間更長的時間。作為 Python 游戲程序員,您希望確保您的 Python 游戲的行為相同,無論它以 60 FPS、30 FPS 或任何其他速率運行。那么你怎么做呢?

      想象一個物體以每分鐘 60 公里的速度在太空中移動。您可以通過將該時間乘以對象的速度來計算該對象在任何時間長度內(nèi)將行進的距離:

      該物體在 2 分鐘內(nèi)移動 120 公里,在半分鐘內(nèi)移動 30 公里。

      無論幀速率如何,您都可以使用相同的計算以恒定速度移動精靈。如果您以每秒像素為單位指定精靈的速度,那么如果您知道自上一幀出現(xiàn)以來已經(jīng)過去了多長時間,您就可以計算出它每幀移動了多少像素。你怎么知道?

      回想一下,.on_update()它采用單個參數(shù)delta_time.?這是自上次.on_update()調(diào)用以來經(jīng)過的時間量(以秒為單位)。對于以 60 FPS 運行的游戲,delta_time將是 1/60 秒或大約 0.0167 秒。如果將經(jīng)過的時間乘以精靈移動的量,那么您將確保精靈移動基于經(jīng)過的時間而不是幀速率。

      只有一個問題——既不接受Sprite.on_update()也不SpriteList.on_update()接受delta_time參數(shù)。這意味著無法將其傳遞給您的精靈來自動處理。因此,要實現(xiàn)此功能,您需要手動更新您的精靈位置。使用以下代碼替換對self.all_sprites.update()in的調(diào)用.on_update():

      def on_update(self, delta_time: float): """Update the positions and statuses of all game objects If paused, do nothing Arguments: delta_time {float} -- Time since the last update """ # If paused, don't update anything if self.paused: return # Did you hit anything? If so, end the game if len(self.player.collides_with_list(self.enemies_list)) > 0: arcade.play_sound(self.collision_sound) arcade.close_window() # Update everything for sprite in self.all_sprites: sprite.center_x = int( sprite.center_x + sprite.change_x * delta_time ) sprite.center_y = int( sprite.center_y + sprite.change_y * delta_time )

      在這個新的代碼,你手動修改每個精靈的位置,乘以.change_x與.change_y通過delta_time。這確保了精靈每秒移動一個恒定的距離,而不是每幀移動一個恒定的距離,這可以使游戲玩法更加流暢。

      當然,這也意味著您應該重新評估和調(diào)整所有精靈的初始位置和速度。回想一下位置,.velocity你的敵人精靈在創(chuàng)建時會給出:

      93def add_enemy(self, delta_time: float): 94 """Adds a new enemy to the screen 95 96 Arguments: 97 delta_time {float} -- How much time as passed since the last call 98 """ 99 100 # First, create the new enemy sprite 101 enemy = FlyingSprite("images/missile.png", SCALING) 102 103 # Set its position to a random height and off screen right 104 enemy.left = random.randint(self.width, self.width + 80) 105 enemy.top = random.randint(10, self.height - 10) 106 107 # Set its speed to a random speed heading left 108 enemy.velocity = (random.randint(-20, -5), 0)

      使用基于時間的新移動計算,您的敵人現(xiàn)在將以每秒 20 像素的最大速度移動。這意味著在 800 像素寬的窗口上,最快的敵人需要 40 秒才能飛過屏幕。此外,如果敵人從窗口右側(cè)八十個像素開始,那么最快的將需要整整四秒才能出現(xiàn)!

      調(diào)整位置和速度是使您的 Python 游戲變得有趣和可玩的一部分。首先將每個調(diào)整為 10 倍,然后從那里重新調(diào)整。對云以及玩家的移動速度也應進行相同的重新評估和調(diào)整。

      調(diào)整和增強

      在您的 Python 游戲設計過程中,您沒有添加一些功能。要添加到該列表中,您可能在 Python 游戲和測試過程中注意到了一些額外的增強和調(diào)整:

      當游戲暫停時,敵人和云彩仍然由預定的函數(shù)生成。這意味著,當游戲未暫停時,一大波游戲正在等著您。你如何防止這種情況發(fā)生?

      如上所述,由于arcade聲音引擎的一些限制,背景音樂不會重復。你如何解決這個問題?

      當玩家與敵人發(fā)生碰撞時,游戲會突然結束而不播放碰撞聲音。您如何在關閉窗口之前保持游戲打開一兩秒鐘?

      您可能還可以添加其他調(diào)整。嘗試將其中一些作為練習來實施,并在評論中分享您的結果!

      關于來源的說明

      您可能已經(jīng)注意到加載背景音樂時的評論,其中列出了音樂來源和知識共享許可的鏈接。這樣做是因為該聲音的創(chuàng)建者需要它。許可證要求規(guī)定,為了使用聲音,必須提供正確的歸屬和許可證鏈接。

      以下是一些音樂、聲音和藝術來源,您可以搜索有用的內(nèi)容:

      OpenGameArt.org:聲音、音效、精靈和其他藝術作品

      Kenney.nl:聲音、音效、精靈和其他藝術作品

      玩家藝術 2D:精靈和其他藝術作品

      CC Mixer:聲音和音效

      Freesound:聲音和音效

      當您制作游戲并使用從其他來源下載的內(nèi)容(例如藝術、音樂或代碼)時,請確保您遵守這些來源的許可條款。

      結論

      電腦游戲是對編碼的一個很好的介紹,arcade圖書館是一個很好的第一步。設計為用于制作游戲的現(xiàn)代 Python 框架,您可以創(chuàng)建具有出色圖形和聲音的引人入勝的 Python 游戲體驗。

      在本教程中,您學習了如何:

      安裝arcade庫

      在屏幕上繪制項目

      使用arcadePython 游戲循環(huán)

      管理屏幕上的圖形元素

      處理用戶輸入

      播放音效和音樂

      描述 Python 游戲編程arcade與pygame

      我希望你arcade試一試。如果你這樣做了,那么請在下面發(fā)表評論,祝 Pythoning 快樂!

      5G游戲 Python

      版權聲明:本文內(nèi)容由網(wǎng)絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權內(nèi)容。

      上一篇:求職的時候才知道“天高地厚”,準畢業(yè)生們看過來,這有一份求職指南!
      下一篇:談談我個人對大數(shù)據(jù)解決方案的一些理解
      相關文章
      亚洲精品成人在线| 337P日本欧洲亚洲大胆艺术图| 亚洲日韩亚洲另类激情文学| 亚洲另类春色校园小说| 亚洲视频免费观看| 亚洲综合一区二区精品久久| 911精品国产亚洲日本美国韩国| 久久久久久亚洲av成人无码国产| 亚洲av午夜成人片精品网站| 亚洲国产成人精品无码区在线观看| 亚洲精品无码国产| 亚洲av无码一区二区三区乱子伦| 国产精品久久久亚洲| 亚洲avav天堂av在线不卡| 久久久久亚洲精品天堂| 亚洲蜜芽在线精品一区| 亚洲国产韩国一区二区| 亚洲人成在线精品| 最新亚洲卡一卡二卡三新区| 国产亚洲高清不卡在线观看| 亚洲欧洲国产精品香蕉网| 久久精品国产精品亚洲艾草网美妙| 亚洲日韩国产成网在线观看| 亚洲日韩人妻第一页| 久久亚洲欧洲国产综合| 亚洲精品无码MV在线观看| 亚洲AV中文无码乱人伦下载| 亚洲人成网站在线播放影院在线| 亚洲视频在线观看免费| 亚洲男人天堂影院| 亚洲不卡影院午夜在线观看| 久久久久亚洲国产AV麻豆| 亚洲精品国产精品国自产观看| 亚洲精品无码成人片在线观看| 亚洲人成无码www久久久| 国产精品亚洲A∨天堂不卡| 久久亚洲AV成人无码国产| 亚洲国产成人精品青青草原| 亚洲男人天堂2018av| 色欲aⅴ亚洲情无码AV蜜桃| 亚洲人成网站观看在线播放|