• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 97, 文章 - 22, 評(píng)論 - 81, 引用 - 0
            數(shù)據(jù)加載中……

            Pygame游戲開發(fā) 之二

            Pygame游戲開發(fā)之二

            初窺門徑

            游戲中需要用到很多對(duì)象,比如人物、野怪、建筑物、UI控件等等,他們的行為不盡相同,但是總有那么些共同點(diǎn),如果將所有的東西都封裝成各自的類難免會(huì)寫上很多重復(fù)的代碼,因?yàn)閮蓚€(gè)對(duì)象如果具有相同的行為(比如人物和野怪都可以行走)如果寫兩個(gè)move()函數(shù),以后改起來就要修改兩個(gè)地方,不容易維護(hù)而且擴(kuò)展很麻煩,但是人物和野怪又有其它不相同的行為,比如技能之類的不同。所以比較好的處理方法是抽象出一個(gè)生物類,然后讓人物和野怪去繼承它,舉個(gè)例子:

            class RenderObject(pygame.sprite.Sprite) :

                # 靜態(tài)變量

                images = []

                def __init__(self, selfdata) :

                    self.image       = self.images[ int(selfdata[0])  ]

                    self.size         = [self.image.get_width(), self.image.get_height()]

                    self.offset       = [0,0]

                    self.position     = [0,0]

                    self.speed       = [0.0,0.0]

                def move(self, xgo, ygo) :

                    do_move()

                def draw(self, screen) :

                    do_draw()

            以上這個(gè)類RenderObject是自己定義的用于渲染的類的基類,它繼承自pygame.sprite.Sprite,這里有必要先介紹一下pygame.sprite,這個(gè)模塊在pygame中是選擇性使用的,他可以作為游戲中很多對(duì)象的基類,并且將它擁有的東西繪制到Surface上,還有一個(gè)Group類是Sprite類的容器類,可以包含很多Sprite。

                   RenderObject有三個(gè)類函數(shù),__init__Python中的特殊函數(shù),類似C語(yǔ)言中的構(gòu)造函數(shù),當(dāng)類實(shí)例被創(chuàng)建的時(shí)候,這個(gè)函數(shù)會(huì)被自動(dòng)執(zhí)行,它的目的是執(zhí)行一些該對(duì)象的必要的初始化工作(我在這個(gè)函數(shù)中加入了一個(gè)selfdata的列表,以便于不同類型的控件添加初始化信息)。move函數(shù)是執(zhí)行移動(dòng)操作,而draw函數(shù)則負(fù)責(zé)渲染。

                   首先來看下__init__函數(shù),在Python中使用變量不需要提前聲明,所以要用到的變量我把它放在了__init__函數(shù)中進(jìn)行初始化,注意到每個(gè)函數(shù)都有一個(gè)self參數(shù),這個(gè)就好比C語(yǔ)言中的this指針,但是它是顯式聲明,隱式調(diào)用的。在使用類內(nèi)部變量的時(shí)候需要加self做修飾,我們可以看到該類的一些成員變量:

            self.image      # 該渲染對(duì)象對(duì)應(yīng)的Surface

            self.size        # 該渲染對(duì)象在Surface的矩形區(qū)域的寬高

            self.offset       # 該渲染對(duì)象在Surface的左上角坐標(biāo)

            self.position     # 該渲染對(duì)象將被繪制到屏幕的位置

            self.speed       # 該渲染對(duì)象的速度

                   之所以要用到offsetsize,是因?yàn)樵?/span>Surface不是完全被用來顯示的,我們只取某一張Surface的某一部分用于顯示,如果不是很明白,那么參看Player類的相關(guān)描述就知道為什么了。

                   對(duì)于move(self, xgo, ygo)函數(shù),我們暫時(shí)可以這樣實(shí)現(xiàn):

            if xgo :

            self.position[0] += self.speed[0] * xgo

            if ygo :

            self.position[1] += self.speed[1] * ygo

            xgoygo都是三值變量(1,0,-1),分別表示在兩個(gè)坐標(biāo)軸方向是否發(fā)生了位移,并且這個(gè)位移是正向的還是逆向的。于是我們可以通過鍵盤輸入來傳遞對(duì)應(yīng)的參數(shù),position的值就會(huì)隨之變化了。注意如果這里速度為0,則表示靜態(tài)物體,position不會(huì)做任何變化。

            對(duì)于draw(self, screen)函數(shù),用于將當(dāng)前對(duì)象繪制到屏幕上,screen參數(shù)是上一節(jié)中提到的屏幕Surface,我們調(diào)用Surfaceblit函數(shù)就可以在屏幕上繪制當(dāng)前對(duì)象了,像這樣:

            screen.blit( self.image, self.position, (self.offset, self.size))

            (self.offset, self.size)表示self.image這個(gè)Surface上對(duì)應(yīng)的矩形區(qū)域。

            基類完成后,我們需要派生出一些子類,例如:

            class Player(RenderObject) :

                actionwait = 50

                def __init__(self, selfdata) :

                    RenderObject.__init__(self, selfdata)

                    self.size[0] /= 4

                    self.size[1] /= 4

                    self.speed = [0.1,0.1]

                    self.direction=3

                    self.action=0

                def move(self, xgo, ygo) :

                    RenderObject.move(self, xgo, ygo)

                    if xgo or ygo :

                        self.action = (self.action + 1) % (self.actionwait * 4)

                    self.offset[0] = (self.action / self.actionwait) * self.size[0]

                    self.offset[1] = self.direction * self.size[1]

             Player類作為游戲中的操作玩家,繼承自RenderObject,并且重寫它的__init__move函數(shù),引入directionaction變量分別表示當(dāng)前Player的方向和動(dòng)作,如圖2-1(資源來自66RPG,一個(gè)基于RPG Maker xp業(yè)余游戲制作的交流、合作、分享的網(wǎng)站,在此感謝66RPG為我們游戲開發(fā)者提供這么多的資源下載,淚流滿面啊||*_*||)。

            2-1

            該圖是游戲人物的四方向行走圖,每一行代表一個(gè)方向,每一列代表一個(gè)動(dòng)作,我們只需要通過鍵盤或者鼠標(biāo)的輸入事件不斷改變offset的值,就可以順利得進(jìn)行人物動(dòng)畫的切換。這就是為什么之前要引入offsetsize的原因了,因?yàn)檫@個(gè)對(duì)象只取當(dāng)前圖片的某一部分用于顯示,而且每次是根據(jù)人物不同的狀態(tài)進(jìn)行切換的,還可以發(fā)現(xiàn)Player類中引入了一個(gè)actionwait的靜態(tài)變量,之所以用到它是因?yàn)楝F(xiàn)在的電腦的處理速度實(shí)在是太快了,每次move調(diào)用,我們會(huì)將self.action的值+1,并且循環(huán)往復(fù),如果沒有一個(gè)延時(shí)操作,人物的動(dòng)作切換會(huì)非???,顯得十分不自然。

            同時(shí)可以根據(jù)自己的喜好派生出很多不同類型的游戲元素,比如野怪、按鈕、菜單等等。

             

            說到這里,還沒講到images這個(gè)靜態(tài)變量。RenderObject中定義了一個(gè)靜態(tài)列表,表示所有圖片的Surface對(duì)象的集合,我們可以在游戲載入的時(shí)候?qū)⑺械膱D片都轉(zhuǎn)化成Surface,然后每次調(diào)用的時(shí)候直接在images中尋找對(duì)應(yīng)的Surface即可,這樣就不用每次都調(diào)用load_image函數(shù)了。

            但是還有個(gè)問題,比如我現(xiàn)在有以下幾張圖片:hero.pnggo.png、title.png、menu.png,那么不免要寫下面這段代碼:

            Imgs = []

            Imgs.append( load_image( ‘hero.png’) )

            Imgs.append( load_image( ‘go.png’) )

            Imgs.append( load_image( ‘title.png’) )

            Imgs.append( load_image( ‘menu.png’) )

            RenderObject.images = Imgs

            append是列表的函數(shù),表示在已有列表中加入一個(gè)新的成員。這樣就可以把所有圖片預(yù)先載入到RenderObject.images列表中,以后訪問的時(shí)候只需要用下標(biāo)索引即可。這也就是問題所在,因?yàn)楝F(xiàn)在圖片只有四張,這個(gè)代碼沒什么大的問題,但是如果圖片的數(shù)量增加到四十張、乃至四百?gòu)?,那么問題也就隨之而來了,你將不斷地添加同一段代碼Imgs.append( load_image( ‘XXX’) ),這會(huì)使程序日漸冗長(zhǎng),而且每次新增一張圖片就需要添加一段代碼,某張圖片不需要了,有需要找到那段代碼將其刪除,維護(hù)會(huì)變得越來越繁瑣。

            圖片的文件名其實(shí)是一系列的字符串?dāng)?shù)據(jù),我們大可以把它放到配置文件中,然后利用Python的文件讀取接口來將它們讀入,這樣以后需要添加一張圖片的時(shí)候只需要在配置文件中加入一個(gè)文件名即可,方便了許多。

            我把所有的圖片的文件名放到了一個(gè)叫image_config.txt的文件中,具體的結(jié)構(gòu)組織可以看個(gè)人喜好,以下是我的組織形式:

             

            /Control

            title 1

            menu 1

            /Player

            hero 1

            monster 0

            /Animation

            cloud 10

             

            ’/’開頭的表示文件夾,比如/Control表示在Control文件夾下有兩種類型的文件(titlemenu),否則是某個(gè)文件的前綴加上當(dāng)前類型文件的數(shù)量,比如title 2表示的其實(shí)是title-0.pngtitle-1.png這兩個(gè)文件,cloud 4則是cloud-0.png、cloud-1.png、cloud-2.pngcloud-3.png這四個(gè)文件。之所以要用編號(hào)的形式,是因?yàn)檫@些圖片有可能是一個(gè)動(dòng)畫,又或許是一類相同的對(duì)象,這樣一來添加新圖片的時(shí)候會(huì)方便許多。

                   然后就是按照你自己定的配置文件格式進(jìn)行解析,從而將圖片進(jìn)行載入。以下是我的圖片載入過程,我把它放在了InitializeImage函數(shù)中:

            def InitializeImage() :

                config = open('image_config.txt', 'r')

                config.seek(0)

                filelist = config.readlines()

                config.close()

                nowfolder = ''

                imgs      = []

                for filename in filelist :

                    if len(filename) < 2:

                        continue

                    if filename[0] == '/' :

                        nowfolder = filename[1:-1]

                    else :

                        idx = filename.find(' ')

                        pre = filename[:idx]

                        for x in range(  int(filename[idx+1:-1])  ) :

                            pfile = nowfolder + '\\' + pre + '-' + str(x) + '.png'

                            print pfile

                            imgs.append( data.load_image(pfile) )

                RenderObject.images = imgs

                   open函數(shù)是Python中的文件讀取接口,類似C語(yǔ)言中的fopen,參數(shù)也是一樣的,返回的則是一個(gè)文件對(duì)象,我們首先調(diào)用seek(0)將文件頭指針定位到文件開頭,然后將該文件中所有的行通過readlines()讀入到filelist列表中,并且關(guān)閉文件。這時(shí)候filelist就儲(chǔ)存了該配置文件中所有的圖片文件信息,接下來的步驟是對(duì)它進(jìn)行解析。

                   通過遍歷filelist的每個(gè)成員,判斷當(dāng)前的這一行表示的是文件夾信息還是圖片文件信息。如果是文件夾則更新nowfoldernowfolder表示當(dāng)前文件夾。filename[1:-1]表示不包含第一個(gè)字符和最后一個(gè)字符的子串(-1是最后一個(gè)字符的索引),之所以不要最后一個(gè)字符是因?yàn)樽x入的時(shí)候是通過讀行的,所以最后一個(gè)字符在Win32下是\n也就是換行;如果是圖片文件信息,那么必定是類似 XXX num 的串,那么我們首先要找到這個(gè)串中的空格(這里需要保證文件名中不包含空格),通過字符串內(nèi)置的find(‘ ‘)函數(shù)可以找到第一個(gè)出現(xiàn)空格的字符的下標(biāo),然后我們就可以把這個(gè)串分成兩部分了,將第二個(gè)部分也就是找到的下標(biāo)到結(jié)尾的子串,將它轉(zhuǎn)換成int類型,這就是當(dāng)前類型圖片的數(shù)量,最后用一個(gè)for循環(huán)將所有當(dāng)前類型的圖片創(chuàng)建出來。一個(gè)個(gè)appendimgs上去即可。

                   再回頭來看RenderObject.__init__(self, selfdata)函數(shù),該函數(shù)中還有selfdata變量,我把它定義成一個(gè)列表,不同的子類將會(huì)擁有各自不同的列表元素和個(gè)數(shù),就好比對(duì)于動(dòng)畫Animation類來說,它的值就是[st, en],表示當(dāng)前動(dòng)畫需要表示的始末圖片在images中的索引編號(hào)。

                   Smpl = Animation([3, 12])

                   可以簡(jiǎn)單得通過以上的語(yǔ)句,就構(gòu)造出一個(gè)Animation的對(duì)象,并且可以通過Smpl訪問它所有的成員變量來進(jìn)行初始化對(duì)象的起始位置以及其它信息。這里的動(dòng)畫指的是最普通的動(dòng)畫,是通過不同圖片的循環(huán)播放來實(shí)現(xiàn)的,以上的實(shí)例就是一個(gè)擁有10張圖片的動(dòng)畫。(未完待續(xù))

                以上內(nèi)容均為原創(chuàng) 轉(zhuǎn)載請(qǐng)注明出處

             

            posted on 2011-04-24 19:55 英雄哪里出來 閱讀(6182) 評(píng)論(3)  編輯 收藏 引用 所屬分類: Pygame

            評(píng)論

            # re: Pygame游戲開發(fā) 之二  回復(fù)  更多評(píng)論   

            留爪,持續(xù)關(guān)注
            2011-04-24 23:11 | zhaoyg

            # re: Pygame游戲開發(fā) 之二  回復(fù)  更多評(píng)論   

            @zhaoyg
            閣下在學(xué)Pygame嗎?
            2011-04-25 10:12 | 英雄哪里出來

            # re: Pygame游戲開發(fā) 之二  回復(fù)  更多評(píng)論   

            不錯(cuò)不錯(cuò),期待更新
            2011-04-27 00:44 | 賊寇在何方
            99久久99久久精品国产| 亚洲精品乱码久久久久久蜜桃不卡 | 99久久精品国产综合一区| 国产福利电影一区二区三区久久老子无码午夜伦不 | 无码任你躁久久久久久| 久久久久亚洲精品日久生情| 国产亚洲精品自在久久| 久久综合五月丁香久久激情| 人妻无码中文久久久久专区| 国产免费久久精品99久久| 男女久久久国产一区二区三区 | 国产精品免费看久久久| 欧美午夜A∨大片久久| 久久精品无码午夜福利理论片 | 久久亚洲AV无码西西人体| 久久精品国产亚洲AV香蕉| 久久一区二区免费播放| 久久er热视频在这里精品| 丁香色欲久久久久久综合网| 四虎久久影院| 无码人妻久久一区二区三区蜜桃| 久久国产成人精品麻豆| 久久综合狠狠综合久久综合88| 久久精品国产一区二区电影| 国产成人无码精品久久久久免费 | 无码专区久久综合久中文字幕| 久久亚洲中文字幕精品一区四| 国产精品成人99久久久久| 久久青青草原综合伊人| 亚洲国产二区三区久久| 韩国三级大全久久网站| 成人妇女免费播放久久久| 成人国内精品久久久久影院| 久久久一本精品99久久精品88| 狼狼综合久久久久综合网| 亚洲va久久久噜噜噜久久天堂| 亚洲精品无码久久千人斩| 久久99亚洲网美利坚合众国| 国产美女久久精品香蕉69| 久久这里只有精品久久| 国产精品九九久久免费视频 |