PyGame Loop with sprite creation in each loop not updating - python

I'm creating a physics simulation which simulates the Rutherford scattering experiment. I'm trying to create an alpha particles (sprite) each time the loop is run and I'm able to create it (shows up on screen) but it doesn't move forwards whereas if I create only one particle, it works fine.
I've attached the sprite's class and the loop as to where it's being created.
Loop:
while running:
clock.tick(20)
allparticles = pygame.sprite.Group(Particle(speed, bwidthmin, bwidthmax, background))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
allparticles.clear(screen, background)
nucgroup.draw(screen)
allparticles.update()
allparticles.draw(screen)
pygame.display.flip()
Sprite Class:
class Particle(pygame.sprite.Sprite):
def __init__(self, speed, bwidthmin, bwidthmax, background):
pygame.sprite.Sprite.__init__(self)
self.background = background
self.image = pygame.Surface((16,16))
self.rect = self.image.get_rect()
currenty = random.randint(bwidthmin,bwidthmax)
self.rect.centery = currenty
self.rect.centerx = 0
pygame.draw.circle(self.image, yellow, (8,8), 5)
self.dx=speed
self.dy = 0
def update(self):
c1 = (self.rect.centerx,self.rect.centery)
self.rect.centerx += self.dx
if self.rect.right >= 570:
pygame.sprite.Sprite.kill(self)
pygame.draw.line(self.background, white, c1, (self.rect.centerx,self.rect.centery), 1)
Where am I going wrong?
I also have the issue of my tkinter window in which this pygame is embedded hanging (buttons not pressing, tabs not changing, can't do anything until pygame stops). Is the loops running forever causing this to happen? I'd like to be able to update the variables to affect the simulation during runtime or is that not possible?
Thanks for the help.

One issue is that you are overwriting allparticles each time through the loop. Perhaps you mean to keep creating particles and appending to a list?
Try this:
allparticles = []
while running:
clock.tick(20)
allparticles.append(pygame.sprite.Group(Particle(speed, bwidthmin, bwidthmax, background)))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
for particle in allparticles: # loop over all particles each time
particle.clear(screen, background)
nucgroup.draw(screen)
particle.update()
particle.draw(screen)
pygame.display.flip()

Related

I'm wondering why my player and my balloon which are both sprites is stuttering when I move the player

My frames drop and the game basically stops whenever I use the WASD keys, which are my movement controls. Its supposed to make the water balloon move but when I stop moving so does the water balloon. They're both sprites.
I might have got them mixed up since there are two sprite families.
main.py:
import pygame
from player import Player
from projectile import WaterBaloon
# Start the game
pygame.init()
game_width = 1000
game_height = 650
screen = pygame.display.set_mode((game_width, game_height))
clock = pygame.time.Clock()
running = True
bg_image = pygame.image.load("../assets/BG_Sand.png")
#make all the sprite groups
playerGroup = pygame.sprite.Group()
projectilesGroup = pygame.sprite.Group()
#put every sprite class in a group
Player.containers = playerGroup
WaterBaloon.containers = projectilesGroup
#tell the sprites where they are gonna be drawn at
mr_player = Player(screen, game_width/2, game_height/2)
# ***************** Loop Land Below *****************
# Everything under 'while running' will be repeated over and over again
while running:
# Makes the game stop if the player clicks the X or presses esc
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
running = False
#player input that detects keys (to move)
keys = pygame.key.get_pressed()
if keys[pygame.K_d]:
mr_player.move(1, 0)
if keys[pygame.K_a]:
mr_player.move(-1, 0)
if keys[pygame.K_w]:
mr_player.move(0, -1)
if keys[pygame.K_s]:
mr_player.move(0, 1)
if pygame.mouse.get_pressed()[0]:
mr_player.shoot()
#blit the bg image
screen.blit((bg_image),(0, 0))
#update the player
mr_player.update()
#update all the projectiles
for projectile in projectilesGroup:
projectile.update()
# Tell pygame to update the screen
pygame.display.flip()
clock.tick(60)
pygame.display.set_caption("ATTACK OF THE ROBOTS fps: " + str(clock.get_fps()))
player.py:
import pygame
import toolbox
import projectile
#Players Brain
class Player(pygame.sprite.Sprite):
#player constructor function
def __init__(self, screen, x, y):
pygame.sprite.Sprite.__init__(self, self.containers)
self.screen = screen
self.x = x
self.y = y
self.image = pygame.image.load("../assets/player_03.png")
#rectangle for player sprite
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
#end of rectangle for player sprite
self.speed = 8
self.angle = 0
#player update function
def update(self):
self.rect.center = (self.x, self.y)
mouse_x, mouse_y = pygame.mouse.get_pos()
self.angle = toolbox.angleBetweenPoints(self.x, self.y, mouse_x, mouse_y)
#get the rotated version of player
image_to_draw, image_rect = toolbox.getRotatedImage(self.image, self.rect, self.angle)
self.screen.blit(image_to_draw, image_rect)
#tells the computer how to make the player move
def move(self, x_movement, y_movement):
self.x += self.speed * x_movement
self.y += self.speed * y_movement
#shoot function to make a WaterBaloon
def shoot(self):
projectile.WaterBaloon(self.screen, self.x, self.y, self.angle)
You're only processing one event per frame. That's because you've put everything you do inside the frame in the event handling loop. That is likely to break down when you start getting events (such as key press and release events) at a higher rate. If events are not all being processed, your program may not update the screen properly.
You probably want your main loop to only do event processing stuff in the for event in pygame.event.get(): loop. Everything else should be unindented by one level, so that it runs within the outer, while running: loop, after all pending events have been handled.
Try something like this:
while running:
# Makes the game stop if the player clicks the X or presses esc
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
running = False
#player input that detects keys (to move) # unindent here (and all the lines below)
keys = pygame.key.get_pressed()
...

Pygame : player sprite that disappears behind the background

So there you go, I wanted to experiment a little pygame but I find myself stuck.
Context
I created a small sprite (with Piskelapp) that represents the player and looks like:
player.png
then add my background in jpg format. However when launching the game, my sprite is cut by the background as follows:
The ship is not placed in front of the background and more I go up it, more it disappears behind the background...
Here is my code:
import pygame
pygame.init()
# class user
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.pv = 100
self.__max_health = 100
self.attack = 2
self.velocity = 5
self.image = pygame.image.load('assets/player.png')
self.rect = self.image.get_rect()
self.rect.x = 400
self.rect.y = 500
# open game window
pygame.display.set_caption("Rocket 'n' Rock")
screen = pygame.display.set_mode((1080, 720))
# background import
bg = pygame.image.load('assets/bg.jpg')
# load player
player = Player()
running = True
# game mainloop
while running:
# bg apply
screen.blit(bg, (0,-400))
# screen update
pygame.display.flip()
# player image apply
screen.blit(player.image, player.rect)
# if player close the window
for event in pygame.event.get():
# *close event
if event.type == pygame.QUIT:
running = False
pygame.quit()
print("close game")
anyone have a tip? I'm wondering if it's not a file format problem?
thank you for your time
In you game mainloop, you should put:
screen.blit(player.image, player.rect)
before:
pygame.display.flip()

Pygame - Creating a "Enemy" class, and then importing it into game

Okay, so basically what I'm trying to do is keep the main file a little cleaner and I'm starting with the "Zombie" enemy by making it's own file which will most likely contain all enemies, and importing it in.
So I'm confused on how I'd set-up the Class for a sprite, you don't have to tell me how to get it to move or anything like that I just want it to simply appear. The game doensn't break when I run it as is, I just wanted to ask this question before I goto sleep so I can hopefully get a lot done with the project done tomorrow (School related)
Code is unfinished like I said just wanted to ask while I get some actual sleep just a few google searches and attempts.
Eventually I'll take from the advice given here to make a "Hero" class as well, and as well as working with importing other factors if we have the time.
Zombie code:
import pygame
from pygame.locals import *
class ZombieEnemy(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('images/zombie.png')
# self.images.append(img)
# self.image = self.images[0]
self.rect = self.image.get_rect()
zombieX = 100
zombieY = 340
zombieX_change = 0
Main Code:
import pygame
from pygame.locals import *
import Zombie
# Intialize the pygame
pygame.init()
# Create the screen
screen = pygame.display.set_mode((900, 567))
#Title and Icon
pygame.display.set_caption("Fighting Game")
# Add's logo to the window
# icon = pygame.image.load('')
# pygame.display.set_icon(icon)
# Player
playerImg = pygame.image.load('images/character.png')
playerX = 100
playerY = 340
playerX_change = 0
def player(x,y):
screen.blit(playerImg,(x,y))
Zombie.ZombieEnemy()
def zombie(x,y):
screen.blit()
# Background
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
self.image = pygame.image.load('images/background.png')
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
BackGround = Background('background.png', [0,0])
# Game Loop
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# If keystroke is pressed check right, left.
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
#playerX_change = -2.0
BackGround.rect.left = BackGround.rect.left + 2.5
if event.key == pygame.K_RIGHT:
#playerX_change = 2.0
BackGround.rect.left = BackGround.rect.left - 2.5
# if event.type == pygame.KEYUP:
# if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
# BackGround.rect.left = 0
screen.blit(BackGround.image, BackGround.rect)
playerX += playerX_change
player(playerX,playerY)
pygame.display.flip()
Your sprite code is basically mostly there already. But as you say, it needs an update() function to move the sprites somehow.
The idea with Sprites in PyGame is to add them to a SpriteGroup, then use the group functionality for handling the sprites together.
You might want to modify the Zombie class to take an initial co-ordinate location:
class ZombieEnemy(pygame.sprite.Sprite):
def __init__( self, x=0, y=0 ):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('images/zombie.png')
self.rect = self.image.get_rect()
self.rect.center = ( x, y ) # NOTE: centred on the co-ords
Which allows the game to create a Zombie at a particular starting point (perhaps even a random one).
So to have a sprite group, first you need to create the container:
all_zombies = pygame.sprite.Group()
Then when you create a new zombie, add it to the group. Say you wanted to start with 3 randomly-positioned zombies:
for i in range( 3 ):
new_x = random.randrange( 0, WINDOW_WIDTH ) # random x-position
new_y = random.randrange( 0, WINDOW_HEIGHT ) # random y-position
all_zombies.add( Zombie( new_x, new_y ) ) # create, and add to group
Then in the main loop, call .update() and .draw() on the sprite group. This will move and paint all sprites added to the group. In this way, you may have separate groups of enemies, bullets, background-items, etc. The sprite groups allow easy drawing and collision detection between other groups. Think of colliding a hundred bullets against a thousand enemies!
while running:
for event in pygame.event.get():
# ... handle events
# Move anything that needs to
all_zombies.update() # call the update() of all zombie sprites
playerX += playerX_change
# Draw everything
screen.blit(BackGround.image, BackGround.rect)
player(playerX,playerY)
all_zombies.draw( screen ) # paint every sprite in the group
pygame.display.flip()
EDIT: Added screen parameter to all_zombies.draw()
It's probably worthwhile defining your player as a sprite too, and having a single-entry group for it as well.
First you could keep position in Rect() in class. And you would add method which draws/blits it.
import pygame
class ZombieEnemy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load('images/zombie.png')
self.rect = self.image.get_rect()
self.rect.x = 100
self.rect.y = 340
self.x_change = 0
def draw(self, screen):
screen.blit(self.image, self.rect)
Next you have to assign to variable when you create it
zombie = Zombie.ZombieEnemy()
And then you can use it to draw it
zombie.draw(screen)
in
screen.blit(BackGround.image, BackGround.rect)
player(playerX,playerY)
zombie.draw(screen)
pygame.display.flip()
The same way you can create class Player and add method draw() to class Background and then use .
background.draw(screen)
player.draw(screen)
zombie.draw(screen)
pygame.display.flip()
Later you can use pygame.sprite.Group() to keep all objects in one group and draw all of them using one command - group.draw()

How can i do a for loop with seconds/milliseconds using pygame?

I'm trying to make a snake game, and am having a little trouble getting the snake to move correctly. When i run this code he does go across the screen. but i cant get the display to update every {}'s .
I was wondering if there was a way to set up a for loop like this:
for seconds in { idk}:
pygame.display.update()
self.x += self.speed
import pygame
import time
pygame.init()
## creates window
win = pygame.display.set_mode((1000, 1000))
## gives titale to window
pygame.display.set_caption("Snake attempt")
class Character():
def __init__(self, height= 40, width = 40,x = 50,y= 50,speed = 20):
self.height = height
self.width = width
self.x = x
self.y = y
self.speed = 20
def create(self):
## rect(draw on, ( colors) (pos.x , pos.y . width, height))
pygame.draw.rect(win, (255,0, 0) , (self.x, self.y,
self.width, self.height))
pygame.display.update()
def movement(self):
self.x += self.speed
pygame.display.update()
game_on = True
game_off = False
player = Character()
while game_on:
pygame.time.delay(100)
## gives all events that happen while Game_on = True
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
## call on sys.exit()Caracter class
player.create()
player.movement()
Basically i just want the player to move across the screen by so many spaces every how ever many seconds. while having the display refresh every time
You already have a perfectly nice event loop with 100ms delay.
Don't create any additional delay loops.
This runs ten times per second:
player.movement()
Whatever speed or behavior you want to produce,
should happen within that method.
You will see the behavior evolving over time due to
the method being repeatedly called, ten times a second.
And as furas helpfully points out, there's no need for .update()
within the create method, better to defer it until movement is complete.
Note that what's visible on-screen for 100ms is the previous position,
before advancing x.

Pygame button won't work on press/only on hover

I am trying to have an onclick button print a check and run my pencil function. At the moment if I hover over the Box sprite.. it will run the print and pencil function. It should be ONCLICK it runs those 2. Can anyone help me out? Thanks! (this should be all relevant code, if you need more please let me know :)
class Box(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((35, 30))
self.image = self.image.convert()
self.image.fill((255, 0, 0))
self.rect = self.image.get_rect()
self.rect.centerx = 25
self.rect.centery = 505
self.dx = 10
self.dy = 10
while keepGoing:
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
box = Box()
allSprites = pygame.sprite.Group(box)
allSprites.draw(screen)
if event.type == MOUSEMOTION:
x,y = event.pos
if box.rect.collidepoint(x,y) and pygame.MOUSEBUTTONUP:
print("collide works")
pencil(background,clock,keepGoing,screen)
pygame.display.flip()
Your code is not checking for mouse clicks, but rather mouse movement.
If you want to be testing for a click on your box, change condition to check for MOUSEBUTTONDOWN or MOUSEBUTTONUP events (depending on what part of the click you want to react to), rather than MOUSEMOTION events.
There are some other issues with your code though. For instance, you're creating your Box and Group after every event. Probably you want to just create them once, before going into the game loop (this will both make more sense and perform better).

Categories

Resources