Positional argument is said to be missing but is given, what is wrong? - python

I'm using pygame and have created a class called Entity and a Subclass Player. The class Entity requires an argument "pos", which is always just "pos", because the value for it is created in the init.
class Entity(pygame.sprite.Sprite):
def __init__(self, color, stype, pos, *groups):
super().__init__(*groups)
self.image = stype
try:
self.image.fill(color)
except:
pass
self.image.convert()
self.rect = self.image.get_rect(topleft=pos)
def update(self, dt, events):
pass
class Player(Entity):
def __init__(self, platforms, pos, *groups):
super().__init__((pygame.image.load("lemon.png")), pos)
self.vel = pygame.Vector2((0, 0))
self.onGround = False
self.platforms = platforms
self.speed = 8
self.jump_strength = 10
However when I try to run the code it gives me this error:
TypeError: init() missing 1 required positional argument: 'pos'

Why only two arguments pass here:
super().__init__((pygame.image.load("lemon.png")), pos)
But the constructor of Entity requires at least 3:
def __init__(self, color, stype, pos, *groups)
I don't know your problem well, but this could be a solution:
super().__init__(color=None, stype=(pygame.image.load("lemon.png")), pos=pos)

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

Getting a type Surface error with argument 1. How would I fix it?

I'm using Python 3.7.4 and Python 1.9.6. I have a class which is the main one for the subclass's. I'm experiencing an error: the line with the error --> window.blit(self.ship_img, (self.x, self.y)) TypeError: argument 1 must be pygame.Surface, not None.
Code:
class Ship:
def __init__(self, x, y, health=100):
self.x = x
self.y = y
self.health = health
self.ship_img = None
self.laser_img = None
self.lasers = []
self.cool_down_counter = 0
def draw(self, window):
window.blit(self.ship_img, (self.x, self.y))
def get_width(self):
return self.ship_img.get_width()
def get_height(self):
return self.ship_img.get_height()
The reason it has to be None because it has several images of the same class(meaning using the same variables) but with different coordinates and properties. It can't be a list, because then the .get_width() and .get_height() won't work. I'm using those to create barriers for the image(s).
I'm following along with the tutorial: https://www.youtube.com/watch?v=Q-__8Xw9KTM&ab_channel=TechWithTim. I'm at around 51:41 of the video. Thank you for the advice as always.
Just test that self.ship_img is not None, before drawing the object:
class Ship:
# [...]
def draw(self, window):
if self.ship_img != None:
window.blit(self.ship_img, (self.x, self.y))

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.

How to remove repetition between similar python classes

I currently have two classes: Block and Pic
Unfortunately they are almost identical. I'm looking for the best way to remove the repeated parts across the two classes.
class Block(pygame.sprite.Sprite):
def __init__(self, colour, width, height):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width, height])
self.image.fill(colour)
def invert(self):
self.image = inverted(self.image)
def set_speed(self, speed):
self.speed = speed
class Pic(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('icon.png').convert()
def invert(self):
self.image = inverted(self.image)
def set_speed(self, speed):
self.speed = speed
How about creating a common base class?
class BaseClass(pygame.sprite.Sprite):
'baseclass for your derived classes, feel free to choose a better name'
def __init__(self):
pygame.sprite.Sprite.__init__(self)
def invert(self):
self.image = inverted(self.image)
def set_speed(self, speed):
self.speed = speed
class Block(BaseClass):
def __init__(self, colour, width, height):
super(Block, self).__init__()
self.image = pygame.Surface([width, height])
self.image.fill(colour)
class Pic(BaseClass):
def __init__(self):
super(Pic, self).__init__()
self.image = pygame.image.load('icon.png').convert()
Use class inheritance, i.e. create a class with the common parts and class methods and derive your classes Block and Pic from this new class.
Some examples are given here: http://www.jesshamrick.com/2011/05/18/an-introduction-to-classes-and-inheritance-in-python/

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