python製做galgame引擎(五)

  姍姍來遲的續篇不是麼(笑)。嗯哼,不要在乎上一篇充滿憂鬱的離別氣氛~~~python

  這一篇是新增語法的設計和解釋,分別是立繪的繪製(渲染?或許用render更靠譜?)語法和跳轉指定幀的語法。前者很麻煩,後者很簡單。git

  所謂人物立繪,galgame遊戲中,就是人物的肖像,通常來講就是對話對方的肖像。非gal玩家估計比較難想象……有興趣的話能夠查一下相關定義。順便,雖然不是gal,可是東方的zun繪,依然是人物立繪……github

  值得注意的是,人物立繪出現的頻度很頻繁,這就要求語法定義必需要很簡單。可是問題是,立繪的渲染自己須要的參數就不少,這算是一個不大不小的矛盾。數據結構

  若是對立繪參數要求不少這一點有異議,請仔細考慮一下。人物立繪的渲染,到底須要哪些參數:本地人物圖片的右下角(切割圖片位置),縮放大小,屏幕位置。對吧,至少須要三個參數。爲了簡便起見,後面也只考慮這三個參數。app

  考慮需求的頻度較高,使用[xxxxxx]語法就過於麻煩了,這裏我使用{}來標明立繪的語法域。函數

  由於立繪有幾個特徵:一樣的圖片能夠在畫面移動(用來表現走路啊什麼的);一樣大小的圖片,雖然位置不動,可是圖片自己改變了(人物表情變化);圖片和圖片位置(中心位置)不改變,大小變化(人物由遠及近)。處於便捷性考慮,每次都填寫一樣的東西並不爽,因而容許缺失值,缺失值會直接使用以前一幀的值,這樣的思路要好一點。不太好理解?請看一下我定義的語法吧:spa

 

{'sanae.png';1.(600,600);2.(200,200);3.(200,200)}設計

{'sanae.png';1.(400,600);3.(100,200)}code

 

  第一行是初始定義,格式 {portrait_name;1.pos;2.pos;3.pos}orm

  語法域爲{}以內,分號分割參數。參數是四個,分別爲:圖片的名字;原圖的切割位置(右下角座標);圖片在屏幕上的大小;圖片在屏幕上的位置。估計你也注意到了,有詭異的1,2,3的標號----抱歉,這個不能少,這個是用來肯定後面的值是屬於哪一個參數。1就是切割位置,2是大小,3是屏幕位置,這樣。

  初始化的時候一個都不能少,不然會拋出異常。

  第二行是有缺失項的狀況,也就是2那項不存在,這種狀況下,程序會沿用前一次的(200,200)。

   

  由於單幀中,立繪人數不必定是一,因此也支持多行解析,好比這樣:

 

{'sanae.png';1.(600,600);2.(200,200);3.(200,200)

'sanae1.png';1.(600,600);2.(300,300);3.(0,0)}

 

  請嚴格遵守格式,一點都不能錯的……恩,由於個人解析器寫得很傻。

  若是圖片名字後面不跟任何參數的話,這幅圖片會被移除,好比這樣:

   

{'sanae1.png';

'sanae.png';}

 

  使用上面的語法,sanae1.png 和sanae.png 就會被移除。嗯哼,即便沒有sanae1.png也不要緊,沒什麼問題的。

  若是多幅圖片都但願無腦移除的話,{}語法能夠拯救你。

   

{}

 

  會移除全部的人物立繪。

   

  上面就是人物立繪的語法,很麻煩。事實上,程序也很麻煩。我使用的數據結構是一個字典的字典,頓時以爲坑爹了?基本相似於這樣:

   

 {'backgound.jpg': {'clip_pos': (100, 100), 'screen_pos': (212, 54), 'name': 'aa.jpg', 'size': (121, 44)}, 'gg.jpg': {'clip_pos': (100, 100), 'screen_pos': (212, 54), 'name': 'aa.jpg', 'size': (121, 44)}, 'aa.jpg': {'clip_pos': (100, 100), 'screen_pos': (212, 54), 'name': 'aa.jpg', 'size': (121, 44)}}

 

  看着很繞吧?其實我也看着繞,可是我這裏沒什麼更好的思路,若是有更好地思路,請務必at我。這個字典正如上面所說是一個嵌套字典,外層的key是每幅圖片的名稱,用來區分不一樣圖片。value是解析出來的值。內層的話,字典是這樣{clip_pos': (100, 100), 'screen_pos': (212, 54), 'name': 'aa.jpg', 'size': (121, 44)},key分別是幾個參數的名稱,value是對應的值。

   

  嘛,就這樣,上面就是定義的語法和使用的數據結構,下面是代碼,沒什麼可說的:

   

Parser部分:

## 初始化正則式
    self.RPPortrait = self.__InitReParserPortrait()
    ##正則式匹配出立繪的語法域
    def __InitReParserPortrait(self):
        pat = r'\{(.*?)\}'
        return re.compile(pat,re.DOTALL)
        
    ## parser方法中增長下面內容
    if self.RPPortrait.search(target):
        target = self.RPPortrait.search(target).group(1)
        if target:
            self.Portrait = self.__parserPortrait(target)
        else:
            self.Portrait = {}
        
    ## 用來解析的函數
    def __parserPortrait(self,target):
        portraits = {}
        target = target.split('\n')
        for element in target:
            portrait = {}
            element = element.split(';')
            ## The way of using eval is dangerous
            ## pray?
            portrait['name'] = eval(element[0])
            ## add the flag which means
            ## the image will remove
            if element[1] == '':
                portrait['flag'] = True
            else:
                for i in element[1:]:
                    i = i.split('.')
                    assert len(i) == 2
                    if int(i[0]) == 1:
                        portrait['clip_pos'] = eval(i[1])
                    elif int(i[0]) == 2:
                        portrait['size'] = eval(i[1])
                    elif int(i[0]) == 3:
                        portrait['screen_pos'] = eval(i[1])
                    else:
                        print 'Wrong Gammer.Please check %d' % self.NodeIndex  
                        raise SystemExit
            portraits[portrait['name']] = portrait
        return portraits

 

NodeItem部分

## 說明,我這裏改寫了載入圖片的方法,以求得複用 
    def __updateImage(self,name,colorkey=None,clip_pos = None,size = None):
        if clip_pos and size :
            fullname = os.path.join('PORTRAIT',name)
        elif not clip_pos and not size:
            fullname = os.path.join('BG',name)
            self.BGName = name
        else:
            raise SystemExit,message
        try:
            image = pygame.image.load(fullname)
        except pygame.error,message:
            print 'Cannot load image:',name
            raise SystemExit, message
        image = image.convert()
        if clip_pos:
            image = image.subsurface((0,0),clip_pos)
        if size:
            image = pygame.transform.scale(image,size)
        else:
            image = pygame.transform.scale(image,(SCREENWIDTH,SCREENHEIGHT))
        if colorkey is not None:
            if colorkey is -1:
                colorkey = image.get_at((0,0))
            image.set_colorkey(colorkey, RLEACCEL)
        return image

    ## 渲染圖片到屏幕上
    def __updatePortrait(self,portraits):
        if portraits == {}:
            self.Portraits = {}
        else:
            ## update self.Portraits
            for key in portraits:
                if key not in self.Portraits:
                    temp_dict = {}
                    temp_dict[key] = portraits[key]
                    self.Portraits.update(temp_dict)
                else:
                    for sec_key in portraits[key]:
                        self.Portraits[key][sec_key] = portraits[key][sec_key]
            ## delete items which isn't contained portraitz
                ## list production can't apply the exceptions
                ##delete_key = [i for i in self.Portraits.keys() if self.Portraits[i]['flag']]
            delete_key_list = []
            for i in self.Portraits.keys():
                try:
                    if self.Portraits[i]['flag']:
                        delete_key_list.append(i)
                except KeyError:
                    pass
            for i in delete_key_list:
                self.Portraits.pop(i)

 

  固然,你總得知道,上面的都不是完整的代碼,完整的代碼在github上,我仍是再貼一遍吧 github

   

  上面是人物立繪相關……挺難的不是麼?下面的很簡單,強制跳入指定一幀……語法是[next]

  恩,隨手寫個示例吧:

   

[next=21]

   

  這樣就強制跳到21幀的位置了,這個語法在你寫錯了index的時候頗有用……後面的save和load的實現上,也使用了這個方式。代碼很短。

   

Parser部分:

def __InitReParserNextIndex(self):
        pat = r'''^\[next\s*?=\s*?(\d+?)\]$'''
        return re.compile(pat,re.M)

 

NodeItem部分

def __updateNextIndex(self,next_index):
        if next_index:
            self.NextIndex = next_index
            
    def setNextIndex(self,index = -1):
        if index == -1:
            self.NextIndex = self.Index + 1
        else:
            self.NextIndex = index

 

  後面那個setNextIndex是支持外部設定nextindex,後面頗有用。

  就這樣吧,寫起來實話說真費力……順便,個人博客意外發現能夠直接用https的方法登陸,免得FQ了,真好真好。宣傳一下:早苗的空想庭院,歡迎圍觀~~~

相關文章
相關標籤/搜索