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

Python Pygame实现旋转立方体时坐标趋近正负无穷问题求助

Pygame渲染旋转立方体时坐标趋近于无穷大问题修复

参考Tsoding的3D图形视频,用Python的Pygame库渲染旋转立方体,但所有坐标位置总是趋近于正无穷或负无穷,以下是我的代码:

# Import Modules
import pygame
import math
# Pygame Setup
pygame.init()
WINDOW_WIDTH = 500
WINDOW_HEIGHT = 500
screen = pygame.display.set_mode((WINDOW_WIDTH,WINDOW_HEIGHT))
Clock = pygame.time.Clock()
running = True

# Classes
class Point:
    def __init__(self, xPos:float, yPos:float, zPos:float): #zPos:float):
        self.xPos = xPos
        self.yPos = yPos
        self.zPos = zPos
    def rotate(self, angle:float):
        # This is just a rotation matrix

        # do trig function
        cosineAngle = math.cos(angle)
        sineAngle = math.sin(angle)

        # apply to positions
        self.xPos = self.xPos * cosineAngle - self.zPos * sineAngle
        self.zPos = self.xPos * sineAngle + self.zPos * cosineAngle
    def translate(self, translateX:float, translateY:float):
        # Code here
        pass
    def translateZ(self, zOffset:float):
        self.zPos = 1 + zOffset
    def screen(self): # project point onto the screen
        # Change from cartesian coordinates to screen coordinates
        self.xPos = ((self.xPos + 1) / 2) * screen.get_width()
        self.yPos = (1 - (self.yPos + 1) / 2) * screen.get_height() # "1 -" so you flip it around
    def project(self):
        self.xPos /= self.zPos
        self.yPos /= self.zPos
    def point(self):
        position = pygame.Vector2(self.xPos, self.yPos)
        pygame.draw.circle(screen, "red", position, 5)

# Variables

offsetZ = 0

angle = 0

deltatime = 0

points = [
    # first plane
    Point(0.5, 0.5, 0.5), # Top Right
    Point(-0.5, 0.5, 0.5), # Top Left
    Point(0.5, -0.5, 0.5), # Bottom Right
    Point(-0.5, -0.5, 0.5), # Bottom Left

    # Other plane
    Point(0.5, 0.5, -0.5), # Top Right
    Point(-0.5, 0.5, -0.5), # Top Left
    Point(0.5, -0.5, -0.5), # Bottom Right
    Point(-0.5, -0.5, -0.5) # Bottom Left
    ]

# NOTES
# If the Z value is set to one then nothing will change
# If the Z value is set to zero then the point won't render

while running:
    
    # Checking for events --> quit
    for event in pygame.event.get():
        if event.type == pygame.QUIT: # pygame.QUIT not pygame.quit
            running = False

    # Draw
    screen.fill((0,0,0)) # Background Color

    #offsetZ += 1*deltatime
    
    points = [
    # first plane
    Point(0.5, 0.5, 0.5), # Top Right
    Point(-0.5, 0.5, 0.5), # Top Left
    Point(0.5, -0.5, 0.5), # Bottom Right
    Point(-0.5, -0.5, 0.5), # Bottom Left

    # Other plane
    Point(0.5, 0.5, -0.5), # Top Right
    Point(-0.5, 0.5, -0.5), # Top Left
    Point(0.5, -0.5, -0.5), # Bottom Right
    Point(-0.5, -0.5, -0.5) # Bottom Left
    ]
    
    
    for i in range(len(points)):
        #points[i].translateZ(offsetZ)
        points[i].rotate(0.00006)
        points[i].project()
        points[i].screen()
        points[i].point()

        
        

    # Flip() to render 
    pygame.display.flip()
    deltatime = Clock.tick(60) / 1000 # set max FPS to 60

# quitting
pygame.quit()

问题原因及修复方案

1. 旋转矩阵计算错误

rotate方法中,计算新的xPos后直接用修改后的值计算zPos,导致旋转逻辑混乱,坐标逐渐发散到无穷大。修复时需要先保存原始坐标:

def rotate(self, angle:float):
    cosineAngle = math.cos(angle)
    sineAngle = math.sin(angle)
    # 保存原始坐标,避免计算时互相干扰
    original_x = self.xPos
    original_z = self.zPos
    self.xPos = original_x * cosineAngle - original_z * sineAngle
    self.zPos = original_x * sineAngle + original_z * cosineAngle

2. Z轴偏移逻辑错误

translateZ方法直接覆盖原始z坐标,应该在原始值基础上增加偏移,确保立方体始终处于相机前方(z值为正,避免除以0或负数导致坐标异常):

def translateZ(self, zOffset:float):
    self.zPos += zOffset

3. 渲染流程缺失Z偏移

循环中注释掉了translateZ调用,导致部分点z值为负,执行project时除以负数会让坐标反向放大,最终趋近无穷。需要启用Z偏移并设置合适的初始值:

# 初始化时设置基础Z偏移,让立方体远离相机
offsetZ = 3

# 循环内启用Z偏移
for point in points:
    point.translateZ(offsetZ)
    point.rotate(0.06)  # 增大角度值让旋转更明显
    point.project()
    point.screen()
    point.point()

4. 优化重复创建points列表

循环内每次重新创建points列表完全没必要,可移到循环外初始化,若需要避免累积变换,可在每次循环前重置。

修复后的完整代码

import pygame
import math

pygame.init()
WINDOW_WIDTH = 500
WINDOW_HEIGHT = 500
screen = pygame.display.set_mode((WINDOW_WIDTH,WINDOW_HEIGHT))
Clock = pygame.time.Clock()
running = True

class Point:
    def __init__(self, xPos:float, yPos:float, zPos:float):
        self.xPos = xPos
        self.yPos = yPos
        self.zPos = zPos
    def rotate(self, angle:float):
        cosineAngle = math.cos(angle)
        sineAngle = math.sin(angle)
        original_x = self.xPos
        original_z = self.zPos
        self.xPos = original_x * cosineAngle - original_z * sineAngle
        self.zPos = original_x * sineAngle + original_z * cosineAngle
    def translateZ(self, zOffset:float):
        self.zPos += zOffset
    def screen(self):
        self.xPos = ((self.xPos + 1) / 2) * screen.get_width()
        self.yPos = (1 - (self.yPos + 1) / 2) * screen.get_height()
    def project(self):
        self.xPos /= self.zPos
        self.yPos /= self.zPos
    def point(self):
        position = pygame.Vector2(self.xPos, self.yPos)
        pygame.draw.circle(screen, "red", position, 5)

offsetZ = 3
deltatime = 0

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill((0,0,0))

    # 每次循环重置点的原始坐标,避免累积变换
    points = [
        Point(0.5, 0.5, 0.5),
        Point(-0.5, 0.5, 0.5),
        Point(0.5, -0.5, 0.5),
        Point(-0.5, -0.5, 0.5),
        Point(0.5, 0.5, -0.5),
        Point(-0.5, 0.5, -0.5),
        Point(0.5, -0.5, -0.5),
        Point(-0.5, -0.5, -0.5)
    ]
    
    for point in points:
        point.translateZ(offsetZ)
        point.rotate(0.06 * deltatime * 60)  # 用deltatime让旋转速度稳定
        point.project()
        point.screen()
        point.point()

    pygame.display.flip()
    deltatime = Clock.tick(60) / 1000

pygame.quit()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.02 04:47:30