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

AI输入时游戏角色坠落出屏幕的技术问题求助

问题描述

单次输入测试时游戏运行正常,但AI执行输入控制时,玩家立方体直接掉出屏幕:player的x位置快速正向增长,y位置与vel_y持续负向增长。排除地板碰撞问题,怀疑AI快速输入引发异常。

问题分析

从提供的代码来看,核心问题集中在帧率未限制、速度计算未做帧率适配、输入逻辑存在缺陷三个方面:

  • 未限制帧率:Game.step()中未调用clock.tick(),AI快速调用step时,每帧移动无时间限制,导致速度被无限放大。
  • 速度计算无帧率适配:Player.move()直接使用原始速度值叠加位置,未乘以帧间隔时间(delta time),导致不同帧率下移动速度差异极大。
  • 输入逻辑缺陷:
    • update_velo中vel_x的赋值逻辑存在漏洞,若AI输出的action中right=1持续触发,vel_x会一直保持300,无减速逻辑。
    • 跳跃逻辑未限制地面状态:只要jump=1就强制设置vel_y=-10,忽略self.ground判断,且覆盖了重力叠加,导致玩家持续向上飞。
    • 碰撞处理逻辑不严谨:垂直碰撞判断未结合速度方向,可能导致碰撞后状态错误。
修复方案

以下是针对性修改后的完整代码:

import pygame
from math import dist

pygame.init()
w, h = 1250, 720

class Direction:
    @staticmethod
    def update(left=0, right=0, jump=0):
        return left, right, jump

class Player:
    def __init__(self, x, y):
        self.rect = pygame.Rect(x, y, 10, 10)
        self.vel_x = 0
        self.vel_y = 0
        self.gravity = 0.2
        self.ground = False
        self.collision_tol = 5
        self.move_speed = 300  # 基础移动速度

    def update_velo(self, left, right, jump):
        # 修复水平速度逻辑:左右互斥,且支持松开后减速
        if right == 1:
            self.vel_x = self.move_speed
        elif left == 1:
            self.vel_x = -self.move_speed
        else:
            # 无输入时逐渐减速
            self.vel_x *= 0.9
        
        # 修复跳跃逻辑:仅在地面时允许跳跃
        self.vel_y += self.gravity
        if jump == 1 and self.ground:
            self.vel_y = -10
            self.ground = False  # 跳跃后离开地面

    def move(self, dt):
        # 使用delta time适配帧率,保证不同设备速度一致
        self.rect.x += self.vel_x * dt
        self.rect.y += self.vel_y * dt

    def collisions(self, objects, walls):
        self.ground = False  # 默认未接触地面,碰撞时再更新

        # 处理平台碰撞
        for obj in objects:
            if self.rect.colliderect(obj):
                # 水平碰撞处理
                if self.vel_x > 0 and self.rect.right > obj.left and self.rect.left < obj.left:
                    self.rect.right = obj.left
                elif self.vel_x < 0 and self.rect.left < obj.right and self.rect.right > obj.right:
                    self.rect.left = obj.right
                
                # 垂直碰撞处理
                if self.vel_y > 0 and self.rect.bottom > obj.top and self.rect.top < obj.top:
                    self.rect.bottom = obj.top
                    self.vel_y = 0
                    self.ground = True
                elif self.vel_y < 0 and self.rect.top < obj.bottom and self.rect.bottom > obj.bottom:
                    self.rect.top = obj.bottom
                    self.vel_y = 0

        # 处理墙壁和地板碰撞
        for wall in walls:
            if self.rect.colliderect(wall):
                # 水平碰撞
                if self.vel_x > 0:
                    self.rect.right = wall.left
                elif self.vel_x < 0:
                    self.rect.left = wall.right
                
                # 垂直碰撞(地板)
                if self.vel_y > 0:
                    self.rect.bottom = wall.top
                    self.vel_y = 0
                    self.ground = True
                elif self.vel_y < 0:
                    self.rect.top = wall.bottom
                    self.vel_y = 0

    def update(self, left, right, jump, objects, walls, dt):
        self.update_velo(left, right, jump)
        self.move(dt)
        self.collisions(objects, walls)

class Game:
    def __init__(self):
        self.display = pygame.display.set_mode((w, h))
        pygame.display.set_caption("AI Game")
        self.clock = pygame.time.Clock()
        self.endpt = pygame.Rect(1200, h - 250, 20, 20)
        self.floor = pygame.Rect(0, h - 20, w, 200)
        self.left_wall = pygame.Rect(0, 0, 20, h)
        self.right_wall = pygame.Rect(w - 20, 0, 20, h)
        self.objects_for_lvl = [
            pygame.Rect(100, h - 100, 100, 80),
            pygame.Rect(250, h - 140, 100, 25),
            pygame.Rect(400, h - 170, 100, 25),
            pygame.Rect(535, h - 170, 75, 25),
            pygame.Rect(735, h - 170, 75, 25),
            pygame.Rect(900, h - 200, 150, 25),
            pygame.Rect(1150, h - 230, 200, 25)
        ]

        self.player = Player(30, 600)
        self.running = True
        self.reset()

    def reset(self):
        self.score = 0
        self.frame_iteration = 0

    def step(self, action):
        self.frame_iteration += 1
        # 限制帧率为60,并获取帧间隔时间(秒)
        dt = self.clock.tick(60) / 1000

        for ev in pygame.event.get():
            if ev.type == pygame.QUIT:
                pygame.quit()
                quit()

        left, right, jump = action
        # 传递delta time给player的update方法
        self.player.update(left, right, jump, self.objects_for_lvl, [self.floor, self.left_wall, self.right_wall], dt)

        reward, game_over = self.calculate_score()

        self._update()

        return reward, game_over, self.score

    def calculate_score(self):
        reward = 0
        game_over = False

        if self.player.rect.colliderect(self.endpt):
            reward += 10
        elif self.player.rect.bottom > h or self.player.rect.top < 0:
            # 增加掉出屏幕的惩罚
            reward -= 10
            game_over = True

        self.score += reward
        # 增加掉出屏幕的判定
        game_over = game_over or self.player.rect.colliderect(self.left_wall) or self.player.rect.colliderect(self.right_wall)

        return reward, game_over

    def _update(self):    
        self.display.fill('white')  

        for obj in self.objects_for_lvl:  
            pygame.draw.rect(self.display, (128, 128, 128), obj)

        pygame.draw.rect(self.display, (128, 128, 128), self.floor)  
        pygame.draw.rect(self.display, (128, 128, 128), self.left_wall)
        pygame.draw.rect(self.display, (128, 128, 128), self.right_wall)
    
        pygame.draw.rect(self.display, (255, 0, 0), self.player.rect)
        pygame.draw.rect(self.display, (0, 255, 0), self.endpt)  

        pygame.display.update()


if __name__ == "__main__":
    game = Game()
    while game.running:
        action = Direction.update(0, 1, 0) # 测试右移
        reward, game_over, score = game.step(action)
        print(f"Score: {score}, Game Over: {game_over}")
        if game_over:
            print("Game Over")
            game.running = False
关键修改说明
  • 帧率控制:在Game.step()中添加clock.tick(60)限制帧率,同时计算delta time传递给Player的移动逻辑,保证移动速度与帧率无关。
  • 水平速度优化:修复vel_x的赋值逻辑,支持左右互斥输入,无输入时添加减速效果,避免速度突变。
  • 跳跃逻辑修复:仅当玩家处于地面时允许跳跃,避免持续跳跃导致的异常上升。
  • 碰撞逻辑优化:结合速度方向处理碰撞,修正ground状态的判断逻辑,避免错误的碰撞响应。
  • 掉出屏幕判定:在calculate_score()中添加玩家上下边界的判定,完善游戏结束条件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 23:55:58