This is an exercise from Crash Course - at this stage I am trying to create a row of raindrops, but I believe in my for loop, something is broken.. I am updating the image's rect position at each iteration and then adding that into sprite Group, why will this not draw() onto the screen?
import sys
import pygame
from raindrops import Raindrop
from pygame.sprite import Group
def let_it_rain():
'''initialize pygame, settings, and screen object'''
pygame.init()
screen_width = 1200
screen_height = 800
bg_color = (144, 177, 226)
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Let It Rain")
raindrop = Raindrop(screen)
raindrops = Group()
#number of drops in a row
spacex = screen_width - (2 * raindrop.rect.width)
raindrop_number_x = int(spacex / (2 * raindrop.rect.width))
#start window for raindrops
while True:
screen.fill(bg_color)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
for raindrop_number in range(raindrop_number_x):
raindrop = Raindrop(screen)
raindrop.rect.x = raindrop.rect.x + 2 * raindrop.rect.x * raindrop_number_x
raindrops.add(raindrop)
raindrops.draw(screen)
pygame.display.flip()
let_it_rain()
and here's my raindrops class in another module
import pygame
from pygame.sprite import Sprite
class Raindrop(Sprite):
def __init__(self, screen):
#load image
super(Raindrop, self).__init__()
self.screen = screen
self.pic = pygame.image.load('rain.png')
self.image = pygame.transform.smoothscale(self.pic,(50,60))
self.rect = self.image.get_rect()
#starting position
self.rect.x = self.rect.width
self.rect.y = self.rect.height
def blit(self):
raindrops.draw(screen)
# self.screen.blit(self.image, self.rect)
Feel like this has something to do with blit vs. draw OR my positions are not updating somehow
In your main loop, when setting the drop rectangles, use the loop variable instead of the drop count:
for raindrop_number in range(raindrop_number_x):
raindrop = Raindrop(screen)
raindrop.rect.x = raindrop.rect.x + 2 * raindrop.rect.x * raindrop_number # use loop variable
raindrops.add(raindrop)
Related
This question already has answers here:
Drag multiple sprites with different "update ()" methods from the same Sprite class in Pygame
(2 answers)
Closed 11 months ago.
I’m trying to make a 2D game where that background or screen can be dragged. When this happens I want all the sprites to move with it. How can I implement this?
You would need to detect the MOUSEMOTION event and then check if the left button is clikced using the event.buttons. From there, you can get the number of pixels the screen has been dragged (both horizontally and vertically) to find the new position that the screen should be set to, and hence, the new position for the sprites. Working example below:
import pygame
from pygame.locals import *
from random import randrange
# init global variables
left_btn = 0
cur_x = 0 # current X position
cur_y = 0 # current Y position
go = True
img_pos = pygame.Rect((0, 0), (0, 0))
# Sprite object
class Sprite(pygame.sprite.Sprite):
def __init__(self, color, width, height):
super().__init__()
self.width = width
self.height = height
self.image = pygame.Surface([width, height])
pygame.draw.rect(self.image, color, pygame.Rect(0, 0, width, height))
self.rect = self.image.get_rect()
def update(self, x, y):
self.rect = Rect(self.rect.x + x, self.rect.y + y, self.width, self.height)
# init display
pygame.display.init()
screen = pygame.display.set_mode((800, 600))
img = pygame.image.load('my_image.png')
# Create random Sprites
all_sprites_list = pygame.sprite.Group()
for _ in range(10):
c = (randrange(255), randrange(255), randrange(255))
s = Sprite(c, 20, 20)
s.rect.x = randrange(500)
s.rect.y = randrange(500)
all_sprites_list.add(s)
while go:
for e in pygame.event.get():
if e.type == pygame.QUIT: go = False
if e.type == MOUSEMOTION:
if e.buttons[left_btn]:
rel = e.rel
img_pos.x += rel[0]
img_pos.y += rel[1]
# calculate diff in X and Y positions
x = img_pos.x - cur_x
y = img_pos.y - cur_y
# update Sprites' position
all_sprites_list.update(x, y)
# update current X and Y positions
cur_x = img_pos.x
cur_y = img_pos.y
screen.fill(0)
screen.blit(img, img_pos) # update screen's position
all_sprites_list.draw(screen)
pygame.display.flip()
pygame.time.delay(30)
pygame.quit()
Cannot figure out why my code won't show sprites when I run it. The code is from a how-to book I found at my library, I have reread the code multiple times and haven't found anything different than what the book says to do. This is my first time coding on python and I haven't seen anything to help me solve my problem yet. This is all the code I've written for the game so far.
import pygame
from pygame import *
from random import randint
pygame.init()
WINDOW_WIDTH = 1100
WINDOW_HEIGHT = 600
WINDOW_RES = (WINDOW_WIDTH, WINDOW_HEIGHT)
WIDTH = 100
HEIGHT = 100
WHITE = (255, 255, 255)
SPAWN_RATE = 360
GAME_WINDOW = display.set_mode(WINDOW_RES)
display.set_caption('Attack of the vampire Pizzas!')
pizza_img = image.load('vampire.png')
pizza_surf = Surface.convert_alpha(pizza_img)
VAMPIRE_PIZZA = transform.scale(pizza_surf, (WIDTH, HEIGHT))
background_img = image.load('restaurant.jpg')
background_surf = Surface.convert_alpha(background_img)
BACKGROUND = transform.scale(background_surf, WINDOW_RES)
class VampireSprite(sprite.Sprite):
def __init__(self):
super().__init__()
self.speed = 2
self.lane = randint(0, 4)
all_vampires.add(self)
self.image = VAMPIRE_PIZZA.copy()
y = 50 + self.lane * 100
self.rect = self.image.get_rect(center = (1100, y))
def update(self, game_window):
game_window.blit(self.image, (self.rect.x, self.rect.y))
all_vampires = sprite.Group()
tile_color = WHITE
for row in range(6):
for column in range(11):
draw.rect(BACKGROUND, tile_color, (WIDTH * column, HEIGHT * row, WIDTH, HEIGHT), 1)
GAME_WINDOW.blit(BACKGROUND, (0,0))
----------------------------------------------
#Start Main Game Loop
game_running = True
#Game Loop
while game_running:
for event in pygame.event.get():
#Exit loop on quit
if event.type == QUIT:
game_running = False
if randint(1, SPAWN_RATE) == 1:
VampireSprite()
for vampire in all_vampires:
vampire.update(GAME_WINDOW)
display.update()
pygame.quit()
The Code seems to have all the correct components, except that it's a bit mixed up.
Generally when you make a sprite, it has the __init__() function - obviously for initialisation, and additionally an update() function. The update() function usually does not draw the object to the display/surface, but adjusts the position (i.e.: the sprite.rect) or changes the "look" (image) used for the sprite, for drawing later.
Sprites are usually grouped into an aptly-named Sprite Group. Once sprites are in a group, a single call to group.update() will call the update() function on every sprite contained. It's really convenient, and works well.
So tweaking your Vampire Pizza Sprite:
class VampireSprite(sprite.Sprite):
def __init__(self):
super().__init__()
self.speed = 2
self.lane = randint(0, 4)
self.image = VAMPIRE_PIZZA.copy()
y = 50 + self.lane * 100
self.rect = self.image.get_rect(center = (1100, y))
def update(self):
# TODO - do Vampire Pizzas Move?
# if so, update the self.rect
pass
And that's all that's needed. I removed the painting code, this will be handled by a sprite group.
So later on:
# Make a group of vampire sprites
all_vampires = sprite.Group()
all_vampires.add( VampireSprite() ) # Start with a single vampire
# Game Loop
game_running = True
while game_running:
# handle events
for event in pygame.event.get():
#Exit loop on quit
if event.type == QUIT:
game_running = False
# spawn some vampires, maybe
if randint(1, SPAWN_RATE) == 1:
all_vampires.add( VampireSprite() ) # add to the group
# Update/move all vampire sprites
all_vampires.update() # updates every sprite in group
# repaint the screen
GAME_WINDOW.blit(BACKGROUND, (0,0))
# draw some columns(?)
tile_color = WHITE
for row in range(6):
for column in range(11):
draw.rect(GAME_WINDOW, tile_color, (WIDTH * column, HEIGHT * row, WIDTH, HEIGHT), 1)
all_vampires.draw() # paints every sprite in group
display.update()
pygame.quit()
There's two calls from the all_vampires sprite group - all_vampires.update() and all_vampires.draw()? With just these two calls, all sprites in the group are moved (adjusted/whatever), and painted to the screen.
Im trying to make a rain effect with pygame but it seems as if the background is not cleaning up before updating the sprites.
this is what it looks like when i execute the code..
I wonder if there's a way to fix this problem.
rain.py (main file)
#!/usr/bin/python
VERSION = "0.1"
import os, sys, raindrop
from os import path
try:
import pygame
from pygame.locals import *
except ImportError, err:
print 'Could not load module %s' % (err)
sys.exit(2)
# main variables
WIDTH, HEIGHT, FPS = 300, 300, 30
# initialize game
pygame.init()
screen = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption("Rain and Rain")
# background
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((40,44,52))
# blitting
screen.blit(background,(0,0))
pygame.display.flip()
# clock for FPS settings
clock = pygame.time.Clock()
def main():
raindrops = pygame.sprite.Group()
# a function to create new drops
def newDrop():
nd = raindrop.Raindrop()
raindrops.add(nd)
# creating 10 rain drops
for x in range(0,9): newDrop()
# variable for main loop
running = True
# event loop
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
raindrops.update()
screen.blit(background,(100,100))
raindrops.draw(screen)
pygame.display.flip()
pygame.quit()
if __name__ == '__main__': main()
raindrop.py ( class for raindrops )
import pygame
from pygame.locals import *
from os import path
from random import randint
from rain import HEIGHT
img_dir = path.join(path.dirname(__file__), 'img')
class Raindrop(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.width = randint(32, 64)
self.height = self.width + 33
self.image = pygame.image.load(path.join(img_dir, "raindrop.png")).convert_alpha()
self.image = pygame.transform.scale(self.image, (self.width, self.height))
self.speedy = 5 #randint(1, 8)
self.rect = self.image.get_rect()
self.rect.x = randint(0, 290)
self.rect.y = -self.height
def update(self):
self.rect.y += self.speedy
if self.rect.y == HEIGHT:
self.rect.y = -self.height
self.rect.x = randint(0, 290)
This is the line you use to clear the screen:
screen.blit(background, (100, 100))
In other words; you're clearing the screen starting at x=100, y=100. Since pygame coordinates starts from topleft and extends to the right and downwards, you're not clearing the screen left of x=100 and above y=100.
Simple fix is blitting at 0, 0 as you did at the start of the program.
screen.blit(background, (0, 0))
EDIT: I have fixed it, there should be a self infront of the return for invader_position_x
I am making a space invaders game. However I have encountered strange behavior.
When I press the right key on my keyboard, the sprite does not move but after holding it down for a few seconds, it jumps to the right boundary.
The space invader game is using classes and this is the main class:
# IMPORTS
import pygame, sys
from pygame.locals import *
import invader
pygame.init()
###################
#IMAGE SIZE AND FPS
width = 800
height = 600
fps = 30
fpsClock = pygame.time.Clock()
DISPLAY = pygame.display.set_mode((width,height))
#################
#OTHER VARIABLES
pygame.display.set_caption('space invaders!')
white = (225,225,225)# The colour white
invader_sprite = pygame.image.load("cross.png")
invader_lenght = 40
invader_position_x = 400
invader_position_y = 560
right_boundary = width- invader_lenght
keypress = ""
my_invader = invader.Invader(invader_position_x,right_boundary,keypress)# Initialising invader
while True: # main game loop
DISPLAY.fill(white)
DISPLAY.blit(invader_sprite,(invader_position_x,invader_position_y))
keypress = pygame.key.get_pressed()
invader_position_x=my_invader.invader_move(keypress,right_boundary,invader_position_x)
print(invader_position_x)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
fpsClock.tick(fps)
This is my invader class in a seperate file:
import pygame, sys
from pygame.locals import *
pygame.init()
class Invader():
def __init__(self,invader_position_x,right_boundary,keypress):
self.invader_position_x= invader_position_x
self.right_boundary=right_boundary
self.keypress= keypress
def invader_move(self,keypress,right_boundary,invader_position_x):
if self.invader_position_x> right_boundary:#Right boundary
invader_position_x=right_boundary-5
if self.invader_position_x<0:
invader_position_x= 5
if keypress[K_RIGHT]:# right
self.invader_position_x = self.invader_position_x+ 5
elif keypress[K_LEFT]:# left
self.invader_position_x =self.invader_position_x- 5
return invader_position_x
So what is the solution to fixing this weird behaviour?
I think problem is that you have two variables to keep position . But I didn't check this - I changed code to make it better organized and problem disappeared.
#!/usr/bin/env python3
import pygame
# --- constants ---
WIDTH = 800
HEIGHT = 600
FPS = 30
WHITE = (225, 225, 225)
# --- classes ---
class Invader():
def __init__(self, x, y):
self.image = pygame.image.load("cross.png")
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def draw(self, screen):
screen.blit(self.image, self.rect)
def update(self, screen_rect, keypress):#EDIT Replaced screen with screem_rect
if keypress[pygame.K_RIGHT]: # right
self.rect.x += 15
elif keypress[pygame.K_LEFT]: # left
self.rect.x -= 15
if self.rect.right > screen_rect.right: # right boundary
self.rect.right = screen_rect.right
if self.rect.left < screen_rect.left:
self.rect.left = screen_rect.left
# --- main ---
# - init -
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
screen_rect = screen.get_rect()
pygame.display.set_caption('space invaders!')
# - objects -
my_invader = Invader(400, 560)
# - mainloop -
fps_clock = pygame.time.Clock()
while True: # main game loop
# - events -
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
keypress = pygame.key.get_pressed()
# - updates -
my_invader.update(screen_rect, keypress)
# - draws -
screen.fill(WHITE)
my_invader.draw(screen)
pygame.display.update()
# - FPS -
fps_clock.tick(FPS)
import time
import sys
import pygame
from pygame.locals import *
from pygame.sprite import Sprite, Group
pygame.init()
running = False
screen_width = 1280
screen_height = 720
size = (screen_width, screen_height)
screen = pygame.display.set_mode((size), pygame.FULLSCREEN)
pygame.display.set_caption('Laser Bits')
class world(Sprite):
def __init__(self):
Sprite.__init__(self)
self.image = pygame.image.load('levels/LBMAPPREALPHA1.png')
self.rect = self.image.get_rect()
self.rect.x = 0
self.rect.y = 0
Group.add(self)
group_sprites = pygame.sprite.Group()
while running == False:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = True
group_sprites.draw(screen)
pygame.display.update()
I'm a newbie at Python and I'm "trying" to make a game and I need some help, I do not know why my sprite won't show. Thank you for your responses in advanced!
You never create an instance of your world class.
Remove this line:
Group.add(self)
and then create an instance of world and add it to the group_sprites group:
the_world = world()
group_sprites = pygame.sprite.Group(the_world)