Trouble with verticle Pygame status bar - python

The following code works, but it looks a bit kludgy to me. I wander if there is a better way to write it than my approach. Also its resolution is far too high. If anyone has any ideas as to a better approach and how to slow down the resolution, I would love to hear it. Thank you!
import pygame as pg
from random import randint###RANDOM LIBRARY
pg.init()
screen = pg.display.set_mode((640, 480))
FONT = pg.font.Font(None, 36)
BACKGROUND_COLOR = (37, 225, 192)
BLUE = (0, 0, 199)
GRAY = (0, 0, 55)
#Rectangle variables.
measure_rect = pg.Rect(200, 40, 100, 400)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
random_bar=randint(33,34)#Manipulation of bar graph.
value_print=random_bar
length = random_bar*4
random_bar = 100 - random_bar
top = 40+(random_bar*4)
screen.fill(BACKGROUND_COLOR)
pg.draw.rect(screen, BLUE, (200, top, 100, length))
pg.draw.rect(screen, GRAY, measure_rect, 2)
txt = FONT.render(str(round(value_print, 2)), True, GRAY)
screen.blit(txt, (120, 20))
pg.display.flip()
pg.quit()

I recommend to implement a Bar class:
import pygame as pg
from random import randint###RANDOM LIBRARY
pg.init()
screen = pg.display.set_mode((640, 480))
FONT = pg.font.Font(None, 36)
BACKGROUND_COLOR = (37, 225, 192)
BLUE = (0, 0, 199)
GRAY = (0, 0, 55)
class Bar():
def __init__(self, rect, bar = BLUE, outline = GRAY):
self.rect = pg.Rect(rect)
self.bar = bar
self.outline = outline
self.value = 0
def draw(self, surf):
length = round(self.value * self.rect.height / 100)
top = self.rect.height - length
pg.draw.rect(surf, self.bar, (self.rect.x, self.rect.y + top, self.rect.width, length))
pg.draw.rect(surf, self.outline, self.rect, 2)
txt = FONT.render(str(round(self.value, 2)), True, GRAY)
txt_rect = txt.get_rect(topright = (self.rect.x - 10, self.rect.y))
screen.blit(txt, txt_rect)
bar = Bar((200, 40, 100, 400))
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
bar.value = randint(33, 34)
screen.fill(BACKGROUND_COLOR)
bar.draw(screen)
pg.display.flip()
pg.quit()

Related

How to create different layer of background for pygame packed into one class

I'm really new to pygame and Python classes. I tried to create a game with different layers.
Layer 1: gameplay
Layer 2: HP: Position:...
It looks a little bit like this:
I think the problem with my code is that i did not know how to use class properly. And I use the wrong method to create a layer for my game.
import pygame
from pygame.locals import *
import sys
pygame.init()
colors = [(255, 255, 255), (204, 229, 255), (0, 0, 0)] #white, light_blue, black
X_AXIS = [x for x in range(11)]
Y_AXIS = ("X A B C D E F G H I").split()
class character:
pass
class Gameplay:
def __init__(self, width, height):
self.width = width
self.height = height
self.display = pygame.display.set_mode((self.width, self.height))
logo = pygame.image.load(r'logo.png')
pygame.display.set_caption("Battle Royale")
pygame.display.set_icon(logo)
def background_layer(self):
self.game_surf = pygame.surface.Surface((800,600))
game_surf.fill(colors[0])
display.blit(game_surf, (0, 0))
gameplay = Gameplay(1300, 760)
while True:
gameplay.background_layer()
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN:
pass
elif event.type == QUIT:
pygame.quit()
sys.exit()
First few mistakes:
You forgot self. in few places and it should gives you error messages (but you didn't mention it).
You forgot pygame.display.flip() to send from buffer to screen.
If you want other layers then you should add other functions to draw them.
At this moment I see only def background_layer() but you could add other functions for other layers and later execute them in loop - like this:
while True:
self.draw_background()
self.draw_player()
self.draw_grid()
self.draw_info()
pygame.display.flip()
for event in pygame.event.get():
# ... code ...
Minimal working code with other changes.
import sys
import pygame
# --- constants ---- # PEP8: `UPPER_CASE_NAMES` for constants
COLORS = [(255, 255, 255), (204, 229, 255), (0, 0, 0)] #white, light_blue, black
X_AXIS = list(range(11))
Y_AXIS = list("XABCDEFGHI")
# --- classes --- # PEP8: `CamelCaseNames` for classes
class Character:
pass
class Game:
def __init__(self, width, height):
self.width = width
self.height = height
pygame.init()
self.display = pygame.display.set_mode((self.width, self.height))
#self.display_rect = self.display.get_rect()
pygame.display.set_caption("Battle Royale")
#logo = pygame.image.load(r'logo.png')
#pygame.display.set_icon(logo)
self.layer_background = pygame.surface.Surface((800, 600))
self.layer_background.fill(COLORS[0])
self.layer_background_rect = self.layer_background.get_rect()
self.pos = 'X0'
self.hp = 100
self.font = pygame.font.SysFont(None, 40)
def draw_background(self):
self.display.blit(self.layer_background, self.layer_background_rect)
def draw_grid(self):
for x in range(0, 800, 50):
pygame.draw.line(self.display, COLORS[2], (x, 0), (x, 600))
for y in range(0, 600, 50):
pygame.draw.line(self.display, COLORS[2], (0, y), (800, y))
def draw_info(self):
text_image = self.font.render(f"POS: {self.pos}", False, COLORS[0])
text_rect = text_image.get_rect(x=880, y=350)
self.display.blit(text_image, text_rect)
text_image = self.font.render(f"HP: {self.hp}", False, COLORS[0])
text_rect = text_image.get_rect(x=880, y=450)
self.display.blit(text_image, text_rect)
def run(self):
while True:
self.draw_background()
#self.draw_player()
self.draw_grid()
self.draw_info()
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
pass
elif event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# --- functions --- # PEP8: `lower_case_names` for functions
# empty
# --- main ---
game = Game(1300, 760)
game.run()

drawing objects on pygame

I am restarting some code for a covid simulation as I cant use the collide function in my current one. I have been able to draw the basic background, and draw one cell. However, when i try create the cell in a different place on my screen it does not appear for some reason.
My code is as seen below:
import random
import pygame
# import numpy
import time
pygame.init()
GREEN1 = (0, 255, 0) # Healthy cells
RED = (255, 0, 0) # Infected cells
GREEN2 = (0, 100, 0) # Healthy cells not susecptible
BLACK = (0, 0, 0) # Dead cells
WHITE = (255, 255, 255)
Bgcolor = (225, 198, 153)
ScreenSize = (800, 800)
Screen = pygame.display.set_mode(ScreenSize)
pygame.display.set_caption("Covid-19 Simualtion")
clock = pygame.time.Clock()
speed = [0.5, -0.5]
class Cells(pygame.sprite.Sprite):
def __init__(self, color, speed, width, height):
super().__init__()
self.color = color
self.x_cord = random.randint(0, 400)
self.y_cord = random.randint(50, 700)
self.radius = 5
self.speed = speed
self.image = pygame.Surface([width, height])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE)
pygame.draw.circle(self.image, self.color, [30, 70], self.radius, width = 0)
self.rect = self.image.get_rect()
self.radius = 5
#x_number = random.randint(0, 1)
#self.xSpeed = speed[x_number]
#y_number = random.randint(0, 1)
#self.ySpeed = speed[y_number]
allCellsList = pygame.sprite.Group()
Cell1 = Cells(GREEN1, 5, 50, 50)
allCellsList.add(Cell1)
End = False
while not End:
for event in pygame.event.get():
if event.type == pygame.QUIT:
End = True
Screen.fill(Bgcolor)
pygame.draw.rect(Screen, BLACK, (0, 50, 400, 700), 3)
allCellsList.update()
allCellsList.draw(Screen)
pygame.display.flip()
clock.tick(60)
Thanks for any help in advance
The main problem is that your cell has size (50,50) and you try to draw on position (20,70) so it draws outside rectangle (50, 50) and you can't see it. You have to draw inside rectangle (50, 50) - for example in center (25,25). And later you should use self.rect to move it on screen.
Second problem is that you keep position in self.x_coord, self.x_coord but you should use self.rect.x self.rect.y because Sprite use self.image and self.rect to draw it on screen.
And it show third problem - in Cell you need method update which will change values in self.rect to move object.
Minimal working example which move 5 cells in random directions.
I organize code in different way and try to use PEP 8 - Style Guide for Python Code
import random
import pygame
# --- constants --- (UPPER_CASE_NAMES)
GREEN1 = (0, 255, 0) # Healthy cells
RED = (255, 0, 0) # Infected cells
GREEN2 = (0, 100, 0) # Healthy cells not susecptible
BLACK = (0, 0, 0) # Dead cells
WHITE = (255, 255, 255)
BACKGROUND_COLOR = (225, 198, 153)
SCREEN_SIZE = (800, 800)
# --- classes --- (CamelCaseNames)
# class keeep only one cell so it should has name `Cell` instead of `Cells`
class Cell(pygame.sprite.Sprite):
def __init__(self, color, speed, width, height):
super().__init__()
self.color = color
self.speed = speed
self.image = pygame.Surface([width, height])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE)
self.radius = width//2 # 25
center = [width//2, height//2]
pygame.draw.circle(self.image, self.color, center, self.radius, width=0)
self.rect = self.image.get_rect()
self.rect.x = random.randint(0, 400)
self.rect.y = random.randint(50, 700)
def update(self):
self.rect.x += random.randint(-10, 10)
self.rect.y += random.randint(-10, 10)
# --- functions --- (lower_case_names)
# empty
# --- main --- (lower_case_names)
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
pygame.display.set_caption("Covid-19 Simualtion")
speed = [0.5, -0.5]
# - objects -
all_cells = pygame.sprite.Group() # PEP8: lower_case_name
for _ in range(5):
cell = Cell(GREEN1, 5, 50, 50) # PEP8: lower_case_name
all_cells.add(cell)
# - loop -
clock = pygame.time.Clock()
end = False
while not end:
# - events -
for event in pygame.event.get():
if event.type == pygame.QUIT:
end = True
# - upadates (without draws) -
all_cells.update()
# - draws (without updates) -
screen.fill(BACKGROUND_COLOR)
pygame.draw.rect(screen, BLACK, (0, 50, 400, 700), 3)
all_cells.draw(screen)
pygame.display.flip()
clock.tick(30) # to use less CPU
# - end
pygame.quit() # some system may need it to close window

Moving rectange using pygame keeps flickering

import pygame
import random
black = (0,0,0)
white = (255,255,255)
green = (0,255,0)
red = (255,0,0)
screen = pygame.display.set_mode((1200, 600))
title = pygame.display.set_caption("Speeding cars")
clock = pygame.time.Clock()
clock.tick(60)
class Car:
def __init__(self, x, y, height, width):
self.x = x
self.y = y
self.height = height
self.width = width
def drive(self, change):
self.change = random.randint(1, 3)
self.x += self.change
pygame.draw.rect(screen, black, (self.x, self.y, self.height, self.width))
pygame.display.flip()
car1 = Car(30, 100, 50, 15)
car2 = Car(30, 200, 50, 15)
car3 = Car(30, 300, 50, 15)
car4 = Car(30, 400, 50, 15)
car5 = Car(30, 500, 50, 15)
driving = True
while driving:
screen.fill(white)
car1.drive(0)
car2.drive(0)
car3.drive(0)
car4.drive(0)
car5.drive(0)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
pygame.display.flip()
When i try to run this code everything works fine but the rectanges keeps flickering. I tried changing flip to update and adding or removing pygame.display.flip() at different points in code but it dosent seem to work.
Edit: I also just realised that when i comment out the rest of the cars and only run the first one, it runs fine. The problem starts with the second car.
Thanks for help.
Instead of updating the screen in the drive() method, do it at the end of your main loop.
Also, try not to use repetitive code for each new object that is created, but use a for loop instead.
Here you go:
import pygame
import random
black = (0,0,0)
white = (255,255,255)
green = (0,255,0)
red = (255,0,0)
screen = pygame.display.set_mode((1200, 600))
title = pygame.display.set_caption("Speeding cars")
clock = pygame.time.Clock()
clock.tick(60)
class Car:
def __init__(self, x, y, height, width):
self.x = x
self.y = y
self.height = height
self.width = width
def drive(self, change):
self.change = random.randint(1, 1)
self.x += self.change
pygame.draw.rect(screen, black, (self.x, self.y, self.height, self.width))
cars = []
for i in range(1,6):
cars.append(Car(30, 100*i, 50, 15))
driving = True
while driving:
screen.fill(white)
for car in cars:
car.drive(0)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
pygame.display.update()
There should be only one flip()/update() which is executed after you draw all shapes and blit all image. It sends buffer screen to video card which display it on monitor.
If you use after every drive then it send screen to monitor with only one car and later it send screen only with second car, etc.
import pygame
import random
black = (0,0,0)
white = (255,255,255)
green = (0,255,0)
red = (255,0,0)
screen = pygame.display.set_mode((1200, 600))
title = pygame.display.set_caption("Speeding cars")
clock = pygame.time.Clock()
clock.tick(60)
class Car:
def __init__(self, x, y, height, width):
self.x = x
self.y = y
self.height = height
self.width = width
def drive(self, change):
self.change = random.randint(1, 3)
self.x += self.change
pygame.draw.rect(screen, black, (self.x, self.y, self.height, self.width))
# pygame.display.flip() # don't use it
car1 = Car(30, 100, 50, 15)
car2 = Car(30, 200, 50, 15)
car3 = Car(30, 300, 50, 15)
car4 = Car(30, 400, 50, 15)
car5 = Car(30, 500, 50, 15)
driving = True
while driving:
screen.fill(white)
car1.drive(0)
car2.drive(0)
car3.drive(0)
car4.drive(0)
car5.drive(0)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
pygame.display.flip() # after `for event`, not inside `for event`

How do you divide the screen from the centre (vertically) to make a two player game?

So, I am programming a game which is two-players. I am trying to make it split screen from the center (vertically) where each player has their own screen and their own game is working. This would allow them to be at different stages in the game. For example, if one gets out, the other is not affected.
My game is where a snake has 5 balls initially of different colours and it is automatically moving in an upward direction. The user has to move it right or left to collect the correct colour ball and then collide with the correct colour block. I want this happening in the same screen but two times which will make it a two player game.
First, you have to encapsulte the game state for each sub-game. Then, instead of drawing the scene of the game(s) directly to the screen, draw them to seperate Surfaces, and then draw those to Surfaces to the screen.
Here's a simple, running example:
import pygame
import pygame.freetype
import random
pygame.init()
FONT = pygame.freetype.SysFont(None, 32)
class Player(pygame.sprite.Sprite):
def __init__(self, color, canvas, bindings):
super().__init__()
self.image = pygame.Surface((32, 32))
self.image.fill(color)
self.rect = self.image.get_rect(center=canvas.get_rect().center)
self.canvas = canvas
self.bindings = bindings
self.pos = pygame.Vector2(self.rect.topleft)
def update(self):
pressed = pygame.key.get_pressed()
direction = pygame.Vector2()
for key in self.bindings:
if pressed[key]:
direction += self.bindings[key]
if direction.length() > 0:
direction.normalize_ip()
self.pos += direction*4
self.rect.topleft = self.pos
self.rect.clamp_ip(self.canvas.get_rect())
self.pos = self.rect.topleft
class Game(pygame.sprite.Sprite):
def __init__(self, color, bg_color, bindings, left):
super().__init__()
self.bg_color = bg_color
self.image = pygame.Surface((400, 600))
self.image.fill(self.bg_color)
self.rect = self.image.get_rect(left=0 if left else 400)
self.player = Player(color, self.image, bindings)
self.target = pygame.Vector2(random.randint(100, 300), random.randint(100, 500))
self.stuff = pygame.sprite.Group(self.player)
self.score = 0
def update(self):
self.stuff.update()
self.image.fill(self.bg_color)
self.stuff.draw(self.image)
FONT.render_to(self.image, (190, 50), str(self.score), (200, 200, 200))
pygame.draw.circle(self.image, (210, 210, 210), [int(v) for v in self.target], 5)
if (self.player.rect.center - self.target).length() <= 20:
self.score += 1
self.target = pygame.Vector2(random.randint(100, 300), random.randint(100, 500))
def main():
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
player1_bindings = {
pygame.K_w: pygame.Vector2(0, -1),
pygame.K_a: pygame.Vector2(-1, 0),
pygame.K_s: pygame.Vector2(0, 1),
pygame.K_d: pygame.Vector2(1, 0)
}
player2_bindings = {
pygame.K_UP: pygame.Vector2(0, -1),
pygame.K_LEFT: pygame.Vector2(-1, 0),
pygame.K_DOWN: pygame.Vector2(0, 1),
pygame.K_RIGHT: pygame.Vector2(1, 0)
}
player1 = Game(pygame.Color('dodgerblue'), (30, 30, 30), player1_bindings, True)
player2 = Game(pygame.Color('orange'), (80, 20, 30), player2_bindings, False)
games = pygame.sprite.Group(player1, player2)
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
return
games.update()
screen.fill((30, 30, 30))
games.draw(screen)
pygame.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()

AttributeError: 'Player' object has no attribute 'collidepoint'

im currently learning to program in python (first language) and im trying to create a clicker game, however, while doing the click function i got this error:
line 73, in <module>
if player.collidepoint(event.pos):
AttributeError: 'Player' object has no attribute 'collidepoint'
it seems that my "player" object (the clickable object) doesnt have a rect ? but after looking it up for hours i could not find an answer
this is my game code
import pygame
from os import path
img_dir = path.join(path.dirname(__file__), "img")
width = 500
height = 600
fps = 30
# Cores
white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0 ,0)
green = (0, 255, 0)
blue = (0, 0, 255)
yellow = (255, 255, 0)
# Iniciar o game
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Trojan Clicker")
clock = pygame.time.Clock()
font_name = pygame.font.match_font("arial")
def draw_text(surf, text, size, x , y):
font = pygame.font.Font(font_name, size)
text_surface = font.render(text, True, white)
text_rect = text_surface.get_rect()
text_rect.midtop = (x, y)
surf.blit(text_surface, text_rect)
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((75, 75))
self.image.fill(red)
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
self.rect.centerx = width / 2
self.rect.bottom = height / 2
self.speedx = 0
def update(self):
self.speedx = 0
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
clicks = 0
# Loop
running = True
while running:
# Fps
clock.tick(fps)
# Eventos
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
# 1 is the left mouse button, 2 is middle, 3 is right.
if event.button == 1:
# `event.pos` is the mouse position.
if player.collidepoint(event.pos):
# Increment the number.
number += 1
# Updates
all_sprites.update()
# Draw / render X
screen.fill(black)
all_sprites.draw(screen)
draw_text(screen, str(clicks), 18, width / 2, 10)
# Depois de desenhar tudo, "flip" o display
pygame.display.flip()
pygame.quit()
Some comments are in portuguese btw, sorry about that
Thanks in advance for everyone who helps
Change the line
if player.collidepoint(event.pos):
to
if player.rect.collidepoint(event.pos):

Categories