When i was making background with screen.fill, my code was checking collision of hero with a lot of projectiles - and program still wasn't lagging. But when i decided to add drawn background (3600x1200 pix) as sprite, and fps fell down to about 20 fps. I removed all projectiles, but fps is still enormously low. Such big background was chosen for moving it, instead of moving main hero. Here is my code and the problematic line displaying the background (everything works fine without it).
#import modules
import pygame as pg
from hero import Hero
#self-made classes
from obstruction import Obstruction
from setting import Settings
import gf
from time import sleep
**from back_ground import Background**
from command_block import Command_Line
#main function
def run_game():
#pygame activation
pg.init()
#window settings
settings = Settings()
screen = pg.display.set_mode((settings.screen_width,settings.screen_height))
pg.display.set_caption(settings.caption)
hero = Hero(0, 0, "Right", 1/240, pg.image.load('images/hero/hero1.png'), 128, 16, 15, 0.08, 0.2, settings,27)
bg = Background('images/sky.png', [0,0])
cl = Command_Line(hero)
#class creating
projectiles = []
#here was some projectile creating
#variables initialization etc.
work=True
myfont = pg.font.SysFont('TimesNewRoman',int(settings.screen_width*0.025))
#main loop
while work:
sleep()
hero.is_running = False
hero.idle = True
gf.check_events(hero, projectiles, settings, cl)
gf.jump(hero, projectiles)
#updating hero rect due to coordinate changing and hero image due to direction changing
hero.update_rect()
hero.orientation()
#screen filling with backgroung color
screen.fill([255, 255, 255])
screen.blit(bg.image, bg.rect)
#displaying projectiles(walls,floor,etc.)
for projectile in projectiles:
projectile.display(screen)
#displaying hero image and all his atributes
hero.display(screen, myfont)
gf.command_block_draw(settings,myfont,screen,cl)
#test string for displaying problematic variables
gf.test_string_draw(settings,myfont,hero,screen)
#display updating
pg.display.flip()
#quit
pg.quit()
#running game
run_game()
Line:
screen.blit(bg.image, bg.rect)
BackGround class:
import pygame as pg
class Background(pg.sprite.Sprite):
def __init__(self, image_file, location):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
Make sure to convert() the image.
Changing this one line of code should increase your performance substantially.
It processes the Surface into a format pygame likes up front, speeding up all the consequent blits.
self.image = pg.image.load(image_file).convert()
Related
I'm building a pong game trying to get better at programming but Im having trouble moving the ball. When the move_right method is called the ellipse stretches to the right instead of moving to the right. I've tried putting the ball variable in the init method but that just makes it not move at all even though the variables should be changing on account of the move_right method. I have also tried setting the x and y positions as parameters in the Ball class,but that just stretches it also.
I don't understand why when I run the following code the ball I'm trying to move stretches to the right instead of moves to the right. Can someone explain why this is happening? I have tried everything I can think of but i can't get it to do what I want.
import pygame,sys
import random
class Ball:
def __init__(self):
self.size = 30
self.color = light_grey
self.x_pos = width/2 -15
self.y_pos = height/2 -15
self.speed = 1
#self.ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
def draw_ball(self):
ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
pygame.draw.ellipse(screen,self.color,ball)
def move_right(self):
self.x_pos += self.speed
class Player:
def __init__(self,x_pos,y_pos,width,height):
self.x_pos = x_pos
self.y_pos = y_pos
self.width = width
self.height = height
self.color = light_grey
def draw_player(self):
player = pygame.Rect(self.x_pos,self.y_pos,self.width,self.height)
pygame.draw.rect(screen,self.color,player)
class Main:
def __init__(self):
self.ball=Ball()
self.player=Player(width-20,height/2 -70,10,140)
self.opponent= Player(10,height/2-70,10,140)
def draw_elements(self):
self.ball.draw_ball()
self.player.draw_player()
self.opponent.draw_player()
def move_ball(self):
self.ball.move_right()
pygame.init()
size = 30
clock = pygame.time.Clock()
pygame.display.set_caption("Pong")
width = 1000
height = 600
screen = pygame.display.set_mode((width,height))
bg_color = pygame.Color('grey12')
light_grey = (200,200,200)
main = Main()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#player = pygame.Rect(width-20,height/2 -70,10,140)
#opponent = pygame.Rect(10,height/2-70,10,140)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#pygame.draw.rect(screen,light_grey,player)
#pygame.draw.rect(screen,light_grey,opponent)
#pygame.draw.ellipse(screen,light_grey,ball)
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
clock.tick(60)
You have to clear the display in every frame with pygame.Surface.fill:
while True:
# [...]
screen.fill(0) # <---
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
# [...]
Everything that is drawn is drawn on the target surface. The entire scene is redraw in each frame. Therefore the display needs to be cleared at the begin of every frame in the application loop. The typical PyGame application loop has to:
handle the events by either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by either pygame.display.update() or pygame.display.flip()
I am brand new to Pygame and am making a game for my A-level course. I am trying to have multiple bats that I can spawn and collide with. I am using Pygames mask function for 'pixel perfect collision' but I cannot get multiple bats to spawn at the same time with the collision system also working. I tried using groups but I haven't been able to get this to work. Does anyone know how to fix my code/ a better way around this problem? Thanks! The relevant code is below...
class Bat(pygame.sprite.Sprite):
def __init__(self, bat_x, bat_y):
pygame.sprite.Sprite.__init__(self)
self.bat1 = pygame.image.load("Sprites\Bat_enemy\Bat-1.png").convert_alpha() # For hit registration for bat
self.bat1 = pygame.transform.scale(self.bat1, (80, 70))
self.bat_mask = pygame.mask.from_surface(self.bat1)
self.bat_rect = self.bat1.get_rect()
self.bat_x = bat_x
self.bat_y = bat_y
bats = pygame.sprite.Group()
Then in main loop:
num_of_bats = [1]
#Bat#
for i in num_of_bats:
bat_x = (random.randint(0, 600))
bat_y = (random.randint(0, 600))
bat = Bat(bat_x, bat_y, i)
bats.add(bat)
for bat in bats:
offsetP2B = (int(x - batx), int(y - self.baty)) #Player to Bat
resultP2B = bat_mask.overlap(player_mask, offsetP2B)
First get rectangle based collision working, then worry about the bitmask accuracy!
There's a couple of problems with your Sprite. The big one is that PyGame uses sprite.image to draw the bitmap. Your sprite code is using bat1 instead. It also needs to position the sprite.rect to the co-ordinate of the Bat. Furthermore, the collision mask must be called mask, and the sprite's collision/position pygame.Rect must be called rect.
I'm not sure if it's just a paste-o, but the sprite group definition shouldn't be inside the Sprite class.
So ... with a few minor fixups:
class Bat(pygame.sprite.Sprite):
def __init__(self, bat_x, bat_y, bat_image):
pygame.sprite.Sprite.__init__(self)
self.image = bat_image
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface( self.image )
self.rect.centre = ( bat_x, bat_y )
def update( self ):
# TODO: code to make this bat move/flat whatever
pass
There's only minor differences here. It's better to load the image in once, outside the sprite class, than loading it in hundreds(?) of times - once for each bat.
Now it's pretty easy to make a colony of bats:
import os.path
START_BAT_COUNT = 30
BAT_IMAGE_PATH = os.path.join( 'Sprites', 'Bat_enemy', 'Bat-1.png' )
# group to hold all the bat sprites
all_bats = pygame.sprite.Group()
# Going Batty!
bat_image = pygame.image.load( BAT_IMAGE_PATH ).convert_alpha()
for i in range( START_BAT_COUNT ):
bat_x = (random.randint(0, 600))
bat_y = (random.randint(0, 600))
new_bat = Bat( bat_x, bat_y, bat_image )
all_bats.add( new_bat )
The in your main loop:
# move every bat
all_bats.update()
...
# paint every bat
all_bats.draw( screen )
So I'm making an rpg project in Pygame and I need a button class that has text. This is my Code so far. I tried to use some code examples online and on this site but I couldn't make them work in the way I wanted. ;-;
What I want is a button that can drawn to my GameWindow that includes text. I'll figure out the event handling later on.
It would be greatly appreciated if someone could give me an explanation of how a button class that utilises text would work in pygame and explain it in a way I could implement in my Button Class. Previously I have tried simply placing text in the centre of the screen by dividing the width and height by two and placing coloured rects adjacent to the text to try and label the rects so I could use them as buttons. However I realised this wasn't a practical solution, as I would be needing many buttons throughout my game and this method took up large portions of my screen.
I do not understand how to blit a message onto a rect using a class. The Button class below is where I attempted to place text onto top of a rect but I found this very hard.
Ideally my goal here is to be able to call an instance of my button class which I can use as a button.
BTW asking here was a last resort. I spent almost three hours trying to figure this out and its bad for me to stare at a screen for that long.
import pygame, random, sys, math, time
from pygame.locals import *
pygame.init()
FPS = 30
fpsClock = pygame.time.Clock()
GameWindow = pygame.display.set_mode((650,520))
#Variables
Blue = (0,0,255)
Green = (0,255,0)
Red = (255,0,0)
White = (255,255,255)
Black = (0,0,0)
def Button():
def__init__(self, surface, x, y, width, height, colour, message, action=None)
self.x = x
self.y = y
self.width = width
self.height = height
self.font = pygame.font.Font(None, 20)
self.message = message
background_image = pygame.image.load('map.JPG')
title_image = pygame.image.load('title.PNG')
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
GameWindow.blit(background_image, [0,0])
GameWindow.blit(title_image, [100,0])
pygame.display.flip()
fpsClock.tick(FPS)
Here is a button class:
class Button(object):
global screen_width,screen_height,screen
def __init__(self,x,y,width,height,text_color,background_color,text):
self.rect=pygame.Rect(x,y,width,height)
self.x=x
self.y=y
self.width=width
self.height=height
self.text=text
self.text_color=text_color
self.background_color=background_color
self.angle=0
def check(self):
return self.rect.collidepoint(pygame.mouse.get_pos())
def draw(self):
pygame.draw.rect(screen, self.background_color,(self.rect),0)
drawTextcenter(self.text,font,screen,self.x+self.width/2,self.y+self.height/2,self.text_color)
pygame.draw.rect(screen,self.text_color,self.rect,3)
Use the check function to see if your button is clicked on, and the draw function to draw your button.
Implemented into your main loop:
button=Button(x,y,width,height,text_color,background_color,text)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type==pygame.MOUSEBUTTONDOWN:
if button.check()==True:
#do what you want to do when button is pressed
GameWindow.blit(background_image, [0,0])
GameWindow.blit(title_image, [100,0])
pygame.display.flip()
fpsClock.tick(FPS)
I also recommend using these functions to draw text:
def drawTextcenter(text,font,screen,x,y,color):
textobj=font.render(text,True,color)
textrect=textobj.get_rect(center=(x,y))
screen.blit(textobj,textrect)
def drawText(text, font, surface, x, y,color):
textobj=font.render(text, 1, color)
textrect=textobj.get_rect()
textrect.topleft=(x, y)
surface.blit(textobj, textrect)
While you're question is still a bit confusing, I can tell you how you blit your text near or in your button. So what you just do is just place the location of the text near the button, basing the texts x and y variables on the buttons x and y variable.
Copied from your code:
def Button():
def__init__(self, surface, x, y, width, height, colour, message, action=None)
self.x = x
self.y = y
self.width = width
self.height = height
self.font = pygame.font.Font(None, 20)
self.message = message
self.font = pygame.font.SysFont('Comic Sans MS', 30) #Example Font
def draw_button(self):
pygame.draw.rect(GameWindow, Red, (self.x, self.y, self.width, self.height))
self.text = myfont.render(message, False, (0, 0, 0))
GameWindow.blit(self.text, (self.x + self.width/2, self.y + self.height/2)) #Displays text at coordinates at middle of the button.
This draws the button (it still doesn't do anything), but also displays the text in the button. HOWEVER, since the text is displayed at the top-left corner of the surface it is on, it will not be exactly in the middle, and will look odd. You can modify the exact location if you want.
I hope this answers your question.
import pygame, random
pygame.init()
screen = pygame.display.set_mode((700,500))
ball = pygame.image.load('C://python32/ball.jpg')
brick = pygame.image.load('C://python32/brick.jpg')
rect1 = ball.get_rect()
rect2 = brick.get_rect()
screen.fill((255,255,198))
screen.blit(ball,rect1)
screen.blit(brick,rect2)
pygame.display.flip()
if rect1.colliderect(rect2):
x=random.randrange(0,550)
y=random.randrange(0,350)
rect2.move(x,y) #<-------This part
pygame.display.flip()
I have 2 images ball and brick. When I load them on pygame the 2 images collide. So the if rect1.colliderect(rect2) should work. I tested that by putting is a print function. But the rect2.move dosen't work it doesn't show the change on the pygame. What is wrong?
pygame.Rect.move creates a new rect object with the new position which you have to assign to a variable if you want to use it later.
If you just want to modify the existing rect, you can call the move_ip method (ip stands for in-place), so that the rect is still the same object with a new position.
I am struggling to move the sprite correctly. Instead of smooth move I can see blur move and I do not know how to solve it.
Is there any chance you can point what I do incorrectly ?
My target with it to drop the pizza so it hits the bottom and bounce back and bounc back if it hits the top and again the bottom -> bounce -> top -> bounce etc. etc.
import pygame
gravity = 0.5
class PizzaSprite:
def __init__(self, image, spritepos):
self.image = image
self.spos = spritepos
(x, y) = spritepos
self.posx = x
self.posy = y
self.xvel = 1
self.yvel = 1
print "x ", x
print "y ", y
def draw(self, target_surface):
target_surface.blit(self.image, self.spos)
def update(self):
self.posy -= self.yvel
self.spos = (self.posx, self.posy)
return
def main():
pygame.init()
screen_width = 800
screen_height = 600
x = screen_width
y = screen_height
screen = pygame.display.set_mode((screen_width, screen_height))
wall_image = pygame.image.load("wall.png")
sky_image = pygame.image.load("sky.png")
pizza_image = pygame.image.load("pizza.png")
screen.blit(wall_image,(0,200))
screen.blit(sky_image,(0,0))
all_sprites = []
pizza1 = PizzaSprite(pizza_image, (x/2, y/2))
all_sprites.append(pizza1)
while True:
ev = pygame.event.poll()
if ev.type == pygame.QUIT:
break
for sprite in all_sprites:
sprite.update()
for sprite in all_sprites:
sprite.draw(screen)
pygame.display.flip()
pygame.quit()
main()
in the beginning of your main game while loop add
white = (255,255,255)
screen.fill(white)
let me give you a small analogy of what is happening now,
you have paper and a lot of pizza stickers with the intent to make a flip book. To make the illusion of movement on each piece of paper you place a sticker a little bit lower. The screen.fill command essentially clears the screen with the rgb color value you give it. When you dont fill the screen essentially what you are doing is trying to make that flipbook on one piece of paper. You just keep placing more and more stickers a little bit lower making a blur when what you want is one on each page.
and place
pygame.init()
screen_width = 800
screen_height = 600
x = screen_width
y = screen_height
screen = pygame.display.set_mode((screen_width, screen_height))
wall_image = pygame.image.load("wall.png")
sky_image = pygame.image.load("sky.png")
all outside of your main game loop assuming you wont be making changes to these variables ever in your program it is tedious and inefficient to redefine screen,,x,y,and your two images over and over again if they dont change.
so to sum it all up:
use the screen.fill(white) command to reset the color of your screen
You only need to import pngs and define variables once if they are never going to change and don't need them in your main loop
hope this helps clear things up.