As a beginner at Pygame, and a relative beginner at Python (around 4 months of knowledge), I thought it would be good practice to try and recreate the popular phone app 'Flappy Bird.' I have been fine with doing this, up to the point I am at now. How do I keep one rectangle scrolling, while drawing another that will scroll using the same function? Is this possible? There is probably a method for just this, but I've only been learning the module for less than 7 hours :D Here's my code so far in Python 3.2. (Not including imports)
def drawPipe():
randh = random.randint(40,270)
scrollx -=0.2
pygame.draw.rect(screen, (0,150,30), Rect((scrollx,0),(30,340)))
bif = "BG.jpg"
mif = "bird.png"
pygame.init()
screen = pygame.display.set_mode((640,900),0,32)
background = pygame.image.load(bif).convert()
bird = pygame.image.load(mif).convert_alpha()
pygame.display.set_caption("Flappy Bird")
pygame.display.set_icon(bird)
x,y = 320,0
movex, movey = 0,0
scrollx = 640
while True:
for event in pygame.event.get():
movey = +0.8
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_SPACE:
movey = -2
x += movex
y += movey
screen.blit(background,(0,0))
screen.blit(bird,(x,y))
drawPipe()
pygame.display.update()
Thank you for any help you can give!
You should first create object that you want to have in the game, with one operation being to draw them.
So instead of having a function that draws a pipe and scrolls, you want to have something along these lines:
class Pipe:
def __init__(self,x,height):
self.rect = Rect((x,0),(30,height))
def update():
self.rect.move_ip(-2,0)
def draw(screen):
pygame.draw.rect(screen,color,self.rect)
And then later in game you can have:
pipes = [Pipe(x*20,random.randint(40,270)) for x in range(5)]
for pipe in pipes:
pipe.draw(screen)
pipe.update()
Later on you could just remove the pipes that are not on the screen, and append new ones when a pipe is removed.
Related
I am currently making a fighting game and was wondering how command inputs could be added. I understand it is kind of irrelevant and many substitutes are possible, but it would be nice to use familiar fighting game inputs.
I currently have something like this:
keys = pygame.key.get_pressed()
if keys[pygame.K_DOWN]:
commandcount +=1
if commandcount > 0 and commandcount < 30 and keys[pygame.K_RIGHT] and keys[pygame.K_z]:
player1.projectile = True
The "commandcount" helps keep the window of action available until a certain amount of time.
The main problem with this is that you could still press the inputs in whatever order and the projectile would still come out.
Thanks
Try using the pygame.KEYDOWN events instead of pygame.key.get_pressed() so the order can be observed. Use a list to keep track of the order of these KEYDOWN events. When the order matches a specific combo then execute the move and reset the list. The list also gets reset after a certain amount of time with a combo. I made an example program with the combo down, right, z activating a fireball.
import pygame
# pygame setup
pygame.init()
# Open a window on the screen
width, height = 600, 600
screen = pygame.display.set_mode((width, height))
def main():
clock = pygame.time.Clock()
black = (0, 0, 0)
move_combo = []
frames_without_combo = 0
while True:
clock.tick(30) # number of loops per second
frames_without_combo += 1
if frames_without_combo > 30 or len(move_combo) > 2:
print("COMBO RESET")
frames_without_combo = 0
move_combo = []
screen.fill(black)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
move_combo.append(event.key) # Only keys for combos should be added
if move_combo == [pygame.K_DOWN, pygame.K_RIGHT, pygame.K_z]:
print("FIRE BALL")
frames_without_combo = 0
print(move_combo)
pygame.display.update()
main()
Sorry for any gramatical mistakes as i'm a non english speaker
I'm working on a small game during this quarantine and wanted to make a map maker in order to speed up the process of designing levels.
My probleme is that even if the grid show as i expect on the screen, the "getcollide" function does not account for the "blit" of the surface. I spend two days on this and can't think of another way to work this around, here is a sample code :
import pygame
def run(l, h, fps, scene):
pygame.init()
ecran = pygame.display.set_mode((l, h))
clock = pygame.time.Clock()
scene_active = scene
while scene_active != None:
key = pygame.key.get_pressed()
filtre_touche = []
for event in pygame.event.get():
close = False
if event.type == pygame.QUIT:
close = True
elif event.type == pygame.KEYDOWN:
alt = key[pygame.K_LALT] or touche_active[pygame.K_RALT]
if event.key == pygame.K_ESCAPE:
close = True
elif event.key == pygame.K_F4 and alt:
close = True
if close :
scene.quit()
else :
mouse_pos = pygame.mouse.get_pos()
filtre_touche.append(event)
scene_active.traite_input(filtre_touche, key, mouse_pos)
scene_active.update()
scene_active.render(ecran)
scene_active = scene_active.next
pygame.display.flip()
clock.tick(fps)
surface = pygame.Surface((320,320))
M = []
for i in range(10):
x = []
for j in range(10):
x.append(pygame.draw.rect(surface, (255,255,255),(i*32, j*32, 32,32), 1))
M.append(x)
print(M)
class FirstScreen():
def __init__(self):
self.next = self
def traite_input(self, evenement, touche, mouse_pos):
for event in evenement :
if event.type == pygame.KEYDOWN and event.key == pygame.K_RETURN:
pygame.quit()
for line in M :
for row in line:
if row.collidepoint(mouse_pos):
print('collide')
def update (self):
pass
def render(self, ecran):
ecran.fill((200,200,200))
ecran.blit(surface, (350,250), surface.get_rect())
run(800,600,60, FirstScreen())
If you run this, you will see that the surface is where we expect it to be and the tile actually are of 32 by 32. But hoovering the mouse over the grid has no effect. The print statement is called when you hoover the mouse where the grid should be if i've blit the surface at 0,0.
Am I missing something ? After searching this website, the pygame doc and all kind of forums i seems to be the only one with this trouble, what am i doing wrong ?
Its because your putting the grid on a surface, the bliting the surface on the screen, so when you make the grid, you start at 0 of the surface. Then blit the surface in the middle(ish) of the screen, when you do the collidepoint. the mouse hovers over it when near 0, the top left of the whole screen. Basically, the position of the mouse on the window is translated to the surface so when you hover the mouse over the grid, the pos of the mouse in the window is not on the grid on the surface. if that makes sense.
Easy fix:
if row.collidepoint((mouse_pos[0] - 350,mouse_pos[1] - 250)):
move the mouse pos back so 0,0 on the surface is 0,0 on the window
I'm working on making a game for a project at my university using pygame. All I'm trying to get done right now is create a ball that can be controlled to go back and forth on the screen when the user presses the left and right arrow keys. Honestly I don't really know what I'm doing, so I'm using the code in the pygame documentation that was used for pong as the base for my game. My code is below, and if someone knows why I'm getting the error that's in the title, please let me know.
try:
import sys
import random
import math
import os
import getopt
import pygame
from socket import *
from pygame.locals import *
except ImportError, err:
print "couldn't load module. %s" % (err)
sys.exit(2)
def load_png(name):
""" Load image and return image object"""
fullname = name
try:
image = pygame.image.load(fullname)
if image.get_alpha() is None:
image = image.convert()
else:
image = image.convert_alpha()
except pygame.error, message:
print 'Cannot load image:', fullname
raise SystemExit, message
return image, image.get_rect()
class Ball(pygame.sprite.Sprite):
def __init__(self, (xy)):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_png('ball.png')
screen = pygame.display.get_surface()
self.area = screen.get_rect()
self.hit = 0
self.speed = 10
self.state = "still"
def reinit(self):
self.state = "still"
self.movepos = [0,0]
if self.side == "left":
self.rect.midleft = self.area.midleft
def update(self):
newpos = self.rect.move(self.movepos)
if self.area.contains(newpos):
self.rect = newpos
pygame.event.pump()
def moveleft(self):
self.movepos[1] = self.movepos[1] - (self.speed)
self.state = "moveleft"
def moveright(self):
self.movepos[1] = self.movepos[1] + (self.speed)
self.state = "moveright"
def main():
running = True
pygame.init()
(width, height) = (800, 600)
screen = pygame.display.set_mode((width, height))
# Fill background
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((0, 0, 0))
screen.blit(background, (0, 0))
pygame.display.flip()
global player
player = Ball("left")
playersprite = pygame.sprite.RenderPlain(player)
playersprite.draw(screen)
player.update()
while running:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_q:
running = False
if event.key == K_LEFT:
player.moveleft()
if event.key == K_RIGHT:
player.moveright()
elif event.type == KEYUP:
if event.key == K_UP or event.key == K_DOWN:
player.movepos = [0,0]
player.state = "still"
#screen.blit(background, ball.rect, ball.rect)
screen.blit(background, player.rect, player.rect)
#screen.blit(background, player2.rect, player2.rect)
#ballsprite.update()
playersprite.update()
#ballsprite.draw(screen)
playersprite.draw(screen)
if __name__ == '__main__': main()
All I'm trying to get done right now is create a ball that can be
controlled to go back and forth on the screen when the user presses
the left and right arrow keys.
You are massively over complicating this, you don't need 102 lines to move a ball around. I wrote and commented a simple example that I think could help you a lot. All you really need to do is detect key presses and then update an x and y variable then draw the image using the x and y variables.
import pygame
screen_width = 1280
screen_height = 720
pygame.init()
screen = pygame.display.set_mode((screen_width,screen_height))
BLACK = (0,0,0)
ballImg = pygame.image.load("ball.jpg")
ballPosition = [0,0]
speed = 1
def game_loop():
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
#get all the keys being pressed
keys = pygame.key.get_pressed()
#depending on what key the user presses, update ball x and y position accordingly
if keys[pygame.K_UP]:
ballPosition[1] -= speed
if keys[pygame.K_DOWN]:
ballPosition[1] += speed
if keys[pygame.K_LEFT]:
ballPosition[0] -= speed
if keys[pygame.K_RIGHT]:
ballPosition[0] += speed
screen.fill(BLACK) #fill the screen with black
screen.blit(ballImg, ballPosition) #draw the ball
pygame.display.update() #update the screen
game_loop()
What versions of python/pygame are you running as when I tested your code with python 3.4, I first got syntax errors with your try: except statements, but this may be due to different syntax over different versions. After fixing that I ran into the issue of movepos not being defined when pressing left or right. Adding self.movepos = [0, 0] to the __init__() of the Ball class fixed this.
I never ran into the error you described, however the game did give a constant black screen no matter what I do.
What I'm trying to say is that errors can sometimes be caused by other mistakes earlier on in the code that don't get picked up. One of the answers here sums it up nicely: error: video system not initialized; Is there a solution?
Also, what variables store the balls x and y position? I couldn't seem to make out how you were controlling that?
This question already has answers here:
Lag when win.blit() background pygame
(2 answers)
Closed 2 years ago.
I have been making a simple python game using pygame and after I added the feature of switching guns the game started to lag. I have no idea why it is lagging. I have tried rebooting but it didn't work. The code is really short so maybe it is just my computer, but if there is anything that may help run it faster please let me know. Here is the code:
import sys, pygame, pygame.mixer
from pygame.locals import *
pygame.init()
size = width, height = 600, 400
screen = pygame.display.set_mode(size)
pygame.display.set_caption('Blue Screen of Death')
#variables
x = 100
y = 200
gun_type = "gun1"
gun = pygame.image.load("gun1.png")
gun = pygame.transform.scale(gun,(500,250))
gun_sound = pygame.mixer.Sound("gun_sound.wav")
clock = pygame.time.Clock()
while 1:
mx, my = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:sys.exit()
elif event.type == KEYDOWN and event.key == K_ESCAPE:
sys.exit()
elif event.type == MOUSEBUTTONDOWN:
gun_sound.play()
elif event.type == KEYDOWN and event.key == K_1:
gun = pygame.image.load("gun1.png")
gun = pygame.transform.scale(gun,(500,250))
gun_type = "gun2"
elif event.type == KEYDOWN and event.key == K_2:
gun = pygame.image.load("gun2.png")
gun = pygame.transform.scale(gun,(500,250))
gun_type = "gun2"
elif event.type == KEYDOWN and event.key == K_TAB:
if gun_type == "gun2":
gun_type = "gun2_aimed"
elif gun_type == "gun2_aimed":
gun_type = "gun2"
elif gun_type == "gun2_aimed":
gun = pygame.image.load("gun2_aimed.png")
gun = pygame.transform.scale(gun,(500,250))
#frames per second
clock.tick(60)
hallway = pygame.image.load("hallway.png")
hallway = pygame.transform.scale(hallway,(600,400))
screen.blit(hallway,(0,0))
screen.blit(gun,(mx-100,y))
pygame.display.flip()
Thanks for your help.
This is probably the most important thing you can learn in Pygame.
For many years, I've had lagging issues in Pygame. I was frustrated, and almost switched to Pyglet. My game was only running at 9 fps.
Then I found some Pygame documentation on my computer. It had some advice from David Clark, and he suggested you add .convert_alpha() at the end of all Pygame image loads. This increased my framerate to 32!
Here's the website:
https://www.pygame.org/docs/tut/newbieguide.html
I always just create a function to do it for me, so I don't have to keep typing '.convert_alpha()' too many times:
def loadify(imgname):
return pygame.image.load(imgname).convert_alpha()
Just replace pygame.image.load( to loadify( when using this function.
Have fun with Pygame!
You could try to load the images of your guns before the while loop and save a reference to them, this way you don't have to load the image on the fly every time.
Don't call pygame.image.load from your event handler.
Instead, call it on all of your resources at startup and just switch out which one you use.
I found a solution to make a sprite move when you hold a key down. The problem is that it forces writing ugly duplicated code. The current solution I found is:
for event in pygame.event.get():
if event.type == KEYDOWN:
keystate = pygame.key.get_pressed()
while keystate[K_RIGHT]:
screen.fill((255,255,255))
pygame.event.get()
for sprite in sprites:
rimage = sprite[1].getimage()
if sprite[2] is None:
x+=3
sprite[1].update(time)
screen.blit(rimage, (x,y))
if sprite[1].isfinished() == True:
sprite[1].reset()
last_dir = "right"
if x >= screen_width - rimage.get_width():
x = screen_width - rimage.get_width()
#update player sprite movement
#update player sprite animation
#update rest of game map
keystate = pygame.key.get_pressed()
time = pygame.time.get_ticks()
pygame.display.update()
The problem is that the while keystate block. It has to be repeated for each direction and the game world needs to be updated in each while block. That is five places where the same code needs to be duplicated....4 directions plus if a key is not pressed. I could wrap it in a function but I was wondering if there was a better way to handle holding down a button in pygame.
The usual way program in pygame is use the events to update the direction, then write the update position code outside events, that way you don't need replicated code.
clock = pygame.time.Clock()
direction = (0,0)
while True: # main loop
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_RIGHT:
direction = (3, 0)
elif event.key == K_LEFT:
direction = (-3, 0)
elif event.key == K_UP:
direction = (0, 3)
elif event.key == K_DOWN:
direction = (0, -3)
else:
print "Unrecognized key"
if event.type == KEYUP:
direction = (0, 0)
screen.fill((255,255,255))
for sprite in sprites:
rimage = sprite[1].getimage()
if sprite[2] is None:
# Check if new position is inside the screen
new_pos = x + direction[0], y + direction[1]
if new_pos[0] + rimage.get_width() < screen_width:
x = new_pos[0]
if new_pos[1] + rimage.get_height() < screen_height:
y = new_pos[1]
# Draw the sprite
sprite[1].update(time)
screen.blit(rimage, (x,y))
if sprite[1].isfinished() == True:
sprite[1].reset()
last_dir = direction
#update player sprite movement
#update player sprite animation
#update rest of game map
time = pygame.time.get_ticks()
pygame.display.update()
clock.tick(40) # Keep game running at 40 fps
If you want you can achieve the same with keystate, in such case you don't need to process key events.
Pygame suggests or implies the division of programs into 3 parts:
The event handling, updating and drawing.
As pmoleri already said, you simply change the direction of the movement.
In the update function, you should pass in a delta time parameter, to move all the sprites according to the time passed. It is quite important, since the other technique doesn't take into account the variable speed of the processor. Games in DOS have been made this way, so now we need emulators to artificially slow down the processor. The draw part simply draws all the sprites.
This way you have a clear division between these 3 distinc parts in games: player input, game logic(movement, collision, actions etc.) and drawing.
Additionally, pygame.key.get_pressed() can be used.
This returns a list of all the keys currently being held down.
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
# Move Up
if keys[pygame.K_a]:
# Move Down
... etc ...
This does not need to be in the event section, but probably where the player updates.
This can make the code less clunky and (possible) be more efficient
-Hope I helped!