Skip to content

Commit 1ba41a9

Browse files
committed
Refactor screen and camera classes
1 parent 2a99c17 commit 1ba41a9

File tree

4 files changed

+47
-77
lines changed

4 files changed

+47
-77
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ test:
88
coverage report
99

1010
tox:
11-
tox -e py39
11+
tox -e py310
1212

1313
lint:
1414
python -m flake8

pysurvive/game/core.py

+36-70
Original file line numberDiff line numberDiff line change
@@ -5,109 +5,75 @@
55
from pysurvive.config import SCREEN_RECT
66

77

8-
class Screen:
8+
class Singleton(type):
9+
10+
_instances = {}
11+
12+
def __call__(cls, *args, **kwargs):
13+
if cls not in cls._instances:
14+
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
15+
16+
print(cls._instances[cls])
17+
return cls._instances[cls]
18+
19+
20+
class Screen(pg.rect.Rect, metaclass=Singleton):
921

1022
"""
1123
Class that represent the screen and is used to detect
1224
wheather objects are visible on the screen.
1325
"""
1426

15-
_screen = None
16-
17-
def __new__(cls, rect: pg.rect.Rect = None):
18-
if cls._screen is None:
19-
cls._screen = super().__new__(cls)
20-
if rect:
21-
cls._screen.image = pg.Surface(rect.size)
22-
cls._screen.rect = rect
23-
else:
24-
cls._screen.image = pg.Surface(SCREEN_RECT.size)
25-
cls._screen.rect = SCREEN_RECT
26-
27-
return cls._screen
28-
2927
def __str__(self) -> str:
3028
return f"Screen (size={self.width}x{self.height})"
3129

3230
@classmethod
33-
def delete(cls):
34-
cls._screen = None
35-
36-
@property
37-
def size(self) -> tuple[int, int]:
38-
return self.rect.size
39-
40-
@property
41-
def width(self) -> int:
42-
return self.rect.width
31+
def delete(cls) -> None:
32+
"""Unset singleton."""
33+
cls._instances = {}
4334

4435
@property
45-
def height(self) -> int:
46-
return self.rect.height
36+
def rect(self) -> pg.rect.Rect:
37+
"""The function spritecollide expects a property rect."""
38+
return self
4739

48-
@property
49-
def centerx(self) -> int:
50-
return self.rect.centerx
51-
52-
@property
53-
def centery(self) -> int:
54-
return self.rect.centery
5540

56-
57-
class Camera:
41+
class Camera(metaclass=Singleton):
5842

5943
"""
60-
Class that represents the absolute position of the camera (player)
61-
in the game world.
44+
Class that represents the absolute position of the
45+
camera (player) in the game world.
6246
"""
6347

64-
_camera = None
65-
66-
def __new__(cls, centerx: int = 0, centery: int = 0):
67-
if cls._camera is None:
68-
cls._camera = super().__new__(cls)
69-
cls._camera.screen = Screen()
70-
cls._camera.x = centerx - cls._camera.screen.centerx
71-
cls._camera.y = centery - cls._camera.screen.centery
72-
73-
return cls._camera
48+
def __init__(self, x: int = 0, y: int = 0) -> None:
49+
self.screen = Screen(SCREEN_RECT)
50+
self.x = x
51+
self.y = y
7452

7553
def __str__(self) -> str:
76-
return f"Camera (x={self.x}, y={self.y}, centerx={self.centerx}, centery={self.centery})"
54+
return f"Camera (x={self.x}, y={self.y})"
7755

7856
@classmethod
79-
def delete(cls):
80-
cls._camera = None
57+
def delete(cls) -> None:
58+
"""Unset singleton."""
59+
cls._instances = {}
8160

8261
@property
8362
def position(self) -> tuple[int, int]:
63+
"""Returns camera position in a tuple."""
8464
return (self.x, self.y)
8565

86-
@position.setter
87-
def position(self, delta: tuple[int, int]) -> None:
66+
def move(self, delta: tuple[int, int]) -> None:
67+
"""Move the camera by delta x, y."""
8868
self.x = round(self.x - delta[0])
8969
self.y = round(self.y - delta[1])
9070

91-
@property
92-
def centerx(self) -> int:
93-
"""Returns the center cornor x position."""
94-
return self.x + self.screen.centerx
95-
96-
@property
97-
def centery(self) -> int:
98-
"""Returns the center cornor y position."""
99-
return self.y + self.screen.centery
100-
101-
@property
102-
def position_center(self) -> tuple[int, int]:
103-
return (self.centerx, self.centery)
104-
10571
def get_relative_position(self, x: int, y: int) -> tuple[int, int]:
10672
"""
10773
Returns the relative position of a coordinate in relation
108-
to the camera position.
74+
to the global camera position in the center of the screen.
10975
"""
11076
return (
111-
round(x - self.x),
112-
round(y - self.y),
77+
round(x - self.x + self.screen.centerx),
78+
round(y - self.y + self.screen.centery),
11379
)

tests/test_camera.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,24 @@ def test_camera__singleton_without_args(self, singleton, delete):
2525
camera_1 = singleton()
2626
camera_2 = singleton()
2727
assert id(camera_1) == id(camera_2)
28+
assert camera_1.position == (0, 0)
29+
assert camera_2.position == (0, 0)
2830
delete()
2931

3032
def test_camera__singleton_with_args_1(self, singleton, delete):
3133
"""Test the camera singleton with arguments."""
3234
camera_1 = singleton(500, 300)
3335
camera_2 = singleton(100, 200)
3436
assert id(camera_1) == id(camera_2)
35-
assert camera_1.position_center == (500, 300)
36-
assert camera_2.position_center == (500, 300)
37+
assert camera_1.position == (500, 300)
38+
assert camera_2.position == (500, 300)
3739
delete()
3840

3941
def test_camera__singleton_with_args_2(self, singleton, delete):
4042
"""Test the camera singleton with arguments."""
4143
camera_1 = singleton(500, 300)
4244
camera_2 = singleton()
4345
assert id(camera_1) == id(camera_2)
44-
assert camera_1.position_center == (500, 300)
45-
assert camera_2.position_center == (500, 300)
46+
assert camera_1.position == (500, 300)
47+
assert camera_2.position == (500, 300)
4648
delete()

tests/test_screen.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#!/usr/bin/env python
22
# coding=utf-8
3+
from typing import Optional
4+
35
import pygame as pg
46
import pytest
57

@@ -10,7 +12,7 @@
1012
class TestScreen:
1113
@pytest.fixture()
1214
def singleton(self):
13-
def _wrapper(rect: pg.rect.Rect = None):
15+
def _wrapper(rect: Optional[pg.rect.Rect] = SCREEN_RECT):
1416
return Screen(rect)
1517

1618
return _wrapper
@@ -24,7 +26,7 @@ def _wrapper() -> None:
2426

2527
def test_screen__singleton_without_args(self, singleton, delete):
2628
"""Test the screen singleton without arguments."""
27-
screen_1 = singleton()
29+
screen_1 = singleton(SCREEN_RECT)
2830
screen_2 = singleton()
2931
assert id(screen_1) == id(screen_2)
3032
assert screen_1.width == SCREEN_RECT.width

0 commit comments

Comments
 (0)