Sprite Depth Controlled by a Cosine Wave

Screenshot of sprites whose depth value is controlled by time-dependent cosine wave.
sprite_depth_cosine.py
  1"""
  2Depth-sort sprites using a depth buffer in the GL context.
  3
  4Press the space bar to toggle depth testing during drawing.
  5
  6During each update, the depth of each sprite is updated to follow a
  7cosine wave. Afterward, the following is drawn:
  8
  9* All sprites in depth-sorted order
 10* A white square centered over each sprite along the x-axis, and moving
 11  with the wave along the y-axis
 12
 13If Python and Arcade are installed, this example can be run from the command line with:
 14python -m arcade.examples.sprite_depth_cosine
 15"""
 16
 17from __future__ import annotations
 18
 19import math
 20
 21from pyglet.graphics import Batch
 22
 23import arcade
 24
 25# All constants are in pixels
 26WINDOW_WIDTH, WINDOW_HEIGHT = 1280, 720
 27
 28WINDOW_TITLE = "Sprite Depth Testing Example w/ a Cosine Wave"
 29NUM_SPRITES = 10
 30
 31SPRITE_X_START = 150
 32SPRITE_X_STEP = 50
 33SPRITE_Y = WINDOW_HEIGHT // 2
 34
 35DOT_SIZE = 10
 36
 37
 38class GameView(arcade.View):
 39
 40    def __init__(self):
 41        super().__init__()
 42
 43        texture = arcade.load_texture(":resources:images/test_textures/xy_square.png")
 44        self.text_batch = Batch()
 45
 46        self.use_depth: bool = True
 47        self.text_use_depth = arcade.Text(
 48            "SPACE: Toggle depth testing (True)",
 49            x=10,
 50            y=30,
 51            font_size=15,
 52            color=arcade.color.WHITE,
 53            batch=self.text_batch,
 54        )
 55
 56        self.sprite_list = arcade.SpriteList()
 57
 58        for i in range(NUM_SPRITES):
 59            sprite = arcade.Sprite(
 60                texture, center_x=SPRITE_X_START + SPRITE_X_STEP * i, center_y=SPRITE_Y
 61            )
 62            self.sprite_list.append(sprite)
 63
 64    def on_draw(self):
 65        self.clear()
 66
 67        ctx = self.window.ctx
 68        if self.use_depth:
 69            # This with block temporarily enables depth testing
 70            with ctx.enabled(ctx.DEPTH_TEST):
 71                self.sprite_list.draw()
 72        else:
 73            self.sprite_list.draw()
 74
 75        # Draw wave visualization markers over each sprite
 76        for i, sprite in enumerate(self.sprite_list):
 77            arcade.draw_point(
 78                SPRITE_X_START + SPRITE_X_STEP * i,
 79                SPRITE_Y + sprite.depth,
 80                arcade.color.WHITE,
 81                DOT_SIZE,
 82            )
 83
 84        self.text_batch.draw()
 85
 86    def on_key_press(self, symbol: int, modifiers: int):
 87        if symbol == arcade.key.SPACE:
 88            self.use_depth = not self.use_depth
 89            self.text_use_depth.text = f"SPACE: Toggle depth testing ({self.use_depth})"
 90
 91    def on_update(self, delta_time):
 92        # Using time from the window's clock simplifies the math below
 93        time = self.window.time
 94        for i, sprite in enumerate(self.sprite_list):
 95            sprite.depth = math.cos(time + i) * SPRITE_X_STEP
 96
 97
 98def main():
 99    """ Main function """
100    # Create a window class. This is what actually shows up on screen
101    window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
102
103    # Create the GameView
104    game = GameView()
105
106    # Show GameView on screen
107    window.show_view(game)
108
109    # Start the arcade game loop
110    arcade.run()
111
112
113if __name__ == "__main__":
114    main()