Skip to content

Commit

Permalink
View these Examples (#2407)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
DragonMoffon authored Oct 13, 2024
1 parent 150af62 commit 95fa66f
Show file tree
Hide file tree
Showing 162 changed files with 3,056 additions and 1,763 deletions.
49 changes: 48 additions & 1 deletion arcade/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -1233,8 +1233,11 @@ class View:
window is used. (Normally you don't need to provide this).
"""

def __init__(self, window: Window | None = None) -> None:
def __init__(
self, window: Window | None = None, background_color: RGBOrA255 | None = None
) -> None:
self.window = arcade.get_window() if window is None else window
self.background_color: RGBOrA255 | None = background_color

def clear(
self,
Expand All @@ -1258,6 +1261,8 @@ def clear(
viewport (optional):
The viewport range to clear
"""
if color is None and color_normalized is None:
color = self.background_color
self.window.clear(color=color, color_normalized=color_normalized, viewport=viewport)

def on_update(self, delta_time: float) -> bool | None:
Expand Down Expand Up @@ -1520,3 +1525,45 @@ def on_mouse_leave(self, x: int, y: int) -> bool | None:
y: The y position the mouse entered the window
"""
pass

@property
def size(self) -> tuple[float, float]:
"""
An alias for `arcade.Window.size`
"""
return self.window.size

@property
def width(self) -> float:
"""
An alias for `arcade.Window.width`
"""
return self.window.width

@property
def height(self) -> float:
"""
An alias for `arcade.Window.height`
"""
return self.window.height

@property
def center(self) -> tuple[float, float]:
"""
An alias for `arcade.Window.center`
"""
return self.window.center

@property
def center_x(self) -> float:
"""
An alias for `arcade.Window.center_x`
"""
return self.window.center_x

@property
def center_y(self) -> float:
"""
An alias for `arcade.Window.center_y`
"""
return self.window.center_y
57 changes: 47 additions & 10 deletions arcade/camera/camera_2d.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,15 @@ def unproject(self, screen_coordinate: Point) -> Vec3:
_view = generate_view_matrix(self.view_data)
return unproject_orthographic(screen_coordinate, self.viewport.viewport, _view, _projection)

def equalise(self) -> None:
"""
Forces the projection to match the size of the viewport.
When matching the projection to the viewport the method keeps
the projections center in the same relative place.
"""
x, y = self._projection_data.rect.x, self._projection_data.rect.y
self._projection_data.rect = XYWH(x, y, self.viewport_width, self.viewport_height)

def match_screen(
self,
and_projection: bool = True,
Expand Down Expand Up @@ -350,6 +359,44 @@ def match_screen(
aspect=aspect,
)

def match_target(
self,
and_projection: bool = True,
and_scissor: bool = True,
and_position: bool = False,
aspect: float | None = None,
) -> None:
"""
Sets the viewport to the size of the Camera2D's render target.
Args:
and_projection: Flag whether to also equalize the projection to the viewport.
On by default
and_scissor: Flag whether to also equalize the scissor box to the viewport.
On by default
and_position: Flag whether to also center the camera to the viewport.
Off by default
aspect_ratio: The ratio between width and height that the viewport should
be constrained to. If unset then the viewport just matches the window
size. The aspect ratio describes how much larger the width should be
compared to the height. i.e. for an aspect ratio of ``4:3`` you should
input ``4.0/3.0`` or ``1.33333...``. Cannot be equal to zero.
Raises:
ValueError: Will be raised if the Camera2D was has no render target.
"""
if self.render_target is None:
raise ValueError(
"Tried to match a non-exsistant render target. Please use `match_screen` instead"
)

self.update_viewport(
LRBT(*self.render_target.viewport),
and_projection=and_projection,
and_scissor=and_scissor,
and_position=and_position,
aspect=aspect,
)

def update_viewport(
self,
new_viewport: Rect,
Expand Down Expand Up @@ -396,7 +443,6 @@ def update_viewport(
self.position = self.viewport.center

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

def equalise(self) -> None:
"""
Forces the projection to match the size of the viewport.
When matching the projection to the viewport the method keeps
the projections center in the same relative place.
"""
x, y = self._projection_data.rect.x, self._projection_data.rect.y
self._projection_data.rect = XYWH(x, y, self.viewport_width, self.viewport_height)

# top_left
@property
def top_left(self) -> Vec2:
Expand Down
3 changes: 3 additions & 0 deletions arcade/camera/grips/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
constrain_boundary_xz,
constrain_boundary_xyz,
)
from arcade.camera.grips.position import look_at, orbit


__all__ = (
Expand All @@ -43,4 +44,6 @@
"constrain_boundary_yz",
"constrain_boundary_xz",
"constrain_boundary_xyz",
"look_at",
"orbit",
)
48 changes: 48 additions & 0 deletions arcade/camera/grips/position.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from __future__ import annotations

from pyglet.math import Vec3

from arcade.camera import CameraData
from arcade.math import quaternion_rotation
from arcade.types import Point3


def look_at(camera: CameraData, target: Point3, up: Point3 | None = None) -> tuple[Point3, Point3]:
"""
Calculate the neccisary forward and up vectors to have the camera look towards
the target point.
Uses the camera's up if none is provided.
Args:
camera: The CameraData to work from
target: The point in 3D world space to look at
up: An optional up axis to refer to when calculating the true up vector.
"""
px, py, pz = camera.position
tx, ty, tz = target
dx, dy, dz = tx - px, ty - py, tz - pz

up = up or camera.up

f = Vec3(dx, dy, dz).normalize()
r = f.cross(up)
u = r.cross(f).normalize()

return f, u


def orbit(camera: CameraData, origin: Point3, axis: Point3, angle: float) -> Point3:
"""
Find the new position for the camera when rotated around the origin
Args:
camera: The CameraData to work from
origin: The point around which to orbit
axis: The axis to rotate around, like the stick on a spinning top.
angle: The angle in degrees to rotate by
"""
px, py, pz = camera.position
tx, ty, tz = origin

rx, ry, rz = quaternion_rotation(axis, (px - tx, py - ty, pz - tz), angle)
return tx + rx, ty + ry, tz + rz
24 changes: 17 additions & 7 deletions arcade/examples/array_backed_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,22 @@
MARGIN = 5

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


class MyGame(arcade.Window):
class GameView(arcade.View):
"""
Main application class.
"""

def __init__(self, width, height, title):
def __init__(self):
"""
Set up the application.
"""

super().__init__(width, height, title)
super().__init__()

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


def main():
MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
""" Main function """
# Create a window class. This is what actually shows up on screen
window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)

# Create the GameView
game = GameView()

# Show GameView on screen
window.show_view(game)

# Start the arcade game loop
arcade.run()


Expand Down
24 changes: 17 additions & 7 deletions arcade/examples/array_backed_grid_buffered.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,21 @@
MARGIN = 5

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


class MyGame(arcade.Window):
class GameView(arcade.View):
"""
Main application class.
"""

def __init__(self, width, height, title):
def __init__(self):
"""
Set up the application.
"""
super().__init__(width, height, title)
super().__init__()
self.shape_list = None

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


def main():
MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
""" Main function """
# Create a window class. This is what actually shows up on screen
window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)

# Create the GameView
game = GameView()

# Show GameView on screen
window.show_view(game)

# Start the arcade game loop
arcade.run()


Expand Down
24 changes: 17 additions & 7 deletions arcade/examples/array_backed_grid_sprites_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,21 @@
MARGIN = 5

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


class MyGame(arcade.Window):
class GameView(arcade.View):
"""
Main application class.
"""

def __init__(self, width, height, title):
def __init__(self):
"""
Set up the application.
"""
super().__init__(width, height, title)
super().__init__()

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


def main():
MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
""" Main function """
# Create a window class. This is what actually shows up on screen
window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)

# Create the GameView
game = GameView()

# Show GameView on screen
window.show_view(game)

# Start the arcade game loop
arcade.run()


Expand Down
Loading

0 comments on commit 95fa66f

Please sign in to comment.