【问题标题】:How Do I Change Perspective Angles When Stacking Sprites?堆叠 Sprite 时如何更改透视角度?
【发布时间】:2020-04-20 02:07:21
【问题描述】:

我使用 Pygame 库在 Python 中创建了一个精灵堆叠预览程序。默认情况下,当每个单元格堆叠在前一个单元格上时,将精灵表在 Y 轴上偏移 -1 像素会创建 45 度透视。当我将单元格间距设置为 0 时,我也可以获得 0 度视角。

有一个基于网络的精灵堆叠程序 Sprite Stack Studio,它允许用户选择 0、45、60 和 90 度的摄像机角度。我还看到了一个 Pico-8 墨盒,它允许在一个岛屿周围进行完整的半球形透视。

我只是好奇这是否复杂,或者我是否可以对我当前的程序进行一些小的修改以获得所需的视角。

我的精灵表:

渲染图像:

Sprite Stack Studio 示例: https://spritestack.io/studio/?cloud_id=349&app=exporter

PICO-8 岛示例: https://www.lexaloffle.com/bbs/?pid=15116#p

【问题讨论】:

  • 您能否澄清一下您所说的“Sprite Stack”。读取的链接就像每个精灵都是通过 3d 对象的“切片”,其中每个像素变成 3d“体素”。在这种情况下,这实际上是一大堆 1 单位立方体的 3d 投影,每个像素一个。堆栈的顺序控制体素的 z 顺序。听起来很接近吗? Voxel - en.wikipedia.org/wiki/Voxel 还有这个软件有描述:rezoner.itch.io/spritestack
  • 这里有一些 PyGame 3D 投影的示例代码:archive.petercollingridge.co.uk/book/export/html/460 不过它不会是“Hardware-3D”。
  • 是的,基本上是一个传统的精灵表,除了多个单元组成一个图像,形成一个体素类型的图像。这是一个看起来像 3D 的 2D 图像。
  • 我认为 3D 原则不适用于这种情况。

标签: python graphics pygame sprite game-development


【解决方案1】:

好的,所以我根据这个 PyGame + OpenGL 教程快速玩了一下:https://stackabuse.com/advanced-opengl-in-python-with-pygame-and-pyopengl/

这段代码运行得不是很好,但它确实将精灵表转换为体素,并将它们渲染到 PyGame 窗口中。真的,真的,真的很慢。至少对我来说,我猜它根本没有使用硬件 3D。但它确实允许以任何角度渲染模型。

最大的问题是它产生 32 * 32 * 19 = 19456 体素(减去透明体素)。优化代码以使相同颜色的相邻体素合并为一个矩形体素会很有帮助。

无论如何,也许它会推动您自己的解决方案发挥作用。

import pygame
import random
from pygame.locals import *

from OpenGL.GL import *
from OpenGL.GLU import *

DISPLAY_WIDTH   = 800
DISPLAY_HEIGHT  = 800
DISPLAY_SURFACE = DOUBLEBUF|OPENGL

# A Unit Cube from (0,0,0) -> (1,1,1)
cube_vertices = ( (1,1,1),
                  (1,1,0),
                  (1,0,0),
                  (1,0,1),
                  (0,1,1),
                  (0,1,0),
                  (0,0,0),
                  (0,0,1)  )

# The Surfaces making up each face-square
cube_surfaces = ( (0,1,2,3),
                  (3,2,7,6),
                  (6,7,5,4),
                  (4,5,1,0),
                  (1,5,7,2),
                  (4,0,3,6)  )


### Class to hold a cube's worth of verticies
class Cube():
    def __init__( self, xmove, ymove, zmove, colour ):
        global cube_vertices
        self.colour   = colour
        self.vertices = []
        for vert in cube_vertices:
            new_x = vert[0] + xmove
            new_y = vert[1] + ymove
            new_z = vert[2] + zmove
            self.vertices.append( [new_x, new_y, new_z] )

    def drawCube( self ):
        """ Call the openGL function to render the cube """
        global cube_surfaces
        glBegin(GL_QUADS)
        for surface in cube_surfaces:
            glColor3fv( self.colour )
            for vertex in surface:
                glVertex3fv( self.vertices[ vertex ])
        glEnd()


### Class to convert a 2D sprite sheet into a (huge) set of 
### voxels-per-pixels
class SpriteSheet3D():
    def __init__( self, sheet_filename, sheet_count, sprite_width, sprite_height ):
        self.cube_list = []
        self.image     = pygame.image.load( sheet_filename ).convert_alpha()
        # TODO: sanity check sprite image matches supplied dimensions

        # Iterate through each sheet's pixels creating cubes/voxels
        # TODO: optimise voxels to combine pixels of the same colour into a single, bigger prism
        for z in range( 0, sheet_count ):
            for y in range( 0, sprite_height ):
                for x in range( 0, sprite_width ):
                    offset_x = x + ( z * sprite_width )
                    pixel_colour = self.image.get_at( ( offset_x, y ) )
                    # Only create opaque pixels (Pixels are RGBA)
                    if ( pixel_colour[3] > 128 ):                     # TODO: handle translucent pixels
                        self.createVoxel( x, y, z, pixel_colour )

    def createVoxel( self, x, y, z, pixel_colour ):
        """ Create a 3D Unit-Cube at (x,y,z) """
        # Pygame maps colours 0-255 for RGBA
        # OpenGL maps colour 0-1 for RGB
        gl_colour = ( pixel_colour[0]/255, pixel_colour[1]/255, pixel_colour[2]/255 )
        self.cube_list.append( Cube( x, y, z, gl_colour ) )

    def drawCubes( self ):
        """ Paint all the cubes """
        for cube in self.cube_list:
            cube.drawCube()


### Main
pygame.init()
pygame.display.set_mode( ( DISPLAY_WIDTH, DISPLAY_HEIGHT ), DISPLAY_SURFACE )
glEnable( GL_DEPTH_TEST ) 
gluPerspective( 45, DISPLAY_WIDTH/DISPLAY_HEIGHT, 0.1, 200.0 )
glTranslatef( -16, -16, -150 )  # move camera here

sprite_3d = SpriteSheet3D( 'sprite_stack.png', 19, 32, 32 ) 

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

    glRotatef( 3.6, 0,1,1 )  # rotate 3.6 degrees about Y and Z-axiz

    glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT )
    sprite_3d.drawCubes()
    pygame.display.flip()
    pygame.time.wait(1)


pygame.quit()
quit()

【讨论】:

  • 我的程序有一个有点功能的转换器,其中每个像素都保存到一个带有 X、Y 和 Z(精灵表单元格编号)的文本文件中。文本文件还存储 RGBA 值。我最初的想法是允许为 RGBA 值分配物理属性,例如生命值、声音等。我相信我可能会在这个项目上达到我的数学极限。我想知道是否有一种“愚蠢”的方式可以至少获得设置的透视值,例如 90 度或 60 度,而无需将我的精灵表转换为实际的 3D 立方体。
  • 感谢您抽出宝贵的时间,顺便说一句,您想出的东西真的很酷,我很感激。
  • 很高兴你喜欢它 - 这是一个有趣的问题。
  • 我仍然认为如果我想设置视角,还有更简单的方法。如果我能弄清楚如何使用精灵表获得 90 度视图,我会是一个快乐的露营者。
猜你喜欢
  • 2022-10-04
  • 2012-12-25
  • 1970-01-01
  • 2013-05-28
  • 2014-12-25
  • 1970-01-01
  • 2016-10-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多