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
Related
Just like the title implies, is there any difference? I was using pygame.display.flip and I saw on the Internet that instead of using flip they used pygame.display.update. Which one is faster?
The main difference between pygame.display.flip and pygame.display.update is, that
display.flip() will update the contents of the entire display
display.update() allows to update a portion of the screen, instead of the entire area of the screen. Passing no arguments, updates the entire display
To tell PyGame which portions of the screen it should update (i.e. draw on your monitor) you can pass a single pygame.Rect object, or a sequence of them to the display.update() function. A Rect in PyGame stores a width and a height as well as a x- and y-coordinate for the position.
PyGame's built-in dawning functions and the .blit() method for instance return a Rect, so you can simply pass it to the display.update() function in order to update only the "new" drawn area.
Due to the fact that display.update() only updates certain portions of the whole screen in comparison to display.flip(), display.update() is faster in most cases.
flip will always update the entire screen. update also updates the entire screen, if you don't give arguments. But, if you give surface(s) as arguments, it will update only those surfaces. So it can be faster, depending on how many surfaces you give it and their width and height.
pygame.display.flip() updates the whole screen.
pygame.display.update() updates only specific section but with no arguments works similar to the pygame.display.flip().
If you are doing double-buffering then you want to be using flip(). With only a single buffer either will work, and single buffering is what you are using unless you specifically create a double buffered window, such as this:
pygame.display.set((w, h), pygame.DOUBLEBUF)
Speed will be the same really if you are doing a single, full display update once a frame, so doesn't matter really which you use in single buffer mode.
What is the difference between importing a rocket sprite (for example) and setting it as an image vs importing a rocket sprite and setting it as a sprite in pygame?
I think you're just getting confused about the terminology:
Image
An image is just a collection of pixels. You're using "sprite" to refer to an image on the disk, but that's just an image file. To use your rocket example, you would load the image like this:
rocket_img = pygame.image.load('rocket.png').convert_alpha()
You can then draw this image anywhere you want with:
screen.blit(rocket_img, (x, y))
Sprite
A sprite in Pygame is an object, with a whole collection of built-in functionality. Sprites have an image as one of their properties, but there are a whole lot more. Plus you can put sprites together in groups to make them easier to update or draw. Sprites have collision functionality built into them. You can add your own properties to track location, velocity, animation, etc.
A simple sprite:
class Rocket(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('rocket.png').convert_alpha()
self.rect = self.image.get_rect()
def update(self):
self.rect.x += 1
This would be a rocket sprite that you would instantiate by using
rocket = Rocket()
You can draw by using
screen.blit(rocket.image, rocket.rect)
and it moves slowly to the right (if you call update() in the game loop:
rocket.update()
I recommend looking at the Sprite docs - there's lots more you can do with groups to make working with lots of sprites very easy.
http://www.pygame.org/docs/ref/sprite.html
In Pygame "images" generally refer only to image files: importing and exporting them to disk. There is the "Surface" object that is a Python object that holds pixels, and can be used to stamp other surfaces, be transformed (scaled/rotated) , yielding other surfaces and so on.
The main screen itself is a Surface subclass - so when you stamp a Surface with data read from a disk image, using the blit method the image shows up on the screen.
Sprites on the other hand are a base class for objects in your game, and they don't even depend of having attached pixels data with them. Some of the Pygame API expect Sprite objects to have a rect attribute, which denotes the position where it will be rendered on a Surface - and an image attribute. If it is to be used, the sprite.image attribute should hold a surface object - usually read from disk (but could have been programatically drawn).
The main call using the sprite image attribute is the Group.draw() method.
But it is possible to create an entirely different game than an interactive one - one that would be the server side for a MMO game, without anything on the screen, for example, using the Sprite and Group classes, without ever making use of the image attribute on sprites.
Worth reading:
https://www.pygame.org/docs/ref/sprite.html
Conversely, you can bypass all the helper logic provided by Sprites and Groups and create a game that will only ever have Surface objects - representing images read from disk. Them you are responsible to track were and when to draw them, without using the elpe rmethods in sprite Groups.
I'm making this classic style asteroids game and I've beaten all the challenges that come with it. Right now there's a new one.
When a bullet hits one of the asteroids, the expected behavior is that it disappear, then three or four smaller asteroids appear in its place, each with random velocity so it looks like a kind of explosion. But instead this weird behavior show in this video I just recorded:
https://youtu.be/2ZJvOcB-ZKM
As you can see it does 90% of what it's supposed to do, the asteroid dissapear, then I create four new and smaller asteroids in its pace but then, for some reason they dissapear when they should behave like normal asteroids, since they inherit everything.
The game source code can be found here: https://github.com/JuanBonnett/pysteroids
The classes in charge of handling the collision Bullet->Asteroid are:
asteroids.py Main file creates all the objects, the ship, the asteroids generator and the collision detector object.
physics.py Where the CollisionDetector class is and which detects when they collide and call its dependencies to do stuff with the collision
asteroid.py has an asteroid generator that generates smaller asteroids in the place of the collision, which I call debris.
When an asteroid blows up, you are passing it a pos vector of where to start.
You are applying that same vector instance to all resulting asteroids.
All smaller asteroids have the same self.pos value as their siblings that came from the same parent asteroid.
It looks weird because the self.points property is working just fine and updated/translated correctly and so you're seeing two positions fight each other and give chaotic/unpredictable behavior.
Basically the problem is the pos = _pos line in AsteroidGenerator.generate. It should be making a copy of the vector instead of assigning the reference directly to the new asteroid.
I discovered this by putting print statements in your asteroid render code printing the position: print(self.pos.x, self.pos.y). I noticed all asteroids had the same self.pos but were being given supposedly random velocity vectors, so I figured it was probably an object reference problem and followed where that was being assigned.
I was wondering if it was possible to find out if a Rect is colliding with another Rect. The problem is that I do not know what/where that other Rect is.
I have a Rect which moves around (of which I know where it is).
I have many other Rects on the same "map".
I dont want to make a list of all Rects on the map and then try collideRect with each and every one of them.
Does anyone have an idea under these circumstances for a function that takes a Rect and returns a list of all other Rects with which it collides? (Without using the collideRect function for all existing Rects?)
Can I somehow "scan" only the area of the first Rect and if there is another Rect in the same "spot" I return the other Rect?
I have come up with nothing so far...
At some point, you will need to check to see if the Rect collides with any other Rect. With that in mind, there are some ways to speed things up, basically relying on grouping Rects.
For example, assuming these Rects are objects in the level that don't move around, you could sort them by the X-coordinate, and remember the maximum width. When you want to run collision detection, start at the main Rect's left side minus the maximum width, and loop through until the Rect's right side. Any Rects outside of that range do not have the ability to collide, and so do not need to be checked.
Alternately, you could divide the level into, say, 16 squares, and give each square a list of all Rects within the square. Then, just decide which square the main Rect is in, and just compare with the Rects in there. (With logic for overlaps, of course.)
There's a large number of ways to do this.
Just like the title implies, is there any difference? I was using pygame.display.flip and I saw on the Internet that instead of using flip they used pygame.display.update. Which one is faster?
The main difference between pygame.display.flip and pygame.display.update is, that
display.flip() will update the contents of the entire display
display.update() allows to update a portion of the screen, instead of the entire area of the screen. Passing no arguments, updates the entire display
To tell PyGame which portions of the screen it should update (i.e. draw on your monitor) you can pass a single pygame.Rect object, or a sequence of them to the display.update() function. A Rect in PyGame stores a width and a height as well as a x- and y-coordinate for the position.
PyGame's built-in dawning functions and the .blit() method for instance return a Rect, so you can simply pass it to the display.update() function in order to update only the "new" drawn area.
Due to the fact that display.update() only updates certain portions of the whole screen in comparison to display.flip(), display.update() is faster in most cases.
flip will always update the entire screen. update also updates the entire screen, if you don't give arguments. But, if you give surface(s) as arguments, it will update only those surfaces. So it can be faster, depending on how many surfaces you give it and their width and height.
pygame.display.flip() updates the whole screen.
pygame.display.update() updates only specific section but with no arguments works similar to the pygame.display.flip().
If you are doing double-buffering then you want to be using flip(). With only a single buffer either will work, and single buffering is what you are using unless you specifically create a double buffered window, such as this:
pygame.display.set((w, h), pygame.DOUBLEBUF)
Speed will be the same really if you are doing a single, full display update once a frame, so doesn't matter really which you use in single buffer mode.