Is there a way to call the only important arguments of a sprite class?

Question:

So I recently made a game in python where the player collects green dots. It’s unfinished, I’ll implement more into the game later.

But I have a problem. I want my sprite to move using the arrow keys, change the position of X and Y depending on which key is used and ignore the rest. But it gives me an error saying that I am missing other arguments. Is there a way around this?

Code:

import pygame
import sys
import random


class Player(pygame.sprite.Sprite):
    def __init__(self, width, height, posX, posY, color):
        super().__init__()
        self.image = pygame.Surface([width, height])
        self.image.fill(color)
        self.rect = self.image.get_rect()
        self.rect.center = [posX, posY]
        self.eat = pygame.mixer.Sound("audio/Apple_Bite-Simon_Craggs-1683647397.mp3")

    def __call__(self, posX, posY):
        self.rect.center = [posX, posY]


class Target(pygame.sprite.Sprite):
    def __init__(self, width, height, posX, posY, color):
        super().__init__()
        self.image = pygame.Surface([width, height])
        self.image.fill(color)
        self.rect = self.image.get_rect()
        self.rect.center = [posX, posY]
        self.eat = pygame.mixer.Sound("audio/Apple_Bite-Simon_Craggs-1683647397.mp3")

    def eat(self):
        self.eat.play()
        pygame.sprite.spritecollide(player, target_group, True)


pygame.init()

screen_width = 600
screen_height = 400
screen = pygame.display.set_mode((screen_width, screen_height))

player = Player(50, 50, 300, 250, (0, 255, 127))
player_group = pygame.sprite.Group()
player_group.add(player)
player_group.update()

target_group = pygame.sprite.Group()
for target in range(6):
    target_sprite = Target(25, 25, random.randrange(15, screen_width), random.randrange(15, screen_height),
                           (127, 255, 0))
    target_group.add(target_sprite)


run = True
while run:
    screen.fill((0, 16, 16))
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
            sys.exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_DOWN:
                change_pos = Player(posX=-0, posY=+0.4)
            if event.key == pygame.K_UP:
                change_pos = Player(posX=-0, posY=-0.4)

    player_group.draw(screen)
    player_group.update()
    target_group.draw(screen)
    target_group.update()
    pygame.display.update()

It gives me this error FYI:

TypeError: __init__() missing 3 required positional arguments: 'width', 'height', and 'color'

Plus I am new, anyways thx.

Asked By: colappse

||

Answers:

The function call operator has to be performed on the instance object (player) rather than on the class (Player):

Player(posX=-0, posY=+0.4)

player(posX=-0, posY=+0.4)

If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object. round the coordinates and assign it to the location (e.g. .center) of the rectangle:

class Player(pygame.sprite.Sprite):
    def __init__(self, width, height, posX, posY, color):
        super().__init__()
        # [...]

        self.x = posX
        self.y = posY
        self.rect.center = round(self.x), round(self.y)

    def __call__(self, posX, posY):
        self.x += posX
        self.y += posY
        self.rect.center = round(self.x), round(self.y)

See also Pygame doesn’t let me use float for rect.move, but I need it.

The keyboard events (see pygame.event module) occur only once when the state of a key changes. The KEYDOWN event occurs once every time a key is pressed. KEYUP occurs once every time a key is released. Use the keyboard events for a single action or a step-by-step movement.

If you want to achieve a continuously movement, you have to use pygame.key.get_pressed(). pygame.key.get_pressed() returns a list with the state of each key. If a key is held down, the state for the key is True, otherwise False. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement:

run = True
while run:
    # [...]

    keys = pygame.key.get_pressed()
    if keys[pygame.K_DOWN]:
        player(posX=-0, posY=+0.4)
    if keys[pygame.K_UP]:
        player(posX=-0, posY=-0.4)

See also How can I make a sprite move when key is held down and How to get keyboard input in pygame?

Use pygame.time.Clock to control the frames per second and thus the game speed. The method tick() of a pygame.time.Clock object, delays the game in that way, that every iteration of the loop consumes the same period of time. See pygame.time.Clock.tick():

This method should be called once per frame.

run = True
clock = pygame.time.Clock()
while run:
    clock .tick(100)
  
    # [...]

See also Pygame clock and event loops.


Complete example:

import pygame
import sys
import random


class Player(pygame.sprite.Sprite):
    def __init__(self, width, height, posX, posY, color):
        super().__init__()
        self.image = pygame.Surface([width, height])
        self.image.fill(color)
        self.rect = self.image.get_rect()
        self.x = posX
        self.y = posY
        self.rect.center = round(self.x), round(self.y)
        self.eat = pygame.mixer.Sound("audio/Apple_Bite-Simon_Craggs-1683647397.mp3")

    def __call__(self, posX, posY):
        self.x += posX
        self.y += posY
        self.rect.center = round(self.x), round(self.y)

class Target(pygame.sprite.Sprite):
    def __init__(self, width, height, posX, posY, color):
        super().__init__()
        self.image = pygame.Surface([width, height])
        self.image.fill(color)
        self.rect = self.image.get_rect()
        self.rect.center = [posX, posY]
        self.eat = pygame.mixer.Sound("audio/Apple_Bite-Simon_Craggs-1683647397.mp3")

    def eat(self):
        self.eat.play()
        pygame.sprite.spritecollide(player, target_group, True)


pygame.init()

screen_width = 600
screen_height = 400
screen = pygame.display.set_mode((screen_width, screen_height))

player = Player(50, 50, 300, 250, (0, 255, 127))
player_group = pygame.sprite.Group()
player_group.add(player)
player_group.update()

target_group = pygame.sprite.Group()
for target in range(6):
    target_sprite = Target(25, 25, random.randrange(15, screen_width), random.randrange(15, screen_height),
                           (127, 255, 0))
    target_group.add(target_sprite)


run = True
clock = pygame.time.Clock()
while run:
    clock .tick(100)
    screen.fill((0, 16, 16))
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
            sys.exit()

    keys = pygame.key.get_pressed()
    if keys[pygame.K_DOWN]:
        player(posX=-0, posY=+1)
    if keys[pygame.K_UP]:
        player(posX=-0, posY=-1)

    player_group.draw(screen)
    player_group.update()
    target_group.draw(screen)
    target_group.update()
    pygame.display.update()
Answered By: Rabbid76
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.