Skip to content

Commit

Permalink
Fix game of life scaling the window instead of the texture (#1986)
Browse files Browse the repository at this point in the history
* Fix window sizing in game of life example (#1985)

* Clean up import order and spacing

* Use more style-compliant main check for running the shader game of life

* Fix grammar in comment

* Update comments and docstrings on implementation

* Rephrase top-level example docstring for clarity

* phrasing tweak
  • Loading branch information
pushfoo committed Feb 24, 2024
1 parent 6aa5341 commit f165c37
Showing 1 changed file with 33 additions and 26 deletions.
59 changes: 33 additions & 26 deletions arcade/examples/gl/game_of_life_colors.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
"""
Game of Life - Shader Version
We're doing this in in a simple way drawing to textures.
We need two textures. One to keep the old state and
another to draw the new state into. These textures are
flipped around every frame.
This version uses shaders which draws to textures to
run Conway's Game of Life with an added twist: colors.
This version of Game of Life also use colors. Dominant
colonies will keep spreading their color.
Press SPACE to reset the simulation with random data.
Press SPACE to generate new initial data
It uses two textures: One to keep the old state and
a second to draw the new state into. These textures are
flipped around after every update.
The cell and window size can be tweaked in the parameters below.
You can configure the cell and window size by changing
the constants at the top of the file after the imports.
If Python and Arcade are installed, this example can be run from the command line with:
python -m arcade.examples.gl.game_of_life_colors
"""
import random
from array import array

from arcade import key
import arcade
from arcade import key
from arcade.gl import geometry


CELL_SIZE = 2 # Cell size in pixels
WINDOW_WIDTH = 512 # Width of the window
WINDOW_HEIGHT = 512 # Height of the window
FRAME_DELAY = 2 # The game will only update every 2th frame
FRAME_DELAY = 2 # The game will only update every 2nd frame


class GameOfLife(arcade.Window):
Expand All @@ -35,17 +36,17 @@ def __init__(self, width, height):
super().__init__(width, height, "Game of Life - Shader Version")
self.frame = 0

# Configure the size of the playfield (cells)
self.size = width // CELL_SIZE, height // CELL_SIZE
# Calculate how many cells we need to simulate at this pixel size
self.texture_size = width // CELL_SIZE, height // CELL_SIZE

# Create two textures for the next and previous state (RGB textures)
self.texture_1 = self.ctx.texture(
self.size,
self.texture_size,
components=3,
filter=(self.ctx.NEAREST, self.ctx.NEAREST),
)
self.texture_2 = self.ctx.texture(
self.size,
self.texture_size,
components=3,
filter=(self.ctx.NEAREST, self.ctx.NEAREST)
)
Expand Down Expand Up @@ -85,9 +86,9 @@ def __init__(self, width, height):
""",
)

# Shader for creating the next game state.
# It takes the previous state as input (texture0)
# and renders the next state directly into the second texture.
# Shader which calculates the next game state.
# It uses the previous state as input (texture0) and
# renders the next state into the second texture.
self.life_program = self.ctx.program(
vertex_shader="""
#version 330
Expand Down Expand Up @@ -164,21 +165,25 @@ def __init__(self, width, height):
)

def gen_initial_data(self, num_values: int):
"""
Generate initial data. We need to be careful about the initial state.
Just throwing in lots of random numbers will make the entire system
die in a few frames. We need to give enough room for life to exist.
"""Generate initial data.
We need to be careful about the initial game state. Carelessly
random numbers will make the simulation die in only a few frames.
Instead, we need to generate values which leave room for life
to exist.
This might be the slowest possible way we would generate the initial
data, but it works for this example :)
The implementtation below is one of the slowest possible ways to
would generate the initial data, but it keeps things simple for
this example.
"""
choices = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 64, 128, 192, 255]
for i in range(num_values):
yield random.choice(choices)

def write_initial_state(self):
"""Write initial data to the source texture"""
self.texture_1.write(array('B', self.gen_initial_data(self.size[0] * self.size[1] * 3)))
"""Write initial data to the source texture."""
size = self.texture_size
self.texture_1.write(array('B', self.gen_initial_data(size[0] * size[1] * 3)))

def on_draw(self):
self.clear()
Expand Down Expand Up @@ -214,4 +219,6 @@ def on_key_press(self, symbol: int, modifiers: int):
self.write_initial_state()


GameOfLife(WINDOW_WIDTH, WINDOW_HEIGHT).run()
if __name__ == "__main__":
game = GameOfLife(WINDOW_WIDTH, WINDOW_HEIGHT)
game.run()

0 comments on commit f165c37

Please sign in to comment.