vendredi 4 mars 2022

Design pattern recommendations for oop game

Hi, I'm making a simple oop game in python using pygame.

Currently, my game object classes have methods like move, draw and check collision. I would like to create a new class with the intention of handling these interactions in a way that my game object classes don't have to know about other game objects anymore

Here are the abstract classes for the game objects

import pygame
from abc import ABC, abstractmethod
import copy


class ABCObject(ABC):
def __init__(self, initial_x: int, initial_y: int, size: int, color: tuple):
    self._size = size
    self._rect = pygame.Rect(
        initial_x, initial_y, self._size, self._size)
    self._color = color

@property
def size(self):
    return self._size

@property
def rect(self):
    return self._rect

@property
def color(self):
    return self._color

@size.setter
def size(self, new_size: int):
    if isinstance(new_size, int):
        self._size = new_size

@color.setter
def color(self, new_color: tuple):
    if isinstance(new_color, tuple):
        if len(new_color) == 3 and isinstance(new_color[1], int) and isinstance(new_color[0], int) and isinstance(new_color[2], int):
            self._color = new_color

def draw(self, win, offset):
    fake_rect = copy.deepcopy(self._rect)
    fake_rect.topleft -= offset
    pygame.draw.rect(win, self._color, fake_rect)
from abc_object import ABCObject


class Kinetic_object(ABCObject, ABC):
    def __init__(self, initial_x: int, initial_y: int, size: int, color: tuple, speed: int):
        super().__init__(initial_x, initial_y, size, color)
        self._speed = speed
        self._velX = 0
        self._velY = 0

    @property
    def speed(self):
        return self._speed

    @property
    def velX(self):
        return self._velX

    @property
    def velY(self):
        return self._velY

    @velX.setter
    def velX(self, new_velX):
        if isinstance(new_velX, (int, float)):
            self._velX = new_velX

    @velY.setter
    def velY(self, new_velY):
        if isinstance(new_velY, (int, float)):
            self._velY = new_velY

    @speed.setter
    def speed(self, new_speed):
        if isinstance(new_speed, int):
            self._speed = new_speed

    @abstractmethod
    def move(self):
        pass

And here is my player class:

import math
import copy
from abc_command import Command
from abc_kinetic_object import Kinetic_object


class Player(Kinetic_object, Command):
    def __init__(self, initial_x: int, initial_y: int, size: int, speed: int, obstacles: list):
        Kinetic_object.__init__(
            self, initial_x, initial_y, size, (250, 160, 60),  speed)
        Command.__init__(
            self, {'up': False, 'down': False, 'right': False, 'left': False})
        # usar depois para mudar sprite
        self._facing_direction = 'down'
        self._obstacles = obstacles

    # usar para normalizar os vetores de velocidade, caso contrario, anda mais rápido nas diagonais
    def normalize(self):
        if self._velX != 0 and self._velY != 0:
            self._velX *= 1/math.sqrt(2)
            self._velY *= 1/math.sqrt(2)


    # Colisoes, funciona se nao colidir com objetos em movimento
    def move(self):
        self.normalize()
        self._rect.x += self._velX
        self.check_collisions('horizontal')
        self._rect.y += self._velY
        self.check_collisions('vertical')

    def check_collisions(self, direction):
        if direction == 'horizontal':
            for obstacle in self._obstacles:
                if obstacle.rect.colliderect(self._rect):
                    if self._velX > 0:  # direita
                        self._rect.right = obstacle.rect.left
                    elif self._velX < 0:  # esquerda
                        self._rect.left = obstacle.rect.right

        if direction == 'vertical':
            for obstacle in self._obstacles:
                if obstacle.rect.colliderect(self._rect):
                    if self._velY > 0:  # baixo
                        self._rect.bottom = obstacle.rect.top
                    elif self._velY < 0:  # cima
                        self._rect.top = obstacle.rect.bottom

    def change_facing_direction(self):
        # so apertando para direita
        if self._commands['right'] and not (self._commands['down'] or self._commands['up'] or self._commands['left']):
            self._facing_direction = 'right'
        # so apertando para esquerda
        elif self._commands['left'] and not (self._commands['down'] or self._commands['up'] or self._commands['right']):
            self._facing_direction = 'left'
        # so apertando para cima
        elif self._commands['up'] and not (self._commands['down'] or self._commands['right'] or self._commands['left']):
            self._facing_direction = 'up'
        # so apertando para baixo
        elif self._commands['down'] and not (self._commands['up'] or self._commands['left'] or self._commands['right']):
            self._facing_direction = 'down'

    def execute_commands(self):
        self.change_facing_direction()
        self._velX = 0
        self._velY = 0
        if self._commands['left'] and not self._commands['right']:
            self._velX = -self._speed
        if self._commands['right'] and not self._commands['left']:
            self._velX = self._speed
        if self._commands['up'] and not self._commands['down']:
            self._velY = -self._speed
        if self._commands['down'] and not self._commands['up']:
            self._velY = self._speed

I would like to know which design pattern would be appropriate in order to remove methods like "move" from game objects and implement them in other controller classes, I have different objects that all move in a different way.

Aucun commentaire:

Enregistrer un commentaire