• <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游戲開(kāi)發(fā) 之三

            Pygame游戲開(kāi)發(fā)之三

            初出茅廬

                     Pygame中除了Sprite,還有一個(gè)DirtySprite,它是由Sprite派生出來(lái)的繪制效率更高的精靈,較之Sprite多了以下幾個(gè)屬性:
            dirty = 1
                 如果設(shè)為1,則進(jìn)行重繪,并且重置為0

                    如果設(shè)為2,則一直重繪,并且永遠(yuǎn)不設(shè)為0
                    如果設(shè)為0表示不需要繪制

            blendmode = 0

                   混合模式,參見(jiàn)blit函數(shù)的最后一個(gè)參數(shù)

            source_rect = None

                   原圖的裁剪矩形,等同于我們之前定義的(self.offset, self.size)

                相對(duì)于self.image的左上角坐標(biāo)永遠(yuǎn)是(0,0)

            visible = 1

                是否需要被繪制

            我們可以發(fā)現(xiàn),source_rect和我們之前定義的(self.offset, self.size)的表示的意義是一樣的,于是可以將原來(lái)的RenderObject類(lèi)加以改進(jìn),如下:

            class RenderObject(pygame.sprite.DirtySprite) :

                framewait = 50

                images = []

                def __init__(self, selfdata) :

                    pygame.sprite.DirtySprite.__init__(self)

                    self.dirty        = 2

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

                    self.rect         = self.image.get_rect()

                    self.source_rect   = self.image.get_rect()

                    self.blendmode   = 0

                    self.visible       = 1

                    self.speed        = [0.0,0.0]

                    self.frame        = 0

                def move(self, xgo, ygo) :

                    if xgo or ygo :

                        self.rect.move_ip(self.speed[0] * xgo, self.speed[1] * ygo)

                def update(self) :

                    pygame.sprite.DirtySprite.update(self)

                    self.frame += 1

            并且讓RenderObject繼承pygame.sprite.DirtySprite,移除self.offsetself.size,而改用self.source_rect來(lái)代替,而且position變量也改用Sprite的成員變量rect來(lái)代替,這樣一來(lái)有個(gè)好處就是繪制函數(shù)不用我們?nèi)ゲ傩牧耍灰峁?/span>image(源Surface),source_rect(源Surface中需要繪制的區(qū)域),rect(目標(biāo)繪制在屏幕的矩形區(qū)域)以及一些輔助變量,繪制工作就由DirtySprite去做了。

                   這里有必要重新解釋一下Sprite的工作原理(如何被繪制以及如何進(jìn)行更新),Sprite有一個(gè)容器類(lèi)Group,pygame.sprite.Group是承載了很多Sprite的容器,它有以下一些方法供調(diào)用:

            Group.sprites()

                   返回所有該容器包含的Sprite的列表

            Group.copy()

                   返回一個(gè)包含當(dāng)前Group內(nèi)相同的SpriteGroup的新的實(shí)例。

            Group.add(*sprites)

                   添加任意數(shù)量的Sprite到這個(gè)Group內(nèi)。

            Group.remove(*sprites)

            移除任意數(shù)量的Sprite從這個(gè)Group內(nèi)。

            Group.has(*sprites)

                   如果給定的sprites都存在那么返回True,否則返回Flase,這個(gè)和in操作類(lèi)似(“if sprite in group: …”)。

            Group.update()

                   調(diào)用所有在當(dāng)前Group內(nèi)的Spriteupdate()函數(shù),注意每個(gè)Sprite都有一個(gè)update()函數(shù),如果某個(gè)類(lèi)繼承了Sprite,可以覆蓋這個(gè)函數(shù)進(jìn)行相應(yīng)的更新操作。

            Group.draw(Surface)

                   將當(dāng)前Group內(nèi)的所有的Sprite繪制到Surface上來(lái),注意這里需要用到Sprite.image來(lái)確定源SurfaceSprite.rect來(lái)確定繪制的位置(當(dāng)然,如果是DirtySprite還需要知道source_rect的值)。

            Group.clear(Surface_dest, bgd)

                   bgd繪制到Surface_dest上來(lái)。一般用于原窗口的背景繪制。

            Group.empty()

                   將所有的Sprite從這個(gè)Group中移除。

            如果有了Group,當(dāng)我們需要更新操作的時(shí)候,只需要將所有的Sprite都加到這個(gè)Group中,然后調(diào)用Group.update()就可以將所有在這個(gè)Group中的Sprite全部更新了,而不需要一個(gè)一個(gè)去調(diào)用Sprite.update(),這個(gè)操作大大便利了我們編程。

                   我們可以在RenderObject中添加一個(gè)update函數(shù)來(lái)覆蓋DirtySpriteupdate函數(shù),并且做一些我們需要做的工作,可以設(shè)定一個(gè)幀數(shù)self.frame,每次調(diào)用update函數(shù),幀數(shù)自增1,方便日后動(dòng)畫(huà)的播放。

                   舉個(gè)最簡(jiǎn)單的例子,我們通過(guò)pygame.sprite.Group()來(lái)創(chuàng)建一個(gè)Sprite的容器類(lèi),然后往里面添加我們的Sprite對(duì)象,并且不斷更新Sprite,將其繪制到屏幕上:

                   myGroup = pygame.sprite.Group()

                   myGroup.add( RenderObject([0]) )

                   myGroup.add( RenderObject([1]) )

                   while True :

                          myGroup.update()

                          myGroup.draw(screen)

                          pygame.display.update()

                   當(dāng)然Group的性質(zhì)是遞歸的,每個(gè)Group可以有多個(gè)子Group,形成一棵樹(shù)或者是一個(gè)森林,當(dāng)調(diào)用根Group的函數(shù)時(shí),它會(huì)將所有它的子孫的函數(shù)全部訪(fǎng)問(wèn)到,這樣一來(lái)只要我們?cè)葘⑺械挠螒蛟氐年P(guān)系用一棵樹(shù)的形式建立起來(lái),這樣每次更新或者渲染都只需要對(duì)根結(jié)點(diǎn)進(jìn)行操作了。

                   繼續(xù)舉例說(shuō)明:

                   Root = pygame.sprite.Group()

                   Son1 = pygame.sprite.Group()

                   Son2 = pygame.sprite.Group()

                   Son1.add(RenderObject([0]))

                   Son1.add(RenderObject([1]))

                   Son2.add(Player([13]))

                   Son2.add(Animation([2,12]))

                   Root.add(Son1)

                   Root.add(Son2)

                  

                   while True :

                          Root.update()

                          Root.draw(screen)

                          pygame.display.update()

                   這段代碼將所有的游戲元素(兩個(gè)普通物體和兩個(gè)Player)組織成一棵樹(shù),并且通過(guò)Root這個(gè)Group來(lái)管理所有的游戲元素的更新以及繪制(為了描述方便,主循環(huán)中將事件處理這一部分暫時(shí)去掉了)。

                   這樣就又帶來(lái)了一個(gè)問(wèn)題,游戲中的元素往往是很多的,比如有100只野怪,那么上面的代碼必然會(huì)出現(xiàn)至少100個(gè)類(lèi)似XXX.add(Monster([num]))的代碼,一來(lái)看起來(lái)很別扭,二來(lái)不好維護(hù),一旦需要變更一些關(guān)系就要大幅度修改代碼,這個(gè)是不可取的。

            于是我們還是學(xué)習(xí)上一節(jié)講到的將數(shù)據(jù)寫(xiě)到文件中,因?yàn)檫@里的數(shù)據(jù)是以樹(shù)狀結(jié)構(gòu)呈現(xiàn)的,所以數(shù)據(jù)的組織我借用了XML的文件格式(語(yǔ)法稍微有些不同,這樣寫(xiě)是為了省去一些不必要的操作),以下是其中一段元素的組織形式:

            <Group name=gameMgr selfdata=(-1,-1) pos=(0,0)>

                   <Group name=loginWnd selfdata=(-1,-1) pos=(0,0)>

                          <Animation name=cloud selfdata=(3,12) pos=(0,0)>

                          </Animation>

                          <Picture   name=title selfdata=(0,0)  pos=(20,80)>

                          </Picture>

                          <Menu      name=menu  selfdata=(1,1)  pos=(50,120)>

                          </Menu>

                   </Group>

            </Group>

            每一對(duì)<>之間表示一個(gè)游戲元素,第一個(gè)字段是當(dāng)前元素的類(lèi)名,便于創(chuàng)建的時(shí)候根據(jù)類(lèi)名來(lái)聲明類(lèi)的實(shí)例;第二個(gè)字段name是當(dāng)前實(shí)例的唯一標(biāo)識(shí);第三個(gè)字段imageidx記錄了當(dāng)前元素的SurfaceRenderObject.images[]中的始末索引;第四個(gè)字段pos表示在創(chuàng)建這個(gè)類(lèi)的實(shí)例的時(shí)候該image在屏幕的左上角坐標(biāo)。每一對(duì)<></>之間的元素是當(dāng)前元素的兒子,這樣一來(lái),最外層的表示的就是Root根結(jié)點(diǎn),層層之間建立父子關(guān)系就成了一棵樹(shù)。

                   接下來(lái)就是需要寫(xiě)一個(gè)Python模塊用于對(duì)當(dāng)前的文件進(jìn)行解析,將所有的游戲元素從文件中讀取并且保存到一個(gè)遞歸的列表中。

                   所謂遞歸的列表其實(shí)就是樹(shù)形列表,我們需要自定義一個(gè)這樣的類(lèi),每個(gè)類(lèi)的實(shí)例表示的是樹(shù)形列表的一個(gè)結(jié)點(diǎn),那么每個(gè)結(jié)點(diǎn)保存的信息有當(dāng)前結(jié)點(diǎn)的數(shù)據(jù)域data,當(dāng)前結(jié)點(diǎn)的孩子結(jié)點(diǎn)的集合sonlist,它又是一個(gè)列表,并且保存當(dāng)前結(jié)點(diǎn)的父親結(jié)點(diǎn)的指針以便回溯。并且分別給它們一個(gè)默認(rèn)值,每個(gè)結(jié)點(diǎn)的父親結(jié)點(diǎn)默認(rèn)為None,也就是C語(yǔ)言中的NULL,表示的是空指針。類(lèi)的實(shí)現(xiàn)如下:

            class myTree :

                def __init__(self, data) :

                    self.data    = data

                    self.sonlist = []

                    self.parent  = None

                def add(self, mytree) :

                    mytree.parent = self

                    self.sonlist.append(mytree)

             

                def output(self) :

                    print self.data

                    for xp in self.sonlist :

                        xp.output()

            添加兩個(gè)成員函數(shù)addoutput,add是為了添加兒子結(jié)點(diǎn),output是用來(lái)測(cè)試用的。

                   有了以上的樹(shù)形列表,我們就可以寫(xiě)自己的文件讀取器了,我把它取名為sXMLReadersimilar with XML),原因是和XML還是不盡相同的。

            class sXMLReader :

            def __init__(self, filename) :

                   do_init()

            def parse(self, string) :

                   do_parse()

            def next(self, nowline) :

                   next_line()

            do_init()主要做三件事情,讀取文件并把所有的行保存到self.filelist中,然后對(duì)每一行進(jìn)行一個(gè)預(yù)處理(主要是去掉串前后的空格和Tab還有回車(chē)等沒(méi)用的字符),最后進(jìn)行遞歸的訪(fǎng)問(wèn)next函數(shù)進(jìn)行讀行并且建立關(guān)系樹(shù)。

            self.root = myTree([])

            self.nowroot = self.root

            self.next(0)

            首先建立一個(gè)空的根結(jié)點(diǎn)root,然后將當(dāng)前結(jié)點(diǎn)指針nowroot指向它,self.next(0)表示當(dāng)前訪(fǎng)問(wèn)第0行,next函數(shù)的操作比較簡(jiǎn)單,如下:

            def next(self, nowline) :

            if nowline == len(self.filelist) :

            return

                   process_this_line()

            if matchPre :

            self.nowroot = self.nowroot.parent

            self.next(nowline+1)

            else :

            son = myTree(nownode)

            self.nowroot.add(son)

            self.nowroot = son

            self.next(nowline+1)

                   首先判斷當(dāng)前行是不是越界,如果越界說(shuō)明無(wú)需訪(fǎng)問(wèn)。否則處理這一行的文字內(nèi)容,這是由process_this_line()來(lái)完成的,要根據(jù)你自己設(shè)定的語(yǔ)法來(lái)處理,就不再累述了。最后一部分才是關(guān)鍵,matchPre用于判斷當(dāng)前這一行是不是和前面某一行進(jìn)行匹配,如果匹配成功(也就是說(shuō)當(dāng)前行是類(lèi)似</XXX>的形式),那么nowroot的指針指向它的parent,然后繼續(xù)訪(fǎng)問(wèn)下一行;否則,生成一個(gè)新的結(jié)點(diǎn),并且把這個(gè)結(jié)點(diǎn)添加到當(dāng)前的nowroot結(jié)點(diǎn)中,然后更新nowroot為這個(gè)新加入的結(jié)點(diǎn),繼續(xù)訪(fǎng)問(wèn)下一行。其實(shí)整個(gè)過(guò)程就是一個(gè)棧,遇到<>好比入棧,遇到</>就好比出棧。

                   文件讀取寫(xiě)完后,我們只需要調(diào)用:

            gameItem = sXMLReader('ctrl_config.sxml')

                   gameItem.root 就保存了游戲元素樹(shù)的根結(jié)點(diǎn),通過(guò)遞歸訪(fǎng)問(wèn)就可以得到所有的游戲?qū)傩裕?/span>GroupSprite將所有游戲元素構(gòu)造出來(lái)即可,具體參見(jiàn)以下代碼:

            def CreateGameItem(node) :

                group = None

                if node.classname() == 'Group':

                    group = pygame.sprite.Group()

                else :

                    classname = node.classname()

                    selflist = node.find('selfdata')

                    render = renderobj.CreateClassByName(classname, selflist)

                    render.setpos(int(node.find('pos')[0]), int(node.find('pos')[1]))

                    return render

                for son in node.sonlist :

                    group.add( CreateGameItem(son) )

            return group

                   根據(jù)node.classname()得到當(dāng)前結(jié)點(diǎn)是容器類(lèi)還是普通的Sprite類(lèi),如果不是容器那么設(shè)置相關(guān)屬性,然后返回當(dāng)前結(jié)點(diǎn)信息;如果是容器類(lèi)則創(chuàng)建一個(gè)Group并且遍歷它的子結(jié)點(diǎn),將所有子結(jié)點(diǎn)加入到當(dāng)前容器中。遍歷完畢后這棵樹(shù)就創(chuàng)建完畢了。(未完待續(xù))

            posted on 2011-04-26 01:07 英雄哪里出來(lái) 閱讀(3774) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): Pygame

            国产精品福利一区二区久久| 一本一本久久a久久综合精品蜜桃| 久久偷看各类wc女厕嘘嘘| 久久66热人妻偷产精品9| 亚洲狠狠久久综合一区77777| 国产午夜精品久久久久九九电影| 无码乱码观看精品久久| 77777亚洲午夜久久多喷| 国产精品gz久久久| 亚洲AV无码成人网站久久精品大| 国产99久久久久久免费看| 一本色道久久HEZYO无码| 99久久国产综合精品五月天喷水 | 久久婷婷五月综合成人D啪| 久久精品欧美日韩精品| 久久免费香蕉视频| 国产成人精品白浆久久69| 久久综合久久综合亚洲| 91精品国产高清91久久久久久| 亚洲午夜无码久久久久小说| 亚洲欧美精品伊人久久| 欧美午夜精品久久久久免费视| 一级做a爰片久久毛片毛片| 国产一区二区三区久久精品| 亚洲人成精品久久久久| 中文字幕精品无码久久久久久3D日动漫 | 亚洲国产天堂久久综合| 精品久久人妻av中文字幕| 亚洲精品国精品久久99热一| 亚洲午夜福利精品久久| 久久国产精品偷99| 久久99精品久久久久久噜噜| 国产精品毛片久久久久久久| 国产V亚洲V天堂无码久久久| 久久超碰97人人做人人爱| 国内精品人妻无码久久久影院| 亚洲人成伊人成综合网久久久| 久久综合综合久久综合| 久久这里只有精品18| jizzjizz国产精品久久| 九九精品99久久久香蕉|