Python: pygame.gfxdraw.texturedpolygon - python

So, I've been coding in Python for a few weeks and I felt like trying to make a game. I've gotten to a point where I have no clue what is wrong with my code except for the fact that when I try to draw a textured polygon the texture stretches across the display and only reveals on the cubes. It creates a really cool effect but it isn't what I'm looking for. The goal is to render the texture onto the face of the cube like in Minecraft.
import pygame, sys, os, math
from pygame import gfxdraw
def rotate2d(pos,rad) : x,y=pos; s,c = math.sin(rad), math.cos(rad); return x*c-y*s, y*c+x*s
class Cam:
def __init__(self,pos=(0,0,0), rot=(0,0)):
self.pos = list(pos)
self.rot = list(rot)
def events (self, event):
if event.type == pygame.MOUSEMOTION:
x,y = event.rel; x/=200; y/=200
self.rot[0]+=y; self.rot[1]+=x
def update(self,dt,key):
s = dt*10
if key[pygame.K_c]: self.pos[1]+=s
if key[pygame.K_SPACE]: self.pos[1]-=s
x,y = s*math.sin(self.rot[1]),s*math.cos(self.rot[1])
if self.rot[0] <= -1.58:
self.rot[0] = -1.58
if self.rot[0] >= 1.58:
self.rot[0] = 1.58
if key[pygame.K_w]: self.pos[0]+=x; self.pos[2]+=y
if key[pygame.K_s]: self.pos[0]-=x; self.pos[2]-=y
if key[pygame.K_a]: self.pos[0]-=y; self.pos[2]+=x
if key[pygame.K_d]: self.pos[0]+=y; self.pos[2]-=x
if key[pygame.K_ESCAPE]: pygame.quit(); sys.exit() #Quits Game#
pygame.init()
w,h = 800,600; cx,cy = w//2, h//2; fov = min(w,h)
os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.display.set_caption('PyCraft')
screen = pygame.display.set_mode((w,h))
clock = pygame.time.Clock()
## Textures ##
dirt_texture = pygame.image.load("textures/dirt.png").convert()
stone_texture = pygame.image.load("textures/stone.png").convert()
bedrock_texture = pygame.image.load("textures/bedrock.png").convert()
class Dirt:
vertices = (-1,-1,-1),(1,-1,-1),(1,1,-1),(-1,1,-1),(-1,-1,1),(1,-1,1),(1,1,1),(-1,1,1)
edges = (0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),(0,4),(1,5),(2,6),(3,7)
faces = (0,1,2,3),(4,5,6,7),(0,1,5,4),(2,3,7,6),(0,3,7,4),(1,2,6,5)
colours = (225,10,0),(255,128,0),(10,250,250),(128,128,128),(0,0,255),(0,255,0)
texture = dirt_texture
def __init__(self,pos=(0,0,0)):
x,y,z = pos
self.verts = [(x+X/2,y+Y/2,z+Z/2) for X,Y,Z in self.vertices]
class Stone:
vertices = (-1,-1,-1),(1,-1,-1),(1,1,-1),(-1,1,-1),(-1,-1,1),(1,-1,1),(1,1,1),(-1,1,1)
edges = (0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),(0,4),(1,5),(2,6),(3,7)
faces = (0,1,2,3),(4,5,6,7),(0,1,5,4),(2,3,7,6),(0,3,7,4),(1,2,6,5)
colours = (0,255,0),(255,128,0),(0,0,250),(128,133,128),(0,0,200),(200,0,255)
texture = stone_texture
def __init__(self,pos=(0,0,0)):
x,y,z = pos
self.verts = [(x+X/2,y+Y/2,z+Z/2) for X,Y,Z in self.vertices]
def TextUI(msg,colour,pos):
screen_text = font.render(msg, True, colour)
screen.blit(screen_text, (pos))
def Draw_Crosshair(colour, width):
pygame.draw.line(screen, colour, (cursorpos1, cursorpos2), (cursorpos3,cursorpos4), width)
pygame.draw.line(screen, colour, (cursorpos5, cursorpos6), (cursorpos7,cursorpos8), width)
cam = Cam((0,0,-5))
pygame.event.get(); pygame.mouse.get_rel()
pygame.mouse.set_visible(0); pygame.event.set_grab(1)
font = pygame.font.SysFont("calibri", 25)
cursorpos1, cursorpos2, cursorpos3, cursorpos4 = w/2, h/2+10, w/2, h/2-10
cursorpos5, cursorpos6, cursorpos7, cursorpos8 = w/2+10, h/2, w/2-10, h/2
cubes = [Dirt((0,0,0)),Stone((2,0,0)),Stone((-2,0,0)),Dirt((0,2,0)),Stone((2,2,0)),Dirt((-2,2,0))]
##cubes = [Dirt((1,0,0)), Dirt((2,0,0)),Dirt((3,0,0)),Dirt((4,0,0)),Dirt((5,0,0)),Dirt((5,1,0)),Dirt((6,1,0)),]
while True:
dt = clock.tick()/1000
for event in pygame.event.get():
if event.type == pygame.QUIT: pygame.quit(); sys.exit()
cam.events(event)
screen.fill((100,100,100))
face_list = []; face_colour = []; depth = [];
for obj in cubes:
vert_list = []; screen_coords = []
for x,y,z in obj.verts:
x-=cam.pos[0]; y-=cam.pos[1]; z-=cam.pos[2]
x,z = rotate2d((x,z), cam.rot[1])
y,z = rotate2d((y,z), cam.rot[0])
vert_list += [(x,y,z)]
f = fov/z
x,y = x*f,y*f
screen_coords+=[(cx+int(x),cy+int(y))]
for f in range (len(obj.faces)):
face = obj.faces[f]
on_screen = False
for i in face:
x,y = screen_coords[i]
if vert_list[i][2]>0 and x>0 and x<w and y>0 and y<h: on_screen = True ; break
if on_screen:
coords = [screen_coords[i] for i in face]
face_list += [coords]
face_colour += [obj.colours[f]]
depth += [sum(sum(vert_list[j][i] for j in face) **2 for i in range (3))]
order = sorted(range(len(face_list)), key=lambda i:depth[i], reverse=1)
for i in order:
try: pygame.gfxdraw.textured_polygon(screen, face_list[i], obj.texture, 0, 0); pygame.gfxdraw.aapolygon(screen, face_list[i], (0,0,0))
except: pass
## OLD CODE - pygame.draw.polygon(screen, face_colour[i], face_list[i]) ##
## NEW CODE - pygame.gfxdraw.textured_polygon(screen, face_list[i], obj.texture, 0, 0) ##
## TEST CODE - pygame.gfxdraw.filled_polygon(screen, face_list[i], face_colour[i]) ##
Draw_Crosshair((255,255,255),2)
TextUI("Press ESCAPE to exit", (255,255,255), (20,20))
TextUI("Movement Controls: W, A, S, D, SPACE and C", (255,255,255), (20,60))
pygame.display.update()
key = pygame.key.get_pressed()
cam.update(dt, key)

That is beautiful code which works exactly as expected, the 2-dimensional polygon is filled with flat texture. To move beyond 2-dimensions to render solid objects, still from pygame, you need to move to OpenGL.

Related

Pygame: Sprites spawn close to each other [duplicate]

Right now, my game blits all the images in random positions correctly and also gets the rect of the images correctly, but I can´t figure out how to use colliderect to make sure the images don´t overlap. How could it work for my code?
Also I´m trying to make the first text fade out and I don´t know why it doesn´t work for me.
Here is the code:
class GAME1:
def __init__(self, next_scene):
self.background = pygame.Surface(size)
# Create an array of images with their rect
self.images = []
self.rects = []
self.imagenes1_array = ['autobus.png','coche.png','barco.png','autobus2.png','grua.png','bici.png']
for i in self.imagenes1_array:
# We divide in variables so we can then get the rect of the whole Img (i2)
i2 = pygame.image.load(i)
self.images.append(i2)
s = pygame.Surface(i2.get_size())
r = s.get_rect()
# Trying to use colliderect so it doesnt overlap
if pygame.Rect.colliderect(r,r) == True:
x = random.randint(300,1000)
y = random.randint(200,700)
self.rects.append(r)
def start(self, gamestate):
self.gamestate = gamestate
for rect in self.rects:
# Give random coordinates (we limit the dimensions (x,y))
x = random.randint(300,1000)
y = random.randint(200,700)
rect.x = x
rect.y = y
def draw(self,screen):
self.background = pygame.Surface(size)
font = pygame.font.SysFont("comicsansms",70)
# First half (Show image to remember)
text1 = font.render('¡A recordar!',True, PURPLE)
text1_1 = text1.copy()
# This surface is used to adjust the alpha of the txt_surf.
alpha_surf = pygame.Surface(text1_1.get_size(), pygame.SRCALPHA)
alpha = 255 # The current alpha value of the surface.
if alpha > 0:
alpha = max(alpha-4, 0)
text1_1 = text1.copy()
alpha_surf.fill((255, 255, 255, alpha))
text1_1.blit(alpha_surf, (0,0), special_flags = pygame.BLEND_RGBA_MULT)
screen.blit(text1_1, (600,50))
# Second half (Show all similar images)
text2 = font.render('¿Cuál era el dibujo?',True, PURPLE)
#screen.blit(text2, (500,50))
for i in range(len(self.images)):
#colliding = pygame.Rect.collidelistall(self.rects)
screen.blit(self.images[i], (self.rects[i].x, self.rects[i].y))
def update(self, events, dt):
for event in events:
if event.type == pygame.MOUSEBUTTONDOWN:
for rect in self.rects:
if rect.collidepoint(event.pos):
print('works!')
Use collidelist() to test test if one rectangle in a list intersects:
for i in self.imagenes1_array:
s = pygame.image.load(i)
self.images.append(s)
r = s.get_rect()
position_set = False
while not position_set:
r.x = random.randint(300,1000)
r.y = random.randint(200,700)
margin = 10
rl = [rect.inflate(margin*2, margin*2) for rect in self.rects]
if len(self.rects) == 0 or r.collidelist(rl) < 0:
self.rects.append(r)
position_set = True
See the minimal example, that uses the algorithm to generate random not overlapping rectangles:
import pygame
import random
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
def new_recs(rects):
rects.clear()
for _ in range(10):
r = pygame.Rect(0, 0, random.randint(30, 40), random.randint(30, 50))
position_set = False
while not position_set:
r.x = random.randint(10, 340)
r.y = random.randint(10, 340)
margin = 10
rl = [rect.inflate(margin*2, margin*2) for rect in rects]
if len(rects) == 0 or r.collidelist(rl) < 0:
rects.append(r)
position_set = True
rects = []
new_recs(rects)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.KEYDOWN:
new_recs(rects)
window.fill(0)
for r in rects:
pygame.draw.rect(window, (255, 0, 0), r)
pygame.display.flip()
pygame.quit()
exit()

Depth issue with 3D graphics

I copied a code off YouTube recently, about 3d graphics in Python without the use of 3D modules, like OpenGL. The video worked with only cubes, so I tried adding a tetrahedron to the shapes, and things started popping up where they weren't supposed to. The code is as follows:
import pygame, sys, math, random
def rotate2d(pos, rad): x,y=pos; s,c = math.sin(rad),math.cos(rad); return x*c-y*s,y*c+x*s
class Cam:
def __init__(self, pos=(0,0,0),rot=(0,0)):
self.pos = list(pos)
self.rot = list(rot)
def events(self, event):
if event.type == pygame.MOUSEMOTION:
x, y = event.rel; x/=200; y/=200
self.rot[0]+=y; self.rot[1]+=x
def update(self, dt, key):
s = dt*10
if key[pygame.K_q]: self.pos[1]+=s
if key[pygame.K_e]: self.pos[1]-=s
x,y = s*math.sin(self.rot[1]),s*math.cos(self.rot[1])
if key[pygame.K_w]: self.pos[0]+=x; self.pos[2]+=y
if key[pygame.K_s]: self.pos[0]-=x; self.pos[2]-=y
if key[pygame.K_a]: self.pos[0]-=y; self.pos[2]+=x
if key[pygame.K_d]: self.pos[0]+=y; self.pos[2]-=x
if key[pygame.K_r]: self.pos[0]=0; self.pos[1]=0;\
self.pos[2]=-5; self.rot[0]=0; self.rot[1]=0
class Cube:
faces = (0,1,2,3),(4,5,6,7),(0,1,5,4),(2,3,7,6),(0,3,7,4),(1,2,6,5)
colors = (255,0,0),(255,128,0),(255,255,0),(255,255,255),(0,0,255),(0,255,0)
def __init__(self,pos=(0,0,0),v0=(-1,-1,-1),v1=(1,-1,-1),v2=(1,1,-1),v3=(-1,1,-1),v4=(-1,-1,1),v5=(1,-1,1),v6=(1,1,1),v7=(-1,1,1)):
self.vertices = (v0,v1,v2,v3,v4,v5,v6,v7)
x,y,z = pos
self.verts = [(x+X/2,y+Y/2,z+Z/2) for X,Y,Z in self.vertices]
class Tetrahedron:
faces = (1,2,3),(0,1,2),(0,1,3),(0,2,3)
colors = (255,0,0),(255,128,0),(255,255,0),(255,255,255)
def __init__(self,pos=(0,0,0),v0=(0,0,.866),v1=(-.866,-1,-1),v2=(-.866,1,-1),v3=(.866,0,-1)):
self.vertices = (v0,v1,v2,v3)
x,y,z = pos
self.verts = [(x+X/2,y+Y/2,z+Z/2) for X,Y,Z in self.vertices]
pygame.init()
w,h = 400,400; cx,cy = w//2, h//2
screen = pygame.display.set_mode((w,h))
clock = pygame.time.Clock()
cam = Cam((0,0,-5))
pygame.event.get(); pygame.mouse.get_rel()
pygame.mouse.set_visible(0); pygame.event.set_grab(1)
objects = [Cube((0,0,0)),Cube((0,0,2)),Tetrahedron((0,0,1))]
while True:
dt = clock.tick()/1000
for event in pygame.event.get():
if event.type == pygame.QUIT: pygame.quit(); sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE: pygame.quit(); sys.exit()
cam.events(event)
screen.fill((0,0,0))
face_list = []; face_color = []; depth = []
for obj in objects:
vert_list = []; screen_coords = []
for x,y,z in obj.verts:
x-=cam.pos[0]; y-=cam.pos[1];z-=cam.pos[2]
x,z=rotate2d((x,z),cam.rot[1])
y,z = rotate2d((y,z),cam.rot[0])
vert_list += [(x,y,z)]
f = 200/z
x,y = x*f,y*f
screen_coords+=[(cx+int(x),cy+int(y))]
for f in range(len(obj.faces)):
face = obj.faces[f]
on_screen = False
for i in face:
x,y = screen_coords[i]
if vert_list[i][2]>0 and x>0 and x<w and y>0 and y<h: on_screen = True; break
if on_screen:
coords = [screen_coords[i] for i in face]
face_list += [coords]
face_color += [obj.colors[f]]
depth += [sum(sum(vert_list[j][i] for j in face)**2 for i in range(3))]
order = sorted(range(len(face_list)),key=lambda i:depth[i],reverse=1)
for i in order:
try: pygame.draw.polygon(screen,face_color[i],face_list[i])
except: pass
key = pygame.key.get_pressed()
pygame.display.flip()
cam.update(dt,key)
My problem is that the tetrahedron part is showing up through the cubes on either side, when the cam.pos is at least 7.5 away from the object:
As you can see, the tetrahedron is supposed to be buried under the cube, but it's still showing up. Can someone please suggest an edit to the code which will stop the tetrahedron from showing up through any objects?
What you actually try to do is to sort the faces by the square of the Euclidean distance to the vertices of the face.
But sum(vert_list[j][i] for j in face)**2 does not compute the square of the euclidean distance. It computes the square of the sum of the components of a coordinate. The square of the euclidean distance is
vert_list[j][0]**2 + vert_list[j][1]**2 + vert_list[j][2]**2
That is not the same as
(vert_list[j][0] + vert_list[j][1] + vert_list[j][2])**2
Furthermore you do not take into account the number of vertices of a face, because the squared distances are summed.
Note, the faces of the cube are quads and have 4 vertices, but the faces of the tetrahedron are triangles and have 3 vertices.
Correct the computation of the squared distance and divide by the number of vertices, to solve the issue:
depth += [sum(sum(vert_list[j][i] for j in face)**2 for i in range(3))]
depth += [sum(sum(vert_list[j][i]**2 for i in range(3)) for j in face) / len(face)]
But note, if faces are close to another there still may occur an issue (e.g. covering faces in the same plane). That is not an exact algorithm. It is not designed for "touching" objects.

Putting the scrolling camera in a mini-window in pygame

I'm trying to create a game where the action is shown in a little box within the main screen object, freeing up the surrounding space for text and menus and what-not. Since the map is larger than the allotted window, I coded a basic "camera" that follows the player around. It mostly works, but I'm having trouble "trimming off" the area outside of this window.
Here's the relevant bits of code (EDITED to provide Working Example):
import pygame, os, sys
from pygame.locals import *
pygame.init()
RIGHT = 'RIGHT'
LEFT = 'LEFT'
UP = 'UP'
DOWN = 'DOWN'
class Camera():
def __init__(self, screen, x_ratio = 1, y_ratio = 1, x_offset = 0, y_offset = 0):
self.screen = screen.copy()
self.rec = self.screen.get_rect()
self.rec.width *= x_ratio
self.rec.height *= y_ratio
self.x_offset = x_offset
self.y_offset = y_offset
def get_pos(self):
return (self.x_offset - self.rec.x, self.y_offset - self.rec.y)
def get_window(self):
w = pygame.Rect(self.rec)
w.topleft = (0 - self.rec.x, 0 - self.rec.y)
return w
def move(self, x, y):
"""Move camera into new position"""
self.rec.x = x
self.rec.y = y
def track(self, obj):
while obj.rec.left < self.rec.left:
self.rec.x -= 1
while obj.rec.right > self.rec.right:
self.rec.x += 1
while obj.rec.top < self.rec.top:
self.rec.y -= 1
while obj.rec.bottom > self.rec.bottom:
self.rec.y += 1
class Map:
def __init__(self, width, height):
self.width = width
self.height = height
self.rec = pygame.Rect(0,0,self.width,self.height)
def draw(self, screen):
pygame.draw.rect(screen, (200,200,200), self.rec)
class Obj:
def __init__(self, char, x = 0, y = 0, width = 0, height = 0):
self.width = width
self.height = height
self.rec = pygame.Rect(x, y, width, height)
self.cur_map = None
self.timers = {}
#Dummying in chars for sprites
self.char = char
self.x_dir = 1
self.y_dir = 1
self.speed = 1
self.moving = False
def move(self):
if self.x_dir != 0 or self.y_dir != 0:
new_x = self.rec.x + (self.x_dir*self.speed)
new_y = self.rec.y + (self.y_dir*self.speed)
new_rec = pygame.Rect(new_x, new_y, self.width, self.height)
#Keep movement within bounds of map
while new_rec.left < self.cur_map.rec.left:
new_rec.x += 1
while new_rec.right > self.cur_map.rec.right:
new_rec.x -= 1
while new_rec.top < self.cur_map.rec.top:
new_rec.y += 1
while new_rec.bottom > self.cur_map.rec.bottom:
new_rec.y -= 1
self.rec = new_rec
def set_dir(self, d):
self.x_dir = 0
self.y_dir = 0
if d == LEFT:
self.x_dir = -1
elif d == RIGHT:
self.x_dir = 1
elif d == UP:
self.y_dir = -1
elif d == DOWN:
self.y_dir = 1
def set_moving(self, val = True):
self.moving = val
class Game:
def __init__(self):
self.screen_size = (800, 600)
self.screen = pygame.display.set_mode(self.screen_size)
self.map_screen = self.screen.copy()
self.title = 'RPG'
pygame.display.set_caption(self.title)
self.camera = Camera(self.screen, 0.75, 0.75)#, 10, 75)
self.fps = 80
self.clock = pygame.time.Clock()
self.debug = False
self.bg_color = (255,255,255)
self.text_size = 18
self.text_font = 'Arial'
self.text_style = pygame.font.SysFont(self.text_font, self.text_size)
self.key_binds = {LEFT : [K_LEFT, K_a], RIGHT : [K_RIGHT, K_d], UP : [K_UP, K_w], DOWN : [K_DOWN, K_s],
'interact' : [K_RETURN, K_z], 'inventory' : [K_i, K_SPACE], 'quit' : [K_ESCAPE]}
self.player = Obj('p', 0, 0, 10, self.text_size)
def draw(self, obj):
char = obj.char
self.draw_text(char, obj.rec.x, obj.rec.y, screen = self.map_screen)
def draw_text(self, text, x, y, color = (0,0,0), screen = None):
textobj = self.text_style.render(text, 1, color)
textrect = textobj.get_rect()
textrect.x = x
textrect.y = y
if screen == None:
"""Use default screen"""
self.screen.blit(textobj, textrect)
else:
screen.blit(textobj, textrect)
def play(self):
done = False
cur_map = Map(800, 800)
self.map_screen = pygame.Surface((cur_map.width, cur_map.height))
self.map_screen.fill(self.bg_color)
bg = pygame.Surface((cur_map.width, cur_map.height))
cur_map.draw(bg)
self.player.cur_map = cur_map
while not done:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key in self.key_binds[LEFT]:
self.player.set_dir(LEFT)
self.player.set_moving()
elif event.key in self.key_binds[RIGHT]:
self.player.set_dir(RIGHT)
self.player.set_moving()
elif event.key in self.key_binds[UP]:
self.player.set_dir(UP)
self.player.set_moving()
elif event.key in self.key_binds[DOWN]:
self.player.set_dir(DOWN)
self.player.set_moving()
elif event.type == KEYUP:
self.player.set_moving(False)
if self.player.moving:
self.player.move()
self.camera.track(self.player)
self.clock.tick()
self.screen.fill(self.bg_color)
self.map_screen.blit(bg, (0,0))
self.draw(self.player)
pygame.draw.rect(self.map_screen, (0,0,0), self.camera.rec, 1)
#self.screen.blit(self.map_screen, (0,0), [0 - self.camera.rec.x, 0 - self.camera.rec.y, self.camera.rec.width, self.camera.rec.height])
self.screen.blit(self.map_screen, self.camera.get_pos(), self.camera.get_window())
pygame.display.flip()
game = Game()
game.play()
Moving the player past past the bounds of the camera's window causes the window to roll up completely and disappear. I tried adjusting the blitting coordinates, as advised earlier, but it seems to only change the direction in which the window rolls up.
From your updated code, the blitting coordinates for self.screen.blit(...) are still changing: self.camera.get_window() changes value because rec.x and rec.y are values referring to the player position within the map. Hence you should define a constant minimap coordinate, this should be the same as the camera offset.
self.screen.blit(self.map_screen, (self.camera.x_offset,self.camera.y_offset), (*self.camera.get_pos(), self.camera.rec.width, self.camera.rec.height))
Change the Camera().get_pos() to:
def get_pos(self):
return (self.rec.x, self.rec.y)
I believe I only changed the self.screen.blit(...) and stopped using or rewrote your Camera functions as you're confusing yourself with all the rec variables.
To illustrate it working amend the Map().draw(screen) to:
def draw(self, screen):
pygame.draw.rect(screen, (200,200,200), self.rec)
pygame.draw.circle(screen, (255, 255, 255), (50, 50), 20, 2)
One tip as well don't draw the entire map at each loop, just the part that will be visible.

Python - 3D mapping, without 3D engines

Does anyone know how to do this?
I want to be able to map a texture to a 3d cube in Python.
here's my code so far,
from pygame import gfxdraw
import pygame, sys, math
def rotate2d(pos,rad): x,y=pos; s,c = math.sin(rad),math.cos(rad); return x*c-y*s,y*c+x*s
class Cam:
def __init__(self,pos=(0,0,0),rot=(0,0)):
self.pos=list(pos)
self.rot=list(rot)
def events(self,event):
if event.type == pygame.MOUSEMOTION:
x,y = event.rel
x/=200;y/=200
self.rot[0]+=y; self.rot[1]+=x
def update(self,dt,key):
s=dt*10
if key[pygame.K_q]: self.pos[1]+=s
if key[pygame.K_e]: self.pos[1]-=s
x,y = s*math.sin(self.rot[1]),s*math.cos(self.rot[1])
if key[pygame.K_w]: self.pos[0]+=x;self.pos[2]+=y
if key[pygame.K_s]: self.pos[0]-=x;self.pos[2]-=y
if key[pygame.K_a]: self.pos[0]-=y;self.pos[2]+=x
if key[pygame.K_d]: self.pos[0]+=y;self.pos[2]-=x
class Cube:
vertices = (-1,-1,-1),(1,-1,-1),(1,1,-1),(-1,1,-1),(-1,-1,1),(1,-1,1),(1,1,1),(-1,1,1)
edges = (0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),(0,4),(1,5),(2,6),(3,7)
faces = (0,1,2,3),(4,5,6,7),(0,1,5,4),(2,3,7,6),(0,3,7,4),(1,2,6,5)
colors = (255,0,0),(0,255,0),(0,0,255),(255,255,0),(255,0,255),(0,255,255)
def __init__(self,pos=(0,0,0)):
x,y,z = pos
self.verts = [(x+X/2,y+Y/2,z+Z/2) for X,Y,Z in self.vertices]
pygame.init()
w,h = 400,400; cx,cy = w//2,h//2; fov = min(w,h)
screen = pygame.display.set_mode((w,h))
clock = pygame.time.Clock()
cam=Cam((0,0,-5))
pygame.event.get(); pygame.mouse.get_rel()
pygame.mouse.set_visible(0); pygame.event.set_grab(1);
i1 = pygame.image.load("Untitled.png")
cubes = [Cube((0,0,0)),Cube((-2,0,0)),Cube((2,0,0))]
while True:
dt = clock.tick()/1000
event = pygame.event.poll()
if event.type == pygame.QUIT: pygame.quit();sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE: pygame.quit();sys.exit()
cam.events(event)
screen.fill((0,0,0))
face_list = []; face_color = []; depth = []
for obj in cubes:
vert_list = []; screen_coords = []
for x,y,z in obj.verts:
x-=cam.pos[0]
y-=cam.pos[1]
z-=cam.pos[2]
x,z = rotate2d((x,z),cam.rot[1])
y,z = rotate2d((y,z),cam.rot[0])
vert_list += [(x,y,z)]
f=fov/z
x,y = x*f,y*f
screen_coords+=[(cx+int(x),cy+int(y))]
for f in range(len(obj.faces)):
face = obj.faces[f]
on_screen = False
for i in face:
x,y = screen_coords[i]
if vert_list[i][2]>0 and x>0 and x<w and y>0 and y<h: on_screen = True; break
if on_screen:
coords = [screen_coords[i] for i in face]
face_list += [coords]
face_color += [obj.colors[f]]
depth += [sum([sum(vert_list[j][i] for j in face)**2 for i in range(3)])]
order = sorted(range(len(face_list)),key=lambda i: depth[i],reverse=1)
for i in order:
try:
x-=cam.pos[0]
y-=cam.pos[1]
z-=cam.pos[2]
x,z = rotate2d((x,z),cam.rot[1])
y,z = rotate2d((y,z),cam.rot[0])
vert_list += [(x,y,z)]
f=fov/z
x,y = x*f,y*f
#x2 = str(x)
#x2 = int(x2.split('.')[0])
#{
# HERE IS THE TEXTURED POLYGON PART!!!!! VVVVVVVV
pygame.gfxdraw.textured_polygon(screen, face_list[i], i1, 0, 0) # !!!
#}
except:
pass
pygame.display.flip()
key=pygame.key.get_pressed()
cam.update(dt/10,key)
So far I'm using gfxdraw.textured_polygon, however, that's meant for 2d. So it looks masked and weird. I've tried using the x,y values instead of the 0,0 in the offsetting part.
I followed this tutorial to do this: https://www.youtube.com/watch?v=g4E9iq0BixA
Any help wanted.
If you need anything else please ask.

How can I mouse-scroll around a 2D tile map in pygame?

I am trying to play around with pygame and I have using some examples that I found to learn and create a simple. My next goal is to create a 2D tile map bigger than the screen size and then being able to scroll around with the mouse. I would like to make something similar as a strategic game, where if you move the mouse to the edges of the screen, the "camara" will move in that direction, showing that part of the map (I would like also to stop the camara if it reaches the end of the map). At this moment, if I hover the mouse over the edges, it will only move once.
import pygame, os
from pygame.locals import *
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
SCREEN_WIDTH = 5*40
SCREEN_HEIGHT = 7*40
#functions to create our resources
def load_image(name, colorkey=None):
try:
image = pygame.image.load(name)
except pygame.error, message:
print 'Cannot load image:', name
raise SystemExit, message
image = image.convert_alpha()
if colorkey is not None:
if colorkey is -1:
colorkey = image.get_at((0,0))
image.set_colorkey(colorkey, RLEACCEL)
return image, image.get_rect()
#classes for our game objects
class Camera(object):
def __init__(self, camera_func, width, height):
self.camera_func = camera_func
self.state = pygame.Rect(100,100, width, height)
def apply(self, rect):
l, t, w, h = rect
if 0 <= self.state[0] <= (SCREEN_WIDTH/5):
l += 10
elif (SCREEN_WIDTH - (SCREEN_WIDTH/5)) < self.state[0] <= SCREEN_WIDTH:
l -=10
if 0 <= self.state[1] <= (SCREEN_HEIGHT/5):
t += 10
elif (SCREEN_HEIGHT - (SCREEN_HEIGHT/5)) < self.state[1] <= SCREEN_HEIGHT:
t -=10
return rect.move(l,t)
def update(self):
pos = pygame.mouse.get_pos()
self.state.topleft = pos
#self.state = self.camera_func(self.state)
def complex_camera(camera):
l, t, w, h = camera
l, t, _, _ = -l, -t, w, h
l = min(0, l) # stop scrolling at the left edge
l = max(-(camera.width-SCREEN_WIDTH), l) # stop scrolling at the right edge
t = max(-(camera.height-SCREEN_HEIGHT), t) # stop scrolling at the bottom
t = min(0, t) # stop scrolling at the top
return pygame.Rect(l, t, w, h)
def main ():
pygame.init()
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
pygame.display.set_caption("W40K")
grasstile = pygame.image.load('./textures/grass.png')
watertile = pygame.image.load('./textures/water.png')
waterbeach = pygame.image.load('./textures/dirt.png')
grassrect = grasstile.get_rect()
waterrect = watertile.get_rect()
waterb = waterbeach.get_rect()
TILESIZE = 40
tilemap = [
[grasstile,grasstile,waterbeach,waterbeach,watertile,watertile,waterbeach,waterbeach,grasstile,grasstile,waterbeach,watertile,watertile,watertile,waterbeach,waterbeach,waterbeach,grasstile,grasstile,waterbeach],
[grasstile,waterbeach,waterbeach,waterbeach,watertile,watertile,watertile,waterbeach,grasstile,grasstile,waterbeach,watertile,watertile,watertile,waterbeach,waterbeach,waterbeach,grasstile,grasstile,waterbeach],
[waterbeach,waterbeach,waterbeach,waterbeach,watertile,watertile,watertile,waterbeach,grasstile,grasstile,waterbeach,watertile,watertile,watertile,waterbeach,waterbeach,waterbeach,grasstile,grasstile,waterbeach],
[grasstile,waterbeach,waterbeach,waterbeach,waterbeach,waterbeach,grasstile,waterbeach,grasstile,grasstile,waterbeach,watertile,watertile,watertile,waterbeach,waterbeach,waterbeach,grasstile,grasstile,waterbeach],
[grasstile,grasstile,waterbeach,waterbeach,watertile,watertile,waterbeach,waterbeach,grasstile,grasstile,waterbeach,waterbeach,watertile,watertile,waterbeach,waterbeach,waterbeach,grasstile,grasstile,waterbeach],
[watertile,grasstile,waterbeach,watertile,watertile,watertile,waterbeach,waterbeach,grasstile,grasstile,waterbeach,watertile,watertile,watertile,waterbeach,waterbeach,waterbeach,grasstile,grasstile,waterbeach],
[watertile,watertile,waterbeach,waterbeach,waterbeach,waterbeach,waterbeach,waterbeach,grasstile,grasstile,waterbeach,waterbeach,watertile,watertile,waterbeach,waterbeach,waterbeach,grasstile,grasstile,waterbeach],
[grasstile,watertile,waterbeach,waterbeach,grasstile,grasstile,waterbeach,waterbeach,grasstile,grasstile,waterbeach,waterbeach,waterbeach,waterbeach,waterbeach,waterbeach,waterbeach,grasstile,grasstile,waterbeach],
[grasstile,grasstile,waterbeach,waterbeach,watertile,watertile,waterbeach,waterbeach,grasstile,grasstile,waterbeach,watertile,watertile,watertile,waterbeach,waterbeach,waterbeach,grasstile,grasstile,waterbeach],
[grasstile,grasstile,waterbeach,waterbeach,watertile,watertile,waterbeach,waterbeach,grasstile,grasstile,waterbeach,grasstile,watertile,watertile,waterbeach,waterbeach,waterbeach,grasstile,grasstile,waterbeach],
[grasstile,grasstile,waterbeach,waterbeach,watertile,watertile,waterbeach,waterbeach,grasstile,grasstile,waterbeach,watertile,grasstile,watertile,waterbeach,waterbeach,waterbeach,grasstile,grasstile,waterbeach],
[grasstile,grasstile,waterbeach,waterbeach,watertile,watertile,waterbeach,waterbeach,grasstile,grasstile,waterbeach,watertile,watertile,watertile,waterbeach,waterbeach,waterbeach,grasstile,grasstile,waterbeach],
[grasstile,grasstile,waterbeach,waterbeach,watertile,watertile,waterbeach,waterbeach,grasstile,grasstile,waterbeach,watertile,watertile,watertile,waterbeach,waterbeach,waterbeach,grasstile,grasstile,waterbeach],
[grasstile,grasstile,waterbeach,waterbeach,watertile,watertile,waterbeach,waterbeach,grasstile,grasstile,waterbeach,watertile,watertile,watertile,waterbeach,waterbeach,waterbeach,grasstile,grasstile,waterbeach],
[grasstile,grasstile,waterbeach,waterbeach,watertile,watertile,waterbeach,waterbeach,grasstile,grasstile,waterbeach,watertile,watertile,watertile,waterbeach,waterbeach,waterbeach,grasstile,grasstile,waterbeach],
]
#Creates surface of the background
map_surface = pygame.Surface((len(tilemap[0])*TILESIZE, len(tilemap)*TILESIZE))
#Display the surface
for y,row in enumerate(tilemap):
for x,tile_surface in enumerate(row):
map_surface.blit(tile_surface,(x*TILESIZE,y*TILESIZE))
total_level_width = len(tilemap[0]) * 40
total_level_height = len(tilemap) * 40
camera = Camera(complex_camera,total_level_width, total_level_height)
#mouse = Mouse()
#allsprites = pygame.sprite.RenderPlain((mouse))
clock = pygame.time.Clock()
while 1:
clock.tick(60)
#Handle Input Events
for event in pygame.event.get():
if event.type == QUIT:
return
elif event.type == KEYDOWN and event.key == K_ESCAPE:
return
screen.fill(BLACK)
#Camera moves.
camera.update()
#Display background.
screen.blit(map_surface, camera.apply(waterrect))
pygame.display.flip()
if __name__ == "__main__":
main()
pygame.quit()
I think that the problem is in the camara function and the apply function, but I have no clue to improve this and make the camara work properly.
Thanks!
It is hard for me to say exactly what is wrong with your sode ( i got too little experience in coding ) , but i just realised movin screen by mouse in my script and i did this :
screen=pygame.display.set_mode((WINDOWWIDTH,WINDOWHEIGHT),32)
Map=pygame.image.load(os.path.join('Image','MAP.png')).convert_alpha()
startX,startY=0,0 # starting coordinates for Map
while 1:
screen.fill(white)
screen.blit(Map,(startX,startY))
for event in pygame.event.get():
if event.type == MOUSEMOTION :
mousepos=pygame.mouse.get_pos()
if WINDOWHEIGHT-mousepos[1]<50: # if y-position of mouse is 50 pixel far from lower edge ..
startY -= 5 # ..move Map by 5 px
if mousepos [1]<50:
startY += 5
if mousepos [0]<50 :
startX += 5
if WINDOWWIDTH - mousepos[0]<50:
startX -= 5
pygame.display.flip()
Hope this can suggest you an idea how to improve your code

Categories