You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pygame开发2D Minecraft的Tile based lighting实现方案

Pygame 2D类Minecraft瓦片光照实现方案

你的现有Tile类已经预留了0-1范围的light_level字段,不用大改核心结构就能跑通瓦片级光照,比常规逐像素叠层方案性能高很多,还能实现不透明方块挡光的经典MC光照效果。

核心逻辑

直接用BFS广度优先光线传播+绘制叠暗层的方案,完全适配tile based架构:

  • 先给所有方块配置透光率参数,空气完全透光,石头、基岩这类固体方块完全不透光,玻璃、树叶这类方块可以设半透值
  • 光照传播只在方块变更、光源(火把、天空光)变化时计算,不用每帧全图重算,哪怕千格规模的地图也不会有性能压力
  • 绘制时根据每个方块的亮度值,在原贴图上叠对应透明度的黑色遮罩,亮度1时遮罩全透明,亮度0时遮罩纯黑,不用改你现有贴图加载、世界生成的逻辑

代码修改步骤

1. 新增方块透光率配置

把配置加在World类的初始化方法里,和你现有的block_textures配置放在同层级即可:

# 世界类初始化时添加,值为方块透光率,1为完全不挡光,0为完全挡光
self.block_opacity = {
    "terrain": {
        "air": 1.0,
        "grass_block": 0.0,
        "dirt": 0.0,
        "stone": 0.0,
        "bedrock": 0.0
        # 后续新增半透方块比如玻璃、树叶,直接在这里加对应透光率即可
    },
    "missing": {
        "missing_texture": 0.0
    }
}

2. 给World类添加光照计算方法

直接把下面的方法复制到World类里,支持人工光源+天空光自动计算,自动处理方块挡光逻辑:

def calc_lighting(self, light_sources, decay_per_tile=0.07):
    """
    计算全图光照
    :param light_sources: 人工光源列表,格式为(格子x坐标, 格子y坐标, 初始亮度0-1),比如火把可以设为(x,y,0.9)
    :param decay_per_tile: 每传播一格的亮度衰减值,数值越小光照传播距离越远
    """
    from collections import deque
    # 重置所有方块亮度
    for x in range(self.width):
        for y in range(self.height):
            self.tiles[x][y].light_level = 0

    propagation_queue = deque()
    # 所有人工光源入队
    for sx, sy, init_brightness in light_sources:
        if 0 <= sx < self.width and 0 <= sy < self.height:
            self.tiles[sx][sy].light_level = init_brightness
            propagation_queue.append((sx, sy, init_brightness))

    # 四方向BFS传播,要更柔和的斜向传播可以加(1,1)(1,-1)等四个斜向方向
    directions = [(1,0), (-1,0), (0,1), (0,-1)]
    while propagation_queue:
        x, y, current_bright = propagation_queue.popleft()
        next_bright = current_bright - decay_per_tile
        if next_bright <= 0:
            continue
        for dx, dy in directions:
            nx, ny = x+dx, y+dy
            if not (0 <= nx < self.width and 0 <= ny < self.height):
                continue
            target_tile = self.tiles[nx][ny]
            # 获取目标方块的透光率
            block_trans = self.block_opacity.get(target_tile.block_category, {}).get(target_tile.block_type, 0.0)
            spread_bright = next_bright * block_trans
            # 只有新亮度高于方块现有亮度才更新,避免重复计算
            if spread_bright > target_tile.light_level:
                target_tile.light_level = spread_bright
                propagation_queue.append((nx, ny, spread_bright))

    # 叠加天空光:从地表往下传播,碰到不透明方块就阻断
    for x in range(self.width):
        sky_bright = 1.0
        for y in range(self.height):
            current_tile = self.tiles[x][y]
            block_trans = self.block_opacity.get(current_tile.block_category, {}).get(current_tile.block_type, 0.0)
            # 碰到完全不透明方块,下方天空光直接切断
            if block_trans == 0:
                sky_bright = 0
            current_tile.light_level = min(1.0, current_tile.light_level + sky_bright)
            # 天空光随深度衰减
            sky_bright = sky_bright * block_trans - decay_per_tile
            if sky_bright < 0:
                sky_bright = 0

3. 修改Tile类的draw方法

在原有贴图绘制逻辑后加暗层叠绘即可:

def draw(self, cam_x, cam_y):
    # 保留你原有的绘制逻辑,比如计算屏幕坐标、blit原贴图
    screen_x = self.x - cam_x
    screen_y = self.y - cam_y
    self.win.blit(self.image, (screen_x, screen_y))
    # 叠加光照暗层
    if self.light_level < 1.0:
        dark_mask = pygame.Surface((self.width, self.height)).convert_alpha()
        mask_alpha = int(255 * (1 - self.light_level))
        dark_mask.fill((0, 0, 0, mask_alpha))
        self.win.blit(dark_mask, (screen_x, screen_y))

性能优化技巧

  • 不用每帧调用calc_lighting,只在玩家放置/破坏方块、手持火把移动位置时,重算受影响范围的光照即可,200*200规模的地图就算全图重算也不会卡
  • 可以提前预生成0-255透明度的黑色遮罩Surface缓存起来,绘制时直接取对应透明度的遮罩用,省掉每帧新建Surface的开销
  • 想要更柔和的光照边缘,可以把传播方向改成八方向,或者最后给全图光照值做一次简单的邻域均值模糊
  • 做昼夜循环只需要调整天空光的初始亮度就行,比如晚上把天空光初始值设为0.2,就能实现夜晚变暗的效果

2D版Minecraft当前运行效果

内容的提问来源于stack exchange,提问作者coder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 17:54:35