讲师中心 微信公众号

工具推荐

视频效率加速
立刻MV
立刻MV

低成本歌曲 MV、视频生成

Laper
Laper

剧本策划到成片协作

Seko
Seko

AI短剧一键生成成片

剧云
剧云

中文短剧剧本制作平台

Python 2D 游戏地图与局部渲染教程

陌枫大大_9040

陌枫大大_9040

发布时间:2025-12-09 13:26:02

|

571人浏览过

|

来源于php中文网

原创

python 2d 游戏地图与局部渲染教程

本教程详细介绍了如何在 Python 中使用嵌套列表构建 2D 游戏地图,并实现以玩家为中心的局部视图渲染系统。文章涵盖了地图数据结构、环境元素表示、视口计算、地图初始化与边界处理,并提供了示例代码,帮助开发者在终端环境中高效地展示游戏世界。

构建 Python 2D 游戏地图

在 Python 中,通常使用“列表的列表”(nested lists)来模拟二维数组或矩阵,这非常适合表示游戏地图。每个内部列表代表地图的一行,而列表中的元素则代表地图上的一个瓦片(tile)。

1. 地图数据结构与元素表示

我们可以用整数值来代表不同的环境元素。例如:

  • 0:空地
  • 1:墙壁
  • 2:玩家
  • 3:道具

为了在终端中显示这些元素,我们需要一个映射表,将整数值转换为可打印的字符。

立即学习“Python免费学习笔记(深入)”;

# 示例地图数据
game_map = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
]

# 元素到字符的映射
tile_textures = {
    0: ' ',  # 空地
    1: '#',  # 墙壁
    2: 'P',  # 玩家
    3: 'X',  # 道具
    -1: '.', # 边界外的“无”区域
}

2. 实现以玩家为中心的局部渲染(视口系统)

为了只渲染玩家周围的区域,我们需要定义一个“视口”(viewport),即屏幕上可见的区域大小。玩家通常位于视口的中心。

a. 定义视口大小与玩家位置

假设我们的终端显示区域(视口)有固定的宽度和高度。玩家的坐标是 (player_x, player_y)。

VIEWPORT_WIDTH = 15
VIEWPORT_HEIGHT = 7

player_x = 5
player_y = 3

b. 计算视口边界

Biopython
Biopython

全面的分子生物学工具集。适用于序列操作、文件解析(FASTA/GenBank/PDB)、系统发育分析,以及通过 Bio.Entrez 以编程方式访问 NCBI/

下载

视口的左上角和右下角坐标需要根据玩家位置和视口大小来计算。

def calculate_viewport_bounds(player_x, player_y, viewport_width, viewport_height, map_width, map_height):
    # 计算视口左上角坐标
    view_left = player_x - viewport_width // 2
    view_top = player_y - viewport_height // 2

    # 调整视口,确保它不会超出地图边界
    # 如果视口左边超出地图左边,则向右平移
    if view_left < 0:
        view_left = 0
    # 如果视口右边超出地图右边,则向左平移
    elif view_left + viewport_width > map_width:
        view_left = map_width - viewport_width

    # 如果视口上边超出地图上边,则向下平移
    if view_top < 0:
        view_top = 0
    # 如果视口下边超出地图下边,则向上平移
    elif view_top + viewport_height > map_height:
        view_top = map_height - viewport_height

    # 确保视口至少为0
    view_left = max(0, view_left)
    view_top = max(0, view_top)

    # 计算视口右下角坐标
    view_right = view_left + viewport_width
    view_bottom = view_top + viewport_height

    return view_left, view_top, view_right, view_bottom

c. 地图初始化与边界填充

为了简化渲染逻辑,尤其是当玩家靠近地图边缘时,我们可以将实际地图嵌入到一个更大的、用“无”区域(例如,用 -1 表示)填充的画布中。这样,即使玩家的视口超出了实际地图的范围,我们也能从这个更大的画布中获取到值,避免索引越界错误。

def create_padded_map(original_map, pad_value=-1):
    map_height = len(original_map)
    map_width = len(original_map[0]) if map_height > 0 else 0

    # 确定需要的额外填充量(通常是视口的一半)
    # 这里我们简化,直接在地图四周添加一圈pad_value
    # 更精确的做法是根据VIEWPORT_WIDTH/HEIGHT来确定
    padding = max(VIEWPORT_WIDTH // 2, VIEWPORT_HEIGHT // 2) + 1

    padded_height = map_height + 2 * padding
    padded_width = map_width + 2 * padding

    padded_map = [[pad_value for _ in range(padded_width)] for _ in range(padded_height)]

    # 将原始地图复制到填充地图的中心
    for y in range(map_height):
        for x in range(map_width):
            padded_map[y + padding][x + padding] = original_map[y][x]

    return padded_map, padding

# 假设原始地图和填充后的地图以及偏移量
# padded_game_map, map_offset = create_padded_map(game_map)
# player_x_padded = player_x + map_offset
# player_y_padded = player_y + map_offset

然而,对于终端渲染,更常见且简单的做法是直接在渲染循环中检查边界,而不是预先填充一个巨大的地图。上述 calculate_viewport_bounds 函数已经处理了视口不会超出实际地图边界的情况。如果需要渲染“地图外”区域,则在渲染时判断坐标是否有效,无效则显示默认的“无”字符。

d. 渲染逻辑

遍历视口内的每个单元格,获取其在地图中的值,并根据 tile_textures 字典打印相应的字符。

def render_viewport(game_map, player_x, player_y, viewport_width, viewport_height, tile_textures):
    map_height = len(game_map)
    map_width = len(game_map[0])

    # 计算视口左上角在地图上的起始坐标
    start_col = player_x - viewport_width // 2
    start_row = player_y - viewport_height // 2

    output_lines = []
    for r in range(viewport_height):
        current_row_chars = []
        for c in range(viewport_width):
            map_row = start_row + r
            map_col = start_col + c

            # 检查是否是玩家当前位置
            if map_row == player_y and map_col == player_x:
                current_row_chars.append(tile_textures[2]) # 玩家纹理
            # 检查坐标是否在地图范围内
            elif 0 <= map_row < map_height and 0 <= map_col < map_width:
                tile_value = game_map[map_row][map_col]
                current_row_chars.append(tile_textures.get(tile_value, '?')) # 获取瓦片纹理
            else:
                # 超出地图范围,显示“无”区域纹理
                current_row_chars.append(tile_textures.get(-1, ' '))
        output_lines.append("".join(current_row_chars))

    # 打印渲染结果
    print("\n".join(output_lines))

3. 玩家移动与边界检查

玩家移动时,需要更新 player_x 和 player_y。同时,必须确保玩家不能移动到无效区域(例如墙壁或地图边界外)。

def move_player(game_map, player_x, player_y, dx, dy):
    map_height = len(game_map)
    map_width = len(game_map[0])

    new_x = player_x + dx
    new_y = player_y + dy

    # 检查新位置是否在地图范围内
    if 0 <= new_x < map_width and 0 <= new_y < map_height:
        # 检查新位置是否可通行(例如,不是墙壁)
        if game_map[new_y][new_x] != 1: # 假设1是墙壁
            return new_x, new_y
    return player_x, player_y # 无法移动,返回原位置

完整示例代码

结合上述组件,我们可以构建一个简单的终端游戏循环:

import os
import time

# 游戏地图数据
game_map = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
]

# 元素到字符的映射
tile_textures = {
    0: ' ',  # 空地
    1: '#',  # 墙壁
    2: 'P',  # 玩家
    3: 'X',  # 道具
    -1: '.', # 边界外的“无”区域
}

# 视口大小
VIEWPORT_WIDTH = 21 # 奇数方便居中
VIEWPORT_HEIGHT = 11 # 奇数方便居中

# 玩家初始位置
player_x = 1
player_y = 1

def clear_terminal():
    os.system('cls' if os.name == 'nt' else 'clear')

def render_viewport(game_map, player_x, player_y, viewport_width, viewport_height, tile_textures):
    map_height = len(game_map)
    map_width = len(game_map[0])

    # 计算视口左上角在地图上的起始坐标
    start_col = player_x - viewport_width // 2
    start_row = player_y - viewport_height // 2

    output_lines = []
    for r in range(viewport_height):
        current_row_chars = []
        for c in range(viewport_width):
            map_row = start_row + r
            map_col = start_col + c

            # 检查是否是玩家当前位置
            if map_row == player_y and map_col == player_x:
                current_row_chars.append(tile_textures[2]) # 玩家纹理
            # 检查坐标是否在地图范围内
            elif 0 <= map_row < map_height and 0 <= map_col < map_width:
                tile_value = game_map[map_row][map_col]
                current_row_chars.append(tile_textures.get(tile_value, '?')) # 获取瓦片纹理
            else:
                # 超出地图范围,显示“无”区域纹理
                current_row_chars.append(tile_textures.get(-1, ' '))
        output_lines.append("".join(current_row_chars))

    # 打印渲染结果
    print("\n".join(output_lines))

def move_player(game_map, player_x, player_y, dx, dy):
    map_height = len(game_map)
    map_width = len(game_map[0])

    new_x = player_x + dx
    new_y = player_y + dy

    # 检查新位置是否在地图范围内
    if 0 <= new_x < map_width and 0 <= new_y < map_height:
        # 检查新位置是否可通行(例如,不是墙壁)
        if game_map[new_y][new_x] != 1: # 假设1是墙壁
            return new_x, new_y
    return player_x, player_y # 无法移动,返回原位置

# 游戏主循环
def game_loop():
    global player_x, player_y # 允许修改全局玩家位置

    # 模拟简单的输入(例如,每秒向右移动一次)
    # 实际游戏中会监听键盘输入
    moves = [(1, 0), (0, 1), (-1, 0), (0, -1)] # 右, 下, 左, 上
    move_index = 0

    running = True
    while running:
        clear_terminal()
        render_viewport(game_map, player_x, player_y, VIEWPORT_WIDTH, VIEWPORT_HEIGHT, tile_textures)
        print(f"Player Pos: ({player_x}, {player_y})")
        print("Press Ctrl+C to exit. (Simulating movement every second)")

        # 模拟移动
        dx, dy = moves[move_index % len(moves)]
        player_x, player_y = move_player(game_map, player_x, player_y, dx, dy)
        move_index += 1

        time.sleep(0.5) # 暂停0.5秒

# 运行游戏
if __name__ == "__main__":
    try:
        game_loop()
    except KeyboardInterrupt:
        print("\nGame Over!")

注意事项与总结

  1. 性能优化: 对于大型地图,每次渲染都遍历整个视口是可行的。如果地图和视口都非常巨大,可以考虑更高级的渲染技术,但对于终端游戏,当前方法已足够。
  2. 输入处理: 上述示例使用简单的 time.sleep 模拟移动。实际游戏中,需要使用 curses (Unix-like) 或 msvcrt (Windows) 等库来捕获实时键盘输入,实现交互式移动。
  3. 地图设计: 地图的边界处理至关重要。本教程中的 render_viewport 函数通过检查 0
  4. 可扩展性: tile_textures 字典使得添加新的环境元素变得非常容易,只需增加新的键值对即可。
  5. 坐标系: 在大多数2D游戏和计算机图形中,通常约定 (0,0) 为左上角,X轴向右增加,Y轴向下增加。本教程也遵循此约定。

通过本文的指导,您应该能够理解并实现一个基本的 Python 2D 游戏地图系统,并掌握以玩家为中心的局部渲染技术,为构建更复杂的终端游戏打下基础。

相关文章

在线游戏
在线游戏

海量精品小游戏合集,无需安装即点即玩,休闲益智、动作闯关应有尽有,秒开即玩,轻松解压,快乐停不下来

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
PixTV
PixTV Hot

PixTV是一款面向AIGC内容创作的AI视频生成工具。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

超级麦吉

一款通过数字员工协作完成调研、数据分析与文档制作的AI办公平台。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

Flowith
Flowith Hot

Flowith是一款基于画布和智能体工作流的 AI 协作工作空间。

相关专题

更多
treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2901

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

416

2025.12.22

深入理解算法:高效算法与数据结构专题
深入理解算法:高效算法与数据结构专题

本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

437

2026.01.06

C++ 数据结构与算法实现教程合集
C++ 数据结构与算法实现教程合集

以 C++ 为实现语言,系统讲解核心数据结构与算法,涵盖链表(单链表/双链表/环检测)、栈与队列(单调栈/优先队列)、二叉树(遍历/BST/AVL/红黑树)、哈希表(开地址法/链地址法)、图(邻接表/BFS/DFS/Dijkstra/拓扑排序)、常见排序算法(快排/归并/堆排/计数排序)的实现与复杂度分析,同时分享 LeetCode 刷题技巧、竞赛编程常用模板(二分/前缀和/滑动窗口/动态规划),帮助开发者夯实算法基础。

492

2026.05.09

windows查看端口占用情况
windows查看端口占用情况

Windows端口可以认为是计算机与外界通讯交流的出入口。逻辑意义上的端口一般是指TCP/IP协议中的端口,端口号的范围从0到65535,比如用于浏览网页服务的80端口,用于FTP服务的21端口等等。怎么查看windows端口占用情况呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

3479

2023.07.26

查看端口占用情况windows
查看端口占用情况windows

端口占用是指与端口关联的软件占用端口而使得其他应用程序无法使用这些端口,端口占用问题是计算机系统编程领域的一个常见问题,端口占用的根本原因可能是操作系统的一些错误,服务器也可能会出现端口占用问题。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

3038

2023.07.27

windows照片无法显示
windows照片无法显示

当我们尝试打开一张图片时,可能会出现一个错误提示,提示说"Windows照片查看器无法显示此图片,因为计算机上的可用内存不足",本专题为大家提供windows照片无法显示相关的文章,帮助大家解决该问题。

1548

2023.08.01

windows查看端口被占用的情况
windows查看端口被占用的情况

windows查看端口被占用的情况的方法:1、使用Windows自带的资源监视器;2、使用命令提示符查看端口信息;3、使用任务管理器查看占用端口的进程。本专题为大家提供windows查看端口被占用的情况的相关的文章、下载、课程内容,供大家免费下载体验。

2739

2023.08.02

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

20

2026.11.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn