Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions arcade/examples/platform_tutorial/03_more_sprites.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)

Expand Down
1 change: 1 addition & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
]

Expand Down
2 changes: 1 addition & 1 deletion doc/tutorials/platform_tutorial/step_02.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
98 changes: 68 additions & 30 deletions doc/tutorials/platform_tutorial/step_03.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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``?

Expand All @@ -74,16 +102,15 @@ 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)

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)

Expand All @@ -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
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading