I have a project where i have to create a 4 way split screen using pygame. On this screen i have to draw the same image on each of the screen just have different view of the image. I just can not figure out how to create this 4 way split screen using pygame.
I need my screen to be divided like above so i can draw my points onto each section.
I have been looking around and I can not find anything like this so any help would be great
thanks
In addition to the surface you have that gets rendered to the display, likely called something like screen, you should create another surface which all of the "action" gets drawn to. You can then use a Rect object for each quadrant of the screen which will represent the "camera" (assuming each quadrant doesn't necessarily need to show exactly the same image). When you draw back to screen, you use each camera Rect object to select a portion of the game space to draw to a specific quadrant.
# canvas will be a surface that captures the entirety of the "action"
canvas = pygame.Surface((800, 600))
# the following are your "camera" objects
# right now they are taking up discrete and even portions of the canvas,
# but the idea is that they can move and possibly cover overlapping sections
# of the canvas
p1_camera = pygame.Rect(0,0,400,300)
p2_camera = pygame.Rect(400,0,400,300)
p3_camera = pygame.Rect(0,300,400,300)
p4_camera = pygame.Rect(400,300,400,300)
On each update, you would then use these "camera" objects to blit various portions of the canvas back to the screen surface.
# draw player 1's view to the top left corner
screen.blit(canvas, (0,0), p1_camera)
# player 2's view is in the top right corner
screen.blit(canvas, (400, 0), p2_camera)
# player 3's view is in the bottom left corner
screen.blit(canvas, (0, 300), p3_camera)
# player 4's view is in the bottom right corner
screen.blit(canvas, (400, 300), p4_camera)
# then you update the display
# this can be done with either display.flip() or display.update(), the
# uses of each are beyond this question
display.flip()
There is no functions to split screen. But you can draw 4 views directly on screen or you can draw on 4 surfaces (pygame.Surface) and than blit surfaces on screen.
Since you were looking for a way to split the screen in to 4 sections and draw some points on to them I'd suggest creating 4 subsurface surfaces of the original "canvas" image for convenience.
These surfaces would act as your player(split screen) canvasses which can easily be modified.
This will enable the usage of normalized coordinates for player specific drawing purposes.
Assuming you have a screen surface set up
# Image(Surface) which will be refrenced
canvas = pygame.Surface((800, 600))
# Camera rectangles for sections of the canvas
p1_camera = pygame.Rect(0,0,400,300)
p2_camera = pygame.Rect(400,0,400,300)
p3_camera = pygame.Rect(0,300,400,300)
p4_camera = pygame.Rect(400,300,400,300)
# subsurfaces of canvas
# Note that subx needs refreshing when px_camera changes.
sub1 = canvas.subsurface(p1_camera)
sub2 = canvas.subsurface(p2_camera)
sub3 = canvas.subsurface(p3_camera)
sub4 = canvas.subsurface(p4_camera)
Now drawing on any of of the subsurfaces with these normalized coordinates
# Drawing a line on each split "screen"
pygame.draw.line(sub2, (255,255,255), (0,0), (0,300), 10)
pygame.draw.line(sub4, (255,255,255), (0,0), (0,300), 10)
pygame.draw.line(sub3, (255,255,255), (0,0), (400,0), 10)
pygame.draw.line(sub4, (255,255,255), (0,0), (400,0), 10)
# draw player 1's view to the top left corner
screen.blit(sub1, (0,0))
# player 2's view is in the top right corner
screen.blit(sub2, (400, 0))
# player 3's view is in the bottom left corner
screen.blit(sub3, (0, 300))
# player 4's view is in the bottom right corner
screen.blit(sub4, (400, 300))
# Update the screen
pygame.display.update()
Note that modifications to the subsurface pixels will affect the canvas as well. I'd recommend reading the full documentation on subsurfaces.
Related
So I am Loading an Image in python and I need to Center the Image in Pygame. As Pygame starts the drawing from the upper left side of the screen so the x and y coordinates 0,0 doesn't work. Can anyone tell me the center x and y coordinates of the pygame window?
You can get the center of the window through the window rectangle (pygame.Surface.get_rect):
screen = pygame.display.set_mode((800, 600))
center = screen.get_rect().center
Use this to blit an image (pygame.Surface object) in the center of the screen:
screen.blit(image, image.get_rect(center = screen.get_rect().center))
pygame.Surface.get_rect() returns a rectangle with the size of the Surface object, that always starts at (0, 0). However, the position of the rectangle can be specified with a keyword argument. In this case, the center of the rectangle is set by the center of the screen.
When the 2nd argument of blit is a rectangle (pygame.Rect), the upper left corner of the rectangle will be used as the position for the blit.
I am writing a program where sensors(the white circles) are moving on the map to cover things. I counted the number of covered things(the small white dots), it's the right number. But the old covered things and sensor(white circles) are still on the screen. How can I update the display so that only newest objects are on the screen(no red dots should be in the circle path, and the circle path history should not be displayed).
Here is my display code. It gets called every frame.
def _drawMap(self,ticks):
# draw the map
# draw the boundary
boundary = self._polygonCoordinationTransformation(self.data.boundary_polygon_list)
pygame.draw.polygon(self.screen, BLUE, boundary, LINEWIDTH)
# draw obstacles
for polygon in self.data.obstacles_list:
polygon = self._polygonCoordinationTransformation(polygon)
pygame.draw.polygon(self.screen, BLUE, polygon, LINEWIDTH)
# draw agents
self._executeSegment(self.agentsGroup.sprites()[0],(5,10),ticks)
for agent in self.agentsGroup:
pygame.draw.circle(self.screen, WHITE, agent.rect.center, self.data.sensor_range * self.scaleForMap,LINEWIDTH)
# draw items
self.updateUnseenItems()
for itemObj in self.unseenItemGroup:
pygame.draw.rect(self.screen, RED, itemObj, LINEWIDTH)
In pygame you can't clear the screen - instead you re-blit the background back over the top of what is currently there.
There's an excellent answer to the same question here.
Edit: In your case you probably want to fill the screen in with black:
screen.fill( (0,0,0) )
In this blit call
screen = pygame.Surface(640, 480)
bgsurf = pygame.Surface(640, 480)
new_rect = pygame.Rect(0, 0, 80, 80)
screen.blit(bgsurf, new_rect, new_rect)
how pygame decides which portion of bgsurf it will copy to the screen in the new_rect area?
From the pygame docs:
blit(source, dest, area=None, special_flags = 0) -> Rect
Draws a source Surface onto this Surface. The draw can be positioned
with the dest argument. Dest can either be pair of coordinates
representing the upper left corner of the source. A Rect can also be
passed as the destination and the topleft corner of the rectangle will
be used as the position for the blit. The size of the destination
rectangle does not effect the blit.
An optional area rectangle can be passed as well. This represents a
smaller portion of the source Surface to draw.
So as you can see, pygame would blit the whole surface at (0,0).
If you want to blit a part of surface, you need to pass in the area Rect.
EDIT:
In your case, it will blit the subsurface given by new_rect onto screen where the top-left corner will be placed at (0,0).
I am learning pygame and want a graphic for a button with the three states: normal, hover, and pressed. I have an image like this one ...
... and I want to get a new Surface using a portion of it.
I'm loading the image with this code:
buttonStates = pygame.image.load(os.path.join('image','button.png'))
How can I make a new surface using just a portion of that graphic?
cropped = pygame.Surface((80, 80))
cropped.blit(buttonStates, (0, 0), (30, 30, 80, 80))
The blit method on a surface 'pastes' another surface on to it. The first argument to blit is the source surface. The second is the location to paste to (in this case, the top left corner). The third (optional) argument is the area of the source image to paste from -- in this case an 80x80 square 30px from the top and 30px from the left.
You can also use the pygame.Surface.subsurface method to create subsurfaces that share their pixels with their parent surface. However, you have to make sure that the rect is inside of the image area or a ValueError: subsurface rectangle outside surface area will be raised.
subsurface = a_surface.subsurface((x, y, width, height))
There are 2 possibilities.
The blit method allows to specify a rectangular sub-area of the source _Surface:
[...] An optional area rectangle can be passed as well. This represents a smaller portion of the source Surface to draw. [...]
In this way you can blit an area of the source surface directly onto a target:
cropped_region = (x, y, width, height)
target.blit(source_surf, (posx, posy), cropped_region)
Alternatively, you can define a subsurface that is directly linked to the source surface with the subsurface method:
Returns a new Surface that shares its pixels with its new parent. The new Surface is considered a child of the original. Modifications to either Surface pixels will effect each other.
As soon as a subsurface has been created, it can be used as a normal surface at any time:
cropped_region = (x, y, width, height)
cropped_subsurf = source_surf.subsurface(cropped_region)
target.blit(cropped_subsurf, (posx, posy))
I think the best way to do it is crop the image of these 3 kind of buttons in a external program and load in different surface instead use pygame to crop it
I have an application written in python that's basically an etch-a-sketch, you move pixels around with WASD and arrow keys and it leaves a trail. However, I want to add a counter for the amount of pixels on the screen. How do I have the counter update without updating the entire surface and pwning the pixel drawings?
Alternatively, can I make a surface that's completely transparent except for the text so you can see the drawing surface underneath?
To solve this problem, you want to have a separate surface for your Etch-a-Sketch pixels, so that they do not get clobbered when you go to refresh the screen. Unfortunately, with Rigo's scheme, the font will continue to render on top of itself, which will get messy for more than two pixel count changes.
So, here's some sample rendering code:
# Fill background
screen.fill((0xcc, 0xcc, 0xcc))
# Blit Etch-a-Sketch surface (with the drawing)
# etch_surf should be the same size as the screen
screen.blit(etch_surf, (0, 0))
# Render the pixel count
arial = pygame.font.SysFont('Arial', 20)
counter_surf = arial.render(str(pixel_count), True, (0, 0, 0))
screen.blit(counter_surf, (16, 16))
# Refresh entire screen
pygame.display.update()
Now, admittedly, updating the entire screen is rather inefficient. For this, you have two options: only refresh the screen when the drawing changes or track the location of drawing changes and refresh individual locations (see the update documentation). If you choose the second option, you will have to refresh the text and where it was previously; I would recommend having a Sprite manage this.
What you need is pygame.font module
#define a font surface
spamSurface = pygame.font.SysFont('Arial', 20)
#then, in your infinite cycle...
eggsPixels = spamSurface.render(str(pixelsOnScreen), True, (255, 255, 255))
hamDisplay.blit(eggsPixels, (10, 10))
Where spamSurface is a new font surface, eggsPixels is the value that spamSurface will render (display/show) and hamDisplay is your main surface display.