e2D 1.4.22__tar.gz → 1.4.24__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: e2D
3
- Version: 1.4.22
3
+ Version: 1.4.24
4
4
  Summary: Python library for 2D games. Streamlines dev with keyboard/mouse input, vector calculations, color manipulation, and collision detection. Simplify game creation and unleash creativity!
5
5
  Home-page: https://github.com/marick-py/e2D
6
6
  Author: Riccardo Mariani
@@ -52,7 +52,6 @@ class Vector2D:
52
52
  def aspect_y(self, new_aspect) -> None:
53
53
  self.y = self.x * new_aspect
54
54
 
55
- @property
56
55
  def copy(self) -> "Vector2D":
57
56
  return Vector2D(self.x, self.y)
58
57
 
@@ -70,7 +69,7 @@ class Vector2D:
70
69
  @property
71
70
  def normalized(self) -> "Vector2D":
72
71
  if (mag:=self.length) == 0:
73
- return self.copy
72
+ return self.copy()
74
73
  return Vector2D(self.x / mag, self.y / mag)
75
74
 
76
75
  @property
@@ -471,47 +470,47 @@ class Vector2D:
471
470
  def down_left_norm(cls) -> "Vector2D": return V2down_left_norm
472
471
 
473
472
  @classmethod
474
- def new_zero(cls) -> "Vector2D": return V2zero.copy
473
+ def new_zero(cls) -> "Vector2D": return V2zero.copy()
475
474
  @classmethod
476
- def new_one(cls) -> "Vector2D": return V2one.copy
475
+ def new_one(cls) -> "Vector2D": return V2one.copy()
477
476
  @classmethod
478
- def new_two(cls) -> "Vector2D": return V2two.copy
477
+ def new_two(cls) -> "Vector2D": return V2two.copy()
479
478
  @classmethod
480
- def new_pi(cls) -> "Vector2D": return V2pi.copy
479
+ def new_pi(cls) -> "Vector2D": return V2pi.copy()
481
480
  @classmethod
482
- def new_inf(cls) -> "Vector2D": return V2inf.copy
481
+ def new_inf(cls) -> "Vector2D": return V2inf.copy()
483
482
  @classmethod
484
- def new_neg_one(cls) -> "Vector2D": return V2neg_one.copy
483
+ def new_neg_one(cls) -> "Vector2D": return V2neg_one.copy()
485
484
  @classmethod
486
- def new_neg_two(cls) -> "Vector2D": return V2neg_two.copy
485
+ def new_neg_two(cls) -> "Vector2D": return V2neg_two.copy()
487
486
  @classmethod
488
- def new_neg_pi(cls) -> "Vector2D": return V2neg_pi.copy
487
+ def new_neg_pi(cls) -> "Vector2D": return V2neg_pi.copy()
489
488
  @classmethod
490
- def new_neg_inf(cls) -> "Vector2D": return V2neg_inf.copy
489
+ def new_neg_inf(cls) -> "Vector2D": return V2neg_inf.copy()
491
490
  @classmethod
492
- def new_up(cls) -> "Vector2D": return V2up.copy
491
+ def new_up(cls) -> "Vector2D": return V2up.copy()
493
492
  @classmethod
494
- def new_right(cls) -> "Vector2D": return V2right.copy
493
+ def new_right(cls) -> "Vector2D": return V2right.copy()
495
494
  @classmethod
496
- def new_down(cls) -> "Vector2D": return V2down.copy
495
+ def new_down(cls) -> "Vector2D": return V2down.copy()
497
496
  @classmethod
498
- def new_left(cls) -> "Vector2D": return V2left.copy
497
+ def new_left(cls) -> "Vector2D": return V2left.copy()
499
498
  @classmethod
500
- def new_up_right(cls) -> "Vector2D": return V2up_right.copy
499
+ def new_up_right(cls) -> "Vector2D": return V2up_right.copy()
501
500
  @classmethod
502
- def new_down_right(cls) -> "Vector2D": return V2down_right.copy
501
+ def new_down_right(cls) -> "Vector2D": return V2down_right.copy()
503
502
  @classmethod
504
- def new_up_left(cls) -> "Vector2D": return V2up_left.copy
503
+ def new_up_left(cls) -> "Vector2D": return V2up_left.copy()
505
504
  @classmethod
506
- def new_down_left(cls) -> "Vector2D": return V2down_left.copy
505
+ def new_down_left(cls) -> "Vector2D": return V2down_left.copy()
507
506
  @classmethod
508
- def new_up_right_norm(cls) -> "Vector2D": return V2up_right_norm.copy
507
+ def new_up_right_norm(cls) -> "Vector2D": return V2up_right_norm.copy()
509
508
  @classmethod
510
- def new_down_right_norm(cls) -> "Vector2D": return V2down_right_norm.copy
509
+ def new_down_right_norm(cls) -> "Vector2D": return V2down_right_norm.copy()
511
510
  @classmethod
512
- def new_up_left_norm(cls) -> "Vector2D": return V2up_left_norm.copy
511
+ def new_up_left_norm(cls) -> "Vector2D": return V2up_left_norm.copy()
513
512
  @classmethod
514
- def new_down_left_norm(cls) -> "Vector2D": return V2down_left_norm.copy
513
+ def new_down_left_norm(cls) -> "Vector2D": return V2down_left_norm.copy()
515
514
 
516
515
 
517
516
  V2 = Vector2D
@@ -195,7 +195,6 @@ class Vector2D:
195
195
  @aspect_y.setter
196
196
  def aspect_y(self: "Vector2D", new_aspect) -> None: ...
197
197
 
198
- @property
199
198
  def copy(self:"Vector2D") -> "Vector2D":
200
199
  """
201
200
  # Create a copy of the current Vector2D other.
@@ -1,5 +1,5 @@
1
1
  from __future__ import annotations
2
- from typing import Literal
2
+ from typing import Literal, Optional
3
3
 
4
4
  from .utils import *
5
5
  import pygame as pg
@@ -63,7 +63,10 @@ class RootEnv:
63
63
  self.show_fps = show_fps
64
64
  self.events :list[pg.event.Event]= []
65
65
 
66
- self.__background_color__ :Color= BLACK_COLOR_PYG
66
+ # Track start time for accurate runtime calculation (excluding compilation time)
67
+ self.set_starting_timer()
68
+
69
+ self.__background_color__ :pg.Color= BLACK_COLOR_PYG
67
70
 
68
71
  self.clear_screen_each_frame = clear_screen_each_frame
69
72
  self.utils :dict[int|str, Util]= {}
@@ -73,9 +76,12 @@ class RootEnv:
73
76
  self.fps_label = Label(str(round(self.current_fps,2)), self.screen_size * .01, V2(250, 50), BLACK_COLOR_PYG, TRANSPARENT_COLOR_PYG, WHITE_COLOR_PYG, border_width=0, starting_hidden=(not self.show_fps), pivot_position="top_left", font=FONT_ARIAL_32)
74
77
  self.add_utils(self.fps_label)
75
78
 
76
- def init_rec(self, fps:int=30, path:str='output.mp4') -> None:
79
+ def set_starting_timer(self) -> None:
80
+ self.__start_time__ = pg.time.get_ticks()
81
+
82
+ def init_rec(self, fps:int=30, draw_on_screen:bool=True, path:str='output.mp4', font:pg.font.Font=FONT_MONOSPACE_16) -> None:
77
83
  from .winrec import WinRec
78
- self.__winrecorder__ = WinRec(self, fps=fps, path=path)
84
+ self.__winrecorder__ = WinRec(self, fps=fps, draw_on_screen=draw_on_screen, path=path, font=font)
79
85
 
80
86
  @property
81
87
  def background_color(self) -> Color:
@@ -97,24 +103,24 @@ class RootEnv:
97
103
  self.screen = pg.display.set_mode(self.__screen_size__(), vsync=self.__vsync__, flags=self.__flags__, display=self.__display_index__)
98
104
 
99
105
  @property
100
- def delta(self) -> int:
106
+ def delta(self) -> float:
101
107
  return self.__dt__
102
108
 
103
109
  def get_teoric_max_fps(self) -> float:
104
110
  rawdelta = self.clock.get_rawtime()
105
111
  return (1000 / rawdelta) if rawdelta != 0 else 1
106
112
 
107
- def update_screen_mode(self, vsync:None|bool=None, flags=None, display_index=None) -> None:
108
- self.__vsync__ = vsync
109
- self.__flags__ = flags
110
- self.__display_index__ = display_index
113
+ def update_screen_mode(self, vsync:Optional[bool]=None, flags:Optional[int]=None, display_index:Optional[int]=None) -> None:
114
+ if vsync is not None: self.__vsync__ = vsync
115
+ if flags is not None: self.__flags__ = flags
116
+ if display_index is not None: self.__display_index__ = display_index
111
117
  self.screen = pg.display.set_mode(self.__screen_size__(), vsync=self.__vsync__, flags=self.__flags__, display=self.__display_index__)
112
118
 
113
119
  def sleep(self, seconds:int|float, precise_delay=False) -> None:
114
120
  if precise_delay:
115
- pg.time.delay(seconds * 1000)
121
+ pg.time.delay(int(seconds * 1000))
116
122
  else:
117
- pg.time.wait(seconds * 1000)
123
+ pg.time.wait(int(seconds * 1000))
118
124
 
119
125
  def add_utils(self, *utils:Util) -> None:
120
126
  for util in utils:
@@ -126,16 +132,16 @@ class RootEnv:
126
132
 
127
133
  def remove_utils(self, *utils:int|str|Util) -> None:
128
134
  for uid in utils:
129
- if uid in self.utils:
130
- del self.utils[uid]
131
- elif isinstance(uid, Util):
135
+ if isinstance(uid, Util):
132
136
  del self.utils[uid.id]
137
+ elif uid in self.utils:
138
+ del self.utils[uid]
133
139
  else:
134
140
  raise Exception(f"Unknown util type: {uid}")
135
141
 
136
142
  def __new_util_id__(self) -> int:
137
143
  if not self.utils: return 0
138
- else: return max(self.utils.keys()) + 1
144
+ else: return len(self.utils.keys()) + 1
139
145
 
140
146
  def get_util(self, uid:int|str) -> Util|None:
141
147
  if isinstance(uid, Util):
@@ -147,7 +153,7 @@ class RootEnv:
147
153
 
148
154
  @property
149
155
  def runtime_seconds(self) -> float:
150
- return pg.time.get_ticks() / 1e3
156
+ return (pg.time.get_ticks() - self.__start_time__) / 1e3
151
157
 
152
158
  def init(self, sub_env:DefEnv) -> None:
153
159
  self.env = sub_env
@@ -170,18 +176,19 @@ class RootEnv:
170
176
  border_radius : int|list[int]|tuple[int,int,int,int] = -1,
171
177
  margin : Vector2D = Vector2D.zero(),
172
178
  personalized_surface : pg.Surface|None = None
173
- ) -> None:
179
+ ) -> Vector2D:
174
180
 
175
181
  text_box = font.render(text, True, color)
176
182
  size = Vector2D(*text_box.get_size()) + margin * 2
177
183
  pivotted_position = position - size * __PIVOT_POSITIONS_MULTIPLIER__[pivot_position] + margin
178
- if not any(isinstance(border_radius, cls) for cls in {tuple, list}): border_radius = [border_radius]*4
184
+ if not any(isinstance(border_radius, cls) for cls in {tuple, list}): border_radius = [border_radius] * 4
179
185
  surface = (self.screen if personalized_surface == None else personalized_surface)
180
186
  if bg_color != None:
181
187
  pg.draw.rect(surface, bg_color, (pivotted_position - margin)() + size(), 0, -1, *border_radius)
182
188
  if border_width:
183
189
  pg.draw.rect(surface, border_color, (pivotted_position - margin)() + size(), border_width, -1, *border_radius)
184
190
  surface.blit(text_box, pivotted_position())
191
+ return size
185
192
 
186
193
  def __draw__(self) -> None:
187
194
  self.__dt__ = self.clock.tick(self.target_fps) / 1000.0
@@ -193,6 +200,11 @@ class RootEnv:
193
200
  self.env.draw()
194
201
  for util in self.utils.values(): util.__draw__()
195
202
 
203
+ # Save frame first (clean), then draw info overlay (not saved)
204
+ if hasattr(self, "__winrecorder__"):
205
+ self.__winrecorder__.update() # Captures frame without stats
206
+ self.__winrecorder__.draw() # Draws stats on screen only
207
+
196
208
  pg.display.flip()
197
209
 
198
210
  def __update__(self) -> None:
@@ -201,10 +213,6 @@ class RootEnv:
201
213
  self.env.update()
202
214
  for util in self.utils.values(): util.__update__()
203
215
 
204
- if hasattr(self, "__winrecorder__"):
205
- self.__winrecorder__.update()
206
- self.__winrecorder__.draw()
207
-
208
216
  def frame(self) -> None:
209
217
  try:
210
218
  self.events = pg.event.get()
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
  from typing import Any, Callable, Literal
3
3
  import pygame as pg
4
- from e2D import *
5
- from e2D.colors import *
4
+ from e2D import * # type: ignore
5
+ from e2D.colors import * # type: ignore
6
6
 
7
7
  import math as _mt
8
8
 
@@ -18,6 +18,10 @@ def NEW_FONT(size, name:__LITERAL_FONTS__="arial", bold:bool=False, italic:bool=
18
18
  FONT_ARIAL_16 = NEW_FONT(16)
19
19
  FONT_ARIAL_32 = NEW_FONT(32)
20
20
  FONT_ARIAL_64 = NEW_FONT(64)
21
+ FONT_MONOSPACE_16 = NEW_FONT(16, "cascadiamonoregular")
22
+ FONT_MONOSPACE_32 = NEW_FONT(32, "cascadiamonoregular")
23
+ FONT_MONOSPACE_64 = NEW_FONT(64, "cascadiamonoregular")
24
+
21
25
 
22
26
 
23
27
  __LITERAL_PIVOT_POSITIONS__ = Literal["top_left", "top_center", "top_right", "center_left", "center_center", "center_right", "bottom_left", "bottom_center", "bottom_right"]
@@ -0,0 +1,211 @@
1
+ from typing import Optional, TYPE_CHECKING
2
+ from e2D import V2
3
+ from e2D.envs import RootEnv, FONT_MONOSPACE_16
4
+ from e2D.colors import BLACK_COLOR_PYG
5
+ import pygame as pg
6
+ import numpy as np
7
+ import cv2
8
+ import threading
9
+ import queue
10
+ import time
11
+
12
+ class WinRec:
13
+ def __init__(self, rootEnv:RootEnv, fps:int=30, draw_on_screen:bool=True, path:str='output.mp4', font:pg.font.Font=FONT_MONOSPACE_16) -> None:
14
+ self.rootEnv = rootEnv
15
+ self.path = path
16
+ self.fps = fps
17
+ self.draw_on_screen = draw_on_screen
18
+ self.font = font
19
+ self.is_recording = True # Recording state (pause/resume)
20
+ self.screenshot_counter = 0 # Counter for screenshot filenames
21
+ self.recording_frames = 0 # Frames actually recorded (excludes paused frames)
22
+ self.pause_start_time = None # Track when recording was paused
23
+ self.total_pause_duration = 0.0 # Cumulative pause time
24
+ size = self.rootEnv.screen_size
25
+ self.video_writer = cv2.VideoWriter(self.path, cv2.VideoWriter_fourcc(*'mp4v'), self.fps, size()) #type: ignore
26
+
27
+ # Pre-allocate buffers for zero-copy operations
28
+ self.frame_buffer = np.empty(shape=(int(size.y), int(size.x), 3), dtype=np.uint8)
29
+
30
+ # Setup async video writing
31
+ self.frame_queue = queue.Queue(maxsize=120) # Buffer up to 4 seconds at 30fps
32
+ self.running = True
33
+
34
+ # Statistics tracking
35
+ self.frames_written = 0
36
+ self.frames_dropped = 0
37
+ self.write_start_time = time.time()
38
+ self.last_stat_update = time.time()
39
+ self.current_write_fps = 0.0
40
+
41
+ self.write_thread = threading.Thread(target=self._write_worker, daemon=False)
42
+ self.write_thread.start()
43
+
44
+ def _write_worker(self) -> None:
45
+ """Background thread that writes frames to video file."""
46
+ while self.running or not self.frame_queue.empty():
47
+ try:
48
+ frame = self.frame_queue.get(timeout=0.1)
49
+ self.video_writer.write(frame)
50
+ self.frames_written += 1
51
+ self.frame_queue.task_done()
52
+
53
+ # Update write FPS every second
54
+ current_time = time.time()
55
+ if current_time - self.last_stat_update >= 1.0:
56
+ elapsed = current_time - self.write_start_time
57
+ self.current_write_fps = self.frames_written / elapsed if elapsed > 0 else 0
58
+ self.last_stat_update = current_time
59
+ except queue.Empty:
60
+ continue
61
+
62
+ def handle_input(self) -> None:
63
+ """Handle recording control keyboard inputs (F9-F12)."""
64
+ # F9: Toggle pause/resume recording
65
+ if self.rootEnv.keyboard.get_key(pg.K_F9, "just_pressed"):
66
+ self.toggle_recording()
67
+ status = "REC" if self.is_recording else "PAUSED"
68
+ print(f"[Recording] {status}")
69
+
70
+ # F10: Restart recording (reset all and resume)
71
+ if self.rootEnv.keyboard.get_key(pg.K_F10, "just_pressed"):
72
+ self.restart()
73
+ print("[Recording] Restarted (buffer cleared, timers reset)")
74
+
75
+ # F12: Take screenshot
76
+ if self.rootEnv.keyboard.get_key(pg.K_F12, "just_pressed"):
77
+ screenshot_path = self.take_screenshot()
78
+ print(f"[Screenshot] Saved: {screenshot_path}")
79
+
80
+ def update(self) -> None:
81
+ # Handle keyboard input first
82
+ self.handle_input()
83
+
84
+ # Skip frame capture if recording is paused
85
+ if not self.is_recording:
86
+ return
87
+
88
+ # Increment recording frame counter
89
+ self.recording_frames += 1
90
+
91
+ # Use pixels3d for zero-copy view, then transpose (creates view, not copy)
92
+ pixels = pg.surfarray.pixels3d(self.rootEnv.screen)
93
+ transposed = np.transpose(pixels, (1, 0, 2))
94
+
95
+ # Convert color in-place to pre-allocated buffer
96
+ cv2.cvtColor(transposed, cv2.COLOR_RGB2BGR, dst=self.frame_buffer)
97
+
98
+ # Queue frame copy for async writing (non-blocking)
99
+ try:
100
+ self.frame_queue.put_nowait(self.frame_buffer.copy())
101
+ except queue.Full:
102
+ self.frames_dropped += 1 # Track dropped frames
103
+
104
+ def get_rec_seconds(self) -> float:
105
+ """Get recorded time in seconds (excludes paused time)."""
106
+ return self.recording_frames / self.fps
107
+
108
+ def draw(self) -> None:
109
+ # Calculate statistics
110
+ buffer_size = self.frame_queue.qsize()
111
+ buffer_percent = (buffer_size / self.frame_queue.maxsize) * 100
112
+ buffer_seconds = buffer_size / self.fps if self.fps > 0 else 0
113
+
114
+ # Estimate optimal buffer size based on write performance
115
+ if self.current_write_fps > 0 and self.fps > 0:
116
+ write_lag = self.fps / self.current_write_fps
117
+ estimated_buffer = int(self.fps * 2 * write_lag) # 2 seconds of lag compensation
118
+ else:
119
+ estimated_buffer = self.frame_queue.maxsize
120
+
121
+ # Recording state indicator
122
+ rec_status = "REC" if self.is_recording else "PAUSED"
123
+
124
+ # Format with fixed width for stable display (monospace-friendly)
125
+ row1 = (f"[{rec_status}] RecFrames:{self.recording_frames:>6} | "
126
+ f"RecTime:{self.get_rec_seconds():>6.2f}s | "
127
+ f"AppTime:{self.rootEnv.runtime_seconds:>6.2f}s")
128
+ row2 = (f"Buffer:{buffer_size:>3}/{self.frame_queue.maxsize:<3} ({buffer_percent:>5.1f}%, {buffer_seconds:>4.1f}s) | "
129
+ f"WriteFPS:{self.current_write_fps:>5.1f}")
130
+ row3 = (f"Written:{self.frames_written:>6} | Dropped:{self.frames_dropped:>4} | OptBuf:{estimated_buffer:>3}")
131
+ row4 = "[F9]Pause/Resume [F10]Restart [F12]Screenshot"
132
+
133
+ if self.draw_on_screen:
134
+ size = self.rootEnv.print(row4, self.rootEnv.screen_size - V2(16, 16), pivot_position='bottom_right', font=self.font, margin=V2(10, 10), bg_color=BLACK_COLOR_PYG, border_radius=10)
135
+ self.rootEnv.print(row3, self.rootEnv.screen_size - V2(16, 16 + size.y), pivot_position='bottom_right', font=self.font, margin=V2(10, 10), bg_color=BLACK_COLOR_PYG, border_radius=10)
136
+ self.rootEnv.print(row2, self.rootEnv.screen_size - V2(16, 16 + size.y * 2), pivot_position='bottom_right', font=self.font, margin=V2(10, 10), bg_color=BLACK_COLOR_PYG, border_radius=10)
137
+ self.rootEnv.print(row1, self.rootEnv.screen_size - V2(16, 16 + size.y * 3), pivot_position='bottom_right', font=self.font, margin=V2(10, 10), bg_color=BLACK_COLOR_PYG, border_radius=10)
138
+
139
+ def pause(self) -> None:
140
+ """Pause recording (stop capturing frames)."""
141
+ if self.is_recording:
142
+ self.is_recording = False
143
+ self.pause_start_time = time.time()
144
+
145
+ def resume(self) -> None:
146
+ """Resume recording (continue capturing frames)."""
147
+ if not self.is_recording and self.pause_start_time is not None:
148
+ self.total_pause_duration += time.time() - self.pause_start_time
149
+ self.pause_start_time = None
150
+ self.is_recording = True
151
+
152
+ def toggle_recording(self) -> None:
153
+ """Toggle between pause and resume."""
154
+ self.is_recording = not self.is_recording
155
+
156
+ def restart(self) -> None:
157
+ """Restart recording: clear buffer, reset all counters and timers, resume recording."""
158
+ self.clear_buffer()
159
+ self.recording_frames = 0
160
+ self.total_pause_duration = 0.0
161
+ self.pause_start_time = None
162
+ self.is_recording = True
163
+
164
+ def clear_buffer(self) -> None:
165
+ """Clear the frame queue and reset write statistics."""
166
+ # Clear the queue
167
+ while not self.frame_queue.empty():
168
+ try:
169
+ self.frame_queue.get_nowait()
170
+ self.frame_queue.task_done()
171
+ except queue.Empty:
172
+ break
173
+
174
+ # Reset write counters (but keep recording frames)
175
+ self.frames_written = 0
176
+ self.frames_dropped = 0
177
+ self.write_start_time = time.time()
178
+ self.last_stat_update = time.time()
179
+ self.current_write_fps = 0.0
180
+
181
+ def take_screenshot(self, filename:Optional[str]=None) -> str:
182
+ """Save current screen as PNG screenshot.
183
+
184
+ Args:
185
+ filename: Optional custom filename. If None, auto-generates with counter.
186
+
187
+ Returns:
188
+ str: Path to saved screenshot
189
+ """
190
+ if filename is None:
191
+ # Auto-generate filename with counter
192
+ base_path = self.path.rsplit('.', 1)[0] # Remove .mp4 extension
193
+ filename = f"{base_path}_screenshot_{self.screenshot_counter:04d}.png"
194
+ self.screenshot_counter += 1
195
+
196
+ # Capture current screen
197
+ pixels = pg.surfarray.pixels3d(self.rootEnv.screen)
198
+ transposed = np.transpose(pixels, (1, 0, 2))
199
+ screenshot_buffer = np.empty(shape=(int(self.rootEnv.screen_size.y), int(self.rootEnv.screen_size.x), 3), dtype=np.uint8)
200
+ cv2.cvtColor(transposed, cv2.COLOR_RGB2BGR, dst=screenshot_buffer)
201
+
202
+ # Save as PNG
203
+ cv2.imwrite(filename, screenshot_buffer)
204
+ return filename
205
+
206
+ def quit(self) -> None:
207
+ # Stop accepting new frames and wait for queue to flush
208
+ self.running = False
209
+ self.frame_queue.join() # Wait for all queued frames to be written
210
+ self.write_thread.join(timeout=5.0) # Wait for thread to finish
211
+ self.video_writer.release()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: e2D
3
- Version: 1.4.22
3
+ Version: 1.4.24
4
4
  Summary: Python library for 2D games. Streamlines dev with keyboard/mouse input, vector calculations, color manipulation, and collision detection. Simplify game creation and unleash creativity!
5
5
  Home-page: https://github.com/marick-py/e2D
6
6
  Author: Riccardo Mariani
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = e2D
3
- version = 1.4.22
3
+ version = 1.4.24
4
4
  author = Riccardo Mariani
5
5
  author_email = ricomari2006@gmail.com
6
6
  description = Python library for 2D games. Streamlines dev with keyboard/mouse input, vector calculations, color manipulation, and collision detection. Simplify game creation and unleash creativity!
e2d-1.4.22/e2D/winrec.py DELETED
@@ -1,27 +0,0 @@
1
- from e2D.envs import *
2
- import pygame as pg
3
- import numpy as np
4
- import cv2
5
-
6
- class WinRec:
7
- def __init__(self, rootEnv:RootEnv, fps:int=30, path:str='output.mp4') -> None:
8
- self.rootEnv = rootEnv
9
- self.path = path
10
- self.fps = fps
11
- self.video_writer = cv2.VideoWriter(self.path, cv2.VideoWriter_fourcc(*'mp4v'), self.fps, self.rootEnv.screen_size()) #type: ignore
12
-
13
- def update(self) -> None:
14
- frame = cv2.cvtColor(np.swapaxes(pg.surfarray.array3d(self.rootEnv.screen), 0, 1), cv2.COLOR_RGB2BGR)
15
- self.video_writer.write(frame)
16
-
17
- def get_rec_seconds(self) -> float:
18
- return self.rootEnv.current_frame/self.fps
19
-
20
- def draw(self, draw_on_screen=False) -> None:
21
- text = f"[cfps:{self.rootEnv.current_frame} || realtime:{round(self.get_rec_seconds(),2)} || apptime:{round(self.rootEnv.runtime_seconds,2)}]"
22
- pg.display.set_caption(text)
23
- if draw_on_screen: self.rootEnv.print(text, self.rootEnv.screen_size, pivot_position='bottom_right')
24
-
25
- def quit(self) -> None:
26
- self.video_writer.release()
27
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes