热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

python制作游戏引擎_python制作galgame引擎(三)

正如上一篇所见,试验代码相当丑陋,效果也极度不堪……而且内部代码全部暴露,甚至没有一个接口,增添功能时也必然是伤筋动骨。于是

正如上一篇所见,试验代码相当丑陋,效果也极度不堪……而且内部代码全部暴露,甚至没有一个接口,增添功能时也必然是伤筋动骨。于是,这一篇的目标是:

1.对代码进行封装,并且代码中不能出现常量或常量字符。

2.增加异常的处理----主要是pygame的异常

说起来,对异常貌似有一个讨论,出自joel?不太记得了,大意是异常不是必要的编程元素。对于这一点,我持保留态度,个人认为引入异常能使得错误更容易被定位,给用户的提示也更加友好,还可以通过引入各种异常来提供某些特定的功能。请大家也仁者见仁。说到joel,那本《More Joel on Software》还是相当不错的一本书,适合吃饭后读着玩,因为其相当易读。Joel的话,这里有一篇他的文章,真的推荐看看:我的七个建议。

貌似我总是习惯性离题?

恩恩,封装的话,得先明确封装那些元素。对galgame来说,这倒是一个易于回答的问题,而就目前的进度而言,更是一个易于回答的问题:需要封装的元素是 背景图片,背景音乐,剧情文本。

Ok,那开始吧。

首先,为这个封装类起一个恰当的名字?我是随手用了NodeItem这个名字----估计是Sakia的影响……说到Sakia,guochaoer君在定义的时候,把文本,图片,音乐都各自用一个类来封装起来,并且都继承自pygame.Sprite.sprite。这当然是一种不错的思路,很清晰。但是我觉得,事实上文本,图片和音乐都只是字符串,大可以简单地处理处理完事。而且,通常来说galgame都是静态的,继承Sprite类也没什么益处。所以我是直接使用object这个超类的。嘛,总之,各有各的想法。我这边的话,直接写代码,就是这样:

# -*- 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))

上面就是一个封装好的类,事实上,工作得很好,当然也能看出来,其实和上一篇博文涉及的代码差不多,恩恩,就是分量增加了不少~~~我的注释很详细,有兴趣的话,请稍微仔细点阅读,谢谢了~~

不过说起来,文本显示有一个地方我没解决。我这里是35个字符为一行显示,问题是,当字符中有英文,日文或者空格的时候,因为字符宽度比中文字符宽度窄,结果就会出现上下行宽度不均匀的情况……个人觉得不太好看。有高人能提出点简单的点子么?



推荐阅读
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了如何使用python从列表中删除所有的零,并将结果以列表形式输出,同时提供了示例格式。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • Day2列表、字典、集合操作详解
    本文详细介绍了列表、字典、集合的操作方法,包括定义列表、访问列表元素、字符串操作、字典操作、集合操作、文件操作、字符编码与转码等内容。内容详实,适合初学者参考。 ... [详细]
  • 本文整理了315道Python基础题目及答案,帮助读者检验学习成果。文章介绍了学习Python的途径、Python与其他编程语言的对比、解释型和编译型编程语言的简述、Python解释器的种类和特点、位和字节的关系、以及至少5个PEP8规范。对于想要检验自己学习成果的读者,这些题目将是一个不错的选择。请注意,答案在视频中,本文不提供答案。 ... [详细]
  • 本文讨论了如何使用GStreamer来删除H264格式视频文件中的中间部分,而不需要进行重编码。作者提出了使用gst_element_seek(...)函数来实现这个目标的思路,并提到遇到了一个解决不了的BUG。文章还列举了8个解决方案,希望能够得到更好的思路。 ... [详细]
  • 本文介绍了Python对Excel文件的读取方法,包括模块的安装和使用。通过安装xlrd、xlwt、xlutils、pyExcelerator等模块,可以实现对Excel文件的读取和处理。具体的读取方法包括打开excel文件、抓取所有sheet的名称、定位到指定的表单等。本文提供了两种定位表单的方式,并给出了相应的代码示例。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 树莓派Linux基础(一):查看文件系统的命令行操作
    本文介绍了在树莓派上通过SSH服务使用命令行查看文件系统的操作,包括cd命令用于变更目录、pwd命令用于显示当前目录位置、ls命令用于显示文件和目录列表。详细讲解了这些命令的使用方法和注意事项。 ... [详细]
  • 怎么在PHP项目中实现一个HTTP断点续传功能发布时间:2021-01-1916:26:06来源:亿速云阅读:96作者:Le ... [详细]
  • MySQL多表数据库操作方法及子查询详解
    本文详细介绍了MySQL数据库的多表操作方法,包括增删改和单表查询,同时还解释了子查询的概念和用法。文章通过示例和步骤说明了如何进行数据的插入、删除和更新操作,以及如何执行单表查询和使用聚合函数进行统计。对于需要对MySQL数据库进行操作的读者来说,本文是一个非常实用的参考资料。 ... [详细]
author-avatar
楼_市早班车_954
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有