Good day,
I have like 15 images I need to be buttons. I have buttons working with a Box() (Box - looks like this)
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
I am trying to make the buttons work with image sprites. So I attempted to copy the class style of the box and do the same for my Icons.. code looks like this...
class Icons(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("images/airbrushIC.gif").convert()
self.rect = self.image.get_rect()
self.rect.x = 25
self.rect.y = 550
the code in the main()
rect = image.get_rect()
rect.x = 25
rect.y = 550
ic1 = Icons((screen.get_rect().x, screen.get_rect().y))
screen.blit(ic1.image, ic1.rect)
pygame.display.update()
This code produces a positional (accepts 1 argument but 2 are there) error or an image is not referenced error (inside the Icon class).
I'm unsure if this is the right way to go about this anyways.. I know for sure that I need to load all the images (as sprites)... store them in an array... and then have my mouse check if it is clicking one of the items in the array using a for loop.
Thanks.
You are trying to pass an argument into Icons(), but your __init__() method takes no arguments. If you wanted to pass those onto the Sprite() constructor, then you probably wanted something like:
class Icons(pygame.sprite.Sprite):
def __init__(self, *args):
pygame.sprite.Sprite.__init__(self, *args)
...
This accepts any number of extra arguments (*args) using the star operator, then passes them into the sprite constructor.
Related
I am fairly new to OOP and pygame, so these may be some stupid and basic questions - but I've been stuck on this for days so anything would help.
I am creating a variable called position3 within Gun.shoot(), I want this variable to move to Bullet.reposition() as Bullet.reposition is called upon. I then want the position3 variable to move to the Bullet.update() function, which gets called upon by a different process elsewhere in the code. During this whole process, the position3 variable should not change but should stay the same. I have managed to get the position3 variable to move to Bullet.reposition() from Gun.shoot(), however I can now not get it into Bullet.update(). Help!
class Bullet(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((5,5))
self.image.fill(red)
self.rect = self.image.get_rect()
# self.rect.center = (200,200)
self.positionofm = (23,0)
self.pos = vec(300,300)
self.centerlocation = vec(0,0)
self.position3 = vec(0,0)
def update(self):
self.position3 = reposition.position3
print("update",self.position3)
# self.rect.center = self.position3
self.centerlocation = random.randint(200,400),random.randint(200,400)
self.rect.center =(self.centerlocation)
def reposition(self,position3):
print("repositioning")
self.centerlocation = random.randint(200,400),random.randint(200,400)
self.rect.move(position3)
print("regular",position3)
self.position3 = position3
print("First update",self.position3)
class Gun(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((30,5), pg.SRCALPHA)
self.image.fill(black)
self.rect = self.image.get_rect()
self.original_image = self.image
self.rect.center = (WIDTH/2 , HEIGHT/2)
self.pos = vec(WIDTH / 2, HEIGHT / 2)
self.offset = vec(20, 0)
self.angle=0
self.position2=vec(0,0)
# self.bullet = Bullet()
def shoot(self):
self.BulletEndPos=vec(0,0)
self.BulletEndPos=vec(pg.mouse.get_pos())
# print(self.BulletEndPos,"akshgdjasgdas")
position2x=self.position2[0]
position2y=self.position2[1]
position3=vec(0,0)
position3=(math.floor(position2x)),(math.floor(position2y))
Bullet.reposition(self, position3)
Well your code snippet already has everything you need there you just need to remove the line
self.position3 = reposition.position3
Given that reposition is not an object and will not hold a attribute
The value for position3 is already updated for the object on the reposition method and written in the Bullet object attribute. Another way you could do it would be to rewrite update() somewhat like this:
def update(self, position3= None):
position_3 = position3 if position3 is not None else self.position3
print("update",position_3)
# self.rect.center = position_3
self.centerlocation = random.randint(200,400),random.randint(200,400)
self.rect.center =(self.centerlocation)
This gives you more freedom to pass position3 somewhere else in the code if needed while retaining the logic.
Now just to clarify a few things:
When you write a class you are just declaring the overall structure of the class and not creating any instance of it.
The self keyword refers to the referred instance of the class object, so you need to create an instance of the object that can keep those variables.
Keeping that in mind on your last line of method shoot you are doing nothing, has there is no bullet created to be repositioned and updated. So you kinda need to change your Gun class to this:
class Gun(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((30,5), pg.SRCALPHA)
self.image.fill(black)
self.rect = self.image.get_rect()
self.original_image = self.image
self.rect.center = (WIDTH/2 , HEIGHT/2)
self.pos = vec(WIDTH / 2, HEIGHT / 2)
self.offset = vec(20, 0)
self.angle=0
self.position2=vec(0,0)
self.bullet = Bullet()
def shoot(self):
self.BulletEndPos=vec(0,0)
self.BulletEndPos=vec(pg.mouse.get_pos())
# print(self.BulletEndPos,"akshgdjasgdas")
position2x=self.position2[0]
position2y=self.position2[1]
position3=vec(0,0)
position3=(math.floor(position2x)),(math.floor(position2y))
self.bullet.reposition(self, position3)
OOP might be confusing at times especially at the beginning so you can try some other resources online (e.g. https://www.tutorialspoint.com/python3/python_classes_objects.htm)
I have tried everything I can think of to fix this, but I can't seem to find it. I know it is probably a simple fix, but I cannot find what is making this happen. This is the first part of my code :
import pygame, sys, time
from pygame.locals import *
pygame.init()
WINDOWWIDTH = 900
WINDOWHEIGHT = 400
MOVERATE = 5
screen = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
TEXTCOLOR = (255, 255, 255)
BACKGROUNDCOLOR = (0, 0, 0)
FPS = 40
clock = pygame.time.Clock()
x = 200
y = 150
class player(pygame.sprite.Sprite):
def __init__(self, x, y):
super(player, self).__init__()
temp_image = pygame.image.load("stand_down.png").convert_alpha()
self.image_down = pygame.transform.scale(temp_image, (100, 200))
temp_image = pygame.image.load("standleft.png").convert_alpha()
self.image_left = pygame.transform.scale(temp_image, (100, 200))
temp_image = pygame.image.load("standright.png").convert_alpha()
self.image_right = pygame.transform.scale(temp_image, (100, 200))
self.image = self.image_down
# keep position and size in pygame.Rect()
# to use it in collision checking
self.rect = self.image.get_rect(x=x, y=y)
def draw(self, x, y):
screen.blit(self.image, self.rect)
def handle_event(self):#, event)
self.image = self.image_down.get_rect()
self.image = pygame.Surface((x, y))
key = pygame.key.get_pressed()
if key[K_LEFT]:
self.rect.x -= 50
self.image = self.image_left
if key[K_RIGHT]:
self.rect.x += 50
self.image = self.image_right
class room1():
#bedroom
def __init__(self):
self.x, self.y = 16, WINDOWHEIGHT/2
self.speed = 3
def draw(self):
background = pygame.image.load("bedroom.jpg").convert()
background = pygame.transform.scale(background, (WINDOWWIDTH, WINDOWHEIGHT))
screen.blit(background, (0, 0))
And this is my main function :
def main():
while True:
for event in pygame.event.get():
player.handle_event.get(event)
player.handle_event(screen)
room1.draw(screen)
player.draw(screen, x, y)
pygame.display.update()
pygame.display.flip()
clock.tick(FPS)
main()
I keep getting the same error :
File "C:\Python32\Project3\proj3pt2.py", line 220, in handle_event
self.image = self.image_down.get_rect()
AttributeError: 'pygame.Surface' object has no attribute 'image_down'
I know it's probably an easy fix, but I don't know where to look for it, and how I messed up. If someone could explain that, it would be much appreciated!
When you have an instance and call one of its methods, the instance gets automatically passed as the first argument, self. So if you have a class MyClass and an instance my_instance and you call its handle_event method, it's the same as calling MyClass.handle_event(my_instance).
In your program you never create an instance of the player class and so you're passing the screen as the self argument directly to the class (the screen is actually a pygame.Surface). That means the self in the handle_event method actually refers to the screen surface and since surfaces don't have an image_down attribute, Python raises an error when the self.image_down.get_rect() part is reached.
To fix this problem, you have to create an instance (also called object) of the player class and must not pass an argument to handle_event (unless you add more parameters to the method):
player_instance = player(x_position, y_position)
Then use the instance inside of the while and event loops:
while True:
player_instance.handle_event()
You also have to create an instance of the room1 class instead of using the class directly.
Here's a complete example with some comments about other problems:
import pygame
pygame.init()
WINDOWWIDTH = 900
WINDOWHEIGHT = 400
screen = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
FPS = 40
clock = pygame.time.Clock()
# Load images once globally and reuse them in the program.
background = pygame.image.load("bedroom.jpg").convert()
background = pygame.transform.scale(background, (WINDOWWIDTH, WINDOWHEIGHT))
temp_image = pygame.image.load("stand_down.png").convert_alpha()
image_down = pygame.transform.scale(temp_image, (100, 200))
temp_image = pygame.image.load("standleft.png").convert_alpha()
image_left = pygame.transform.scale(temp_image, (100, 200))
temp_image = pygame.image.load("standright.png").convert_alpha()
image_right = pygame.transform.scale(temp_image, (100, 200))
class player(pygame.sprite.Sprite):
def __init__(self, x, y):
super(player, self).__init__()
self.image_down = image_down
self.image_left = image_left
self.image_right = image_right
self.image = self.image_down
# keep position and size in pygame.Rect()
# to use it in collision checking
self.rect = self.image.get_rect(x=x, y=y)
# You don't have to pass x and y, since you already
# use the `self.rect` as the blit position.
def draw(self, screen):
screen.blit(self.image, self.rect)
def handle_event(self):
# These two lines don't make sense.
#self.image = self.image_down.get_rect()
#self.image = pygame.Surface((x, y))
# I guess you want to switch back to image_down.
self.image = self.image_down
key = pygame.key.get_pressed()
if key[pygame.K_LEFT]:
self.rect.x -= 5
self.image = self.image_left
if key[pygame.K_RIGHT]:
self.rect.x += 5
self.image = self.image_right
class room1():
def __init__(self):
self.x, self.y = 16, WINDOWHEIGHT/2
# Reference to the background image.
self.background = background
def draw(self, screen): # Pass the screen.
screen.blit(self.background, (0, 0))
def main():
# Create player and room instances.
player_instance = player(200, 150)
room1_instance = room1()
while True:
for event in pygame.event.get():
# Users can press the "X" button to quit.
if event.type == pygame.QUIT:
return
player_instance.handle_event()
room1_instance.draw(screen)
player_instance.draw(screen)
# You don't need both update and flip.
# pygame.display.update()
pygame.display.flip()
clock.tick(FPS)
main()
pygame.quit()
Side note: PEP 8 recommends uppercase names for classes, so Player instead of player. Then you could call the instance player.
I suspect you do somewhere something like this
player = player.transform.scale(player.image)
player is Sprite but scale returns Surface - so you replace Sprite with Surface and later you have problems.
(BTW: I saw the same problem in some question few days ago)
If you have to rescale image then do it in __init__ as you already do with some images.
In real game you should create images with correct sizes using any Image Editor so you don't have to use scale()
BTW: in handle_event you do
self.image = self.image_down.get_rect()
self.image = pygame.Surface((x, y))
You assign Rect to Surface (self.image) and later you assing new empty Surface with size x, y. Surface doesn't keep positon, it uses only width, height.
You have self.rect to keep positiona and you already change it with
self.rect.x -= 50
and
self.rect.x += 50
BTW: use UpperCaseNames for classes to make code more readable
class Player(...)
class Room1(...)
Event Stackoverflow knows this rule and it uses light blue color for classes to make code more readable.
More: PEP 8 -- Style Guide for Python Code
BTW: in room1.draw() you read and rescale image again and again - it can slow down program. Do it in room.__init__
I have an image of a vertical line, and want to create two of them. I want them to randomly spread apart and close the gap (while maintaining a minimum and maximum distance - I'm waiting to fix this first), while moving along the x-axis. The y coords don't move.
There are many examples of creating more complex sprites and games using pygame, but I can't find one simple enough to pinpoint what I'm supposed to do, so any help is appreciated. This code is based off of a simple example found here.
This is the very basic code that I have so far.
class Squeeze(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.img_load()
def update(self):
self.rect.x += random.randint(-1, 1)
if self.rect.x >= 1920:
self.rect.x += -1
if self.rect.x <= 0:
self.rect.x += 1
def img_load(self):
self.image = pygame.Surface([SCREEN_WIDTH, SCREEN_HEIGHT])
self.image.fill(BLACK)
self.rect = self.image.get_rect()
pygame.init()
SCREEN_WIDTH = 1920
SCREEN_HEIGHT = 1080
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
FPS = 60
wall = pygame.image.load('wall.png')
wall_list = pygame.sprite.Group()
BLACK = (0, 0, 0)
for i in range(2):
wall = Squeeze()
wall.rect.x = random.randrange(SCREEN_WIDTH)
wall.rect.y = random.randrange(SCREEN_HEIGHT)
wall_list.add(wall)
done = False
clock = pygame.time.Clock()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(BLACK)
wall_list.update()
wall_list.draw(screen)
clock.tick(60)
pygame.display.flip()
pygame.quit()
I'm confused about the pygame.sprite.Group() deal. How do I get the two images to load and operate on them separately? Any other advice is appreciated as well.
Basically a "sprite" is a visible game object in your game. Pygames pygame.sprite.Sprite class is known as the "base class" for all these objects.
Every time when creating the Squeeze class, you derive form Pygames build-in sprite base class:
class Squeeze(pygame.sprite.Sprite):
def __init__(self):
...
Each object (or instance) of the pygame.sprite.Sprite class holds
an .update() method and
an .image and
an .rect attribute.
As the documentation states, "* ... derived classes will want to override the Sprite.update() and assign a Sprite.image and Sprite.rect attributes.*"
You did this by calling the your img_load() method in the __init__() method of your Squeeze class.
The problem is now that your .rect is assigned to the same size as your screen (self.image = pygame.Surface([SCREEN_WIDTH, SCREEN_HEIGHT])) and the .image attribute has the same color as your display screen, so you canĀ“t see it.
Try to modify your img_load() method to
def img_load(self, img):
self.image = img
self.rect = self.image.get_rect()
As you can see, the modified img_load() method needs an image object -- which is passed to the img argument. So you also need to change your __init__() method:
def __init__(self, img):
pygame.sprite.Sprite.__init__(self)
self.img_load(img)
When now instantiating (e.g. creating an object of a class) a Squeeze object, you need to pass an image (in our case wall_img) to the constructor:
#... main programm
wall_img = pygame.image.load('wall.png') #load an image
wall_list = pygame.sprite.Group() #create a sprite group
BLACK = (0, 0, 0)
for i in range(2):
wall = Squeeze(wall_img) #create a sprite
wall.rect.x = random.randrange(SCREEN_WIDTH)
wall.rect.y = random.randrange(SCREEN_HEIGHT)
wall_list.add(wall) #add the sprite to the sprite group
The second build-in class you use is the pygame.sprite.Group class -- a container, which can hold and manage (modify) sprites it contains.
Calling the .update() method of a sprite group instance, Pygame automatically calls the update() methods of all sprites in this group. This means you can control a sprites behavior by implementing its update() method, as you did:
#update method of the Squeeze class
def update(self):
#any logic which changes sprite
self.rect.x += random.randint(-1, 1)
if self.rect.x >= 1920:
self.rect.x += -1
if self.rect.x <= 0:
self.rect.x += 1
By calling
wall_list.draw(screen)
your spites will be blitted onto the screen surface using the .image attribute for the source surface, and .rect for the position.
If you now wont to have different images you just need to pass other images (or surfaces) to the Squeeze constructor:
#... main programm
wall_imgs = [pygame.image.load('wall.png'),
pygame.image.load('other_wall.png')] #load images in list
wall_list = pygame.sprite.Group() #create a sprite group
BLACK = (0, 0, 0)
for i in range(2):
wall = Squeeze(wall_imgs[i]) #create a sprite
wall.rect.x = random.randrange(SCREEN_WIDTH)
wall.rect.y = random.randrange(SCREEN_HEIGHT)
wall_list.add(wall) #add the sprite to the sprite group
I hope this helps you a little bit :)
This is a the class of a sprite that goes left and right on the screen, when it hits the boundaries, it makes a "boing" sound and goes the opposite direction, everything works perfectly fine except for there is no boing sound when it hits the edge
class MySprite(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("vegetables.gif")
self.image = self.image.convert()
self.rect = self.image.get_rect()
self.rect.left = 0
self.rect.top = 167
self.__direction = 10
def update(self):
self.rect.left += self.__direction
sound = pygame.mixer.Sound("Bounce.mp3")
sound.set_volume(0.5)
if (self.rect.left < 0) or (self.rect.right > screen.get_width()):
sound.play()
self.__direction = -self.__direction
If you want the class to play its own sound, just load it like any attribute on __init__.
class MySprite(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("vegetables.gif")
self.image = self.image.convert()
self.sound = pygame.mixer.Sound("Bounce.mp3") #like this
self.rect = self.image.get_rect()
self.rect.left = 0
self.rect.top = 167
self.__direction = 10
Then whenever it's correct to do so, just call self.sound.play().
def update(self):
self.rect.left += self.__direction
if (self.rect.left < 0) or (self.rect.right > screen.get_width()):
self.sound.play() #as seen here
self.__direction = -self.__direction
For whatever it's worth - if you're going to do it in this way (have the sprite play its own sounds, etc), I would recommend loading them beforehand and then passing them as arguments (perhaps default arguments to avoid errors), such that each instance of the class might call a unique sound if need be.
So in your code prior to these classes, one could do something like:
JumpSound = pygame.Mixer.Sound("jump.wav")
BonkSound = pygame.Mixer.Sound("bonk.wav")
#etc etc etc...
...and then later on, pass the sounds as arguments:
class MySprite(pygame.sprite.Sprite):
def __init__(self, jumpsound, bonksound):
#...your other code precedes...
self.jumpsound = jumpsound
self.bonksound = bonksound
#...your other code continues...
myHero = MySprite(JumpSound, BonkSound)
The names are a little bit lousy b/c they are the same barring the CamelCasing, but forgetting that, this is probably a much cleaner approach. You can set your volume on the sounds way before they are passed into the sprites, along with whatever other changes you feel are necessary before the sprite gets ahold of them.
Specifically, I would like each sprite in sg_fireball to have 'bounces', without giving 'bounces' to every sprite from Spell(). Is there a clean way to do this without making 'bounces' an argument of Spell(), or looping through sg_fireball?
The relevant code snippets:
self.sg_fireball = pygame.sprite.Group()
self.sg_fireball.speed = 6.0
self.sg_fireball.image = pygame.image.load("fireball.png")
self.sg_fireball.bounces = 1
if event.type == pygame.MOUSEBUTTONDOWN:
self.character.cast(self.sg_fireball)
def cast(self, sg):
sg.add(Spell(self.rect.center, sg.speed, self.dir, sg.image))
class Spell(pygame.sprite.Sprite):
def __init__(self,pos, speed, direction, img, bounces):
pygame.sprite.Sprite.__init__(self)
self.bounces = bounces
self.image = img
self.rect = pygame.Rect(pos, (8,8))
self.posx = self.rect.x
self.posy = self.rect.y
self.speed = speed
self.dir = direction
self.velx = self.speed*math.cos(self.dir)
self.vely = self.speed*math.sin(self.dir)
If I understood correctly, you wish some of the sprites to have a certain attribute, while others won't. This is a perfect example of polimorphism and inheritance.
This is one of the options that you can do:
You subclass a normal spell as a bouncy spell. You can then have another update function where you will take care of bouncing. You can normally add a BouncySpell in the same sprite group as the NormalSpell.