python3+libtcod学习(三)绘制地图

上一篇 python3+libtcod学习(二)移动符号

面向对象

为了解决系统的可维护性,可扩展性,可重用性,现在需要修改之前的代码。
首先设计的这个类代表这个游戏中的所有实体,无论是主角、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)
...

这时候运行程序我们可以看到屏幕上出现了一堵无法穿越的小墙。

下一篇 python3+libtcod学习(四)生成地牢

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章