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)
Related
I am making a space shooter game which has a planets in the background. I've decided to draw some planets in the background and when i move to the right the planets should move to the left. That is for the player to feel the spaceship is moving around the space. However i could have done it for one planet only. When try to apply the other planets in one class it is constantly changing to the other planet.
lanetdic = {'planets':[]}
imagestoload = ['Feza/graphs/sprites/saturne.png']
for i in imagestoload:
img = pg.image.load(i).convert_alpha()
planetdic['planets'].append(img)
this is to load the sprite. and in the below i created a class for the planets.
class Planets(pg.sprite.Group):
def __init__(self):
pg.sprite.Group.__init__(self)
self.frame = 0
self.image = planetdic['planets'][self.frame]
self.rect = self.image.get_rect()
self.rect.center = (500+100*self.frame,HEIGHT/2)
self.pos = vec(500,HEIGHT/2)
self.vel = vec(0,0)
self.acc = vec(0,0)
def update(self):
#self.acc = vec(0,0)
self.frame = (self.frame + 1)%len(planetdic['planets'])
Maybe it is not sensible to create a class for a planet but i couldnt have find anotherway if there is please tell me.
if we get to the point again. In the below i made a for loop to load images. and used again the same for loop
planetdic = {'planets':[]}
imagestoload = ['Feza/graphs/sprites/saturne.png','Feza/graphs/sprites/jupiter.png','Feza/graphs/sprites/venus.png','Feza/graphs/sprites/uranus.png','Feza/graphs/sprites/neptune.png']
for i in imagestoload:
img = pg.image.load(i).convert_alpha()
planetdic['planets'].append(img)
When i apply multi images it changes one to other in miliseconds how can i prevent this happen. I just want to show every planets in the background and make them move.
How to draw multiple sprites in one class
You do not do that. However, you can create multiple instance objects of a class.
The image (Surface) must be a parameter of the constructor of the class Planet. The Planet class is a subcalss of sprite.Sprite, not sprite.Group:
class Planet(pg.sprite.Sprite):
def __init__(self, image):
pg.sprite.Group.__init__(self)
self.frame = 0
self.image = image
self.rect = self.image.get_rect()
self.rect.center = (500+100*self.frame,HEIGHT/2)
self.pos = vec(500,HEIGHT/2)
self.vel = vec(0,0)
self.acc = vec(0,0)
Create a new instance object of the Planet class for each planet:
planets = pg.sprite.Group()
imagestoload = ['Feza/graphs/sprites/saturne.png','Feza/graphs/sprites/jupiter.png','Feza/graphs/sprites/venus.png','Feza/graphs/sprites/uranus.png','Feza/graphs/sprites/neptune.png']
for filepath in imagestoload:
img = pg.image.load(filepath).convert_alpha()
planet = Planet(img)
planets.add(planet)
I am trying to make a python library:
There is a class (game) in it is a function that defines a variable (display)
Then there is another class (char) in main, I want to access display in char
How do I do it?
In the past I have tried: self.display, global display, and game.display
class game():
def __init__(self, disp, width, height):
self.display = disp # VARIABLE I WANT TO ACCESS
self.height = height
self.width = width
class sprite():
def __init__(self, size, position, image):
self.image = image
self.size = size
self.rect = self.image.get_rect()
self.rect.x = position[0]
self.rect.y = position[1]
self.x = position[0]
self.y = position[1]
self.collisionDirection = 5
self.hasCollided = False
self.mask = pygame.mask.from_surface(self.image)
self.velocity = 0
def render(self):
self.rect.x = self.x
self.rect.y = self.y
self.mask = pygame.mask.from_surface(self.image)
display.blit(self.image, (self.x, self.y)) # WHERE I WANT TO ACCESS IT
I keep getting AttributeError what can I do?
You can pass the Game instance to another class. For example
# instantiate the game
g = game()
# create an instance of char
c = char(game)
assuming the __init__ of char() looks something like this:
class char():
def __init__(self, game):
# save a reference to the game object as an attribute
self.game = game
# now you can access the game's attributes
print(self.game.display)
Allow that the below example is contrived, and not likely the way you would design pen/paper, but it shows that there are a couple different ways that you can accomplish what you ask.
class Pen:
def __init__(self, thickness, color):
self.thickness = thickness
self.color = color
class Paper:
def __init__(self, pen: Pen):
self.pen = pen
def draw(self):
pen_color = self.pen.color
return pen_color
def draw2(self, pen: Pen):
pen_color = pen.color
return pen_color
red_pin = Pen(2, 'Red')
blue_pin = Pen(1, 'Blue')
paper = Paper(red_pin)
print(paper.draw()) # prints Red
print(paper.draw2(blue_pin)) # prints Blue
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.
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.