How to define and play sound effects within the class - python

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.

Related

How to make a class follow another in python

How do I make a class follow another, instead of moving in a single direction?
for example:
class Character(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
char_walk1 = pygame.image.load("").convert_alpha()
char_walk2 = pygame.image.load("").convert_alpha()
self.char_walk = [char_walk1, char_walk2]
self.char_index = 0
self.image = self.char_walk[self.char_index]
self.rect = self.image.get_rect(midbottom=(100, 100))
def update(self):
key_input = pygame.key.get_pressed()
if key_input[pygame.K_d]:
self.rect.x += 3
if key_input[pygame.K_a]:
self.rect.x -= 3
Here the Character is moving left or right per key press and,
class Mobs(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
mob_walk1 = pygame.image.load("").convert_alpha()
mob_walk2 = pygame.image.load("").convert_alpha()
self.frames = [mob_walk1, mob_walk2]
pos = 452
self.mob_index = 0
self.image = self.frames[self.mob_index]
self.rect = self.image.get_rect(midbottom=(randint(200, 200), pos))
def update(self):
self.rect.x -= 3
Here the mob is moving right, How do I make it follow the character around on screen?
Character is sprite.Groupsingle and Mobs is sprite.Group
You'll probably need a reference to the character as an attribute in the Mobs class. then in the update method you'll have to decide the direction of the movement based on the relative position of the character.
def update(self):
if self.character.rect.x < self.rect.x:
self.rect.x -= 3
else:
self.sect.x += 3
It's been a while since i fooled around with pygame so I'm not sure about the best way to implement it but I hope it at least serves as guidance.

How do I move variables between Classes and functions without them changing?

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)

pygame: replacing image of an item/enemy

I'm trying to write a program where the image for the 'enemy' I have changes once its health goes to 0. I have different classes for each element, but I have this under this class.
class Enemy(pygame.sprite.Sprite):
def __init__(self, gs=None):
pygame.sprite.Sprite.__init__(self)
...initialization stuff...
self.image = pygame.image.load("enemy.png") #enemy image
self.rect = self.image.get_rectangle()
self.hp = 40 #initial health
def damage(self):
if self.rect.colliderect(self.user.rect): #collision/how the health goes down
self.hp = self.hp - 5
Now here's the part I'm curious about, I want to load a new image that replaces the old image I have for this enemy. Can I do/add (in the damage function)
if(self.hp == 0):
self.image = pygame.image.load("dead.png")
Will this replace it? Or just load another image on top of it?
Let me know what I'm missing, thank you!
You should load all your images into your init method when you create the object and then you can do the assigning/changing later using lists. Here is an example:
class Enemy(pygame.sprite.Sprite):
def __init__(self, gs=None):
pygame.sprite.Sprite.__init__(self)
self.images = [pygame.image.load("enemy.png"), pygame.image.load("dead.png")]
self.current_image = self.images[0]
self.rect = self.current_image.get_rectangle()
Then later you can do:
if(self.hp == 0):
self.current_image = self.images[1]
This will in fact, as per your concern, replace the current image instead of just drawing over it. Hope that helps.

Cleanest way for a sprite to inherit an attribute from its group?

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.

Trying to create a group of button sprites

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.

Categories

Resources