Related
I have a single battery image appearing once here and it moves from right to left then goes away. I want batteries constantly coming and going until the player hits one of them.
My question is how do I get the batteries to keep on coming until a player hits it? I want them to appear maybe 100 units apart.
from pygame import *
import os
import random
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d, %d" %(0, 0)
init()
#set screen size
size = width, height = 800, 600
screen = display.set_mode(size)
#set fonts
fontGame=font.SysFont("Times New Roman", 30)
fontBack=font.SysFont("Ariel", 30)
fontTitle=font.SysFont("Ariel", 100)
fontResearch=font.SysFont ("Times New Roman", 18)
#set button and page to 0
button = 0
page=0
#setting colours
BLACK = (0, 0, 0)
RED = (255,0,0)
GREEN = (0, 255, 0)
BLUE = (106,186,232)
#loading image
backgroundPic=image.load("Background.jpg")
backgroundGame=image.load("gameBackground.jpg")
backgroundGame=transform.scale(backgroundGame,(800,600))
battery=image.load("Battery.png")
battery=transform.scale(battery,(100,100))
backgroundx=0
playerPic=image.load("player.png")
playerPic=transform.scale(playerPic,(70,70))
batteryx=[]
#defining what is going to be shown on the screen
def drawScene(screen, button,page,locationx,locationy):
global batteryx
mx, my = mouse.get_pos() #will get where the mouse is
#if the user does nothing
if page==0:
draw.rect(screen, BLACK, (0,0, width, height))
screen.fill(BLACK)
rel_backgroundx= backgroundx % backgroundGame.get_rect().width
screen.blit(backgroundGame, (rel_backgroundx - backgroundGame.get_rect().width,0))
if rel_backgroundx < width:
screen.blit (backgroundGame, (rel_backgroundx,0))
screen.blit(playerPic,(locationx,locationy))
screen.blit(battery,(batteryx,420))
batteryx-=1
display.flip()
return page
#def collision (battery, playerPic):
#if battery.colliderect(playerPic):
#return True
#return False
running = True
myClock = time.Clock()
KEY_LEFT= False
KEY_RIGHT= False
KEY_UP= False
KEY_DOWN= False
locationx=0
jumping=False
accel=20
onGround= height-150
locationy=onGround
batteryx=random.randrange(50,width,10)
# Game Loop
while running:
button=0
print (KEY_LEFT, KEY_RIGHT)
for evnt in event.get(): # checks all events that happen
if evnt.type == QUIT:
running=False
if evnt.type == MOUSEBUTTONDOWN:
mx,my=evnt.pos
button = evnt.button
if evnt.type== KEYDOWN:
if evnt.key==K_LEFT:
KEY_LEFT= True
KEY_RIGHT= False
if evnt.key==K_RIGHT:
KEY_RIGHT= True
KEY_LEFT= False
if evnt.key==K_UP and jumping==False:
jumping=True
accel=20
if evnt.key== K_DOWN:
KEY_DOWN= True
KEY_UP= False
if evnt.type==KEYUP:
if evnt.key==K_LEFT:
KEY_LEFT= False
if evnt.key==K_RIGHT:
KEY_RIGHT= False
if evnt.key==K_DOWN:
KEY_DOWN=False
if KEY_LEFT== True:
locationx-=10
backgroundx+=10
if KEY_RIGHT== True:
locationx+=10
backgroundx-=10
if jumping==True:
locationy-=accel
accel-=1
if locationy>=onGround:
jumping=False
locationy=onGround
#player cannot move off screen
if locationx<0:
locationx=0
if locationx>400:
locationx=400
#if collision(battery, playerPic)==True:
#screen.fill(BLACK)
page=drawScene(screen,button,page,locationx,locationy)
myClock.tick(60) # waits long enough to have 60 fps
if page==6: #if last button is clicked program closes
running=False
quit()
Create a list of rects which serve as the positions of the batteries. Use for loops to update the rects and blit the battery image at the rects. Remove rects that have left the screen and append new rects to create new batteries
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
BG_COLOR = pg.Color('gray12')
battery_image = pg.Surface((30, 50))
battery_image.fill(pg.Color('sienna1'))
# Append pygame.Rect objects to this list.
batteries = []
batteries.append(battery_image.get_rect(topleft=(700, 100)))
battery_speed = -5
battery_timer = 40
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
# A simple frame based timer.
battery_timer -= 1
if battery_timer <= 0:
battery_timer = 40
# After 40 frames a new rect gets appended to the list.
batteries.append(battery_image.get_rect(topleft=(700, 100)))
temp_list = []
# Iterate over the rects to update them.
for battery_rect in batteries:
battery_rect.x += battery_speed
# Rects with x <= 50 won't be appended to the temp_list.
if battery_rect.x > 50:
temp_list.append(battery_rect)
# Assign the list with the remaining rects to the batteries variable.
batteries = temp_list
# Blit everything.
screen.fill(BG_COLOR)
for battery_rect in batteries:
screen.blit(battery_image, battery_rect)
pg.display.flip()
clock.tick(30)
pg.quit()
The desired outcome is to have a box that can be clicked by the player, and then slowly fill up. I tried using while and for loops after testing if the user clicked the button, but rather than increasing slowly, it only increases a fraction of the entire box. The entire games code is below, but my issue seems to be with lines 70-80.
import pygame, sys, time, threading, tqdm
pygame.init()
#Setting up background colors, etc
WHITE = (255,255,255)
BLACK = (0,0,0)
RED = (255,0,0)
GREEN = (0,255,0)
BLUE = (0,0,255)
YELLOW =(255,255,0)
#Setting up window and caption
DISPLAYSURF_x = 460
DISPLAYSURF_y = 720
DISPLAYSURF = pygame.display.set_mode((DISPLAYSURF_x,DISPLAYSURF_y))
pygame.display.set_caption('Adventure Capitalist')
logo = pygame.image.load('Logo.png')
menu = pygame.image.load('menu.png')
DISPLAYSURF.fill(WHITE)
cash = 30
amount = 0
barlength = 102
storeBoard = pygame.image.load('storeBoard.png')
def buyDraw(amount, minxbuy, minybuy):
Font1 = pygame.font.SysFont('monaco', 24)
buySurface = Font1.render('{0}'.format(amount), True, BLACK)
buyRect = buySurface.get_rect()
buyRect.midtop = (75, minybuy)
DISPLAYSURF.blit(buySurface,buyRect)
def cashDraw(cash):
Font2 = pygame.font.SysFont('monaco', 40)
cashSurface = Font2.render(' ${0}'.format(cash), True, GREEN)
cashRect = cashSurface.get_rect()
cashRect.midtop = (420, 10)
DISPLAYSURF.blit(cashSurface,cashRect)
# amount of item, cost to buy, time taken to complete action, money per run
def capitalist(amount, cost, timez, gain, minxbuy, maxxbuy, minybuy, maxybuy, minxgain, maxxgain, minygain, maxygain, cash):
print ("ran")
# Buy button
DISPLAYSURF.fill(WHITE)
DISPLAYSURF.blit(storeBoard, (0, 0))
buyDraw(amount, minxbuy, minybuy)
cashDraw(cash)
pygame.display.flip()
button_rect = pygame.Rect(minxgain, minygain, maxxgain, maxygain)
# Coefficient to calculate the width of the rect for a given time.
coefficient = maxxgain / timez
time = 0
clock = pygame.time.Clock()
dt = 0
done = False
while True:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
(x, y) = pygame.mouse.get_pos()
if x < maxxbuy and x > minxbuy and y < maxybuy and y > minybuy and cash >= cost:
DISPLAYSURF.fill(WHITE, (minxbuy+25, minybuy, maxxbuy+30, maxybuy))
amount += 1
buyDraw(amount, minxbuy, minybuy)
print (cash)
DISPLAYSURF.fill(WHITE, (375, 0 , 460, 51))
cash -= cost
cashDraw(cash)
pygame.display.flip()
if x < maxxgain and x > minxgain and y < maxygain and y > minygain and amount > 0:
# If mouse is over the button, increase the timer.
if time < timez: # Stop increasing if max_time is reached.
time += dt
if time >= timez:
time = timez
inc = time * coefficient
pygame.draw.rect(DISPLAYSURF, BLACK, (minxgain, minygain, inc/2, maxygain/2))
pygame.display.flip()
dt = clock.tick(60) / 100
def opening():
DISPLAYSURF.blit(logo, (155, 50))
DISPLAYSURF.blit(menu, (0 , 125))
while True:
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
(x, y) = pygame.mouse.get_pos()
if x < 375 and x > 80 and y < 545 and y > 395:
# 1 2 3 4 5 6 7 8 9 10 11 12 13
#amount, cost, time, gain, minxbuy, maxxbuy, minybuy, maxybuy, minxgain, maxxgain, minygain, maxygain, cash
# capitalist(0, 5, 1, 1, 21, 41, 21, 41, 120, 204, 20, 41, cash)
capitalist(1, 10, 3, 5, 21, 41, 21, 41, 120, 204, 20, 41, cash)
pygame.display.flip()
opening()
Do not have 2 main loops. Neither nested nor threaded. There should be only one loop checking the event queue. If you need threading, then you should create your own event distribution in the main loop or manually manage the event queue. Take and remove all events in main-main loop except those needed in the other one. And the other loop should remove only itsown events.
Do not draw directly to display surface. It is better to have one surface that you modify and then you blit it fully or parts of it to the screen and flip()/update() it. Sometimes treating display surface as any other causes problems.
Perhaps you should use subsurfaces to simplify your task of filling-in rectangles.
When you tidy your code up a little, the problem should go away or it will be more visible.
I'm not sure how exactly your buttons should behave, but I can tell you how to fix the event handling and logic in your capitalist function. Your main problem is that you use the event loop to increase the amount, whereas you rather want to use pygame.mouse.get_pressed in the while loop in this case to see which mouse buttons are currently pressed. Then check if the mouse_pos collides with one of the predefined buttons (pygame.Rects) and run the desired code.
The drawing code should also be separated from the event handling and the game logic. I've got a complete, working example here with some additional side notes (as I said I'm not sure how the buttons should behave, so you have to adjust them as you want):
import pygame, sys
pygame.init()
WHITE = (255,255,255)
BLACK = (0,0,0)
GREEN = pygame.Color('green')
DISPLAYSURF = pygame.display.set_mode((460, 720))
clock = pygame.time.Clock()
cash = 30
amount = 0
barlength = 102
storeBoard = pygame.Surface((102, 40))
storeBoard.fill((100, 100, 250))
# Side note: Define the fonts once here.
Font1 = pygame.font.SysFont('monaco', 24)
Font2 = pygame.font.SysFont('monaco', 40)
def buyDraw(amount, minxbuy, minybuy):
buySurface = Font1.render('{0}'.format(amount), True, BLACK)
buyRect = buySurface.get_rect()
buyRect.midtop = (75, minybuy)
DISPLAYSURF.blit(buySurface, buyRect)
def cashDraw(cash):
cashSurface = Font2.render(' ${0}'.format(cash), True, GREEN)
cashRect = cashSurface.get_rect()
cashRect.midtop = (420, 10)
DISPLAYSURF.blit(cashSurface, cashRect)
# amount of item, cost to buy, time taken to complete action, money per run
def capitalist(amount, cost, timez, gain, minxbuy, maxxbuy, minybuy, maxybuy, minxgain, maxxgain, minygain, maxygain, cash):
pygame.display.set_caption('capitalist loop')
# Define the buttons here.
buy_button = pygame.Rect(minxbuy, minybuy, maxxbuy, maxybuy)
gain_button = pygame.Rect(minxgain, minygain, maxxgain, maxygain)
# Coefficient to calculate the width of the rect for a given time.
coefficient = maxxgain / timez
time = 0
dt = 0
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# pygame.mouse.get_pressed() returns a tuple of zeros and ones that
# show you which mouse buttons are currently being held down.
mouse_pressed = pygame.mouse.get_pressed()
mouse_pos = pygame.mouse.get_pos()
# Use the collidepoint method of the rects to see if they collide with the mouse.
# `mouse_pressed[0]` is the left mouse button.
if buy_button.collidepoint(mouse_pos) and mouse_pressed[0] and cash >= cost:
amount += 1
print(cash)
cash -= cost
if gain_button.collidepoint(mouse_pos) and mouse_pressed[0] and amount > 0:
# If the mouse is over the button, increase the timer.
if time < timez: # Stop increasing if max_time is reached.
time += dt
if time >= timez:
time = timez
inc = time * coefficient
# Draw everything and then flip the display.
DISPLAYSURF.fill(WHITE)
pygame.draw.rect(DISPLAYSURF, BLACK, (minxgain, minygain, inc/2, maxygain/2))
pygame.draw.rect(DISPLAYSURF, BLACK, buy_button, 2)
pygame.draw.rect(DISPLAYSURF, BLACK, gain_button, 2)
buyDraw(amount, minxbuy, minybuy)
cashDraw(cash)
pygame.display.flip()
dt = clock.tick(60) / 1000 # dt is the passed time in seconds.
def opening():
pygame.display.set_caption('opening loop')
rect = pygame.Rect(20, 20, 100, 70)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
# Collidepoint with the event.pos to see if the mouse
# collides with the button.
if rect.collidepoint(event.pos):
# Named arguments make the code more readable. Passing
# so many arguments can be a code smell and indicates
# that the code could probably be refactored.
capitalist(
amount=1, cost=10, timez=3, gain=5, minxbuy=21,
maxxbuy=41, minybuy=21, maxybuy=41, minxgain=120,
maxxgain=204, minygain=20, maxygain=41, cash=cash)
DISPLAYSURF.fill(WHITE)
# Draw the orange start button.
pygame.draw.rect(DISPLAYSURF, (200, 100, 10), rect)
pygame.display.flip()
clock.tick(60) # Call clock.tick here as well.
opening()
I am trying to make a 'Runner' style game in PyGame (like Geometry Dash) where the background is constantly moving. So far everything works fine, but the rendering of the background images restricts the frame rate from exceeding 35 frames per second. Before I added the infinite/repeating background element, it could easily run at 60 fps. These two lines of code are responsible (when removed, game can run at 60+fps):
screen.blit(bg, (bg_x, 0)) |
screen.blit(bg, (bg_x2, 0))
Is there anything I could do to make the game run faster? Thanks in advance!
Simplified Source Code:
import pygame
pygame.init()
screen = pygame.display.set_mode((1000,650), 0, 32)
clock = pygame.time.Clock()
def text(text, x, y, color=(0,0,0), size=30, font='Calibri'): # blits text to the screen
text = str(text)
font = pygame.font.SysFont(font, size)
text = font.render(text, True, color)
screen.blit(text, (x, y))
def game():
bg = pygame.image.load('background.png')
bg_x = 0 # stored positions for the background images
bg_x2 = 1000
pygame.time.set_timer(pygame.USEREVENT, 1000)
frames = 0 # counts number of frames for every second
fps = 0
while True:
frames += 1
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.USEREVENT: # updates fps every second
fps = frames
frames = 0 # reset frame count
bg_x -= 10 # move the background images
bg_x2 -= 10
if bg_x == -1000: # if the images go off the screen, move them to the other end to be 'reused'
bg_x = 1000
elif bg_x2 == -1000:
bg_x2 = 1000
screen.fill((0,0,0))
screen.blit(bg, (bg_x, 0))
screen.blit(bg, (bg_x2, 0))
text(fps, 0, 0)
pygame.display.update()
#clock.tick(60)
game()
Here is the background image:
Have you tried using convert()?
bg = pygame.image.load('background.png').convert()
From the documentation:
You will often want to call Surface.convert() with no arguments, to create a copy that will draw more quickly on the screen.
For alpha transparency, like in .png images use the convert_alpha() method after loading so that the image has per pixel transparency.
I'm trying to create "end credits" like the ones at the end of a movie, using pygame. I've googled for other ways to achieve this using python, but I haven't found any yet.
I've almost achieved this with the following code: http://pastebin.com/nyjxeDYQ
#!/usr/bin/python
import time
import threading
import pygame
from pygame.locals import *
# Initialise pygame + other settings
pygame.init()
pygame.fastevent.init()
event_get = pygame.fastevent.get
pygame.display.set_caption('End credits')
screen = pygame.display.set_mode((1920, 1080))
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((255, 255, 255))
fontsize = 40
font = pygame.font.SysFont("Arial", fontsize)
x = 0
def main():
global x
credit_list = ["CREDITS - The Departed"," ","Leonardo DiCaprio - Billy","Matt Damon - Colin Sullivan", "Jack Nicholson - Frank Costello", "Mark Wahlberg - Dignam", "Martin Sheen - Queenan"]
going = True
while going:
events = event_get()
for e in events:
if e.type in [QUIT]:
going = False
if e.type in [KEYDOWN] and e.key == pygame.K_ESCAPE:
going = False
# Loop that creates the end credits
ypos = screen.get_height()
while ypos > (0 - len(credit_list)*50) and x == 0: # Loop through pixel by pixel, screenheight + height of all the textlines combined
drawText(credit_list,ypos)
ypos = ypos - 1
x = 1
pygame.quit()
def drawText(text,y):
for line in text:
text = font.render(line, 1, (10, 10, 10))
textpos = text.get_rect()
textpos.centerx = background.get_rect().centerx
background.blit(text, (textpos.x,y))
y = y + 45
# Blit all the text
screen.blit(background, (0, 0))
pygame.display.flip()
time.sleep(0.0001) # Sleep function to adjust speed of the end credits
# Blit white background (else all the text will stay visible)
background.fill((255, 255, 255))
screen.blit(background, (0, 0))
pygame.display.flip()
if __name__ == '__main__': main()
The problem is that the scrolling text is flickering. This is because I use a time.sleep()-function to control the speed of the scrolling. When I use a value like 0.04 sec, it works pretty well, but the text moves too slow and there is still a bit of flickering. When I use a much lower value, like: 0.001 sec, the text is moving at a speed that I like, but there is a lot more flickering going on.
There is another value I can use to adjust the speed of the scrolling: the number of pixels to move. But when I set this to anything higher than 1, the scrolling isn't smooth anymore.
Does anyone know a solution to this problem? I don't necessarily have to use pygame, I do have to use python though.
Many thanks in advance!
Albrecht
Here are some simpe rule you should follow that will help you with your problem:
Don't call pygame.display.flip() more than once per frame
Don't use time.sleep() to control the speed of something in your application
Use a Clock to control the framerate
Here's a cleaned up, minimal working example:
#!/usr/bin/python
import pygame
from pygame.locals import *
pygame.init()
pygame.display.set_caption('End credits')
screen = pygame.display.set_mode((800, 600))
screen_r = screen.get_rect()
font = pygame.font.SysFont("Arial", 40)
clock = pygame.time.Clock()
def main():
credit_list = ["CREDITS - The Departed"," ","Leonardo DiCaprio - Billy","Matt Damon - Colin Sullivan", "Jack Nicholson - Frank Costello", "Mark Wahlberg - Dignam", "Martin Sheen - Queenan"]
texts = []
# we render the text once, since it's easier to work with surfaces
# also, font rendering is a performance killer
for i, line in enumerate(credit_list):
s = font.render(line, 1, (10, 10, 10))
# we also create a Rect for each Surface.
# whenever you use rects with surfaces, it may be a good idea to use sprites instead
# we give each rect the correct starting position
r = s.get_rect(centerx=screen_r.centerx, y=screen_r.bottom + i * 45)
texts.append((r, s))
while True:
for e in pygame.event.get():
if e.type == QUIT or e.type == KEYDOWN and e.key == pygame.K_ESCAPE:
return
screen.fill((255, 255, 255))
for r, s in texts:
# now we just move each rect by one pixel each frame
r.move_ip(0, -1)
# and drawing is as simple as this
screen.blit(s, r)
# if all rects have left the screen, we exit
if not screen_r.collidelistall([r for (r, _) in texts]):
return
# only call this once so the screen does not flicker
pygame.display.flip()
# cap framerate at 60 FPS
clock.tick(60)
if __name__ == '__main__':
main()
I'm making an arcade game using pygame and I'm trying to have a sprite change positions every few seconds.
I've tried using time.sleep(1) and changing the frame rate to .5 (clock.tick(.5)).
Both worked to make the object change position only after the time interval has passed, however they also make the sprite following my mouse update coordinates at the same rate.
I've been researching and can't seem to find another way to make the sprite move without making my program refresh slower or 'sleep' every time it runs.
You can use an Event for this together with pygame.time.set_timer():
pygame.time.set_timer()
repeatedly create an event on the event queue
set_timer(eventid, milliseconds) -> None
Set an event type to appear on the event queue every given number of milliseconds
Here's a simple, complete example. Note how the enemies move every 1000ms sideways, every 3500ms downwards, and you can shoot every 450ms (all using events).
import pygame
# you'll be able to shoot every 450ms
RELOAD_SPEED = 450
# the foes move every 1000ms sideways and every 3500ms down
MOVE_SIDE = 1000
MOVE_DOWN = 3500
screen = pygame.display.set_mode((300, 200))
clock = pygame.time.Clock()
pygame.display.set_caption("Micro Invader")
# create a bunch of events
move_side_event = pygame.USEREVENT + 1
move_down_event = pygame.USEREVENT + 2
reloaded_event = pygame.USEREVENT + 3
move_left, reloaded = True, True
invaders, colors, shots = [], [] ,[]
for x in range(15, 300, 15):
for y in range(10, 100, 15):
invaders.append(pygame.Rect(x, y, 7, 7))
colors.append(((x * 0.7) % 256, (y * 2.4) % 256))
# set timer for the movement events
pygame.time.set_timer(move_side_event, MOVE_SIDE)
pygame.time.set_timer(move_down_event, MOVE_DOWN)
player = pygame.Rect(150, 180, 10, 7)
while True:
clock.tick(40)
if pygame.event.get(pygame.QUIT): break
for e in pygame.event.get():
if e.type == move_side_event:
for invader in invaders:
invader.move_ip((-10 if move_left else 10, 0))
move_left = not move_left
elif e.type == move_down_event:
for invader in invaders:
invader.move_ip(0, 10)
elif e.type == reloaded_event:
# when the reload timer runs out, reset it
reloaded = True
pygame.time.set_timer(reloaded_event, 0)
for shot in shots[:]:
shot.move_ip((0, -4))
if not screen.get_rect().contains(shot):
shots.remove(shot)
else:
hit = False
for invader in invaders[:]:
if invader.colliderect(shot):
hit = True
i = invaders.index(invader)
del colors[i]
del invaders[i]
if hit:
shots.remove(shot)
pressed = pygame.key.get_pressed()
if pressed[pygame.K_LEFT]: player.move_ip((-4, 0))
if pressed[pygame.K_RIGHT]: player.move_ip((4, 0))
if pressed[pygame.K_SPACE]:
if reloaded:
shots.append(player.copy())
reloaded = False
# when shooting, create a timeout of RELOAD_SPEED
pygame.time.set_timer(reloaded_event, RELOAD_SPEED)
player.clamp_ip(screen.get_rect())
screen.fill((0, 0, 0))
for invader, (a, b) in zip(invaders, colors):
pygame.draw.rect(screen, (150, a, b), invader)
for shot in shots:
pygame.draw.rect(screen, (255, 180, 0), shot)
pygame.draw.rect(screen, (180, 180, 180), player)
pygame.display.flip()
How about
var = 0
while True:
event_handling()
game_logic()
if var == 5:
sprite.update.position()
var = 0
pygame.display.flip()
var += 1
Obviously, this is just pseudo code, but you get the idea.