From 1c5eeaf6b3c9f570e4e3502d1bdcff0eea8a5110 Mon Sep 17 00:00:00 2001 From: Dickerson Date: Tue, 20 May 2025 12:29:58 -0400 Subject: [PATCH] done with step_03.rst --- .../platform_tutorial/03_more_sprites.py | 5 +- doc/conf.py | 1 + doc/tutorials/platform_tutorial/step_02.rst | 2 +- doc/tutorials/platform_tutorial/step_03.rst | 98 +++++++++++++------ pyproject.toml | 1 + 5 files changed, 73 insertions(+), 34 deletions(-) diff --git a/arcade/examples/platform_tutorial/03_more_sprites.py b/arcade/examples/platform_tutorial/03_more_sprites.py index 053e12cc1a..5541bc34af 100644 --- a/arcade/examples/platform_tutorial/03_more_sprites.py +++ b/arcade/examples/platform_tutorial/03_more_sprites.py @@ -49,7 +49,7 @@ def __init__(self): # Create the ground # This shows using a loop to place multiple sprites horizontally for x in range(0, 1250, 64): - wall = arcade.Sprite(":resources:images/tiles/grassMid.png", scale=0.5) + wall = arcade.Sprite(":resources:images/tiles/grassMid.png", scale=TILE_SCALING) wall.center_x = x wall.center_y = 32 self.wall_list.append(wall) @@ -61,8 +61,7 @@ def __init__(self): for coordinate in coordinate_list: # Add a crate on the ground wall = arcade.Sprite( - ":resources:images/tiles/boxCrate_double.png", scale=0.5 - ) + ":resources:images/tiles/boxCrate_double.png", scale=TILE_SCALING) wall.position = coordinate self.wall_list.append(wall) diff --git a/doc/conf.py b/doc/conf.py index 29ef09dbc3..c3b187d76e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -196,6 +196,7 @@ def run_util(filename, run_name="__main__", init_globals=None): 'sphinx.ext.viewcode', # display code with line numbers and line highlighting 'sphinx_copybutton', # Adds a copy button to code blocks 'sphinx_sitemap', # sitemap.xml generation + 'sphinx_togglebutton', #A way to toggle sections of text/code on or off. 'doc.extensions.prettyspecialmethods', # Forker plugin for prettifying special methods ] diff --git a/doc/tutorials/platform_tutorial/step_02.rst b/doc/tutorials/platform_tutorial/step_02.rst index ccd15f978d..c346817320 100644 --- a/doc/tutorials/platform_tutorial/step_02.rst +++ b/doc/tutorials/platform_tutorial/step_02.rst @@ -134,4 +134,4 @@ Source Code .. literalinclude:: ../../../arcade/examples/platform_tutorial/02_draw_sprites.py :caption: 02_draw_sprites - Draw and Position Sprites :linenos: - :emphasize-lines: 24-30, 44-45 + :emphasize-lines: 24-32, 46-47 diff --git a/doc/tutorials/platform_tutorial/step_03.rst b/doc/tutorials/platform_tutorial/step_03.rst index 167d8a8a08..6ab576f954 100644 --- a/doc/tutorials/platform_tutorial/step_03.rst +++ b/doc/tutorials/platform_tutorial/step_03.rst @@ -15,16 +15,21 @@ At the end, we'll have something like this: SpriteList ~~~~~~~~~~ -:class:`arcade.SpriteList` exists to draw a collection of Sprites all at once. Let's say for example that you have -100,000 box Sprites that you want to draw. Without SpriteList you would have to put all of your sprites into a list, -and then run a for loop over that which calls ``draw()`` on every sprite. - -This approach is extremely un-performant. Instead, you can add all of your boxes to a :class:`arcade.SpriteList` +Imagine how you (as a human) would draw this picture. Maybe you would draw the character first. Then +the first box, then the second box, etc... Now a long strip of green and brown stuff we will call grass. +So likely your first thought to programming would be to make a loop over a list of sprites and call ``draw()`` +on every sprite, one at a time. Computers are fast right? Yes, but this approach is extremely inefficient. +See the note below on how Arcade uses GPUs. What if we could draw EVERY object at the same time! Computers +can surpass the frail limitiation of you mortal humans! + +But to do that, the computer does need your help (and the computer humbly apologizes for the insult!). +:class:`arcade.SpriteList` exists to draw a collection of Sprites all at once. Let's say for example that +you have 100,000 box Sprites that you want to draw. You can add all of your boxes to a :class:`arcade.SpriteList` and then draw the SpriteList. Doing this, you are able to draw all 100,000 sprites for approximately the exact -same cost as drawing one sprite. +same cost as drawing one sprite. Which is pretty amazing. .. note:: - This is due to Arcade being a heavily GPU based library. GPUs are really good at doing things in batches. + Arcade is a heavily GPU based library. GPUs are really good at doing things in batches. This means we can send all the information about our sprites to the GPU, and then tell it to draw them all at once. However if we just draw one sprite at a time, then we have to go on a round trip from our CPU to our GPU every time. @@ -33,34 +38,57 @@ Even if you are only drawing one Sprite, you should still create a SpriteList fo it is never better to draw an individual Sprite than it is to add it to a SpriteList. In fact, calling ``draw()`` on a Sprite just creates a SpriteList internally to draw that Sprite with. -Let's go ahead and create one for our player inside our ``__init__`` function, and add the player to it. +Quiz - where in our code should we place the data object of our :class:`arcade.SpriteList`? + +.. toggle:: + + If you guessed the ``__init__`` function you are correct! + +So that is where we are going to put the following code. Just add it to the bottom of the list of other data objects. .. code-block:: self.player_list = arcade.SpriteList() self.player_list.append(self.player_sprite) -Then in our ``on_draw`` function, we can draw the SpriteList for the character instead of drawing the Sprite directly: +Then in our ``on_draw`` function, we can draw the SpriteList for the character instead of drawing the Sprite directly. +So we will replace this line of code: + +.. code-block:: + + arcade.draw_sprite(self.player_sprite) + +with this line of code: .. code-block:: self.player_list.draw() +Now run it and you get your character on the screen. All by herself... alone in an empty world... sad face. + +Time to make the world! +~~~~~~~~~~~~~~~~~~~~~~~ + Now let's try and build a world for our character. To do this, we'll create a new SpriteList for the objects we'll draw, -we can do this in our ``__init__`` function. +we can do this again in our ``__init__`` function. .. code-block:: self.wall_list = arcade.SpriteList(use_spatial_hash=True) -There's a little bit to unpack in this snippet of code. Let's address each issue: +Wait, why did we make a new list of Sprites? I thought the idea was to make one master list of everything we needed to draw? +What is a spatial_hash and why is it True? + +Great questions! 1. Why not just use the same SpriteList we used for our player, and why is it named walls? Eventually we will want to do collision detection between our character and these objects. In addition to drawing, SpriteLists also serve as a utility for collision detection. You can for example check for collisions between two SpriteLists, or pass SpriteLists into several physics - engines. We will explore these topics in later chapters. + engines. We will explore these topics in later chapters. And you will notice that we are going to + pass in pictures of grass and boxes - which we will treat as barriers. So they will be added to + the 'walls' 2. What is ``use_spatial_hash``? @@ -74,7 +102,7 @@ With our newly created SpriteList, let's go ahead and add some objects to it. We .. code-block:: for x in range(0, 1250, 64): - wall = arcade.Sprite(":resources:images/tiles/grassMid.png", TILE_SCALING) + wall = arcade.Sprite(":resources:images/tiles/grassMid.png", scale=TILE_SCALING) wall.center_x = x wall.center_y = 32 self.wall_list.append(wall) @@ -82,8 +110,7 @@ With our newly created SpriteList, let's go ahead and add some objects to it. We coordinate_list = [[512, 96], [256, 96], [768, 96]] for coordinate in coordinate_list: wall = arcade.Sprite( - ":resources:images/tiles/boxCrate_double.png", scale=0.5 - ) + ":resources:images/tiles/boxCrate_double.png", scale=TILE_SCALING) wall.position = coordinate self.wall_list.append(wall) @@ -103,26 +130,37 @@ Finally all we need to do in order to draw our new world, is draw the SpriteList self.wall_list.draw() -Source Code -~~~~~~~~~~~ +Run the code and... wait the image is instantly disappearing! What did we do wrong? Try and debug it! -.. literalinclude:: ../../../arcade/examples/platform_tutorial/03_more_sprites.py - :caption: 03_more_sprites - Many Sprites with a SpriteList - :linenos: - :emphasize-lines: 35-65, 80-81 +.. toggle:: -* Documentation for the :class:`arcade.SpriteList` class + You might have noticed that we use TILE_SCALING but we never defined it! So the image starts creating, but crashes + before we get to even see what is wrong! This is a hard bug to find as there is no error message. -.. note:: + Solution: add this up by the '# constants' section + TILE_SCALING = 0.5 - Once you have the code up and working, try-out the following: - * See if you can change the colors of all the boxes and ground using the SpriteList - * Try and make a SpriteList invisible +Challenge +~~~~~~~~~ +* Try changing the new variable TILE_SCALING, what does it do? +* Make some floating boxes. Do it either randomly or fixed. +* Read the documentation for the :class:`arcade.SpriteList` class +* See if you can change the colors of all the boxes and ground using the SpriteList +* Try and make a SpriteList invisible +* EXTREME Challeng Our __init__ class is getting pretty bloated. The loops we created to make the walls would be better suited + to have their own function that we call. See if you can make a function 'def build_walls(self):' that has that same + code. Then call that function with build_walls() after you define the self.wall_list. -Run This Chapter -~~~~~~~~~~~~~~~~ +Your code should produce the same image as shown on the top of this page. Before proceeding, make sure you comment +out custom code so we are on the same page. Also, if you are still having problems, compare with this code: -.. code-block:: +Source Code +~~~~~~~~~~~ + +.. literalinclude:: ../../../arcade/examples/platform_tutorial/03_more_sprites.py + :caption: 03_more_sprites - Many Sprites with a SpriteList + :linenos: + :emphasize-lines: 13-14, 37-39, 41-47, 49-66, 80-82 - python -m arcade.examples.platform_tutorial.03_more_sprites +* Documentation for the :class:`arcade.SpriteList` class diff --git a/pyproject.toml b/pyproject.toml index ffd028c5d3..e6f66f707a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,7 @@ dev = [ "sphinx-autobuild==2024.10.3", # April 2024 | Due to this, Python 3.10+ is required to serve docs "sphinx-copybutton==0.5.2", # April 2023 "sphinx-sitemap==2.6.0", # April 2024 + "sphinx-togglebutton==0.3.2", # May 2025 "pygments==2.19.1", # 2.18 has breaking changes in lexer "docutils==0.21.2", # ? # "pyyaml==6.0.1",