Skip to content

Commit 95fa66f

Browse files
authored
View these Examples (#2407)
* Updated Tetris example to use View and improved stone selection logic * Updated text localisation example to use View and removed unneccisary values from MyGame class * Updated Timer example to use View and GLOBAL_CLOCK * Convert template platformer to use a View, and greatly imrpove multiple aspects of the example. * Updated tiled map examples to use View, and corrected many mistakes with the examples * Updated tank example and fixed more glaring issues * Added aliases for window properties commonly used in views * Updated sprite enemies in platformer to use a View, and added ability to reset game. * Updated pymunk joint builder to use View and fixed assertion error * Updated the perspective example to use View, and Camera. Plus created two new grips for use in the example. * Update transform_multi example to not use custom window * Updated all standard examples to use a View over a custom Window * Renaming all `MyGame` instances to `GameVIew` * Linting, and testing pass * tank example cleanup * updating minimap rst * Updated view to store a per-instance background color rather than an alias to the window's.
1 parent 150af62 commit 95fa66f

File tree

162 files changed

+3056
-1763
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

162 files changed

+3056
-1763
lines changed

arcade/application.py

+48-1
Original file line numberDiff line numberDiff line change
@@ -1233,8 +1233,11 @@ class View:
12331233
window is used. (Normally you don't need to provide this).
12341234
"""
12351235

1236-
def __init__(self, window: Window | None = None) -> None:
1236+
def __init__(
1237+
self, window: Window | None = None, background_color: RGBOrA255 | None = None
1238+
) -> None:
12371239
self.window = arcade.get_window() if window is None else window
1240+
self.background_color: RGBOrA255 | None = background_color
12381241

12391242
def clear(
12401243
self,
@@ -1258,6 +1261,8 @@ def clear(
12581261
viewport (optional):
12591262
The viewport range to clear
12601263
"""
1264+
if color is None and color_normalized is None:
1265+
color = self.background_color
12611266
self.window.clear(color=color, color_normalized=color_normalized, viewport=viewport)
12621267

12631268
def on_update(self, delta_time: float) -> bool | None:
@@ -1520,3 +1525,45 @@ def on_mouse_leave(self, x: int, y: int) -> bool | None:
15201525
y: The y position the mouse entered the window
15211526
"""
15221527
pass
1528+
1529+
@property
1530+
def size(self) -> tuple[float, float]:
1531+
"""
1532+
An alias for `arcade.Window.size`
1533+
"""
1534+
return self.window.size
1535+
1536+
@property
1537+
def width(self) -> float:
1538+
"""
1539+
An alias for `arcade.Window.width`
1540+
"""
1541+
return self.window.width
1542+
1543+
@property
1544+
def height(self) -> float:
1545+
"""
1546+
An alias for `arcade.Window.height`
1547+
"""
1548+
return self.window.height
1549+
1550+
@property
1551+
def center(self) -> tuple[float, float]:
1552+
"""
1553+
An alias for `arcade.Window.center`
1554+
"""
1555+
return self.window.center
1556+
1557+
@property
1558+
def center_x(self) -> float:
1559+
"""
1560+
An alias for `arcade.Window.center_x`
1561+
"""
1562+
return self.window.center_x
1563+
1564+
@property
1565+
def center_y(self) -> float:
1566+
"""
1567+
An alias for `arcade.Window.center_y`
1568+
"""
1569+
return self.window.center_y

arcade/camera/camera_2d.py

+47-10
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,15 @@ def unproject(self, screen_coordinate: Point) -> Vec3:
318318
_view = generate_view_matrix(self.view_data)
319319
return unproject_orthographic(screen_coordinate, self.viewport.viewport, _view, _projection)
320320

321+
def equalise(self) -> None:
322+
"""
323+
Forces the projection to match the size of the viewport.
324+
When matching the projection to the viewport the method keeps
325+
the projections center in the same relative place.
326+
"""
327+
x, y = self._projection_data.rect.x, self._projection_data.rect.y
328+
self._projection_data.rect = XYWH(x, y, self.viewport_width, self.viewport_height)
329+
321330
def match_screen(
322331
self,
323332
and_projection: bool = True,
@@ -350,6 +359,44 @@ def match_screen(
350359
aspect=aspect,
351360
)
352361

362+
def match_target(
363+
self,
364+
and_projection: bool = True,
365+
and_scissor: bool = True,
366+
and_position: bool = False,
367+
aspect: float | None = None,
368+
) -> None:
369+
"""
370+
Sets the viewport to the size of the Camera2D's render target.
371+
372+
Args:
373+
and_projection: Flag whether to also equalize the projection to the viewport.
374+
On by default
375+
and_scissor: Flag whether to also equalize the scissor box to the viewport.
376+
On by default
377+
and_position: Flag whether to also center the camera to the viewport.
378+
Off by default
379+
aspect_ratio: The ratio between width and height that the viewport should
380+
be constrained to. If unset then the viewport just matches the window
381+
size. The aspect ratio describes how much larger the width should be
382+
compared to the height. i.e. for an aspect ratio of ``4:3`` you should
383+
input ``4.0/3.0`` or ``1.33333...``. Cannot be equal to zero.
384+
Raises:
385+
ValueError: Will be raised if the Camera2D was has no render target.
386+
"""
387+
if self.render_target is None:
388+
raise ValueError(
389+
"Tried to match a non-exsistant render target. Please use `match_screen` instead"
390+
)
391+
392+
self.update_viewport(
393+
LRBT(*self.render_target.viewport),
394+
and_projection=and_projection,
395+
and_scissor=and_scissor,
396+
and_position=and_position,
397+
aspect=aspect,
398+
)
399+
353400
def update_viewport(
354401
self,
355402
new_viewport: Rect,
@@ -396,7 +443,6 @@ def update_viewport(
396443
self.position = self.viewport.center
397444

398445
def aabb(self) -> Rect:
399-
# TODO test
400446
"""
401447
Retrieve the axis-aligned bounds box of the camera's view area.
402448
If the camera isn't rotated , this will be precisely the view area,
@@ -820,15 +866,6 @@ def zoom(self, _zoom: float) -> None:
820866
"""
821867
self._camera_data.zoom = _zoom
822868

823-
def equalise(self) -> None:
824-
"""
825-
Forces the projection to match the size of the viewport.
826-
When matching the projection to the viewport the method keeps
827-
the projections center in the same relative place.
828-
"""
829-
x, y = self._projection_data.rect.x, self._projection_data.rect.y
830-
self._projection_data.rect = XYWH(x, y, self.viewport_width, self.viewport_height)
831-
832869
# top_left
833870
@property
834871
def top_left(self) -> Vec2:

arcade/camera/grips/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
constrain_boundary_xz,
2222
constrain_boundary_xyz,
2323
)
24+
from arcade.camera.grips.position import look_at, orbit
2425

2526

2627
__all__ = (
@@ -43,4 +44,6 @@
4344
"constrain_boundary_yz",
4445
"constrain_boundary_xz",
4546
"constrain_boundary_xyz",
47+
"look_at",
48+
"orbit",
4649
)

arcade/camera/grips/position.py

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from __future__ import annotations
2+
3+
from pyglet.math import Vec3
4+
5+
from arcade.camera import CameraData
6+
from arcade.math import quaternion_rotation
7+
from arcade.types import Point3
8+
9+
10+
def look_at(camera: CameraData, target: Point3, up: Point3 | None = None) -> tuple[Point3, Point3]:
11+
"""
12+
Calculate the neccisary forward and up vectors to have the camera look towards
13+
the target point.
14+
15+
Uses the camera's up if none is provided.
16+
Args:
17+
camera: The CameraData to work from
18+
target: The point in 3D world space to look at
19+
up: An optional up axis to refer to when calculating the true up vector.
20+
"""
21+
px, py, pz = camera.position
22+
tx, ty, tz = target
23+
dx, dy, dz = tx - px, ty - py, tz - pz
24+
25+
up = up or camera.up
26+
27+
f = Vec3(dx, dy, dz).normalize()
28+
r = f.cross(up)
29+
u = r.cross(f).normalize()
30+
31+
return f, u
32+
33+
34+
def orbit(camera: CameraData, origin: Point3, axis: Point3, angle: float) -> Point3:
35+
"""
36+
Find the new position for the camera when rotated around the origin
37+
38+
Args:
39+
camera: The CameraData to work from
40+
origin: The point around which to orbit
41+
axis: The axis to rotate around, like the stick on a spinning top.
42+
angle: The angle in degrees to rotate by
43+
"""
44+
px, py, pz = camera.position
45+
tx, ty, tz = origin
46+
47+
rx, ry, rz = quaternion_rotation(axis, (px - tx, py - ty, pz - tz), angle)
48+
return tx + rx, ty + ry, tz + rz

arcade/examples/array_backed_grid.py

+17-7
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,22 @@
2828
MARGIN = 5
2929

3030
# Do the math to figure out our screen dimensions
31-
SCREEN_WIDTH = (WIDTH + MARGIN) * COLUMN_COUNT + MARGIN
32-
SCREEN_HEIGHT = (HEIGHT + MARGIN) * ROW_COUNT + MARGIN
33-
SCREEN_TITLE = "Array Backed Grid Example"
31+
WINDOW_WIDTH = (WIDTH + MARGIN) * COLUMN_COUNT + MARGIN
32+
WINDOW_HEIGHT = (HEIGHT + MARGIN) * ROW_COUNT + MARGIN
33+
WINDOW_TITLE = "Array Backed Grid Example"
3434

3535

36-
class MyGame(arcade.Window):
36+
class GameView(arcade.View):
3737
"""
3838
Main application class.
3939
"""
4040

41-
def __init__(self, width, height, title):
41+
def __init__(self):
4242
"""
4343
Set up the application.
4444
"""
4545

46-
super().__init__(width, height, title)
46+
super().__init__()
4747

4848
# Create a 2 dimensional array. A two-dimensional
4949
# array is simply a list of lists.
@@ -104,7 +104,17 @@ def on_mouse_press(self, x, y, button, modifiers):
104104

105105

106106
def main():
107-
MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
107+
""" Main function """
108+
# Create a window class. This is what actually shows up on screen
109+
window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
110+
111+
# Create the GameView
112+
game = GameView()
113+
114+
# Show GameView on screen
115+
window.show_view(game)
116+
117+
# Start the arcade game loop
108118
arcade.run()
109119

110120

arcade/examples/array_backed_grid_buffered.py

+17-7
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,21 @@
2828
MARGIN = 5
2929

3030
# Do the math to figure out our screen dimensions
31-
SCREEN_WIDTH = (WIDTH + MARGIN) * COLUMN_COUNT + MARGIN
32-
SCREEN_HEIGHT = (HEIGHT + MARGIN) * ROW_COUNT + MARGIN
33-
SCREEN_TITLE = "Array Backed Grid Buffered Example"
31+
WINDOW_WIDTH = (WIDTH + MARGIN) * COLUMN_COUNT + MARGIN
32+
WINDOW_HEIGHT = (HEIGHT + MARGIN) * ROW_COUNT + MARGIN
33+
WINDOW_TITLE = "Array Backed Grid Buffered Example"
3434

3535

36-
class MyGame(arcade.Window):
36+
class GameView(arcade.View):
3737
"""
3838
Main application class.
3939
"""
4040

41-
def __init__(self, width, height, title):
41+
def __init__(self):
4242
"""
4343
Set up the application.
4444
"""
45-
super().__init__(width, height, title)
45+
super().__init__()
4646
self.shape_list = None
4747

4848
# Create a 2 dimensional array. A two dimensional
@@ -136,7 +136,17 @@ def on_mouse_press(self, x, y, button, modifiers):
136136

137137

138138
def main():
139-
MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
139+
""" Main function """
140+
# Create a window class. This is what actually shows up on screen
141+
window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
142+
143+
# Create the GameView
144+
game = GameView()
145+
146+
# Show GameView on screen
147+
window.show_view(game)
148+
149+
# Start the arcade game loop
140150
arcade.run()
141151

142152

arcade/examples/array_backed_grid_sprites_1.py

+17-7
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,21 @@
2727
MARGIN = 5
2828

2929
# Do the math to figure out our screen dimensions
30-
SCREEN_WIDTH = (WIDTH + MARGIN) * COLUMN_COUNT + MARGIN
31-
SCREEN_HEIGHT = (HEIGHT + MARGIN) * ROW_COUNT + MARGIN
32-
SCREEN_TITLE = "Array Backed Grid Example"
30+
WINDOW_WIDTH = (WIDTH + MARGIN) * COLUMN_COUNT + MARGIN
31+
WINDOW_HEIGHT = (HEIGHT + MARGIN) * ROW_COUNT + MARGIN
32+
WINDOW_TITLE = "Array Backed Grid Example"
3333

3434

35-
class MyGame(arcade.Window):
35+
class GameView(arcade.View):
3636
"""
3737
Main application class.
3838
"""
3939

40-
def __init__(self, width, height, title):
40+
def __init__(self):
4141
"""
4242
Set up the application.
4343
"""
44-
super().__init__(width, height, title)
44+
super().__init__()
4545

4646
# Create a 2 dimensional array. A two dimensional
4747
# array is simply a list of lists.
@@ -143,7 +143,17 @@ def on_mouse_press(self, x, y, button, modifiers):
143143

144144

145145
def main():
146-
MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
146+
""" Main function """
147+
# Create a window class. This is what actually shows up on screen
148+
window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
149+
150+
# Create the GameView
151+
game = GameView()
152+
153+
# Show GameView on screen
154+
window.show_view(game)
155+
156+
# Start the arcade game loop
147157
arcade.run()
148158

149159

0 commit comments

Comments
 (0)