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
相关产品推荐
相关产品推荐

