面向对象
为了解决系统的可维护性,可扩展性,可重用性,现在需要修改之前的代码。
首先设计的这个类代表这个游戏中的所有实体,无论是主角、NPC、敌人,这些在屏幕上看得到的“实体”都会属于这个类。创建一个新文件entity.py输入以下代码:
class Entity:
def __init__(self, x, y, char, color):
self.x = x
self.y = y
self.char = char
self.color = color
def move(self, dx, dy):
self.x += dx
self.y += dy
这个类中包含了一些最基本的信息和功能,现在来修改之前的engine.py代码
import tcod as libtcod
from entity import Entity
from input_handlers import handle_keys
def main():
screen_width = 80
screen_height = 50
# 0.创建可操控的白色的“主角”和一个黄色的NPC
player = Entity(int(screen_width / 2), int(screen_height / 2), '@', libtcod.white)
npc = Entity(int(screen_width / 2 - 5), int(screen_height / 2), '@', libtcod.yellow)
entities = [npc, player]
...
...
while not libtcod.console_is_window_closed():
libtcod.sys_check_for_event(libtcod.EVENT_KEY_PRESS, key, mouse)
libtcod.console_set_default_foreground(con, libtcod.white)
# 1.注意这里和下面的座标都已经改为player.x和player.y
libtcod.console_put_char(con, player.x, player.y, '@', libtcod.BKGND_NONE)
libtcod.console_blit(con, 0, 0, screen_width, screen_height, 0, 0, 0)
libtcod.console_flush()
libtcod.console_put_char(con, player.x, player.y, ' ', libtcod.BKGND_NONE)
action = handle_keys(key)
move = action.get('move')
exit = action.get('exit')
fullscreen = action.get('fullscreen')
if move:
dx, dy = move
# 2.这里修改了移动的方式
player.move(dx, dy)
if exit:
return True
if fullscreen:
libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen())
if __name__ == '__main__':
main()
现在我们还需要一个用于“绘制”和“清除”的函数。创建一个名为的新文件render_functions.py,写下以下代码:
import tcod as libtcod
# 这个函数的参数分别是控制台、一个要进行绘制的队列、屏幕的宽、高
def render_all(con, entities, screen_width, screen_height):
# 把列表中的实体取出进行绘制
for entity in entities:
draw_entity(con, entity)
libtcod.console_blit(con, 0, 0, screen_width, screen_height, 0, 0, 0)
# 删除
def clear_all(con, entities):
for entity in entities:
clear_entity(con, entity)
# 对实体进行绘制
def draw_entity(con, entity):
libtcod.console_set_default_foreground(con, entity.color)
libtcod.console_put_char(con, entity.x, entity.y, entity.char, libtcod.BKGND_NONE)
# 删除在屏幕上的实体
def clear_entity(con, entity):
libtcod.console_put_char(con, entity.x, entity.y, ' ', libtcod.BKGND_NONE)
有了上面的代码再来修改engine.py中的代码:
...
libtcod.sys_check_for_event(libtcod.EVENT_KEY_PRESS, key, mouse)
# 绘制
render_all(con, entities, screen_width, screen_height)
libtcod.console_flush()
# 删除
clear_all(con, entities)
action = handle_keys(key)
...
注意还需要import clear_all和render_all函数,现在运行程序,应该可以看到你的白色’@'符号和一个代表NPC的黄色‘@’符号。
创建地图
创建一个tile.py文件并将以下代码放入其中:
class Tile:
"""
这个类的作用是给组成地图的字符(区块)一些性质,现在这里只写下了是否被阻塞和是否遮挡视野,也就是‘墙’的意思
"""
def __init__(self, blocked, block_sight=None):
self.blocked = blocked
# 默认一个区块如果可以阻拦玩家前进就阻拦视野,如果不想要这样的效果就传入block_sight的值
if block_sight is None:
block_sight = blocked
self.block_sight = block_sight
现在有了地图的组成部分,可以写下地图类了,创建一个文件game_map.py:
from tile import Tile
class GameMap:
def __init__(self, width, height):
self.width = width
self.height = height
self.tiles = self.initialize_tiles()
def initialize_tiles(self):
# 直接生成一个二维数组保存地图,这是一张空地图。
tiles = [[Tile(False) for y in range(self.height)] for x in range(self.width)]
# 给一些地方加上‘墙’,这里只是为了演示目的,可以随便修改。
tiles[30][22].blocked = True
tiles[30][22].block_sight = True
tiles[31][22].blocked = True
tiles[31][22].block_sight = True
tiles[32][22].blocked = True
tiles[32][22].block_sight = True
return tiles
#既然有‘墙’的存在,就不让玩家通过
def is_blocked(self, x, y):
if self.tiles[x][y].blocked:
return True
return False
下一步要绘制地图,来到render_functions.py中,修改绘制函数:
...
def render_all(con, entities, game_map, screen_width, screen_height, colors):
# 绘制地图
for y in range(game_map.height):
for x in range(game_map.width):
wall = game_map.tiles[x][y].block_sight
if wall:
libtcod.console_set_char_background(con, x, y, colors.get('dark_wall'), libtcod.BKGND_SET)
else:
libtcod.console_set_char_background(con, x, y, colors.get('dark_ground'), libtcod.BKGND_SET)
# 绘制实体的部分依然不变
for entity in entities:
draw_entity(con, entity)
libtcod.console_blit(con, 0, 0, screen_width, screen_height, 0, 0, 0)
...
最后回到engine.py文件,进行一些更改。
...
screen_height = 50
# 设置地图大小、相关区域颜色
map_width = 80
map_height = 45
colors = {
'dark_wall': libtcod.Color(0, 0, 100),
'dark_ground': libtcod.Color(50, 50, 150)
}
player = Entity(int(screen_width / 2), int(screen_height / 2), '@', libtcod.white)
...
...
con = libtcod.console_new(screen_width, screen_height)
# 创建地图
game_map = GameMap(map_width, map_height)
key = libtcod.Key()
...
...
while not libtcod.console_is_window_closed():
libtcod.sys_check_for_event(libtcod.EVENT_KEY_PRESS, key, mouse)
# 绘制地图和实体
render_all(con, entities, game_map, screen_width, screen_height, colors)
libtcod.console_flush()
clear_all(con, entities)
action = handle_keys(key)
move = action.get('move')
exit = action.get('exit')
fullscreen = action.get('fullscreen')
if move:
dx, dy = move
# 如果有‘墙’则不能移动
if not game_map.is_blocked(player.x + dx, player.y + dy):
player.move(dx, dy)
...
这时候运行程序我们可以看到屏幕上出现了一堵无法穿越的小墙。