Pygame Top-Down Scrolling [closed] - python

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
For a project I want to do for my class' Pygame final, I want to make The Legend of Zelda: A Link To The Past. However right when I started I realized that the scrolling would be quite an issue.
How do you implement a scrolling technique that follows the player until the edge of a map or image but still allows the player to move closer to the edge?
A reference because I feel as if I am not correctly wording myself:
https://www.youtube.com/watch?v=kWyT9d8CiKY
My personal idea was to use a switch that switches the background image moving to Link's image moving.

A major component of any branch of engineering is breaking down big problems into smaller ones. So let's break down your problem.
How would you design a scrolling technique that follows the player
until the edge of a map or image but still allows the player to move
closer to the edge?
Okay, so there are three problems here. Moving the player sprite, moving the background sprite, and working out when to do each. Moving the player sprite is pretty straight forward - give it an (x,y) coordinate on the screen and move that according to the input controls (keyboard/mouse/etc).
Now let's consider the background sprite. For simplicity we'll assume that your whole background can be loaded as one big sprite. We want to render a portion of that background onto the screen - so we need to maintain the position of the background relative to the screen with it's own coordinates.
You can think about this two ways - either the screen stays stationary and the background moves behind it, or the background stays and the screen moves. Given that you'll eventually be tracking lots of other items (baddies, treasure, etc) and their position on the map, I would suggest thinking about everything moving relative to the background (even though this may seem less intuitive at first). Let's call this the world coordinate. To render things to the screen we'll need to work out their screen coordinate.
Okay, so we now have two coordinates - the positions of the screen and the player. For consistency, let's make the player position use world coordinates too.
So how do we render this to the screen? Start by listing out the rules:
the background should always fill the screen (i.e. don't scroll so far
that you can see outside of the background sprite)
the player should be centred on screen, except when that would violate #1
So the position of the screen is dependent on the player, but with some limits depending on where it is on the map. Let's consider the x coordinate (note this is untested):
# start by centring the screen on the player
screen_x = player_x - screen_width/2
# limit the screen to within the bounds of the background
if screen_x < 0:
screen_x = 0
if screen_x > (background_width - screen_width):
screen_x = (background_width - screen_width)
You can now calculate the render position of the player (position on screen) by subtracting screen_x from player_x. The background render position is calculated the same way (but should result in a negative coordinate).

Related

How to implement smooth tile based movement [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
The player in my game is centered in the screen, and I 'scroll' the background to move around.
I get a list of keys pressed with pygame.key.get_pressed(), if the player's move cooldown is over, I call a move function. This function scrolls the map in the direction of movement for one tile.
However the movement is not smooth. It 'jumps' to the next tile.
How do I implement a movement system so that the player moves in small steps (speed * dt) but still only moves the distance of one tile every time the move function is called? The game needs to be rendered in between each small step I think.
Full source here: https://github.com/HCollings/rpg-game/blob/alternate_movement/game.py
Animations and movement are all about what you perceive rather than what's actually happening. In a grid system, you can abstract the visual movement of the player away from the actual mechanics by taking the drawing part of the code and updating the draw position independently of the gameplay position.
The problem you are experiencing with jumping is commonly handled with some kind of interpolation technique. lerp() is a common function that provides linear interpolation, but in my below example I will use a different technique.
Here is a resource about interpolation techniques: http://paulbourke.net/miscellaneous/interpolation/
You could implement your movement as destination, direction, and "unspent" increments of movement.
If you wanted to move to a new square, you would set your direction and add a standard number of increments (representing one square) to your "unspent increment counter". Then you draw your graphic at the new location offset by the number of increments you still have. If you want to move more than one square at a time, then you would increase the number of unspent increments.
Movement would only take place so long as you have unspent increments (think movement points). This technique also lends itself to visual collision detection; if the remaining number of increments is some fraction of the total increments (~25%?) between the number of squares you are moving, then the graphic is "practically" in the square according to what the player sees.

How would you draw multiple screens in PyGame (in only one window)?

I'm trying to port over some code from Javascript to Python, and I'm having trouble even figuring out where to start. I've looked at a good dozen tutorials for PyGame, but none of them seem to click for me. So, I was hoping to get a quick example here, or at least a point in the right direction.
I'm wanting to make a number of screens I can switch back and forth between, depending on what the user is doing at the time, and even display two side by side. At the moment, all I've got is some Javascript that draws random circles onto the screen. The PyGame logic is the only thing I'm having trouble with.
Here's my javascript for reference.
You can create a Subsurface for each subscreen that you want to create.
Then you may treat each as if it were a full screen / single surface, yet they still reference the original screen.
Pygame is a wrapper for SDL. SDL uses a surface to represent a bitmap, or anything that can be drawn on screen. With the pygame.display.set_mode((w,h),0,d) you can get the surface, or the whole canvas. You can then draw or blit the other surfaces and then call flip(), to show changes. If you wish to have a few screens, you could have a current state number, and blit the screens accordingly.
For example:
if(current_state == MAIN_SCREEN):
drawAll(screen)
else
drawEnemiesOnly(screen)
you could change the screens with the number keys:
for event in pygame.event.get():
if event.type == KEY_DOWN:
if(event.key == K_1):
current_state = 1

Pygame Tile Based Movement Speed

Thanks for taking the time to read this.
Right now I'm making a really basic tile based game. The map is a large amount of 16x16 tiles, and the character image is 16x16 as well. My character has its own class that is an extension of the sprite class, and the x and y position is saved in terms of the tile position.
To note I am fairly inexperienced with pygame.
My question is, I am planning to have character movement restricted to one tile at a time, and I'm not sure how to make it so that, even if the player hits the directional key dozens of time quickly, (WASD or arrow keys) it will only move from tile to tile at a certain speed. How could I implement this generally with pygame? (Similar to game movement of like Pokemon or NexusTk). One movement would result in a player being in a tile. They couldn't stop halfway between tiles for example.
Thanks for your time! Ryan
You store your characters location as a grid coordinate. So if he's at (2,0) he is rendered at (32,0). The game then animates him moving between tiles, but, he's either on one or the other. While in the move state, you render an (x,y) offset between 0 to tilewidth.
It sounds like you want one move per keypress, if time elapsed / animation has completed. So:
On keypress, toggle to: animating state
Set destination tile coordinate
draw offset, between 0 and tilewidth, depending on time elapsed. offset = (elapsed_ms / 1000.) * tile_w would scale between 0 to 16 if time is less<= 1 second.
Once time elapsed is >= animation length (I chose 1000. above), switch to stationary state.
If keypress happens while in animation state, ignore it.
Pygame example: using numpy for map array.

How to avoid copying the level Surface every frame in worms-like game?

I am working on a game that has destructible terrain (like in the game Worms, or Scorched Earth) and uses pixel perfect collision detection via masks.
The level is a single surface and how it works now is that I create a copy every frame, draw all sprites that need drawing on it, then blit the visible area to the display surface.
Is there any way to avoid copying the whole level surface every frame and still be able to use the pixel perfect collision tools found in pygame?
I tried blitting the level surface first, then blitting every sprite on the screen (with their blit coordinates adjusted by the camera, except for the player character whose coordinates are static), but in that case the collision detection system falls apart and I can't seem to be able to fix it.
UPDATE
I have managed to make it work the following way:
When drawing the sprites, I convert their game world coordinates (which are basically coordinates relative to the origin of the level bitmap) to screen coordinates (coordinates relative to the camera, which is the currently visible area of the level).
During the collision detection phase I use the coordinates and bounding boxes that are positioned relative to the level surface; so just like above. The thing is that the camera's position is bound to the player's position which is not and should not have been a static value (I am really not sure how I managed to not realize that for so long).
While this fixes my problem, the answer below is a much more comprehensive look on how to improve performance in a situation like this.
I am also open to suggestions to use other libraries that would make the ordeal easier, or faster. I have thought about pyglet and rabbyt, but it looks like the same problem exists there.
This is an issue that used to come up a lot in the days before graphics accelerators, when computers were slow. You basically want to minimize the work required to refresh the screen. You are on the right track, but I recommend the following:
Keep a copy of the background available offscreen, as you are doing
now.
Allocate a working bitmap that is the same size as the screen.
For each sprite, compute the bounding rectangle (bounding box) for
its new and old positions.
If the new and old bounding boxes overlap, combine them into one
larger box. If they do not overlap, treat them separately.
Group all the bounding boxes into sets that overlap. They might all
end up in one set (when the sprites are close to each other), or
each bounding box might be in a set by itself (when the sprites are
far apart).
Copy the background to regions of the working bitmap corresponding
to each bounding box set.
Copy the sprites for each set to the working bitmap in their new
positions (in the correct z-order, of course!).
Finally, copy the finished offscreen bitmap to the display surface,
set bounding box by set bounding box.
This approach minimizes the amount of copying that you have to do, both of background and sprite. If the sprites are small relative to the display area, the savings should be significant. The worst case is where the sprites are all arranged on a diagonal line, just barely overlapping each other. In this case, you might want to switch to a more generalized bounding shape than a box. Take a look at QuickDraw Regions for an example: Wikipedia Discussion Patent Source.
Now, you may be thinking that the work to group the bounding boxes into sets is a O(n^2) operation, and you would be right. But it grows only with the square of the number of sprites. 16 sprites implies 256 comparisons. That's probably less work than a single sprite blit.
I focused on minimizing the pixel copying work. I must admin I am not familiar with the particulars of your collision detection library, but I get the idea. Hopefully that is compatible with the algorithm I have proposed.
Good luck. If you finish the game and post it online, put a link to it in your question or a comment.

Pygame: Updating Rects with scrolling levels

I'm currently making a 2D side-scrolling run'n'jump platform game in PyGame. Most of the stuff is working OK and very well in fact - I am exploiting the fast pyGame Sprite objects & groups.
What I'm interested to know is how people usually deal with Rects for scrolling games. I obviously have a level that is much bigger than visible area, and the player, enemies, bullets etc each have their own (x,y) coordinates which describe where they are in the level.
But now, since we use the "spriteGroup.draw(surface)" call, it will not display them in the right spot unless each objects Rects have been adjusted so that the right part displays on the screen. In other words, everytime a player/enemy/bullet/whatever else is updated, the Camera information needs to be passed, so that their Rect can be updated.
Is this the best method to use? It works but I don't really like passing the camera information to every single object at every update to offset the Rects.
Obviously the ideal method (I think) is to use Rects with "real" coordinates, blit everything to a buffer as big as the level, and then just blit the visible part to the screen, but in practice that slows the game down A LOT.
Any comments/insight would be appreciated.
Thanks
You could extend de Sprite.Group so it recives the camera information.
Then do one of these options:
A. Override the update method so it updates the on-screen coordinates of every sprite.
B. Override the draw method so it updates the on-screen coordinates of every sprite and then calls its parent draw method.
I think A it's easier and cleaner.
I don't really like passing the camera information to every single
object at every update to offset the Rects.
Camera may be global, or, a member of a global Game() class instance. Then your sprite class's draw method doesn't need an argument.
You can override draw yourself, so it does:
dest = game.camera.topleft + self.rect.topleft
screen.blit( self.surface, dest )
This keeps the bullet's rect in world-coordinates, yet blits using screen-coordinates.
One method I found is to keep track of a scrollx and a scrolly. Then, just add scrollx and scroll y to the coordinates when you move the rectangles.
You can have a 2 variables level_landlevel_d which see where you are in the level, Then check which sprites are in the visible area
level_d+height and level_l+width,
and draw them on the screen.
The simple way to do it is like that:
Create a CameraX and CameraY variables, and when you blit objects on the screen use this:
blit(surface, (x -CameraX, y -CameraY))
any object that gets affected by the camera should be drawn like that, but keep in mind that there are objects that you may want to remain uneffected (like health bars or status windows)
just keep in mind everytime you want to move camera do this
#Move Camera Right
CameraX += 10
#Move Camera Left
CameraX -= 10
#Move Camera Down
CameraY += 10
#Move Camera Up
CameraY -= 10
Just keep in mind that if they get negative values they may not work correctly, also you must probably define some limits (you dont want your camera to move over the limits of your map

Categories

Resources