I'm a 17 year old programmer, trying to program an isometric game in python, with pygame. After finishing a tile engine, working with not good looking, gimp-drawn PNG's, I wondered, if it would be possible to render some Tiles by texture. I hope I provided all what's needed to understand, what's my issue and please excuse my not perfect English.
Simply what I want to do, is to generate a 128 by 128 Pixel width Image of an Isometric Tile, using the following picture as texture for all three sides of the Block:
(Links here because I'm not yet allowed to put pictures in, due to it's my first post)
To explain better, what I'm trying to do, I have drawn this little picture:
I have already searched the Internet for about 2 hours and didn't come to a solution, except for the top part of the Tile, here is what I already got in Code:
This is the Image Manipulation Module, the transformToRightPart() is the method where I need help:
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)
And this is the Module, which creates a window with the tile to render:
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)
The output of the code looks like this:
What I'm trying to achieve is, that it looks, something like this:
Big thanks to Spektre for all the effort he made, trying to help me, but all in all, after two days of over-thinking the problem and bug-fixing, I came up with a solution myself. It might not be as fast or efficient as targeting the pixels directly in an array, like Spektre did in his c++ example, but it is a way, you're only dependencies are pygame, and it is easy to understand.
What did I do? - I wrote two functions, the first getting a surface containing only a single column of another surface, with an index, referring to the x position of the column. And the second, calculating a coefficient, how far down each row should get moved, if the last row should get shifted down a certain amount of pixels and then returning a surface with the shifted picture.
Here is the magic Code:
After all, big respect to Spektres coding skills, even though I'm to dumb to understand anything from the c plus plus example, as I'm a total beginner.
Well I did this by simply copy the texture pixels into sprite using plane projections (basis vectors of each side) + some rescaling as the texture does not correspond with your sprite resolution. I did it in C++ so here my commented code (you can extract the equations from it):
The texture must be square otherwise the right side rendering will have access violation troubles not to mention visible seams ...
Here output sprite example of this code:
Now how it works:
ignore the VCL init/load/exit stuff handling images as the important stuff is just the rendering.
Each part consist of setting start point (red square) and convert texture
x,y
coordinates into offset from that start point in plane projection basis vectors (the black arrows).And the offset is also multiplied by the resolution ratio between texture and sprite to handle their different sizes.
Look here to understand the direct pixel access I used:
PS
You can add lighting to enhance 3D look ... This is how it looks when top side is 100% left side is 75% and right side is 50% of intensity:
simulating light coming from above left side