Ways to reach variables from one class to another class? - python

I'm trying to create a basic game, but I'm fairly new to the python programming scene. I've come across a problem where with two classes (a player and enemy class), I want to access class variables like health from the player and enemy, and vice versa. What are some ways of doing this?
Here's the code to better emphasize what I'm asking:
class Player(object):
def __init__(self, image):
self.x = 100
self.y = 240
self.health = 30
self.defense = 25
self.image = image
self.black = (0, 0, 0)
self.draw()
def draw(self):
screen.blit(self.image, (self.x, self.y))
line = pygame.draw.rect(screen, self.black, (80, 300, 100, 5))
def attack(self):
pass
class Enemy(object):
def __init__(self, image):
self.x = 480
self.y = 240
self.health = 20
self.defense = 15
self.image = image
self.black = (0, 0, 0)
self.draw()
def draw(self):
screen.blit(self.image, (self.x, self.y))
line = pygame.draw.rect(screen, self.black, (460, 300, 100, 5))
def attack(self):
pass
Basically, I don't understand how I can take something like the "self.health" from one class, and easily access it from the other class. I've tried some methods of using return methods etc., but I'm curious if there are any simple ways to do this. Help is appreciated!

This code creates a class, and in the special __init__ method, it assigns values to various member variables.
class Player(object):
def __init__(self, image):
self.x = 100
self.y = 240
self.health = 30
self.defense = 25
self.image = image
self.black = (0, 0, 0)
self.draw()
These member variables are also called properties in Python. If you have a reference to a Player instance (an instance of the Player class):
p = Player()
You can access the properties all you like:
print(p.health)
Perhaps you need some kind of "main loop" or controller class that has access to players and enemies and can set their properties accordingly:
class MainLoop(object):
def __init__(self):
self.player_1 = Player()
self.enemy_1 = Enemy()
def run(self):
if fight(): # fight() is some function of your game
self.player_1.health -= 10
self.enemy_1.health -= 20

Related

Inherited __init__() function not working as intended

I was working on a game using the pygame library on Python. I basically defined a Character class from which the Knight class and Enemy class would inherit functions. Since both children classes use the same initialize functions, I defined the __init__() function under the parent class. However, I don't fully understand how it works and I'm getting the following error:
TypeError: __init__() takes 1 positional argument but 3 were given
Here's my code:
class Character():
def __init__(self, img, hitbox, vel, pos_x, pos_y):
self.img = img
self.hitbox = hitbox
self.vel = vel
self.pos_x = pos_x
self.pos_y = pos_y
def draw(self):
if self.right:
pygame.transform.flip(self.img, True, False)
win.blit(self.img, (self.pos_x, self.pos_y))
class Knight(Character):
def __init__(self):
Character.__init__(self)
def move(self):
if self.right:
if self.x + self.vel < win_width:
self.x += self.vel
if self.left:
if self.x - self.vel > 0:
self.x -= self.vel
main_plr = Knight("img", (19, 20), 5, 30, 20)
For a quick fix: just remove the __init__ method from Knight.
The error is raised because you create a Knight object with 6
arguments (self, "img", (19, 20), 5, 30, 20) whereas the __init__ method accepts only one (self).
So if your Knight objects do not have any additional attributes
compared to Character objects, it will be just fine to remove the
__init__ method. Now if you want your knight to have weapons, for
example, you will have to do something like that:
class Knight(Character):
def __init__(self, img, hitbox, vel, pos_x, pos_y, weapon):
super().__init__(img, hitbox, vel, pos_x, pos_y)
self.weapon = weapon
k = Knight("img", (19, 20), 5, 30, 20, "sword")
[Edit]
Additionaly, as suggested by #Matiiss, you can use *args to avoid
repeating all arguments of Character.__init__ in Knight.__init__.
One advantage, besides conciseness, is that you do not have to modify
Knight if you add attributes to your Character objects.
class Knight(Character):
def __init__(self, *args, weapon):
super().__init__(*args)
self.weapon = weapon
k = Knight("img", (19, 20), 5, 30, 20, weapon="sword")
But now the drawback is that you have to specify the weapon with
weapon="the-weapon", since it is now a keyword argument (placed
after *args).
As the error you are seeing says, your Knight constructor does not accept those arguments; if you are going to use that kind of inherited method extension, the class and subclass methods need to have matching argument signatures. It's also best to use super() to refer to the superclass rather than naming it explicitly.
The simplest way of handling this is to use *args and **kwargs, to concisely pass arguments that aren't needed by the subclass method to the superclass method, ie
class Character():
def __init__(self, img, hitbox, vel, pos_x, pos_y):
self.img = img
self.hitbox = hitbox
self.vel = vel
self.pos_x = pos_x
self.pos_y = pos_y
class Knight(Character):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def move(self):
if self.right:
if self.x + self.vel < win_width:
self.x += self.vel
if self.left:
if self.x - self.vel > 0:
self.x -= self.vel

Class inheritance in python with pygame

So I'm working on a hobby game, and i'm not able to get one class to inherit another properly. The file structure is as shown below:
main.py
Enemy
|walker.py
|genericEnemy.py
main.py calls walker.py, whose main class inherits from genericEnemy.py. Both of their contents are below:
walker.py
import pygame
from .genericEnemy import generic
class walker(generic):
pass
genericEnemy.py
'''
This class controls generic capabilities for all enemies.
Specific abilities are in the enemy type's class in the superfolder
'''
import pygame
class generic:
def __init__(self, speed, pos, size):
'''
speed - The entity speed, an int greater than 0
pos - The (x,y) position of the entity, a list of length 2
size - The entity hitbox, a list with length 2
'''
#Movement Variables
self.speed = speed
self.currDir = 1
self.isMoving = True
#Drawing Variables
self.pos = pos
self.size = size
#Gravity Variables
self.isJumping = False
self.fallCounter = 0
self.gravityTimer = 0
#==================================================
def draw(self, surface):
pygame.draw.rect(surface, (255, 0, 0), (self.pos[0], self.pos[1], self.size[0], self.size[1]))
#==================================================
def updateGravity(self):
self.fallCounter += 1
if self.fallCounter == 8:
self.fallCounter = 0
self.gravityTimer += 1
#==================================================
def walk(self):
if self.isMoving:
self.pos[0] += self.speed * self.currDir
The issue I'm having is that in main when I say:
SCREEN = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
ENEMY = walker(6, [120, 1000], [10, 30])
and then later on
ENEMY.draw(SCREEN)
I get the error: AttributeError: 'walker' object has no attribute 'draw'
Any help would be greatly appreciated, like I said this is a hobbyist project, so i'm fairly inexperienced in python/pygame
User Barmar was correct. I, as an absolute walnut, had my functions indented too far.
Use class generic(pygame.sprite.Sprite):
and
all_sprites = pygame.sprite.Group()
all_sprites.add(ENEMY)
all_sprites.draw(SCREEN)
pygame sprite group has
draw function
And maybe it need sprite.image!
It can be surface
self.image = pygame.Surface([self.size[0], self.size[1]])
self.image.fill((255, 0, 0))
or image file
self.image = pygame.transform.scale(pygame.image.load(imgname).convert_alpha(), (self.size[0], self.size[1]))

Updating multiple items in a class, not just one

In the update section of this code, only the first bat that gets made is affected by update() in class Bat()...
Outside of main loop:
START_BAT_COUNT = 30
BAT_IMAGE_PATH = os.path.join( 'Sprites', 'Bat_enemy', 'Bat-1.png' )
bat_image = pygame.image.load(BAT_IMAGE_PATH).convert_alpha()
bat_image = pygame.transform.scale(bat_image, (80, 70))
class Bat(pygame.sprite.Sprite):
def __init__(self, bat_x, bat_y, bat_image, bat_health):
pygame.sprite.Sprite.__init__(self)
self.bat_health = bat_health
self.image = bat_image
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image)
self.rect.topleft = (bat_x, bat_y)
def update(self):
self.bat_health -= 1
if self.bat_health < 0:
new_bat.kill()
all_bats = pygame.sprite.Group()
for i in range(START_BAT_COUNT):
bat_x = (random.randint(0, 600))
bat_y = (random.randint(0, 600))
bat_health = 5
new_bat = Bat(bat_x, bat_y, bat_image, bat_health)
all_bats.add(new_bat)
Inside main loop...
all_bats.update()
all_bats.draw(display)
Any help would be great! Thanks.
In Method Objects you must use the instance parameter (self) instead of an object instance in the global namespace. This means you have to call self.kill() instead of new_bat.kill():
class Bat(pygame.sprite.Sprite):
# [...]
def update(self):
self.bat_health -= 1
if self.bat_health < 0:
self.kill()

Pygame returning locking error during blitting

I am trying to make a 2D game in pygame, and have a camera class that has a surface attribute, and each time the cameras get updated their surface is updated. All graphics are blitted to game.worldSurface, and then the main camera take a picture of that and blits it to the display surface. However, when using other cameras, i am unable to blit to the worldsurface and get a locking error. i have tried .unlock(). what could be causing this?
import pygame
import pickle
class Tileset:
def __init__(self, location):
pass
class Tilemap:
def __init__(self):
pass
class Collisionmap(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
class Player(pygame.sprite.Sprite):
def __init__(self, spritesheet):
super().__init__()
self.spritesheet = pygame.image.load(spritesheet)
self.x = 0
self.y = 0
def draw(self, surface):
surface.blit(self.spritesheet, (self.x, self.y))
class Mob(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
class Camera:
def __init__(self):
self.x = 0
self.y = 0
self.width = 100
self.height = 100
self.surface = pygame.Surface((self.width, self.height))
def moveToSprite(self, sprite):
self.x = sprite.rect.centerx - WIDTH // 2
self.y = sprite.rect.centery - HEIGHT // 2
def update(self, world):
self.surface = world.subsurface((self.x, self.y, self.width, self.height))
class Level:
def __init__(self, terrarin, collision, mobs):
self.terrain = terrain
self.collision = collision
self.mobs = mobs
class Game:
def __init__(self):
pygame.init()
self.DISPLAYSURF = pygame.display.set_mode((0, 0))
self.mainCamera = Camera()
self.mainCamera.width = self.DISPLAYSURF.get_width()
self.mainCamera.height = self.DISPLAYSURF.get_height()
self.otherCameras = []
self.worldSurface = pygame.Surface((10000, 10000))
self.player = Player("marioSS.jpg")
self.otherCameras.append(Camera())
self.run()
def run(self):
while True:
for event in pygame.event.get():
pass
self.earlyUpdate()
self.update()
self.lateUpdate()
self.graphicsUpdate()
def update(self):
pass
def earlyUpdate(self):
pass
def lateUpdate(self):
pass
def graphicsUpdate(self):
for each in self.otherCameras:
each.update(self.worldSurface)
self.player.draw(self.worldSurface)
self.otherCameras[0].surface.unlock()
self.worldSurface.unlock()
self.worldSurface.blit(self.otherCameras[0].surface, (100, 100)) ##Error here
self.mainCamera.update(self.worldSurface)
self.DISPLAYSURF.blit(self.mainCamera.surface, (0, 0))
pygame.display.update()
x = Game()
Problem makes world.subsurface() in Camera.update()
It doesn't copy data from world to surface but it assigns access to original world. And later you have: camera.surface keeps access to world, and blit try to copy from camera.surface to world - so finally it tries to copy from world to world. And maybe it locks it.
But if in Camera.update() you use .copy()
self.surface = world.subsurface((self.x, self.y, self.width, self.height)).copy()
or blit it
self.surface.blit(world.subsurface((self.x, self.y, self.width, self.height)), (0,0))
then it works.
DOC: subsurface
subsurface(Rect) -> Surface
Returns a new Surface that shares its pixels with its new parent.

Pass an __init__ value from a higher class into a subclass requiring that same argument

I am creating a small game that creates a tower.
The tower has two components, the base and the gun. In order to do so I am trying to pass the value of self.x into the __init__ function of a subclass. I will further add an update() function once I have a solution.
Here is my code. (Poorly formatted and incomplete... sorry!)
import pygame
from math import atan2
screen = pygame.display.set_mode((800, 800))
pygame.display.set_caption('Foongus v0.1')
pygame.init()
class home_base:
def __init__(self, x, y):
class base:
def __init__(self, x, y):
self.image = pygame.image.load('base.png')
screen.blit(self.image, (x, y))
class gun:
def __init__(self, x, y):
self.image = pygame.image.load('gun.png')
screen.blit(self.image, (x, y))
home = home_base(300, 300)
while True:
screen.fill((0,0,0))
pygame.display.update()
The following code should help guide you in the direction you should be going:
import math
import pygame
def main():
screen = pygame.display.set_mode((800, 800))
pygame.display.set_caption('Foondus v0.1')
pygame.init()
home = HomeBase(300, 300, screen)
while True:
screen.fill((0, 0, 0))
pygame.display.update()
class HomeBase:
def __init__(self, x, y, screen):
self.x, self.y, self.screen = x, y, screen
self.base_image = pygame.image.load('base.png')
self.gun_image = pygame.image.load('gun.png')
self.screen.blit(self.base_image, (self.x, self.y))
self.screen.blit(self.gun_image, (self.x, self.y))
if __name__ == '__main__':
main()
you wouldn't usually define a class inside the __init__ method of another class. instead, I would do something along these lines:
class A(object):
def __init__(self, instance_classB):
# here self is an instance of A and instance_classB an instance of B
pass
class B(object):
def __init__(self):
# self is the newly created instance of B
self._instance_classA = A(self)
# create an instance of B - calls B.__init__ on the new instance
b = B()
that way, you have instances of class B have a reference to an instance of class A in self._instance_classA, where each instance of class A has a backwards reference to the instance of class B it belongs to passed into the constructor.

Categories