Blitting a Pygame Tile-map Efficiently - python

My game map is a 2d-matrix that consists of different tiles (ex. map[y][x] = tile). Each tile has an image, and a rectangle. Currently the map is nearly 1000 tiles in size, and it takes quite some time to blit every one of them to the screen.
My current goal is to find a way to reduce the amount of time it takes to access each item of the matrix, and blit only the necessary tile-objects to the screen. Here is my main obstacle in trying to find a solution:
- Because it is a side-scrolling game, none of the tiles are static (the rectangles are always being adjusted with the player's movement, thus making it mandatory to re-blit the entire screen).
Here is generally how the map functions in the game:
For tile in tile matrix: blit tile to screen
Blit player and NPCs
Update player position
If player moves: adjust all tiles (camera system)
I'm looking for more efficient ideas of doing the same thing. As I said above, blitting every darn tile takes a lot of time, and to add to that, I'm not sure how to selectively blit different tiles when they are constantly changing location.
All ideas are welcome. Thank you.

When you're iterating over your tiles you can do a test to check if the current tile is contained within the camera's view port, if it is you can draw, otherwise you can skip blitting the tile.
for tile in tiles:
if camera.viewport.contains(tile.rect):
tile.draw()
The contains method is determining if a rectangle is inside another. You'll also need to use 2 different frames of reference, screen space and world space.

Related

Changing the Origin of a Animation in Python and Pygame

I am currently working on a 2D platformer and the sprites that I have animate from the bottom left point of the animation and when I draw the animation using a x and y point it still animates from the bottom left, so when I draw the animation to the screen the sprite should get shorter but the sprites feet just lift up of the ground like this https://www.dropbox.com/s/ofeggmlcp4f6qsk/Animation_probs_video.mp4
I know the video is not high quality but so what.
His head should go up and down not his feet. If you guy's can help me I would be most greatful. I could also use a program that fixes that I have a Linux computer with a windows xp virtual box and I am using python 2.7 and pygame.
Thanks.
Assuming you are animating a series of rectangular sprites each being an instance of pygame.Surface, you will be adding the difference between the surface with the greatest height and the current sprite's surface to the y position every time you blit.
Find the height of the tallest sprite only once:
max_height = tallest_sprite.get_height()
Now while you are cycling through your sprints each frame with current_sprite:
screen.blit(current_sprite, (x, y+(max_height - current_sprite.get_height())
If framerate is an issue, you may want to calculate these differences beforehand and associate them with each sprite so you have one less get_height() call per frame.

(pygame) Empty squares displaying copies of what was previously there instead of background

I made a 2D project with a lot of tile sprites, and one player sprite. I'm trying to get the camera to follow the player, and for the most part it's working. However, there's one problem:
If you go to the edge of the map, it scrolls normally, but instead of the black background, it displays copies of the sprites on the edge of the map instead of the background (black). It has the same problem if I leave some squares empty, when I move it displays a copy of the tile that was previously there.
The camera works like this:
Select sprites that should be visible
Do sprite.visible = 1 for them, and sprite.visible = 0 for all other sprites
Set the position sprite.rect of all sprites to coords - offset
Update the screen (I use flip(), because the camera moves every turn, so the whole screen has to be updated every turn)
All DirtySprites have dirty = 2.
Does anyone know why it's displaying copies of the sprites on the edge instead of the background?
Help would be appreciated!
Unless you manually clear your screen surface, flip will not change its content.
Thus, if you neglect to draw to a certain location, it will remain the same.
If you want to get rid of this effect, usually called "hall of mirrors", you will have to keep track of what portions of the screen have not been drawn to yet and draw over these yourself.
It may be easier to define background sprites around your map's contours and block your camera from going off too far.
Since you use a "dirty/clean" approach to only redrawing what's changed, you won't have the option to just fill the whole screen surface before you draw your frame, because that would draw over anything that's stayed the same since the last frame.

Get a sprite to follow/chase another in Livewires python?

I am trying to make a top down shooter style game and am having a hard time getting a sprite to "chase" the player's sprite.
Take a look at seek behavior will work for chasing. http://gamedev.tutsplus.com/tutorials/implementation/understanding-steering-behaviors-seek/
If you don't want that, you can use vectors to get the direction from one unit to the other. ( Then scale the length / cap it )

How to calculate the number of box according to their position?

I'm developing a python memory based game and having a problem to find the number of box the user clicks, according of the position of the cursor.
I already have this:
number = ev.pos[y]//boxsize*numboxsx+ev.pos[x]//boxsize
(with this line I get what you can see on the left of the picture in red)
But I have to consider: mx and my, they are the margin, because the boxes are centered. (What I show in the right of the picture in green).
So, how can I modify the line to get the number of box with the margin?
You need to subtract the margins from the cursor position. Think of it as translating from absolute coordinates (where the cursor is on the screen) to relative coordinates (where the cursor is relative to your boxes).
relx, rely = ev.pos[x] - mx, ev.pos[y] - my
number = rely//boxsize*numboxsx + relx//boxsize
A much better approach would be to use sprites, and sprite collision. If you will define your boxes, as sprites, you can then check if a sprite(your mouse) has collided with your box.
The advantage of this approach is that you will not have to operate on coordinates, and in the future you can move, resize, or increase the amount of boxes on screen without code change on this side.

Wrapping a pygame surface around a viewport

I'm coding a game where the viewport follows the player's ship in a finite game world, and I am trying to make it so that the background "wraps" around in all directions (you could think of it as a 2D surface wrapped around a sphere - no matter what direction you travel in, you will end up back where you started).
I have no trouble getting the ship and objects to wrap, but the background doesn't show up until the viewport itself passes an edge of the gameworld. Is it possible to make the background surface "wrap" around?
I'm sorry if I'm not being very articulate. It seems like a simple problem and tons of games do it, but I haven't had any luck finding an answer. I have some idea about how to do it by tiling the background, but it would be nice if I could just tell the surface to wrap.
I don't think so, I have an idea though. I'm guessing your background wraps horizontally and always to the right, then you could attach part of the beginning to the end of the background.
Example, if you have a 10,000px background and your viewport is 1000px, attach the first 1000px to the end of the background, so you'll have a 11,000px background. Then when the vieport reaches the end of the background, you just move it to the 0px position and continue moving right.
I was monkeying around with something similar to what you described that may be of use. I decided to try using a single map class which contained all of my Tiles, and I wanted only part of it loaded into memory at once so I broke it up into Sectors (32x32 tiles). I limited it to only having 3x3 Sectors loaded at once. As my map scrolled to an edge, it would unload the Sectors on the other side and load in new ones.
My Map class would have a Rect of all loaded Sectors, and my camera would have a Rect of where it was located. Each tick I would use those two Rects to find what part of the Map I should blit, and if I should load in new Sectors. Once you start to change what Sectors are loaded, you have to shift
Each sector had the following attributes:
1. Its Coordinate, with (0, 0) being the topleft most possible Sector in the world.
2. Its Relative Sector Coordinate, with (0, 0) being the topleft most loaded sector, and (2,2) the bottom right most if 3x3 were loaded.
3. A Rect that held the area of the Sector
4. A bool to indicate of the Sector was fully loaded
Each game tick would check if the bool to see if Sector was fully loaded, and if not, call next on a generator that would blit X tiles onto the Map surface. I
The entire Surface
Each update would unload, load, or update and existing Sector
When an existing Sector was updated, it would shift
It would unload Sectors on update, and then create the new ones required. After being created, each Sector would start a generator that would blit X amount of tiles per update
Thanks everyone for the suggestions. I ended up doing something a little different from the answers provided. Essentially, I made subsurfaces of the main surface and used them as buffers, displaying them as appropriate whenever the viewport included coordinates outside the world. Because the scrolling is omnidirectional, I needed to use 8 buffers, one for each side and all four corners. My solution may not be the most elegant, but it seems to work well, with no noticeable performance drop.

Categories