| @@ -1,13 +1,15 @@ | |||||
| from __future__ import annotations | from __future__ import annotations | ||||
| import math | |||||
| import sys | import sys | ||||
| from datetime import datetime | |||||
| from datetime import datetime, timedelta | |||||
| from enum import Enum, auto | from enum import Enum, auto | ||||
| from typing import Callable, TypedDict | from typing import Callable, TypedDict | ||||
| import cv2 | import cv2 | ||||
| import pygame | import pygame | ||||
| from cv2 import VideoCapture | from cv2 import VideoCapture | ||||
| from ephem import Moon, Observer, Sun # type: ignore | |||||
| from pygame import Rect, Surface | from pygame import Rect, Surface | ||||
| from pygame.font import Font | from pygame.font import Font | ||||
| from pygame.mixer import Sound | from pygame.mixer import Sound | ||||
| @@ -23,14 +25,15 @@ SYSTEM_FONT = pygame.font.SysFont ('notosanscjkjp', 24, bold = True) | |||||
| USER_FONT = pygame.font.SysFont ('notosanscjkjp', 32, italic = True) | USER_FONT = pygame.font.SysFont ('notosanscjkjp', 32, italic = True) | ||||
| DEERJIKA_FONT = pygame.font.SysFont ('07nikumarufont', 50) | DEERJIKA_FONT = pygame.font.SysFont ('07nikumarufont', 50) | ||||
| def main ( | def main ( | ||||
| ) -> None: | ) -> None: | ||||
| game = Game () | game = Game () | ||||
| bg = Bg (game) | bg = Bg (game) | ||||
| KitaSun (game) | |||||
| deerjika = Deerjika (game, DeerjikaPattern.RELAXED, | deerjika = Deerjika (game, DeerjikaPattern.RELAXED, | ||||
| x = CWindow.WIDTH * 3 / 4, y = CWindow.HEIGHT - 120) | x = CWindow.WIDTH * 3 / 4, y = CWindow.HEIGHT - 120) | ||||
| balloon = Balloon (game) | balloon = Balloon (game) | ||||
| balloon.talk ('あいうえおかきくけこさしすせそたちつてとなにぬねの', 'あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやいゆえよらりるれろわゐうゑを') | |||||
| CurrentTime (game, SYSTEM_FONT) | CurrentTime (game, SYSTEM_FONT) | ||||
| Sound ('assets/bgm.mp3').play (loops = -1) | Sound ('assets/bgm.mp3').play (loops = -1) | ||||
| while True: | while True: | ||||
| @@ -42,21 +45,49 @@ def main ( | |||||
| class DeerjikaPattern (Enum): | class DeerjikaPattern (Enum): | ||||
| """ | |||||
| ニジカの状態 | |||||
| Members: | |||||
| NORMAL: 通常 | |||||
| RELAXED: 足パタパタ | |||||
| SLEEPING: 寝ニジカ | |||||
| """ | |||||
| NORMAL = auto () | NORMAL = auto () | ||||
| RELAXED = auto () | RELAXED = auto () | ||||
| SLEEPING = auto () | SLEEPING = auto () | ||||
| class Direction (Enum): | class Direction (Enum): | ||||
| """ | |||||
| クリーチャの向き | |||||
| Members: | |||||
| LEFT: 左向き | |||||
| RIGHT: 右向き | |||||
| """ | |||||
| LEFT = auto () | LEFT = auto () | ||||
| RIGHT = auto () | RIGHT = auto () | ||||
| class Game: | class Game: | ||||
| """ | |||||
| ゲーム・クラス | |||||
| Attributes: | |||||
| clock (Clock): Clock オブゼクト | |||||
| frame (int): フレーム・カウンタ | |||||
| redrawers (list[Redrawer]): 再描画するクラスのリスト | |||||
| screen (Surface): 基底スクリーン | |||||
| """ | |||||
| clock: Clock | clock: Clock | ||||
| frame: int | frame: int | ||||
| redrawers: list[Redrawer] | redrawers: list[Redrawer] | ||||
| screen: Surface | screen: Surface | ||||
| sky: Sky | |||||
| def __init__ ( | def __init__ ( | ||||
| self, | self, | ||||
| @@ -65,6 +96,7 @@ class Game: | |||||
| self.clock = Clock () | self.clock = Clock () | ||||
| self.frame = 0 | self.frame = 0 | ||||
| self.redrawers = [] | self.redrawers = [] | ||||
| self._create_sky () | |||||
| def redraw ( | def redraw ( | ||||
| self, | self, | ||||
| @@ -76,19 +108,46 @@ class Game: | |||||
| self.clock.tick (FPS) | self.clock.tick (FPS) | ||||
| def _create_sky ( | |||||
| self, | |||||
| ) -> None: | |||||
| self.sky = Sky () | |||||
| self.sky.observer = Observer () | |||||
| self.sky.observer.lat = '35' | |||||
| self.sky.observer.lon = '139' | |||||
| class GameObject: | class GameObject: | ||||
| """ | |||||
| 各ゲーム・オブゼクトの基底クラス | |||||
| Attributes: | |||||
| arg (float): 回転角度 (rad) | |||||
| ax (float): X 軸に対する加速度 (px/frame^2) | |||||
| ay (float): y 軸に対する加速度 (px/frame^2) | |||||
| enabled (bool): オブゼクトの表示可否 | |||||
| frame (int): フレーム・カウンタ | |||||
| game (Game): ゲーム基盤 | |||||
| height (int): 高さ (px) | |||||
| vx (float): x 軸に対する速度 (px/frame) | |||||
| vy (float): y 軸に対する速度 (px/frame) | |||||
| width (int): 幅 (px) | |||||
| x (float): X 座標 (px) | |||||
| y (float): Y 座標 (px) | |||||
| """ | |||||
| arg: float = 0 | |||||
| ax: float = 0 | |||||
| ay: float = 0 | |||||
| enabled: bool = True | |||||
| frame: int | frame: int | ||||
| game: Game | game: Game | ||||
| width: int | |||||
| height: int | height: int | ||||
| x: float | |||||
| y: float | |||||
| vx: float = 0 | vx: float = 0 | ||||
| vy: float = 0 | vy: float = 0 | ||||
| ax: float = 0 | |||||
| ay: float = 0 | |||||
| arg: float = 0 | |||||
| enabled: bool = True | |||||
| width: int | |||||
| x: float | |||||
| y: float | |||||
| def __init__ ( | def __init__ ( | ||||
| self, | self, | ||||
| @@ -121,6 +180,13 @@ class GameObject: | |||||
| class Bg (GameObject): | class Bg (GameObject): | ||||
| """ | |||||
| 背景 | |||||
| Attributes: | |||||
| surface (Surface): 背景 Surface | |||||
| """ | |||||
| surface: Surface | surface: Surface | ||||
| def __init__ ( | def __init__ ( | ||||
| @@ -139,11 +205,20 @@ class Bg (GameObject): | |||||
| class Deerjika (GameObject): | class Deerjika (GameObject): | ||||
| surfaces: list[Surface] | |||||
| size: int | |||||
| width: int | |||||
| """ | |||||
| 伊地知ニジカ | |||||
| Attributes: | |||||
| height (int): 高さ (px) | |||||
| scale (float): 拡大率 | |||||
| surfaces (list[Surface]): ニジカの各フレームを Surface にしたリスト | |||||
| width (int): 幅 (px) | |||||
| """ | |||||
| height: int | height: int | ||||
| scale: float = .8 | scale: float = .8 | ||||
| surfaces: list[Surface] | |||||
| width: int | |||||
| def __init__ ( | def __init__ ( | ||||
| self, | self, | ||||
| @@ -172,18 +247,24 @@ class Deerjika (GameObject): | |||||
| surface.subsurface (x, 0, self.width, self.height)) | surface.subsurface (x, 0, self.width, self.height)) | ||||
| case Direction.RIGHT: | case Direction.RIGHT: | ||||
| ... | ... | ||||
| self.size = len (self.surfaces) | |||||
| def redraw ( | def redraw ( | ||||
| self, | self, | ||||
| ) -> None: | ) -> None: | ||||
| surface = pygame.transform.scale (self.surfaces[self.frame % self.size], | |||||
| surface = pygame.transform.scale (self.surfaces[self.frame % len (self.surfaces)], | |||||
| (self.width * self.scale, self.height * self.scale)) | (self.width * self.scale, self.height * self.scale)) | ||||
| self.game.screen.blit (surface, surface.get_rect (center = (self.x, self.y))) | self.game.screen.blit (surface, surface.get_rect (center = (self.x, self.y))) | ||||
| super ().redraw () | super ().redraw () | ||||
| class CurrentTime (GameObject): | class CurrentTime (GameObject): | ||||
| """ | |||||
| 現在日時表示 | |||||
| Attributes: | |||||
| font (Font): フォント | |||||
| """ | |||||
| font: Font | font: Font | ||||
| def __init__ ( | def __init__ ( | ||||
| @@ -205,13 +286,26 @@ class CurrentTime (GameObject): | |||||
| class Balloon (GameObject): | class Balloon (GameObject): | ||||
| surface: Surface | |||||
| query: str = '' | |||||
| """ | |||||
| 吹出し | |||||
| Attributes: | |||||
| answer (str): 回答テキスト | |||||
| image_url (str, None): 画像 URL | |||||
| length (int): 表示する時間 (frame) | |||||
| query (str): 質問テキスト | |||||
| surface (Surface): 吹出し Surface | |||||
| x_flip (bool): 左右反転フラグ | |||||
| y_flip (bool): 上下反転フラグ | |||||
| """ | |||||
| answer: str = '' | answer: str = '' | ||||
| image_url: str | None = None | image_url: str | None = None | ||||
| length: int = 300 | |||||
| query: str = '' | |||||
| surface: Surface | |||||
| x_flip: bool = False | x_flip: bool = False | ||||
| y_flip: bool = False | y_flip: bool = False | ||||
| length: int = 300 | |||||
| def __init__ ( | def __init__ ( | ||||
| self, | self, | ||||
| @@ -259,20 +353,130 @@ class Balloon (GameObject): | |||||
| query: str, | query: str, | ||||
| answer: str, | answer: str, | ||||
| image_url: str | None = None, | image_url: str | None = None, | ||||
| length: int = 300, | |||||
| ) -> None: | ) -> None: | ||||
| self.query = query | self.query = query | ||||
| self.answer = answer | self.answer = answer | ||||
| self.image_url = image_url | self.image_url = image_url | ||||
| self.length = length | |||||
| self.frame = 0 | self.frame = 0 | ||||
| self.enabled = True | self.enabled = True | ||||
| class KitaSun (GameObject): | |||||
| """ | |||||
| き太く陽 | |||||
| Attributes: | |||||
| sun (Sun): ephem の太陽オブゼクト | |||||
| surface (Surface): き太く陽 Surface | |||||
| """ | |||||
| alt: float | |||||
| az: float | |||||
| sun: Sun | |||||
| surface: Surface | |||||
| def __init__ ( | |||||
| self, | |||||
| game: Game, | |||||
| ): | |||||
| super ().__init__ (game) | |||||
| self.surface = pygame.transform.scale (pygame.image.load ('assets/sun.png'), (200, 200)) | |||||
| self.sun = Sun () | |||||
| def redraw ( | |||||
| self, | |||||
| ) -> None: | |||||
| self.game.sky.observer.date = datetime.now () - timedelta (hours = 9) | |||||
| self.sun.compute (self.game.sky.observer) | |||||
| self.alt = self.sun.alt | |||||
| self.az = self.sun.az | |||||
| print (self.x, self.y) | |||||
| self.game.screen.blit ( | |||||
| pygame.transform.rotate (self.surface, -(90 + math.degrees (self.arg))), | |||||
| (self.x, self.y)) | |||||
| super ().redraw () | |||||
| self.x = self.new_x | |||||
| self.y = self.new_y | |||||
| if abs (self.new_arg - self.arg) > math.radians (3): | |||||
| self.arg = self.new_arg | |||||
| @property | |||||
| def new_x ( | |||||
| self, | |||||
| ) -> float: | |||||
| return CWindow.WIDTH * (math.degrees (self.az) - 80) / 120 | |||||
| @property | |||||
| def new_y ( | |||||
| self, | |||||
| ) -> float: | |||||
| return ((CWindow.HEIGHT / 2) | |||||
| - ((CWindow.HEIGHT / 2 + 100) * math.sin (self.alt) | |||||
| / math.sin (math.radians (60)))) | |||||
| @property | |||||
| def new_arg ( | |||||
| self, | |||||
| ) -> float: | |||||
| return math.atan2 (self.new_y - self.y, self.new_x - self.x) | |||||
| class Jojoko (GameObject): | |||||
| """ | |||||
| 大月ヨヨコ | |||||
| Attributes: | |||||
| phase (float): 月齢 | |||||
| """ | |||||
| phase: float | |||||
| def __init__ ( | |||||
| self, | |||||
| ): | |||||
| ... | |||||
| def redraw ( | |||||
| self, | |||||
| ) -> None: | |||||
| ... | |||||
| class Sky: | |||||
| """ | |||||
| 天体に関する情報を保持するクラス | |||||
| Attributes: | |||||
| observer (Observer): 観測値 | |||||
| """ | |||||
| observer: Observer | |||||
| class CWindow: | class CWindow: | ||||
| """ | |||||
| ウィンドゥに関する定数クラス | |||||
| Attributes: | |||||
| WIDTH (int): ウィンドゥ幅 | |||||
| HEIGHT (int): ウィンドゥ高さ | |||||
| """ | |||||
| WIDTH = 1024 | WIDTH = 1024 | ||||
| HEIGHT = 768 | HEIGHT = 768 | ||||
| class Redrawer (TypedDict): | class Redrawer (TypedDict): | ||||
| """ | |||||
| 再描画処理を行ふゲーム・オブゼクトとその優先順位のペア | |||||
| Attributes: | |||||
| layer (int): レイア | |||||
| obj (GameObject): ゲーム・オブゼクト | |||||
| """ | |||||
| layer: int | layer: int | ||||
| obj: GameObject | obj: GameObject | ||||