I want to add a grid to my level that stays with the terrain and not the screen. The way I thought of doing it is to add all the lines that form the grid as sprites and move them with the terrain, but I can't figure out how to represent the line as an image.
I tried to do this myself, but had no success.
EDIT: Here's what I've tried
class Grid():
def __init__(self):
self.grid = pygame.Surface(size)
self.grid.set_colorkey((0,0,0))
def draw(self):
# DRAW TILE LINES ----------------------------------------------------------
grid_x = 0
grid_y = 0
for i in range(total_level_width // TILE_SIZE):
pygame.draw.aaline(self.grid,BLACK,[grid_x,0],[grid_x,total_level_height])
pygame.draw.aaline(self.grid,BLACK,[0,grid_x],[total_level_width,grid_y])
grid_x += TILE_SIZE
grid_y += TILE_SIZE
# tile test
pygame.draw.rect(screen,BLACK,(49*TILE_SIZE,34*TILE_SIZE,TILE_SIZE,TILE_SIZE))
screen.blit(self.grid,(0,0))
Creating the object:
grid = Grid()
Calling class: (in main program loop)
grid.draw()
I had a similar problem while i was trying to do a project. I used the following code to get a line onto a surface and then bliting it onto my screen. I hope this entire function might help you.
def blitBoundary(self):
""" helper function to blit boundary on screen """
# create a surface
self.boundSurf=pygame.Surface((1024,768))
self.boundSurf.set_colorkey((0,0,0))
"""
if not self.boundary.closePoly:
(x,y)=pygame.mouse.get_pos()
pointList=self.boundary.pointList +[[x,y]]
else:
pointList=self.boundary.pointList"""
if len(pointList)>1:
pygame.draw.aalines(self.boundSurf, (255,255,255),
self.boundary.closePoly , pointList, 1)
self.screen.blit(self.boundSurf,(0,0))
I was trying to draw a polygon. The commented out the if statement that would be most probably not useful for you.
All my lines were in a polygon class object.
You might want to look into pygame.draw.aalines function.
Related
Just learning to use pyglet for some graphics, i have a grid in the image below. The "player" is a circle. When i change the x positon of the circle:
circle.x = new x
cirle.draw()
This works to render the second circle as you can see, but how do i now remove the original circle? It doesn't update the actual initial object it seems.
You have to redraw the entire scene in every frame. Clear the window with clear. e.g.:
window.event
def on_draw():
window.clear()
# draw board
# [...]
cirle.draw()
I am currently working on a pygame, trying to animate my character so that as the player moves him the program will cycle through four sprite image subsurfaces. I already setup a class for this:
import pygame
from pygame.locaks import *
class Prota:
def __init__(self, sheet):
self.sheet = pygame.image.load(sheet).convert_alpha()
self.image_rect = self.sheet.get_rect()
self.image_rect_h = (self.image_rect.height) #num of rows
self.image_rect_w = (self.image_rect.width/16) #num of columns
self.image_reel = self.fetch()
self.image_cue = 0 #index: 0 - 3 (Right), 4 - 7 (Left), 8 - 11 (Front), 12 - 15 (Back)
self.clock = pygame.time.Clock()
def draw(self, screen):
self.clock.tick(60)
screen.blit(self.image_reel[self.image_cue], (400, 300))
def fetch(self):
sprites = []
for x in range(0, 15):
self.sheet.set_clip(pygame.Rect(self.image_rect_h*x, 0, self.image_rect_w, self.image_rect_h))
sprite = self.sheet.subsurface(self.sheet.get_clip())
sprites.append(sprite)
return sprites
And it worked perfectly when I used a dummy sprite sheet (just a simple 50 x 50 square sprite that changes colors), but when I tried to implement my (partially complete) actually character sheet, I got back
ValueError: subsurface rectangle outside surface area
I am not sure if it's the size of sheets (the dummy sheet was 832 x 52px, and the character sheet is 1008 x 79px), or what, and I can't seem to find any article that addresses this issue. (The closest I could find in a quick search was How to rotate images in pygame
Any ideas?
The mistake was to use self.image_rect_h*x as the first argument (the x-coord) of the pygame.Rect. Changing it to self.image_rect_w*x fixed it.
self.sheet.set_clip(pygame.Rect(self.image_rect_w*x, 0, self.image_rect_w, self.image_rect_h))
I'm not sure why the error occurs, because pygame.Surface.set_clip and .get_clip should be restricted to the surface area. You should post the full traceback (error message).
I actually think it would be better to let the program crash if something is wrong with the image loading and cutting, so that it's easier to find the error, otherwise you would get incorrect sprites anyway. So you could just use pygame.Surface.subsurface instead of set_clip andget_clip.
rect = (self.image_rect_w*x, 0, self.image_rect_w, self.image_rect_h)
sprite = self.sheet.subsurface(rect)
I've created a surface that I've used pixel array to put pixels on, but i want to make the surface transparent but leaving the pixels opaque, I've tried making the surface transparent then drawing the pixels tot he surface but that just makes the pixels also transparent, any help or something I've missed?
-Edit- Hopefully this'll help in some way, this is the class object that creates the surface that is the galaxy
Also I have stated what I've tried, there's not much more to tell
class Galaxy(object):
def __init__(self,posx=0,posy=0,radius=0,depth=0):
radius = int(radius)
self.size = [radius*2,radius*2,depth]
self.posx = posx
self.posy = posy
self.radius = radius
#create array for stars
self.starArray = []
#create surface for stars
self.surface = pygame.Surface([radius*2,radius*2])
self.starPixel = pygame.PixelArray(self.surface)
#populate
for x in range(radius*2):
for y in range(radius*2):
#generate stars
num1 = noise.snoise2(x+posx,y+posy,repeatx=radius*10,repeaty=radius*10)
distance = math.sqrt(math.pow((x-radius),2)+math.pow((y-radius),2))
if distance < 0:
distance = distance * -1
#print(x,y,"is",distance,"from",radius,radius)
val = 5
#glaxy density algorithm
num = (num1 / ( ((distance+0.0001)/radius)*(val*10) )) * 10
#density
if num > (1/val):
#create star
self.starArray.append(Stars(x,y,seed=num1*100000,distance=distance))
#print(num*1000)
self.addPixels()
#adds all star pixels to pixel array on surface
def addPixels(self):
for i in self.starArray:
self.starPixel[i.x,i.y] = i.colour
del self.starPixel
#sends to screen to await rendering
def display(self):
screen.displaySurface(self.surface,[self.posx+camPosX,self.posy+camPosY])
Use MyGalaxy.set_colorkey(SomeUnusedRGB) to define a zero-alpha (invisible) background colour, fill MyGalaxy with that colour, then draw the pixels on top of that. You can use pixelArray functions to draw to that surface, but you're probably better to use MyGalaxy.set_at(pixelLocationXY, pixelColourRGB) instead, for reasons of managability and performance.
Make sure that SomeUnusedRGB is never the same as any pixelColourRGB, or those pixels won't appear (since pygame will interpret them as invisible). When you blit MyGalaxy to wherever you want it, it ought to only blit the non-SomeUnusedRGB-coloured pixels, leaving the rest unaltered.
(This is the best I can offer you without knowing more about your code; revise the question to include what you're already trying, and I'll update this answer.)
I have an almost-working piece of code (I hope). In the update method of this class, random black points should be drawn at locations bounded by the width and height of the window - the problem is that the points are not drawn. A gtk window containing the background image that is loaded with the cairo ImageSurface.create_from_png(BG_IMG) is displayed and I've also verified that the update function is called (every 17ms with a gobject.timeout_add callback function). I've searched here and elsewhere, but I can't quite see what's wrong with this code..
class Screen(gtk.DrawingArea):
__gsignals__ = {"expose-event": "override"}
def do_expose_event(self, event):
self.cr = self.window.cairo_create()
self.cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
self.cr.clip()
self.draw(*self.window.get_size())
def draw(self, width, height):
x = y = 0
self.bg = c.ImageSurface.create_from_png(BG_IMG)
self.cr.set_source_surface(self.bg, x, y)
self.cr.paint()
def update(self):
x = randint(0, DOCK_W)
y = randint(0, DOCK_H)
self.cr.rectangle(x, y, 1, 1)
self.cr.set_source_rgba(0, 0, 0, 1)
self.cr.fill()
self.cr.paint()
Anybody have some insights into why this is code is failing? Big thanks in advance!
Solved
I was unaware that a new cairo context could be used at each draw operation. That turned out to be the main problem.
Generally speaking, you should not draw directly to the window outside of an expose event. And do not keep the cairo context for later use: create one for each event run.
When you want to draw your points, just do: widget.queue_draw(), and a new expose event will be delivered to you ASAP. But note that in the expose event you will have to paint all the points, not just the new one.
There a useful optimization to your type of code: from the timer do not call queue_draw as it is fairly inefficient. Instead just draw the new point. However that doesn't excuse you to draw all the points in the do_expose_event, as an expose event can happen at any time and you do not want to lose the already painted points.
To do the one-point draw you have to create a new cairo context, but you do not need to save it:
def update(self):
cr = self.window.cairo_create()
x = randint(0, DOCK_W)
y = randint(0, DOCK_H)
self.points.append((x,y)) #for the expose event ;-)
cr.rectangle(x, y, 1, 1)
cr.set_source_rgba(0, 0, 0, 1)
cr.fill()
cr.paint()
Another common optimization, particularly if you have a lot of points is to keep the painted image in a bitmap, so when the expose event happens, you simply blit the bitmap, instead of iterating all along the list of points.
I thought you guys might be able to help me wrap my head around this. I want to be able to generate rects and assign images to those rects. I've been doing this for the whole project and isn't too hard. The hard part here is that I want this particular function to be able to generate as many different rects as I want. The project is a game that takes place on like a chess board. I figure I can write like... if statements for every single space and then have like a bazillion parameters in the function that dictate which rects get generated and where, but I was hoping someone might be able to think of a more elegant solution.
You could use two nested "for" loops --
def make_chessboard(upper_x=0, upper_y=0, size=30):
chessboard = []
for y in range(8):
row = []
for x in range(8):
coords = (upper_x + x * size, upper_y + y * size)
row.append(pygame.Rect(coords, (size, size)))
chessboard.append(row)
return chessboard
Then, to get the rect that's in the top-left corner, you could do chessboard[0][0]. To get the rect that's in the top-right corner, you could do chessboard[0][7].
You wouldn't be able to explicitly name each rect, but then again, you really wouldn't need to.
Note: I'm assuming that you wanted to create a chessboard-like pattern of rects of some kind. I can edit my question if you detail specifically what you're trying to do.
class ChessTile(pygame.sprite.Sprite):
def __init__(self, image, location):
pygame.sprite.Sprite.__init__(self)
self.image = image.convert()
self.mask = pygame.mask.from_surface(self.image)
self.rect = pygame.Rect(location, self.image.get_size())
Then make another method called like "MakeBoard". Call MakeBoad and have a loop setup with the size of the board. so the pseudo code would be:
(let's assume "img" is a 32x32 white or black square)
for y in range(0,7):
for x in range(0,7):
# alternate the tile image from black/white before calling ChessTile with it
# the location parameter is going to be x*32,y*32.. something like that
# so you'd have a tile at (0,0) then at (32,0), then (64,0), etc...
# after the first "j" run is done, "i" increments so now we have
# (0, 32), (32, 32), etc etc.
#
tile = ChessTile(img, (x,y))
then just draw the tile object as you normall would in some render method!
hope that helps!!!