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

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.

Related

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]))

TypeError: start() takes 0 positional arguments but 1 was given

class mainmenu(object):
def __init__(self, screen):
pygame.init()
self.screen = screen
def button(self, rect, radius=0, action=None):
click = pygame.mouse.get_pressed()
if rect.collidepoint(pygame.mouse.get_pos()):
color = (141,182,205)
if click[0] == 1 and action != None:
action()
else:
color = (28,134,238)
pygame.draw.rect(self.screen, color, rect, border_radius = radius)
def menu(self):
screenw = self.screen.get_width()
screenh = self.screen.get_height()
mid_w = screenw/2
mid_h = screenh/2
btn_start = pygame.Rect(mid_w-150,mid_h*3/5,300,100)
self.button(btn_start, 50, self.start)
def start():
return 'start'
I don't give any arguments in start() but I catch this error
TypeError: start() takes 0 positional arguments but 1 was given
self.start() is shorthand for writing start(self). So you can't pass in a method that's bound to an object, you'll have to pass in a free function (or static method). All methods on a class expects the argument self (your start method shouldn't work).
class Example:
def method(self): # Takes `self` as parameter
pass
Instead, you must create a static method that doesn't take any parameters:
class Example:
#staticmethod
def my_static_method():
# Doesn't take `self` as parameter, which means you cannot use
# `self` in this method.
pass
or a free-standing function that's not bound to a class at all.
def my_function():
pass
These can be passed as functions that doesn't take any arguments.
In your specific case, you can make start a static method, as it doesn't use self, or a free function:
class MainMenu:
def __init__(self, screen):
pygame.init()
self.screen = screen
def button(self, rect, radius=0, action=None):
click = pygame.mouse.get_pressed()
if rect.collidepoint(pygame.mouse.get_pos()):
color = (141,182,205)
if click[0] == 1 and action != None:
action()
else:
color = (28, 134, 238)
pygame.draw.rect(self.screen, color, rect, border_radius=radius)
def menu(self):
screen_width = self.screen.get_width()
screen_height = self.screen.get_height()
mid_width = screen_width / 2
mid_height = screen_height / 2
btn_start = pygame.Rect(mid_width-150, mid_height*3/5, 300, 100)
# Do this to call the static method.
self.button(btn_start, 50, MainMenu.start)
# Or do this to call the free standing function.
self.button(btn_start, 50, start)
#staticmethod
def start():
return 'start'
def start():
return 'start'

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.

Ways to reach variables from one class to another class?

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

Python: Calling class method instead of parent constructor

Say I have the following class definition:
class WorldObject(pygame.sprite.Sprite):
#classmethod
def fromImgRect(cls, rect, image, collideable = True):
return cls(rect.left, rect.top, rect.width, rect.height, image, collideable)
def __init__(self, x, y, w, h, image, collideable = True):
self.rect = pygame.rect.Rect(x,y,w,h)
self.collideable = collideable
self.image = image
Then I have the following child class:
class Doodad(WorldObject):
def __init__(self,c1x, c1y, c2x, c2y, color = (200,0,180)):
self.color = color
self.rect = orderPoints(c1x, c1y, c2x, c2y)
x1 = self.rect.left
y1 = self.rect.top
w = self.rect.width
h = self.rect.height
super(Doodad, self).__init__(x1,y1,w,h,self.surface, False)
This works just fine, however it is annoying to have to unpack self.rect like this all throughout my code, instead of just doing it once in the class method. This is happening in many places throughout my project, where several of my methods return a rectangle object, but I need to pass coordinates to a super constructor. It doesn't look like its possible to have everything return either coordinates or a rectangle, sometimes it just makes more sense to do one or the other. Since python doesn't support overloading methods, I'd like to be able to use the class method to initialize the object. However I haven't been able to figure out the syntax. Is this possible? If so, how?
In your situation, I would add a method for "sub-initializing". This would post-process the given data:
class WorldObject(pygame.sprite.Sprite):
#classmethod
def fromImgRect(cls, rect, *a, **k):
return cls(rect.left, rect.top, rect.width, rect.height, *a, **k)
def __init__(self, x, y, w, h, image, collideable=True):
self._init_coords(x, y, w, h)
self.collideable = collideable
self.image = image
def _init_coords(self, x, y, w, h):
self.rect = pygame.rect.Rect(x,y,w,h)
Then you can have the following child class:
class Doodad(WorldObject):
def _init_coords(self, c1x, c1y, c2x, c2y):
self.rect = orderPoints(c1x, c1y, c2x, c2y)
def __init__(self,c1x, c1y, c2x, c2y, color=(200, 0, 180)):
super(Doodad, self).__init__(c1x, c1y, c2x, c2y, self.surface, False)
self.color = color
Besides, you might want to have
def unpack_rect(rect):
return rect.left, rect.top, rect.width, rect.height
You can even have
class WorldObject(pygame.sprite.Sprite):
def __init__(self, *a, **k):
if hasattr(a[0], 'left'):
rect = a[0]
self._init_coords(rect.left, rect.top, rect.width, rect.height)
rest = a[1:]
else:
self._init_coords(*a[0:4])
rest = a[4:]
self._init_rest(*rest, **k)
def _init_coords(self, x, y, w, h):
self.rect = pygame.rect.Rect(x,y,w,h)
def _init_rest(self, image, collideable=True):
self.collideable = collideable
self.image = image
class Doodad(WorldObject):
def _init_coords(self, c1x, c1y, c2x, c2y):
self.rect = orderPoints(c1x, c1y, c2x, c2y)
def _init_rest(color=(200, 0, 180)):
super(Doodad, self)._init_rest(self.surface, False)
self.color = color
(I didn't change self.surface here, but it is not defined at this moment. You should change that.)

Categories