2017-06-18 125 views
-1

我是一个17岁的程序员,试图用pygame编写python中的等距游戏。在完成一个平铺引擎后,使用不好看的gimp-drawn PNG工作,我想知道,是否可以通过纹理渲染一些Tiles。我希望我提供了所有需要理解的内容,我的问题是什么,请原谅我的不完美的英语。Python,Pygame,图像处理:Restretch加载PNG,成为等轴测图的纹理

只要是我想做的事情,就是要产生一个等距瓷砖的128×128像素宽度图像,使用下面的图片作为纹理块的所有三个方面:

texture

(这里的环节,因为我还没有允许把图片中,由于这是我的第一篇)

更好地说明,我想要做的,我得出这个小图片: desired output

我已经在互联网上搜索了大约2个小时,但没有找到解决方案,除了平铺的顶部,这里是我已经得到的代码:

这是图像操作模块,在transformToRightPart()是在我需要帮助的方法:

import pygame 

class Image(object): 
    ''' 
    Use this Module to create Tiles by Texture to use them later in the Tileengine. 
    It is important to run pygame.init() before creating objects of this class! 
    Contains unfinished Elements! 
    ''' 
    def __init__(self, path): 
     self.loadFromPath(path) 

    def getIMG(self): 
     assert self.originalIMG is not None, "No picture to return" 
     if not self.IMG == None: 
      return self.IMG 
     else: 
      return self.originalIMG 

    def loadFromPath(self, path): 
     ''' 
     Don't do convert() or convert_alpha() here, 
     as Objects of this class are created during the loading process, 
     with no pygame.display() created. 
     ''' 
     self.originalIMG = pygame.image.load(path) 
     self.IMG = None 

    def transformToTopPart(self): 
     ''' 
     Transforms the loaded Image to the Top Part of an Isometric Tile, with the Dimensions 2:1, 
     said in Pixels: 128 px Width by 64 px Height. 
     ''' 
     self.IMG = pygame.transform.rotate(self.originalIMG, 45) 
     self.IMG = pygame.transform.scale(self.IMG, (128, 64)) 

    def transformToRightPart(self): 
     ''' 
     TODO!! Don't ask how (X.X) 
     Transforms the loaded Image to the right Part of an Isometric Tile. 
     ''' 
     assert False, "This method isn't finished, try something different ;)" 

    def transformToLeftPart(self): 
     ''' 
     Transforms the loaded Image to the left Part of an Isometric Tile. 
     Due to the nice geometric fact, that the shape of the left part, 
     is just the flipped right part shape and we don't lose quality by flipping, 
     we do this little trick, to enshorten the code. 
     ''' 
     self.originalIMG = pygame.transform.flip(self.originalIMG, True, False) 
     self.transformToRightPart() 
     self.IMG = pygame.transform.flip(self.IMG, True, False) 
     self.originalIMG = pygame.transform.flip(self.originalIMG, True, False) 

这是模块,它与瓷砖创建一个窗口来呈现:

import pygame, sys 

from ImageManipulation import Image 
from pygame.locals import * 

if __name__ == '__main__': 
    pygame.init() 
    FPS=20 
    fpsClock = pygame.time.Clock() 
    picture = Image("Stone_Floor_texture.png") 
    picture.transformToTopPart() 
    DISPLAY = pygame.display.set_mode((400,400),0,32) 
    while True: 
     for event in pygame.event.get(): 
      if event.type == QUIT: 
       pygame.quit() 
       sys.exit() 
     DISPLAY.blit(picture.getIMG(),(0,0)) 
     pygame.display.update() 
     fpsClock.tick(FPS) 

代码的输出是这样的:

my output

我想要实现的是,它看起来,像这样:

desired output

+0

请参阅[2D钻石(等距)地图编辑器 - 纹理无限延伸?](https://stackoverflow.com/a/36454198/2521214)的一些想法。是的,它可以用无缝纹理完成。通过将像素复制到等距平面视图中,使用3D渲染或2D。在后者中,您需要定义精灵的3个区域并将纹理像素映射到它。我不使用Python,所以我不能告诉你的代码有什么问题,但使用旋转看起来像矫枉过正,你只需交换基本向量... – Spektre

+0

非常感谢Spektre,用于改变我的问题链接到图片,并感谢您提到的文章,对我的proplem没有任何帮助,但是虽然很有趣! :D - 其实我是绑定到python的,所以不幸的是我不能使用3D渲染引擎。 – Blarify

+0

你有直接的像素访问?否则你将需要构建你的精灵与旋转翻译和裁剪/掩码,你需要知道坐标系... – Spektre

回答

1

非常感谢来Spektre所有他做出的努力,想帮我,但所有的一切,经过两天的过度思考问题和bug-修复,我自己想出了一个解决方案。它可能不如直接针对数组中的像素那样快速或有效,就像Spektre在他的C++示例中所做的那样,但这是一种方式,只有依赖关系是pygame,并且很容易理解。

我做了什么? - 我写了两个函数,第一个函数获取一个只包含另一个曲面的单个列的曲面,并带有一个索引,指向列的x位置。 第二,计算一个系数,每行应该移动多远,如果最后一行应该向下移动一定量的像素,然后返回一个表面与移动的图片。

这里是一个神奇的代码:

import pygame 

from pygame.locals import * 
from pygame import Surface 

def getColumn(surface, index): 
    assert index <= surface.get_width(), "index can't be bigger, than surface width" 
    height = surface.get_height() 
    subsurf = Surface((1,height)) # Create Surface 1 px by picture-height high, to store the output in 
    subsurf.blit(surface.subsurface(pygame.Rect((index,0),(1,height))),(0,0)) # Blit a one pixel width subsurface with x Position at index of the image to subsurf 
    return subsurf 

def shiftRightDown(surface, pixels): 
    size = surface.get_size() 
    newSize = (size[0], size[1]+pixels) 
    coeff = pixels/size[0] 
    returnSurface = Surface(newSize) 
    for i in range(size[1]): # here happens the magic 
     returnSurface.blit(getColumn(surface, i), (i,0+int(i*coeff))) 
    return returnSurface 

毕竟,大对于Spektres编码技能,即使我哑了解从C加再加例如任何事,因为我是一个总的初学者。

0

嗯,我这样做是简单地使用平面上的投影纹理像素复制到精灵(每边的基础向量)+一些缩放,因为纹理不符合你的精灵分辨率。我这样做是在C++所以在这里我注释掉的代码(您可以从中提取出方程式):

// [constants] 
const int sxs=128;    // target sprite resolution [pixels] 
const int sys=128; 
const int height=32;   // height/thickness of your tile [pixels] 
const DWORD cback=0x00FFFFFF; // background color (or invisible for the sprite) 

// [variables] 
DWORD **ptxr,**pspr;   // direct pixel access pointers (any 32bit variable type) 
Graphics::TBitmap *txr,*spr; // VCL bitmaps 
int txs,tys,x,y,x0,y0,xx,yy,th; 

// [init] 
// create VCL bitmaps (can ignore this) 
txr=new Graphics::TBitmap; // input texture 
spr=new Graphics::TBitmap; // output sprite 
// load texture 
txr->LoadFromFile("texture.bmp"); 
txs=txr->Width; 
tys=txr->Height; 

// prepare sprite resolution 
spr->SetSize(sxs,sys); 
// allow direct pixel access 
txr->HandleType=bmDIB; txr->PixelFormat=pf32bit; ptxr=new DWORD*[tys]; for (y=0;y<tys;y++) ptxr[y]=(DWORD*)txr->ScanLine[y]; 
spr->HandleType=bmDIB; spr->PixelFormat=pf32bit; pspr=new DWORD*[sys]; for (y=0;y<sys;y++) pspr[y]=(DWORD*)spr->ScanLine[y]; 

// [render sprite] 
th=height*(txs-1)/(sxs-1); // height of tile in texture [pixels] 
// clear 
for (y=0;y<sys;y++) 
for (x=0;x<sxs;x++) 
    pspr[y][x]=cback; 
// top side 
x0=0; y0=(sys*3/4)-height; 
for (y=0;y<tys;y++) 
for (x=0;x<txs;x++) 
    { 
    // isometric projection of top side 
    xx=x0+(x+y)*(sxs-1)/((txs-1)*2); 
    yy=y0+(x-y)*(sxs-1)/((txs-1)*4); 
    // copy pixel from texture to sorite 
    if ((xx>=0)&&(xx<sxs)&&(yy>=0)&&(yy<sys)) 
    pspr[yy][xx]=ptxr[y][x]; 
    } 
// left side 
x0=0; y0=(sys*3/4)-height; 
for (y=0;(y<tys)&&(y<th);y++) 
for (x=0;x<txs;x++) 
    { 
    // isometric projection of top side 
    xx=x0+(x  )*(sxs-1)/((txs-1)*2); 
    yy=y0+(x+(4*y))*(sxs-1)/((txs-1)*4); 
    // copy pixel from texture to sorite 
    if ((xx>=0)&&(xx<sxs)&&(yy>=0)&&(yy<sys)) 
    pspr[yy][xx]=ptxr[y][x]; 
    } 
// right side 
x0=sxs/2; y0=sys-height-1; 
for (y=0;(y<txs)&&(y<th);y++) // x,y are swapped to avoid connection seems 
for (x=0;x<tys;x++) 
    { 
    // isometric projection of top side 
    xx=x0+(+x  )*(sxs-1)/((txs-1)*2); 
    yy=y0+(-x+(4*y))*(sxs-1)/((txs-1)*4); 
    // copy pixel from texture to sorite 
    if ((xx>=0)&&(xx<sxs)&&(yy>=0)&&(yy<sys)) 
    pspr[yy][xx]=ptxr[x][y]; 
    } 

// here do your stuff with your sprite spr I render source and resulting images into bitmap to show on screen 
// you can ignoe this 
bmp->SetSize(txs+5+sxs,max(tys,sys)); 
bmp->Canvas->Brush->Color=clBtnFace; 
bmp->Canvas->FillRect(TRect(0,0,bmp->Width,bmp->Height)); 
bmp->Canvas->Draw(0,0,txr); 
bmp->Canvas->Draw(txs+5,0,spr); 

// [exit] 
// release memory 
delete[] ptxr; 
delete[] pspr; 
if (txr) delete txr; txr=NULL; 
if (spr) delete spr; spr=NULL; 

质地必须是正方形否则右侧渲染将有访问冲突的烦恼不提可见接缝...此代码的

输出。这里的精灵例如:

rendered sprite

现在,它的工作原理:

overview

忽略VCL的init /负载/退出的东西处理图像作为重要的东西就是渲染。

每个部分由设置起始点(红色正方形)组成,并将纹理的坐标转换为平面投影基向量(黑色箭头)中该起始点的偏移量。

偏移量也乘以纹理和精灵之间的分辨率比率来处理它们的不同大小。

看这里了解我所使用的直接像素访问:

PS

您可以添加照明,以提高3D看......这是怎么回事当顶侧是100%左侧时看起来是75%,右侧是强度的50%:

lighting

模拟光从上面左侧