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

Pygame碰撞异常:分数/生命值不更新及KeyError问题求助

Pygame开发问题排查:碰撞交互与KeyError修复

问题描述

玩家碰撞ID为163的金币或掉入陷阱时,分数与生命值既不在控制台也不在屏幕上更新,同时触发KeyError: id。已确认金币ID与Tiled地图及控制台输出一致,自行排查一周未找到原因。

原代码

主函数run

def run(self):
        player_right_frame = 0  # index to track members of right image list
        player_left_frame = 0  # index to track members of left image list
        player_jump_frame = 0  # index to track jumping image
        quit = False
        player_speed = 10
        player_health = 100 #allows decrement of health of player
        player_points = 0
        clock = pygame.time.Clock()
        direction = 'stand'  # by default, sprite starts by standing
        ground_position = self.screen.get_height() - 355
        x = 170
        y = ground_position
 
        while not quit:
            self.redraw()
            points_image = self.font.render(f"Points: {player_points}", 1, white)
            health_image = self.font.render(f'Health: {player_health}', 1, yellow)
            self.screen.blit(points_image, (100, 5))
            self.screen.blit(health_image, (200, 5))
 
            for event in pygame.event.get():
                if event.type == KEYDOWN:
                    if event.key == K_ESCAPE:
                        quit = True
                    if event.key == K_RETURN:
                        pause = False
                elif event.type == KEYUP:
                    direction = 'stand'
                elif event.type == QUIT:
                    quit = True
            keys = pygame.key.get_pressed()  # checking pressed keys
            if keys[K_UP]:
                # check to see if solid tile is above the player
 
                above_tile = fetch_tile_properties(self.tmx_data, x + image_width, y - player_speed,self.view_hole)
                if not above_tile['solid']:
                    y -= player_speed
                    # check to see if player is on ground to allow a jump
                    if on_tile['ground']:
                        player_jump_frame = 10
            elif keys[K_DOWN]:
                pass  # no longer needed since gravity takes care of coming down
            if keys[K_RIGHT]:
                right_tile = fetch_tile_properties(self.tmx_data, x + image_width + player_speed, y + image_height/2, self.view_hole)
                if not right_tile['solid']:
                    # move right
                    x += player_speed
                    direction = 'right'
                
                # check property of tile player is standing on now
 
            elif keys[K_LEFT]:
                left_tile = fetch_tile_properties(self.tmx_data, x - player_speed, y + image_height/2, self.view_hole)
                if not left_tile['solid']:
                    # move left
                    x -= player_speed
                    direction = 'left'
 
            # check property of tile player is standing on now
            on_tile = fetch_tile_properties(
                self.tmx_data, x + image_width/2, y + image_height, self.view_hole)
            # Check player collision
            collided = fetch_tile_properties(self.tmx_data, x + image_width, y, self.view_hole)
            player_health = player_health + collided['health']
            player_points = player_points + collided['points']
 
            # Quit game once player is out of life
            if player_health <= 0:
                quit = True
 
            # Remove coin that is collected by player via collision check
 
            if collided['id'] == 163:
                player_points = player_points + collided['points']
                tile_x = collided['x']
                tile_y = collided['y']
                #  remove coin touched from tmx_data
                self.tmx_data.layers[0].data[tile_y][tile_y] = 0
                # self.tmx_data.layers[0].remove(tile_x, tile_y)
 
            print(on_tile) # testing to see if points or health change values
 
            # check if player is jumping up
            if player_jump_frame > 0:
                y -= player_speed
                direction = 'up'
                player_jump_frame = player_jump_frame - 1
            # check if player is  on a solid otherwise fall down
            elif on_tile['ground'] == False:
                direction = 'down'
                y += player_speed

fetch_tile_properties函数

def fetch_tile_properties(tmxdata, x, y, view_hole):
    view_x = x - view_hole[0]
    view_y = y - view_hole[1]
 
    tile_x = view_x // 35  # scaling by size of the tile
    tile_y = view_y // 35  # ensuring only the integer value of division
 
    try:
        properties = tmxdata.get_tile_properties(
            tile_x, tile_y, 0)  # this would be a dictionary returned
    except ValueError:
        properties = {id: -1, 'canClimb': False, 'ground': False, 'health': -99999,  
        'points': 0, 'provide': '', 'require': '', 'solid' : False
        }
 
    #  check if player is moving in free space, no tiles beneath it
    if properties is None:
        # specify defined custom properties since the get_properties function only
        # works for where there is a tile. Use id of -1 to check error
        #  and free space motion
        properties = {id: -1, 'canClimb': False, 'ground': False, 'health': 0,
                      'points': 0, 'provide': '', 'require': '', 'solid': False}
    # Since properties is a dictionary, we can append extra data to it
    # useful data would be the position of the tile player is colliding wit
    # or standing on
 
    properties['x'] = tile_x
    properties['y'] = tile_y
    return properties

问题分析

  • KeyError根源:fetch_tile_properties中字典的键id未加引号,使用了Python内置函数id作为键名,导致后续访问collided['id']时找不到对应键。
  • 分数重复累加:碰撞检测时已执行分数累加,金币判断中又重复加分,逻辑冗余。
  • 碰撞区域不准确:当前碰撞检测点位于玩家右上角,未覆盖玩家主体,可能无法触发有效碰撞。
  • 金币移除索引错误:self.tmx_data.layers[0].data[tile_y][tile_y] = 0使用了两次tile_y,行列索引混淆,无法正确移除金币。
  • 变量提前引用:跳跃逻辑中访问了未定义的on_tile变量,会触发NameError。

修复方案

修复fetch_tile_properties函数

def fetch_tile_properties(tmxdata, x, y, view_hole):
    view_x = x - view_hole[0]
    view_y = y - view_hole[1]

    tile_x = view_x // 35  # 按tile尺寸缩放
    tile_y = view_y // 35  # 取整数部分

    try:
        properties = tmxdata.get_tile_properties(
            tile_x, tile_y, 0)  # 返回字典类型属性
    except ValueError:
        # 修复键名:将id改为字符串'id'
        properties = {'id': -1, 'canClimb': False, 'ground': False, 'health': -99999,  
        'points': 0, 'provide': '', 'require': '', 'solid' : False
        }

    # 处理无tile的空白区域
    if properties is None:
        # 修复键名:将id改为字符串'id'
        properties = {'id': -1, 'canClimb': False, 'ground': False, 'health': 0,
                      'points': 0, 'provide': '', 'require': '', 'solid': False}
    # 添加tile的坐标信息
    properties['x'] = tile_x
    properties['y'] = tile_y
    return properties

修复主函数run

def run(self):
    player_right_frame = 0  # 右侧动画帧索引
    player_left_frame = 0  # 左侧动画帧索引
    player_jump_frame = 0  # 跳跃动画帧索引
    quit = False
    player_speed = 10
    player_health = 100  # 玩家生命值
    player_points = 0
    clock = pygame.time.Clock()
    direction = 'stand'  # 默认初始状态为站立
    ground_position = self.screen.get_height() - 355
    x = 170
    y = ground_position
    # 提前初始化on_tile,避免未定义错误
    on_tile = {'ground': False}

    while not quit:
        self.redraw()
        points_image = self.font.render(f"Points: {player_points}", 1, white)
        health_image = self.font.render(f'Health: {player_health}', 1, yellow)
        self.screen.blit(points_image, (100, 5))
        self.screen.blit(health_image, (200, 5))

        for event in pygame.event.get():
            if event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                    quit = True
                if event.key == K_RETURN:
                    pause = False
            elif event.type == KEYUP:
                direction = 'stand'
            elif event.type == QUIT:
                quit = True
        keys = pygame.key.get_pressed()  # 获取当前按下的键
        if keys[K_UP]:
            # 检测玩家上方是否有实心tile
            above_tile = fetch_tile_properties(self.tmx_data, x + image_width, y - player_speed,self.view_hole)
            if not above_tile['solid']:
                y -= player_speed
                # 只有在地面上才能跳跃
                if on_tile['ground']:
                    player_jump_frame = 10
        elif keys[K_DOWN]:
            pass  # 下落由重力逻辑处理
        if keys[K_RIGHT]:
            right_tile = fetch_tile_properties(self.tmx_data, x + image_width + player_speed, y + image_height/2, self.view_hole)
            if not right_tile['solid']:
                x += player_speed
                direction = 'right'

        elif keys[K_LEFT]:
            left_tile = fetch_tile_properties(self.tmx_data, x - player_speed, y + image_height/2, self.view_hole)
            if not left_tile['solid']:
                x -= player_speed
                direction = 'left'

        # 检测玩家脚下的tile
        on_tile = fetch_tile_properties(
            self.tmx_data, x + image_width//2, y + image_height, self.view_hole)
        # 调整碰撞检测点为玩家中心,提升检测准确性
        collided = fetch_tile_properties(self.tmx_data, x + image_width//2, y + image_height//2, self.view_hole)
        
        # 更新生命值和分数,移除重复累加
        player_health += collided['health']
        # 仅在碰撞到有效tile时更新分数
        if collided['id'] != -1:
            player_points += collided['points']

        # 生命值为0时退出游戏
        if player_health <= 0:
            quit = True

        # 处理金币收集逻辑
        if collided['id'] == 163:
            tile_x = collided['x']
            tile_y = collided['y']
            # 修复索引错误,正确定位tile行列
            self.tmx_data.layers[0].data[tile_y][tile_x] = 0

        print(on_tile)  # 调试输出tile属性

        # 跳跃逻辑
        if player_jump_frame > 0:
            y -= player_speed
            direction = 'up'
            player_jump_frame -= 1
        # 重力下落逻辑
        elif not on_tile['ground']:
            direction = 'down'
            y += player_speed
        
        # 控制帧率为60,稳定游戏速度
        clock.tick(60)

修复说明

  • KeyError修复:将字典中的id改为字符串'id',确保键名与访问逻辑一致。
  • 变量初始化:提前初始化on_tile,避免跳跃逻辑中出现未定义变量错误。
  • 碰撞区域优化:将碰撞检测点改为玩家中心,确保更准确地触发与金币/陷阱的交互。
  • 移除重复逻辑:删除金币判断中的重复加分代码,避免分数异常。
  • 索引错误修复:修正金币移除时的行列索引,正确定位并移除目标tile。
  • 帧率控制:添加clock.tick(60),稳定游戏运行帧率,避免移动速度异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 00:18:20