Skip to content

Add tutorial for adding custom resource handlers #2086

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 29, 2024
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
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ The Python Arcade Library
tutorials/lights/index
tutorials/bundling_with_pyinstaller/index
tutorials/compiling_with_nuitka/index
tutorials/resource_handlers/index
tutorials/shader_tutorials
tutorials/menu/index
tutorials/framebuffer/index
Expand Down
9 changes: 7 additions & 2 deletions doc/tutorials/bundling_with_pyinstaller/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ Create a file called ``main.py`` that contains the following:

import arcade

arcade.open_window(400, 400, "My Game")
window = arcade.open_window(400, 400, "My Game")

self.clear()
window.clear()
arcade.draw_circle_filled(200, 200, 100, arcade.color.BLUE)
arcade.finish_render()

Expand Down Expand Up @@ -230,6 +230,11 @@ the contents of the folder and make sure all of the files are where you expect t
to be. The one-file bundle produced by ``--onefile`` is simply a
self-uncompressing archive of this one-folder bundle.

Specifying Your Own Resource Handler in ``--onefile`` mode
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

See: :ref:`resource_handlers_one_file_builds`.

PyInstaller Not Bundling a Needed Module
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
4 changes: 3 additions & 1 deletion doc/tutorials/compiling_with_nuitka/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ To bundle a whole folder:

This will copy the whole folder named ``assets`` at the specified location to the root of the executable.

.. Note::

If you want to use a custom resource handler and Nuitka's one-file build see: :ref:`resource_handlers_one_file_builds`.

Removing The Console Window
---------------------------
Expand Down Expand Up @@ -129,7 +132,6 @@ This will set the app icon to icon.png

This will set the app icon to Python's icon 😉


Additional Information
----------------------

Expand Down
97 changes: 97 additions & 0 deletions doc/tutorials/resource_handlers/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
.. _resource_handlers:

Adding Your Own Resource Handlers
=================================

Arcade provides a convenient way to locate asset through it's resource handlers. Arcade already has a number of
Built-In sprites, images and other resources available for use inside the ``:resources:`` handler, which you
may already be familiar with:

.. code-block:: python

my_sprite = arcade.Sprite(":resources:images/animated_characters/female_person/femalePerson_idle.png", SPRITE_SCALE)


Arcade also allows the ability to register additional resources handlers. This is helpful when you want to include
your own resource folders for your project.

Basic Usage
-----------

You can register a new resource handler by using ``arcade.resources.add_resource_handler(handle: str, path: Union[str, Path])``:

.. code-block:: python

arcade.resources.add_resource_handler("my_resources", "/home/users/username/my_game/my_res_folder")

.. note::

The ``add_resource_handler`` function must be given an **absolute** path.

Then, you can use resources from your handler:

.. code-block:: python

self.texture = arcade.load_texture(":my_resources:images/characters/my_character.png")

Despite needing an absolute path, you can use Python's ``Path.resolve()`` to resolve a relative path:

.. code-block:: python

from pathlib import Path
...
arcade.resources.add_resource_handler("my_resources", Path("assets/my_res_folder").resolve())

Adding Multiple Directories to a Resource Handler
-------------------------------------------------

You can also add multiple directories to a single resource handler:

.. code-block:: python

# Adding multiple resources folders to the same resource handler:
arcade.resources.add_resource_handler("my_resources", "/home/users/username/my_game/my_first_res_folder/")
arcade.resources.add_resource_handler("my_resources", "/home/users/username/my_game/my_second_res_folder/")

When multiple directories are added to a single resource handler, Arcade will search through the added directories until
it locates the requested resource. Here, Arcade will start it's search in the last added directory first, in this case
``my_second_res_folder``. If the requested resource is not present within ``my_second_res_folder`` it will then move
onto the directories added before it, in this case, ``my_first_res_dir``.

.. _resource_handlers_one_file_builds:

Resources Handlers and PyInstaller/Nuitka one-file builds
---------------------------------------------------------

When distributing your file as a one-file, standalone build with either Nuitka or PyInstaller you will need to specify
relative paths differently to ensure that your distributed code can correctly locate your resource folder(s) on other
people's computers.

With one-file builds for both Nuitka and PyInstaller, the created executable is a bundled file that contains everything
that is needed to run your program, this includes all your `.py` files and the the data folders you specified in the
build command.

When the executable is ran, the files and folders are unbundled and placed inside a temporary location, (on Window's
this is normally ``C:\Users\UserName\AppData\Local\Temp``). This includes an exact copy of your data directory and it is
from here that your application is ran from. To ensure that the running executable correctly finds this data directory,
we can use the ``__file__`` dunder variable to locate temporary folder's location.

.. code-block:: Python

asset_dir = os.path.join(Path(__file__).parent.resolve(), "assets")
arcade.resources.add_resource_handle("assets", asset_dir)

Here ``__file__``, will either resolve to the temporary folder location or file which it is in when running your game
as a Python program: ``python mygame.py``.

.. note::

``sys.argv[0]`` is not the same as ``__file__``. ``sys.argv[0]`` will point to the original executable's location
and not the temporary folders location. ``__file__`` is a special python dunder variable that contains the absolute
file location from which a Python module was loaded from.

.. warning::

Do not use a ``./`` (single dot) to specify the relative location (even when you use ``Path.resolve()``). The
``./`` will be interpreted to the location of the executable and not the temporary location your executable your
application is unbundled too.
Loading