I can make no sense of this problem. I have a Player object and some Enemy objects that both inherit from the Actor class. Both Player and Enemy have shoot(self) methods that makes them shoot a bullet. This bullet is supposed to be added to their respective list of projectiles, but when the program calls self.projectiles.append(Projectile()) for an enemy, it adds it to the player's list of projectiles.
I've run the program where the only Actor shooting any bullets was the enemies and I watch as len(player.projectiles) returns greater and greater values, even though it should not be growing. Any help is appreciated. This block runs every time the program updates, it goes through the Game object's list of enemies and updates each one respectively:
for enemy in self.enemies:
enemy.update(self.player)
Here's the Enemy class:
class Enemy(Actor):
def shoot(self):
image = pygame.transform.rotate(ProjectileImage, self.angle)
self.projectiles.append(Projectile(self.getCenter()[0] - 6, self.getCenter()[1] - 16, 12, 32, image, 5, self.angle, True))
shootingSound.play()
def tryToShoot(self):
if self.attackCoolDown >= 30:
self.attackCoolDown = 0
self.shoot()
def update(self, player):
self.pointTowards(player.x, player.y)
Actor.update(self)
self.tryToShoot()
The Actor class initializes projectiles:
class Actor(Entity):
projectiles = []
Your posted code shows projectiles as a class attribute, not an instance attribute. Thus, all Actors share a single projectiles list. To make separate lists, initialize the attribute inside the __init__ method.
When you add projectiles as a class varable,both Enemy and Player inheret the same list object .
Also,when you ref something using self (and it can't be found in the instance dictionary) , it defaults to the class varable, that is why your player's and enemy's lenths are both growing:they share the same list object.
To fix this,put the projectiles=[] line into the __init__ method:
Change:
class Actor(Entity):
projectiles = []
To:
class Actor(Entity):
def __init__(self):
self.projectiles = []
To fix the bug.
PS: You don't seem to have a update method in the Actor class.
And if you do, using super().update() will be more Pythonic than Actor.update(self).
Related
I am trying to create a board game and I use code to simulate some games.
I have a class Player
When I simulate one game, I simulate a list of players
jo = Player(name='jo', point='0)
pierre = Player(name=pierre, point=0)
...
Some actions of pierre will impact jo. So I need to access the others Player's object I have created inside some methods of the Player's class .
I would like to create a dictionary that will be different any time I simulate a new game:
mapping_dictionnary = {'jo': jo, 'pierre': pierre}
key are strings. values are Player's object
and I would like this dictionary to be accessible in each methods of the class Player. So I can do for exemple:
def attack(self):
# all players other than self lost 3 points
for player in mapping_dictionnary.values():
if player != self:
player.point -= 3
I tried to create a parent class ElementGame in which I define my mapping_dictionnary but Player's object are not recognized in it (because class Player is a child).
I could add this mapping_dictionnary as an element of the Player constructor but when I will create jo, the script will not know pierre so I will get an error (plus it is an attribut of the game not of each players)
Have you any ideas on how to do that?
I am attempting to construct classes to play out a game of MTG (A card game). I have three relevant classes: MTGGame(...), MTGCard(...), and AbilityList(). An object of MTGGame has several attributes about the player (turn, mana,..., deck).
A player must have a deck of cards to play, so I create a list of MTGCard objects for each player that is a deck, and create an MTGGame object for each from the respective decks. The cards have abilities, and when creating the cards I store abilities as functions/params into each MTGCard. However, I need the abilities to inherit and access methods/attributes from MTGGame and update them, but if I use super().__init__, then I will need to call my deck as a parameter for AbilityList when making MTGCards, which I wouldn't have yet.
Can this be achieved? If not, any suggestions improving my OOP logic to achieve this task?
I am aware that I can do something like this:
class MTGGame():
def __init__(self, deck, turn = 0, mana = 0, lifeTotal = 20, cavalcadeCount = 0, hand = [], board = []):
self.turn = turn
self.mana = mana
self.lifeTotal = lifeTotal
...
def gainLife(self, lifeGained):
self.lifeTotal = self.lifeTotal +lifeGained
def combatPhase(self):
for card in self.board:
card.attackingAbility()
class MTGCard():
def __init__(self, name, CMC, cardType, power, toughness, castedAbility, attackingAbility, activatedAbility, canAttack = False):
....
self.attackingAbility = attackingAbility
Class abilityList():
def healersHawkAbility(self, lifeAmt):
MTGGame.gainLife(lifeAmt)
But this would affect all instances of MTGGame, not the specific MTGGame object this would've been called from. I'd like it to simply update the specific object in question. I'd like to do something like this but I don't know how abilityList methods could access MTGGame attributes/methods ('AbilityList' object has no attribute 'gainLife'):
Class abilityList():
def healersHawkAbility(self, lifeAmt):
#How do I access methods/attributes in MTGGame from here? self?
self.gainLife(lifeAmt)
aL = abilityList()
#One example card:
card1 = MTGCard("Healers Hawk",1,'Creature',1,1, aL.nullAbility(), aL.healersHawkAbility, aL.nullAbility())
whiteDeck = [list of constructed MTGCard() objects, card1, card2,...,cardLast]
player1 = MTGGame(whiteDeck)
...
#Call the ability in a method contained in MTGGame:
player1.combatPhase()
#Would call something like this inside
card.attackingAbility()
#Which may refer to card.healersHawkAbility() since we stored healersHawkAbility() as an attribute for that MTGCard,
#and would declare gainLife(), which refers to self.lifeTotal or player1.lifeTotal in this case.
This is an excellent start and clearly you have already thought a lot of this through. However, you haven't thought through the relationship between the classes.
First thing to note:
MTGGame.gainLife(lifeAmt) is a method call accessed via the class rather than an instance. This means that the self paramter is not actually filled in i.e. you will get an error becuase your method expects 2 arguments but only receive one.
What you perhaps meant to do is the following:
class MTGGame:
lifeTotal = 20 # Notice this is declared as a class variable
def __init__(self, ...):
...
#classmethod
def healersHawkAbility(cls, lifeGained):
cls.lifeTotal = cls.lifeTotal + lifeGained
However, this requires class variables which here defeats the point of having an instance.
Your naming throughout the program should suggest that your classes are a little off.
For instance player1 = MTGGame(). Is player a game? No, of course not. So actually you might want to rename your class MTGGame to Player to make it clear it refers to the player, not the game. A seperate class called MTGGame will probably need to be created to manage the interactions between the players e.g. whose turn it is, the stack holding the cards whilst resolving.
The main focus of your question: how to deal with the cards accessing the game/player object.
Cards should be able to access instances of the player and game classes, and if the player has a is_playing attribute, the card should not have this. The rule of thumb for inheritance is 'is a'. Since card 'is not a' player, it should not inherit from it or MTGGame. Instead, card should be like this for example:
game = RevisedMTGGame()
player1 = Player()
player2 = Player()
class Card:
def __init__(self, name, text, cost):
self.name = name
self.text = text
self.cost = cost
self.owner = None
self.game = None
class Creature(Card):
def __init__(self, name, text, cost, power, toughness):
super().__init__(self, name, text, cost)
self.power = power
self.toughness = toughness
def lifelink(self):
self.owner.heal(self.power) # NOTE: this is NOT how I would implement lifelink, it is an example of how to access the owner
healersHawk = Creature("Healer's Hawk", "Flying, Lifelink", 1, 1, 1)
healersHawk.game = game
healersHawk.owner = player1
You can see from this incomplete example how you can set up your cards easily, even with complex mechanics, and as the base classes have been defined you can avoid repitition of code. You might want to look into the event model in order to implement the lifelink mechanic, as an example. I wish you luck in continuing your game!
I'm Trying to make a game. Like in any other shooting game, the enemies are supposed to disappear when shot. Apparently, you can't kill surfaces, but you can kill sprites. So I'm trying to load my images as sprites, but I'm getting a weird error. I make the class, apply a variable to it, make a variable for pygame.sprite.Group, and add the class variable to the group. When I try to update the group, It says "'NoneType' object has no attribute 'update'". Here's the code for the class:
class Spawn(pygame.sprite.Sprite):
def __init__(self,primaryx,primaryy):
pygame.sprite.Sprite.__init__(self)
global directionM
self.directionM=directionM
x1=random.randint(100,400)
y1=random.randint(100,400)
self.x1=x1
self.y1=y1
self.primaryx=primaryx
self.primaryy=primaryy
def AIPrototype(self):
minionup=pygame.image.load("Alien.png").convert_alpha()
miniondown=pygame.image.load("Aliendown.png").convert_alpha()
minionleft=pygame.image.load("Alienleft.png").convert_alpha()
minionright=pygame.image.load("Alienright.png").convert_alpha()
global x,y,posx,posy
seperate=random.randint(1,1000)
screen.blit(self.directionM,(self.primaryx,self.primaryy))
if seperate==2:
self.primaryx=x+100
if seperate==20:
self.primaryx=x-100
if seperate==150:
self.primaryy=y+100
if seperate==200:
self.primaryy=y-100
self.x1=self.primaryx
self.y1=self.primaryy
if self.x1<x:
xspeed1=1
slopex1=x-self.x1
if self.x1>x:
xspeed1=-1
slopex1=self.x1-x
if self.y1<y:
yspeed1=1
slopey1=y-self.y1
if self.y1>y:
yspeed1=-1
slopey1=self.y1-y
#
hypo1=((slopex1**2)+(slopey1**2))**0.5
speedmark1=hypo1/1
speedy1=slopey1/speedmark1
speedx1=slopex1/speedmark1
movex1=speedx1
movey1=speedy1
if self.x1<=640 and self.x1>=0:
if self.x1>x:
self.x1+=xspeed1*movex1
if self.x1<x:
xspeed1=0
if self.y1<=480 and self.x1>=0:
if self.y1>y:
self.y1+=yspeed1*movey1
if self.y1<y:
yspeed1=0
if self.x1<=640 and self.x1>=0:
if self.x1<x:
self.x1+=xspeed1*movex1
if self.x1>x:
xspeed1=0
if self.y1<=480 and self.x1>=0:
if self.y1<y:
self.y1+=yspeed1*movey1
if self.y1>y:
yspeed1=0
#
if self.x1>640:
self.x1=640
if self.x1<0:
self.x1=0
if self.y1>480:
self.y1=480
if self.y1<0:
self.y1=0
if self.y1>=posy-20 and self.y1<=posy+20 and self.x1>=x-20 and self.x1<=x+20:
Spawn.kill()
self.primaryx=self.x1
self.primaryy=self.y1
And this is the game loop:
spritegroup=pygame.sprite.Group()
spawn=Spawn(600,200)
spritegroup=spritegroup.add(spawn)
clock = pygame.time.Clock()
keepGoing = True
try:
while keepGoing:
clock.tick(60)
screen.fill(THECOLORS['red'])
char()#start
x+1
posxlist.append(x)
posylist.append(y)
spritegroup.update()
pygame.display.flip()
I realize that my code is really messy and inefficient, and I'm really sorry about that. I'm completely new to using classes and sprites.
spritegroup.add(spawn) returns None. Don't assign that back to spritegroup, the operation altered the object in place.
In other words, do this instead:
spritegroup = pygame.sprite.Group()
spawn = Spawn(600,200)
spritegroup.add(spawn)
From the spritegroup.Group.add() documentation:
add Sprites to this Group
add(*sprites) -> None
Been trying to get a dictionary working for some sprites.
class Player(Digimon):
def __init__(self):
super(Player, self).__init__(0,2,0,0,0,0) # age, weight, str, def, speed, intelligence
self.image_dict = {(0,2,0,0,0,0): itertools.cycle([pygame.image.load("images/egg_sprite_1.png"),
pygame.image.load("images/egg_sprite_2.png"),
pygame.image.load("images/egg_sprite_3.png")]),
(2,4,0,0,2,5): itertools.cycle([pygame.image.load("images/leaf_1.png"),
pygame.image.load("images/leaf_2.png"),
pygame.image.load("images/leaf_3.png"),
pygame.image.load("images/leaf_4.png")])}
and then in the main loop
Player.images = Player.image_dict[(Player.age, Player.weight, Player.strength, Player.defence, Player.speed, Player.intelligence)]
Player.image = next(Player.images)
I get the error stated in the title. I've looked at similar questions, but they don't seem to help. As far as I can see, it should work. Player.image_dict is created in the Player(digimon) subclass, so I don't know how there is no attribute.
Well, you actually need to create an instance of the Player class as follows:
player = Player()
Then you can call:
player.images = player.image_dict[(player.age, player.weight, player.strength, player.defence, player.speed, player.intelligence)]
player.image = next(player.images)
Note these are player the object, not Player the class
Player.image_dict
this way you can only access ststic members of class Player. If you dont want to do that you need to create a object of the class player. like this
Player player = Player(<arguments>)
player.image_dict(...)
I'm using Livewires and pygame and one of my objects in the game that gives you extra lives is being mistaken as an asteroid object, and when the extra lives objects collides with the player it returns the 'Extra lives object has no attribute handle_caught' error message, so can I please have some help.
class Extralives(games.Sprite):
global lives
image = games.load_image('lives.png', transparent = True)
speed = 2
def __init__(self,x,y = 10):
""" Initialize a asteroid object. """
super(Extralives, self).__init__(image = Extralives.image,
x = x, y = y,
dy = Extralives.speed)
def update(self):
""" Check if bottom edge has reached screen bottom. """
if self.bottom>games.screen.height:
self.destroy()
self.add_extralives
def add_extralives(self):
lives+=1
The asteroid class:
class Asteroid(games.Sprite):
global lives
global score
"""
A asteroid which falls through space.
"""
image = games.load_image("asteroid_med.bmp")
speed = 1.7
def __init__(self, x,image, y = 10):
""" Initialize a asteroid object. """
super(Asteroid, self).__init__(image = image,
x = x, y = y,
dy = Asteroid.speed)
def update(self):
""" Check if bottom edge has reached screen bottom. """
if self.bottom>games.screen.height:
self.destroy()
score.value+=10
def handle_caught(self):
if lives.value>0:
lives.value-=1
self.destroy_asteroid()
if lives.value <= 0:
self.destroy_asteroid()
self.end_game()
def destroy_asteroid(self):
self.destroy()
part of the player class which handles the collisions:
def update(self):
""" uses A and D keys to move the ship """
if games.keyboard.is_pressed(games.K_a):
self.x-=4
if games.keyboard.is_pressed(games.K_d):
self.x+=4
if self.left < 0:
self.left = 0
if self.right > games.screen.width:
self.right = games.screen.width
self.check_collison()
def ship_destroy(self):
self.destroy()
def check_collison(self):
""" Check if catch pizzas. """
global lives
for asteroid in self.overlapping_sprites:
asteroid.handle_caught()
if lives.value <=0:
self.ship_destroy()
for extralives in self.overlapping_sprites:
extralives.add_extralives()
Here is your problem:
for asteroid in self.overlapping_sprites:
asteroid.handle_caught()
if lives.value <=0:
self.ship_destroy()
The fact that you call your loop variable asteroid does not mean that it's magically only going to ever be an asteroid. Not if you have other kinds of objects you can collide with! overlapping_sprites is all overlapping sprites, not just asteroids. At some point asteroid is an ExtraLives object. When you try to call handle_caught() on it, this obviously fails because ExtraLives doesn't have a handle_caught() method.
The simplest solution here is to rename add_extralives to handle_caught on your ExtraLives class. After all, you're doing the same thing: handling the situation where you collide with (or "catch") the object, it's just a different kind of object so the result needs to be different, which you specify by providing different code. Being able to implement entirely different kinds of behavior by calling the same methods (called "polymorphism") is kinda the whole point of object-oriented programming.
The following loop has a similar problem, in that you're calling add_extralives() on objects that might not be of type ExtraLives. Fortunately you can remove this code since you're already handling this situation by renaming add_extralives to handle_caught.
for extralives in self.overlapping_sprites:
extralives.add_extralives()