فهرست منبع

Large scene feature added.

mdo 1 سال پیش
والد
کامیت
a4df42a1cf
4فایلهای تغییر یافته به همراه79 افزوده شده و 22 حذف شده
  1. 5 0
      data/02.yaml
  2. 40 13
      jogai/character.py
  3. 19 8
      jogai/scene.py
  4. 15 1
      jogai/videogame.py

+ 5 - 0
data/02.yaml

@@ -15,3 +15,8 @@ characters:
     speed: 5
     width: 150
     position: [400, 200]
+    keys:
+      forward: RIGHT
+      backward: LEFT
+      up: UP
+      down: DOWN

+ 40 - 13
jogai/character.py

@@ -1,4 +1,5 @@
 import os
+from typing import Type
 
 import pygame
 
@@ -46,6 +47,8 @@ class Character(pygame.sprite.Sprite):
         self._original_rect = None
         self._rect = None
         self.name = name
+        self.x = 0
+        self.y = 0
 
         super().__init__()
         self.load(image_filename)
@@ -75,14 +78,6 @@ class Character(pygame.sprite.Sprite):
     def width(self):
         return self._rect.width
 
-    @property
-    def x(self):
-        return self._rect.x
-
-    @property
-    def y(self):
-        return self._rect.y
-
     def _eval_predefined_function(
         self, function: str, arguments: str | list | dict
     ):
@@ -95,6 +90,39 @@ class Character(pygame.sprite.Sprite):
 
         eval(f'self.{function}({processed_arguments})')
 
+    def _update_rect_position(
+        self, vg_scene: Type['scene.Scene'], tracking: bool = False
+    ) -> int:
+        """
+        Calculate and update the position where the character must be painted.
+        Only one character can be tracked.
+
+        Parameters:
+            vg_scene: The scene information.
+            tracking: True if the character must be followed, False otherwise.
+        """
+        # TODO: check vg_scene type
+        if not isinstance(tracking, bool):
+            raise TypeError(_('The tracking parameter must be a boolean.'))
+
+        if tracking:
+            if self.x < settings.WINDOW_WIDTH // 2:
+                self._rect.x = self.x
+            elif (
+                settings.WINDOW_WIDTH // 2
+                < self.x
+                < vg_scene.width - settings.WINDOW_WIDTH
+            ):
+                self._rect.x = settings.WINDOW_WIDTH // 2
+            elif self.x > vg_scene.width - settings.WINDOW_WIDTH:
+                self._rect.x = settings.WINDOW_WIDTH * 3 / 2 - (
+                    -self.x % vg_scene.width
+                )
+            self._rect.y = self.y
+        else:
+            self._rect.x = self.x + vg_scene.x
+            self._rect.y = self.y
+
     def move(
         self,
         x: int | None = None,
@@ -129,10 +157,11 @@ class Character(pygame.sprite.Sprite):
 
         if not key or _get_keys()[utils.eval_key(key)]:
             if relative:
-                self._rect.move_ip(x, y)
+                self.x += x
+                self.y += y
             else:
-                self._rect.x = x
-                self._rect.y = y
+                self.x = x
+                self.y = y
 
     def move_to(self, x: int, y: int, key: str = ''):
         """
@@ -288,9 +317,7 @@ class Character(pygame.sprite.Sprite):
             ),
         )
         self.scale_factor = scale_factor
-        x, y = self._rect.x, self._rect.y
         self._rect = self._image.get_rect()
-        self.move_to(x, y)
 
     def set_height(self, height: int = 0):
         """

+ 19 - 8
jogai/scene.py

@@ -30,6 +30,18 @@ class Scene:
         if image_filename:
             self.change_image(image_filename)
 
+    @property
+    def height(self):
+        return self._rect.height
+
+    @property
+    def width(self):
+        return self._rect.width
+
+    @property
+    def x(self):
+        return self._rect.x
+
     def change_background(self, background: Tuple[int, int, int]):
         """
         Change the background color.
@@ -77,6 +89,8 @@ class Scene:
     def set_position(self, x: int):
         """
         Set the scene abscissa position.
+        The scene painted will never exceed the edges of the image.
+        The x coordinate is a non-positive number (scene is moving to the left).
 
         Parameters:
             x: coordinate in horizontal axis
@@ -88,11 +102,8 @@ class Scene:
         if not isinstance(x, int):
             raise TypeError(_('The x coordinate must be an integer.'))
 
-        if not (0 <= x <= self._rect.width - settings.WINDOW_WIDTH):
-            raise ValueError(
-                _(
-                    'The range of x coordinate is between 0 and the scene width.'
-                )
-            )
-
-        self._rect.x = -x
+        # Set x in range to prevent the scene from being outside the window.
+        # The minus sign is essential to correctly position the scene.
+        self._rect.x = -min(
+            max(0, x), self._rect.width - settings.WINDOW_WIDTH
+        )

+ 15 - 1
jogai/videogame.py

@@ -185,6 +185,13 @@ def get_color(x: int, y: int) -> Tuple[int, int, int]:
 
 
 def paint(sprite: Type['scene.Scene'] | Type['character.Character']):
+    """
+    Paint both a scene and a character.
+
+    Parameters:
+        sprite: The element to be painted.
+    """
+    # TODO: check sprite type
     if hasattr(sprite, 'background'):
         _window.fill(sprite.background)
     if sprite._image and sprite._rect:
@@ -215,6 +222,9 @@ def prepare(data_filename: str = ''):
 
 
 def stop():
+    """
+    Finish the infinite videogame loop.
+    """
     global running
     running = False
 
@@ -225,9 +235,13 @@ def update():
     """
     global _clock, _window, vg_scene, vg_characters
     if vg_scene:
+        vg_scene.set_position(vg_characters[0].x)
         paint(vg_scene)
     if vg_characters:
-        for vg_character in vg_characters:
+        for pos, vg_character in enumerate(vg_characters):
+            vg_character._update_rect_position(
+                vg_scene, False if pos else True
+            )
             paint(vg_character)
             vg_character.update()
     pygame.display.flip()