python製做galgame引擎(三)

  正如上一篇所見,試驗代碼至關醜陋,效果也極度不堪……並且內部代碼所有暴露,甚至沒有一個接口,增添功能時也必然是傷筋動骨。因而,這一篇的目標是:python

  1.對代碼進行封裝,而且代碼中不能出現常量或常量字符。算法

  2.增長異常的處理----主要是pygame的異常編程

   

  提及來,對異常貌似有一個討論,出自joel?不太記得了,大意是異常不是必要的編程元素。對於這一點,我持保留態度,我的認爲引入異常能使得錯誤更容易被定位,給用戶的提示也更加友好,還能夠經過引入各類異常來提供某些特定的功能。請你們也仁者見仁。說到joel,那本《More Joel on Software》仍是至關不錯的一本書,適合吃飯後讀着玩,由於其至關易讀。Joel的話,這裏有一篇他的文章,真的推薦看看:個人七個建議函數

  貌似我老是習慣性離題?字體

  恩恩,封裝的話,得先明確封裝那些元素。對galgame來講,這卻是一個易於回答的問題,而就目前的進度而言,更是一個易於回答的問題:須要封裝的元素是 背景圖片,背景音樂,劇情文本。編碼

  Ok,那開始吧。spa

  首先,爲這個封裝類起一個恰當的名字?我是隨手用了NodeItem這個名字----估計是Sakia的影響……說到Sakia,guochaoer君在定義的時候,把文本,圖片,音樂都各自用一個類來封裝起來,而且都繼承自pygame.Sprite.sprite。這固然是一種不錯的思路,很清晰。可是我以爲,事實上文本,圖片和音樂都只是字符串,大能夠簡單地處理處理完事。並且,一般來講galgame都是靜態的,繼承Sprite類也沒什麼益處。因此我是直接使用object這個超類的。嘛,總之,各有各的想法。我這邊的話,直接寫代碼,就是這樣:code

   

# -*- coding: utf-8 -*-
import pygame
from pygame.locals import *
import os

## 這些都是須要使用到的一些常數,具體的意思,看看就明白了
SCREENWIDTH = 800
SCREENHEIGHT = 600
TEXTRECTHEIGHT = 160
LINENUMBER = 35 
OFFY = 10
OFFX = 35
VSIZE = 30
ALPHA = 180

class NodeItem(object):

    ## 由於得最後綁定到遊戲屏幕上,因此類初始化的時候,
    ## 就能夠傳入一個對screen這個surface的引用
    ## 就我試驗的結果的話,python傳遞類是實參傳遞,因此大可放心
    ## 之因此初始化這些變量,是爲了能保存值,畢竟傳來的某項是有
    ## None的可能性的,當該項爲None是,意味着使用之前的值
    ## 就是這樣。這種方式免去了每次書寫相同BG或BGM的麻煩
    def __init__(self,surface):
        self.BGName = ''
        self.BGMName = ''
        self.Text = ''
        
        
        ## 這下面幾行都和文本顯示有關。TextBox是文本顯示的容器
        ## 就galgame效果來講,就是文本顯示時那個半透明的文本框
        ## TextBox其實由於有半透明這個緣由,我是單獨放到一個函數
        ## 裏面去實現的。
        ## Font的話,其實也須要考慮不少東西,還要考慮異常。因此
        ## 我也是單獨用一個函數去實現。
        ## 順便一說,python函數的代價其實挺高的,可是考慮到這個
        ## 程序計算不密集,資源消耗也不嚴重,因此放心使用就好。
        ## TextBoxRect是TextBox的區域,TextBoxPos固然是位置
        ## 固然,是TextBox左上角的座標。貌似通常都是
        ## fgColor是控制文字的顏色,bgColor是文本框的顏色
        
        self.TextBox , self.TextBoxRect = self.__initTextRect()
        self.TextBoxPos = (0,SCREENHEIGHT-TEXTRECTHEIGHT)
        self.Font = self.__initFont()
        self.bgColor = ((0x00,0x00,0x00))
        self.fgColor = ((0xFF,0xFF,0xFF))
        
        ## 綁定傳入的surface
        self.Surface = surface
       
    ## 對TextBox的初始化
    def __initTextRect(self,colorkey = ALPHA):
        size = (SCREENWIDTH,TEXTRECTHEIGHT)
        TextRect = pygame.Surface(size)
        
        ## 文本框的底色……我通常習慣黑色爲底,這樣設置
        ## 透明以後很好看,你喜歡的話,能夠設置成其餘的
        TextRect.fill(self.bgColor)
        
        ## 這裏設置文本框的透明度
        ## 180這是我以爲比較合適的值,能夠本身多試試,
        ## 若是傳入None的話,就是隻有底色,恩,有人
        ## 會喜歡吧?
        if colorkey is not None:
            TextRect.set_alpha(colorkey)
        return TextRect , TextRect.get_rect()   
        
    ## 字體的初始化,考慮的問題是傳入的字體並不存在,這樣的
    ## 話會跳出一個異常。固然,爲了便於字體的管理,放在FONT
    ## 這樣的文件夾也是理所應當的。 
    def __initFont(self,name = 'hksn.ttf',size = 20):
        fullname = os.path.join('FONT',name)
        try:
            font = pygame.font.Font(fullname,size)
        except pygame.error,message:
            print 'Cannot load font:',name
            raise SystemExit,message
        return font  
        
     ###############以上是初始化工做###################
     
    ## 提供update方法,該方法做用爲接受解析器,並把解析器中內容
    ## 渲染到屏幕上,該方法爲一個關鍵方法
    ## 爲了避免使得這個函數太臃腫,把這玩意分解成了多個子方法
    ## 而且這一行爲也利於擴展,當須要增長方法時,添加一個方法
    ## 就能夠了。
    def update(self,parser):
        self.__updateBGM(parser.getBGM())
        self.__updateBackground(parser.getBackground())
        self.__updateText(parser.getName(),parser.getText())

        self.Surface.blit(self.Background,(0,0))
        self.Surface.blit(self.TextBox,self.TextBoxPos)
        
    ## 更新背景圖片,爲了爲透明背景提供支持(恩?有這個可能性?)
    ## 順便一提,背景圖片也是須要轉換大小的,嘛,顯然的。
    ## 這個函數借鑑了pygame官網上某個打大猩猩(……)遊戲的教程
    def __updateBackground(self,name,colorkey=None):
        fullname = os.path.join('BG',name)
        try:
            image = pygame.image.load(fullname)
        except pygame.error,message:
            print 'Cannot load image:',name
            raise SystemExit, message
        self.BGName = fullname
        image = image.convert_alpha()
        if colorkey is not None:
            if colorkey is -1:
                colorkey = image.get_at((0,0))
            image.set_colorkey(colorkey, RLEACCEL)
        image = pygame.transform.scale(image,(SCREENWIDTH,SCREENHEIGHT))
        self.Background = image

    ## 一樣借鑑了打大猩猩遊戲的代碼,我的以爲寫得很妙
    ## 特別是定義一個小類來充當啞值這一點,歷來沒碰見過……
    def __updateBGM(self,name):
        class NoneSound:
            def play(self):pass
        if not pygame.mixer:
            return NoneSound()
        fullname = os.path.join('BGM',name)
        if self.BGMName == fullname:
            return 
        try:
            pygame.mixer.music.load(fullname)
        except pygame.error, message:
            print 'Cannot load sound:',fullname
            raise SystemExit,message
        self.BGMName = fullname
        self.play()
    
    ##播放函數,沒啥好說的
    def play(self):
        pygame.mixer.music.play(-1,0.0)
    
    ## 這個函數其實挺麻煩的……
    def __updateText(self,name,text):
        ## 刷新文本框。
        self.TextBox.fill(self.bgColor)
        
        ## 這裏是控制字符編碼的部分,兼實現了跨平臺
        ## 這個程序開始是在Linux下開發的,因此我先
        ## 說一下。中文的顯示,必須把字符編碼轉成
        ## utf8,不然全是亂碼。這部分我之後還會詳述
        ## 暫且就這樣
        if WINDOWS:
                name = name.decode('gb18030')
                text = text.decode('gb18030')
        else:
                name = name.decode('utf8')
                text = text.decode('utf8')        
        
        ## 把長字符串分隔開,每35個字爲一個列表,再分別render
        ## 之因此這樣寫,是由於render函數只支持單行……因此得用一個
        ## 循環來處理。分割成列表這個列表推導式,出自felinx,我以爲
        ## 很好玩。注意的是,字符必須是統一的編碼,否則就悲劇了
        textLines = [text[i:i+LINENUMBER] for i in range(len(text)) if i % LINENUMBER == 0]
        
        ## 這個是名字,指名當前文本的說話人,都見過吧?
        ## 這個值可能爲None,也可能有,若是有的話,把它放第一行顯示
        if name != '':
            name += ' :'
        textLines.insert(0,name)
        
        vSize = VSIZE

        ## render顯示文字的話,把座標必定要算對,否則很差看
        ## 我這裏是左對齊的算法,中對齊的話是註釋掉的那個
        ## Sakia用的是中對齊,我的以爲很差看……
        ## 這裏用到的常量全是試驗出來的……
        for lineNum in range(len(textLines)):
            currentLine = textLines[lineNum]
            fontSurface = self.Font.render(currentLine,True,self.fgColor,self.bgColor)
            ##xPos = (SCREENWIDTH- fontSurface.get_width())/2
            xPos = OFFX
            yPos = OFFY + lineNum * vSize
            self.TextBox.blit(fontSurface,(xPos,yPos))

 

  上面就是一個封裝好的類,事實上,工做得很好,固然也能看出來,其實和上一篇博文涉及的代碼差很少,恩恩,就是份量增長了很多~~~個人註釋很詳細,有興趣的話,請稍微仔細點閱讀,謝謝了~~orm

  不過提及來,文本顯示有一個地方我沒解決。我這裏是35個字符爲一行顯示,問題是,當字符中有英文,日文或者空格的時候,由於字符寬度比中文字符寬度窄,結果就會出現上下行寬度不均勻的狀況……我的以爲不太好看。有高人能提出點簡單的點子麼?繼承

相關文章
相關標籤/搜索