Reload-Pyxel is a library for the retro game engine Pyxel that adds the ability to hot-reload both code and assets.
That means that now, while the game is running, if you change your graphics or edit the code, the changes will be reflected live in your game - without restarting it!
The detected changes include your source files (.py
), resource files (.pyxres
) and all their contents (images, tilemaps, music), external images (.png/.gif/.jpg
), and external tilemaps (.tmx
). A mechanism is included so you can write code to hot-reload any other file type as well.
The gif below shows an example where you walk to a part of the level, realize the collison detection isn't working right, fix it, and you can see the result of your change immediately.
Without Reload-Pyxel, you would have to quit your game, restart it, and navigate back to the level you updated to see the effect of your changes. This can be automated to a point with pyxel watch
, but it still puts you back at the beginning.
The use is very similar to normal Pyxel, with a few tweaks.
-
Copy both the
reloadpyxel.py
andmain.py
files onto your project folder. -
Create your own game and call it
game.py
. Here are the starting contents (or copygame.py.template
):
import pyxel
import reloadpyxel
class App:
@staticmethod
def init_pyxel():
pyxel.init(160, 120, title="My Pyxel Game!")
def __init__(self, repyxel):
# Load your resources here
def update(self):
if pyxel.btnp(pyxel.KEY_Q):
pyxel.quit()
# Code to update your game state goes here
def draw(self):
pyxel.cls(0)
pyxel.rect(10, 10, 20, 20, 11)
# Code to draw your game goes here
def reload(self, old_self):
"""Called when code is reloaded"""
reloadpyxel.copy_all_attributes(old_self, self)
You can use all the Pyxel functions as before, refer to the Pyxel README for instruction on how to make a Pyxel game.
You also start the program as before, with
pyxel run main.py
The differences are:
- Your App class must be in
game.py
(you may add other files as needed). You can use the template in this repository as a starting point. - You must copy the reloadpyxel's
main.py
andreloadpyxel.py
files in the same folder as yourgame.py
pyxel.init
is moved to a required function calledinit_pyxel
pyxel.load
is replaced withrepyxel.load
pyxel.run
is replaced withrepyxel.run
, which takes your class as argument- (optional) To enable code hot reload, include the
reload
method as shown above. In that function you must make whatever adjustments are needed to copy the state of your game from the old code to the new code and re-instantiate all your objects using the new code.
When you are done developing you app you can replace repyxel.run
with pyxel.run
to remove the hot reloading and get back the small bit of performance it consumes.
Note that if you really don't want to call your game file game.py
you can use a different name, you just have to update main.py
to point to it.
The repository includes multiple examples in the examples/
folder, split into those that only hot-reload resources and those that also hot-reload the code.
These include examples from the original pyxel that were adapted to include the hot-reload capability. As you can see in the code, not much needed to be modified.
A simple example that is a good starting point is examples/code_and_resources/pyxel_originals/01_hello_pyxel/
The main file you need to include will do four things:
- Create a reloadpyxel object
- Call your
init_pyxel
function - Create your
App
, passing it the reloadpyxel object - Start the game
-
copy_all_attributes(source,destination)
Copies all the attributes from the source object to the destination object. for example ifsource.x=2
before the call, thendestination.x=2
after the call. -
load(name_of_resource_file, [excl_images], [excl_tilemaps], [excl_sounds], [excl_musics])
Load the resource file (.pyxres). If an option isTrue
, that resource will not be loaded. Afterwards, watches that file and will reload it if if changes. When that happens, your App'sreload_resource
method will be called (if it exists). -
run(app)
Runs the Pyxel game. Your app must have theupdate(self)
anddraw(self)
methods that Pyxel needs. It will reload resources and code if they are modified. You typically do not need to call this yourself, it's called inmain.py
for you. -
watch_resource(filename)
Watches that file. If it changes, will call your App'sreload_resource
method (if it exists). -
images[i].load(name_of_image_file, [excl_images], [excl_tilemaps], [excl_sounds], [excl_musics])
Load the specified image (png/gif/jpg) into the image bank numberi
. Afterwards, watches that file and will reload it if if changes. When that happens, your App'sreload_resource
method will be called (if it exists). -
tilemap[i].load(x, y, tmx_filename, layer)
Load the specified tilemap (tmx format) into the tilemap bank numberi
at the specified layer. Afterwards, watches that file and will reload it if if changes. When that happens, your App'sreload_resource
method will be called (if it exists).
Your game must be in game.py
in a class named App
with the following methods:
update(self)
draw(self)
In addition, you can have the following optional methods:
-
reload(self, old_self)
This method needs to be present for hot-reloading of code to be enabled. ReloadPyxel will call this method whenever any code was reloaded. In that case, it will have created a new instance of yourApp
object and calls that method on it, passing to it the old instance. It is your responsibility to transfer the state to the new object, for example via the helper functionreloadpyxel.copy_all_attributes(source,destination)
. If you're holding on to other objects (perhaps aPlayer
orEnemy
you wrote), then you will want to replace those with a new instance. -
reload_resources(self, list_of_file_names)
ReloadPyxel will call this method whenever any resource was reloaded. It includes the list of resources that have changed. Asset hot-reloading will happen even if this method is not present. Normally you don't need to do anything special when a resource is reloaded, but this can be useful if you asked for a custom file to be watched (say, a.json
) and want to reload it yourself.
See also Hot-reload, explained for an explanation of how to use hot-reload and how it works.