Module pyboy.api
Tools to help interfacing with the Game Boy hardware
Expand source code
#
# License: See LICENSE.md file
# GitHub: https://github.com/Baekalfen/PyBoy
#
"""
Tools to help interfacing with the Game Boy hardware
"""
from . import constants
from .gameshark import GameShark
from .screen import Screen
from .sprite import Sprite
from .tile import Tile
from .tilemap import TileMap
# __pdoc__ = {
# "constants": False,
# "manager": False,
# }
__all__ = [
"constants",
"GameShark",
"Screen",
"Sprite",
"Tile",
"TileMap",
]
Sub-modules
pyboy.api.constants
-
Memory constants used internally to calculate tile and tile map addresses.
pyboy.api.gameshark
pyboy.api.memory_scanner
pyboy.api.screen
-
This class gives access to the frame buffer and other screen parameters of PyBoy.
pyboy.api.sound
-
This class gives access to the sound buffer of PyBoy.
pyboy.api.sprite
-
This class presents an interface to the sprites held in the OAM data on the Game Boy.
pyboy.api.tile
-
The Game Boy uses tiles as the building block for all graphics on the screen. This base-class is used both for
Sprite
and … pyboy.api.tilemap
-
The Game Boy has two tile maps, which defines what is rendered on the screen.
Classes
class GameShark (memory)
-
Expand source code
class GameShark: def __init__(self, memory): self.memory = memory self.cheats = {} self.enabled = False def _convert_cheat(self, gameshark_code): """ A GameShark code for these consoles is written in the format ttvvaaaa. tt specifies the type, which is usually 01. vv specifies the hexadecimal value the code will write into the game's memory. aaaa specifies the memory address that will be modified, with the low byte first (e.g. address C056 is written as 56C0). Example 011556C0 would output: type = 01 value = 0x15 address = 0x56C0 For more details: https://doc.kodewerx.org/hacking_gb.html There seems to be conflicting information about the presence of other types than 01. """ # Check if the input cheat code has the correct length (8 characters) if len(gameshark_code) != 8: raise ValueError("Invalid cheat code length. Cheat code must be 8 characters long.") # Extract components from the cheat code _type = int(gameshark_code[:2], 16) value = int(gameshark_code[2:4], 16) # Convert hexadecimal value to an integer unconverted_address = gameshark_code[4:] # Ex: 1ED1 lower = unconverted_address[:2] # Ex: 1E upper = unconverted_address[2:] # Ex: D1 address_converted = upper + lower # Ex: 0xD11E # Converting to Ram Readable address address = int(address_converted, 16) if not 0x8000 <= address: raise ValueError("Invalid GameShark code provided. Address not in the RAM range") return (_type, value, address) def _get_value(self, _type, address): if _type == 0x01: # 8-bit RAM write # Writes the byte xx to the address zzyy. return self.memory[address] # elif (_type & 0xF0) == 0x80: # # 8-bit RAM write (with bank change) # # Changes the RAM bank to b, then writes the byte xx to the address zzyy. # bank = _type & 0xF # pass # elif (_type & 0xF0) == 0x90: # # 8-bit RAM write (with WRAM bank change) # # Changes the WRAM bank to b and then writes the byte xx to the address zzyy. GBC only. # bank = _type & 0xF # pass else: raise ValueError("Invalid GameShark type", _type) def _set_value(self, _type, value, address): if _type == 0x01: # 8-bit RAM write # Writes the byte xx to the address zzyy. self.memory[address] = value # elif (_type & 0xF0) == 0x80: # # 8-bit RAM write (with bank change) # # Changes the RAM bank to b, then writes the byte xx to the address zzyy. # bank = _type & 0xF # pass # elif (_type & 0xF0) == 0x90: # # 8-bit RAM write (with WRAM bank change) # # Changes the WRAM bank to b and then writes the byte xx to the address zzyy. GBC only. # bank = _type & 0xF # pass else: raise ValueError("Invalid GameShark type", _type) def add(self, code): """ Add a GameShark cheat to the emulator. Example: ```python >>> pyboy.gameshark.add("01FF16D0") ``` Args: code (str): GameShark code to add """ self.enabled = True _type, value, address = self._convert_cheat(code) if code not in self.cheats: self.cheats[code] = (self._get_value(_type, address), (_type, value, address)) else: raise PyBoyInvalidInputException("GameShark code already applied!") def remove(self, code, restore_value=True): """ Remove a GameShark cheat from the emulator. Example: ```python >>> pyboy.gameshark.add("01FF16D0") >>> pyboy.gameshark.remove("01FF16D0") ``` Args: code (str): GameShark code to remove restore_value (bool): True to restore original value at address, otherwise don't restore """ if code not in self.cheats: raise ValueError("GameShark code cannot be removed. Hasn't been applied.") original_value, (_type, _, address) = self.cheats.pop(code) if restore_value: self._set_value(_type, original_value, address) if len(self.cheats) == 0: self.enabled = False def clear_all(self, restore_value=True): """ Remove all GameShark cheats from the emulator. Example: ```python >>> pyboy.gameshark.clear_all() ``` Args: restore_value (bool): Restore the original values of the memory addresses that were modified by the cheats. """ # NOTE: Create a list so we don't remove from the iterator we are going through for code in list(self.cheats.keys()): self.remove(code, restore_value) def tick(self): if not self.enabled: return 0 # https://gbdev.io/pandocs/Shark_Cheats.html # "As far as it is understood, patching is implemented by hooking the original VBlank interrupt handler, and # re-writing RAM values each frame." for _, (_, (_type, value, address)) in self.cheats.items(): self._set_value(_type, value, address)
Methods
def add(self, code)
-
Add a GameShark cheat to the emulator.
Example:
>>> pyboy.gameshark.add("01FF16D0")
Args
code
:str
- GameShark code to add
Expand source code
def add(self, code): """ Add a GameShark cheat to the emulator. Example: ```python >>> pyboy.gameshark.add("01FF16D0") ``` Args: code (str): GameShark code to add """ self.enabled = True _type, value, address = self._convert_cheat(code) if code not in self.cheats: self.cheats[code] = (self._get_value(_type, address), (_type, value, address)) else: raise PyBoyInvalidInputException("GameShark code already applied!")
def remove(self, code, restore_value=True)
-
Remove a GameShark cheat from the emulator.
Example:
>>> pyboy.gameshark.add("01FF16D0") >>> pyboy.gameshark.remove("01FF16D0")
Args
code
:str
- GameShark code to remove
restore_value
:bool
- True to restore original value at address, otherwise don't restore
Expand source code
def remove(self, code, restore_value=True): """ Remove a GameShark cheat from the emulator. Example: ```python >>> pyboy.gameshark.add("01FF16D0") >>> pyboy.gameshark.remove("01FF16D0") ``` Args: code (str): GameShark code to remove restore_value (bool): True to restore original value at address, otherwise don't restore """ if code not in self.cheats: raise ValueError("GameShark code cannot be removed. Hasn't been applied.") original_value, (_type, _, address) = self.cheats.pop(code) if restore_value: self._set_value(_type, original_value, address) if len(self.cheats) == 0: self.enabled = False
def clear_all(self, restore_value=True)
-
Remove all GameShark cheats from the emulator.
Example:
>>> pyboy.gameshark.clear_all()
Args
restore_value
:bool
- Restore the original values of the memory addresses that were modified by the cheats.
Expand source code
def clear_all(self, restore_value=True): """ Remove all GameShark cheats from the emulator. Example: ```python >>> pyboy.gameshark.clear_all() ``` Args: restore_value (bool): Restore the original values of the memory addresses that were modified by the cheats. """ # NOTE: Create a list so we don't remove from the iterator we are going through for code in list(self.cheats.keys()): self.remove(code, restore_value)
def tick(self)
-
Expand source code
def tick(self): if not self.enabled: return 0 # https://gbdev.io/pandocs/Shark_Cheats.html # "As far as it is understood, patching is implemented by hooking the original VBlank interrupt handler, and # re-writing RAM values each frame." for _, (_, (_type, value, address)) in self.cheats.items(): self._set_value(_type, value, address)
class Screen (mb)
-
As part of the emulation, we generate a screen buffer in 32-bit RGBA format. This class has several helper methods to make it possible to read this buffer out.
If you're making an AI or bot, it's highly recommended to not use this class for detecting objects on the screen. It's much more efficient to use
PyBoy.tilemap_background
,PyBoy.tilemap_window
, andPyBoy.get_sprite()
instead.Expand source code
class Screen: """ As part of the emulation, we generate a screen buffer in 32-bit RGBA format. This class has several helper methods to make it possible to read this buffer out. If you're making an AI or bot, it's highly recommended to _not_ use this class for detecting objects on the screen. It's much more efficient to use `pyboy.PyBoy.tilemap_background`, `pyboy.PyBoy.tilemap_window`, and `pyboy.PyBoy.get_sprite` instead. """ def __init__(self, mb): self.mb = mb self.raw_buffer = self.mb.lcd.renderer._screenbuffer """ Provides a raw, unfiltered `bytes` object with the data from the screen. Check `Screen.raw_buffer_format` to see which dataformat is used. **The returned type and dataformat are subject to change.** The screen buffer is row-major. Use this, only if you need to bypass the overhead of `Screen.image` or `Screen.ndarray`. Example: ```python >>> import numpy as np >>> rows, cols = pyboy.screen.raw_buffer_dims >>> ndarray = np.frombuffer( ... pyboy.screen.raw_buffer, ... dtype=np.uint8, ... ).reshape(rows, cols, 4) # Just an example, use pyboy.screen.ndarray instead ``` Returns ------- memoryview: 92160 bytes memoryview of screen data. """ self.raw_buffer_dims = self.mb.lcd.renderer.buffer_dims """ Returns the dimensions of the raw screen buffer. The screen buffer is row-major. Example: ```python >>> pyboy.screen.raw_buffer_dims (144, 160) ``` Returns ------- tuple: A two-tuple of the buffer dimensions. E.g. (144, 160). """ self.raw_buffer_format = self.mb.lcd.renderer.color_format """ Returns the color format of the raw screen buffer. **This format is subject to change.** Example: ```python >>> from PIL import Image >>> pyboy.screen.raw_buffer_format 'RGBA' >>> image = Image.frombuffer( ... pyboy.screen.raw_buffer_format, ... pyboy.screen.raw_buffer_dims[::-1], ... pyboy.screen.raw_buffer, ... ) # Just an example, use pyboy.screen.image instead >>> image.save('frame.png') ``` Returns ------- str: Color format of the raw screen buffer. E.g. 'RGBA'. """ self.image = None """ Reference to a PIL Image from the screen buffer. **Remember to copy, resize or convert this object** if you intend to store it. The backing buffer will update, but it will be the same `PIL.Image` object. Convenient for screen captures, but might be a bottleneck, if you use it to train a neural network. In which case, read up on the `pyboy.api` features, [Pan Docs](https://gbdev.io/pandocs/) on tiles/sprites, and join our Discord channel for more help. Example: ```python >>> image = pyboy.screen.image >>> type(image) <class 'PIL.Image.Image'> >>> image.save('frame.png') ``` Returns ------- PIL.Image: RGB image of (160, 144) pixels """ if not Image: logger.warning('Cannot generate screen image. Missing dependency "Pillow".') self.image = utils.PillowImportError() else: self._set_image() self.ndarray = np.frombuffer( self.mb.lcd.renderer._screenbuffer_raw, dtype=np.uint8, ).reshape(ROWS, COLS, 4) """ References the screen data in NumPy format. **Remember to copy this object** if you intend to store it. The backing buffer will update, but it will be the same `ndarray` object. The format is given by `pyboy.api.screen.Screen.raw_buffer_format`. The screen buffer is row-major. Example: ```python >>> pyboy.screen.ndarray.shape (144, 160, 4) >>> # Display "P" on screen from the PyBoy bootrom >>> pyboy.screen.ndarray[66:80,64:72,0] array([[255, 255, 255, 255, 255, 255, 255, 255], [255, 0, 0, 0, 0, 0, 255, 255], [255, 0, 0, 0, 0, 0, 0, 255], [255, 0, 0, 255, 255, 0, 0, 255], [255, 0, 0, 255, 255, 0, 0, 255], [255, 0, 0, 255, 255, 0, 0, 255], [255, 0, 0, 0, 0, 0, 0, 255], [255, 0, 0, 0, 0, 0, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 255, 255, 255, 255, 255, 255, 255]], dtype=uint8) ``` Returns ------- numpy.ndarray: Screendata in `ndarray` of bytes with shape (144, 160, 4) """ def _set_image(self): self.image = Image.frombuffer( self.mb.lcd.renderer.color_format, self.mb.lcd.renderer.buffer_dims[::-1], self.mb.lcd.renderer._screenbuffer_raw, ) @property def tilemap_position_list(self): """ This function provides the screen (SCX, SCY) and window (WX, WY) position for each horizontal line in the screen buffer. These parameters are often used for visual effects, and some games will reset the registers at the end of each call to `pyboy.PyBoy.tick()`. See `Screen.get_tilemap_position` for more information. Example: ```python >>> pyboy.tick(25) True >>> swoosh = pyboy.screen.tilemap_position_list[66:77] >>> print(*swoosh, sep=newline) # Just to pretty-print it [0, 0, -7, 0] [1, 0, -7, 0] [2, 0, -7, 0] [2, 0, -7, 0] [2, 0, -7, 0] [3, 0, -7, 0] [3, 0, -7, 0] [2, 0, -7, 0] [1, 0, -7, 0] [1, 0, -7, 0] [0, 0, -7, 0] ``` Returns ------- list: Nested list of SCX, SCY, WX and WY for each scanline (144x4). Returns (0, 0, 0, 0) when LCD is off. """ if self.mb.lcd._LCDC.lcd_enable: return [[line[0], line[1], line[2] - 7, line[3]] for line in self.mb.lcd._scanlineparameters] else: return [[0, 0, 0, 0] for line in range(144)] def get_tilemap_position(self): """ These coordinates define the offset in the tile map from where the top-left corner of the screen is place. Note that the tile map defines 256x256 pixels, but the screen can only show 160x144 pixels. When the offset is closer to the right or bottom edge than 160x144 pixels, the screen will wrap around and render from the opposite site of the tile map. For more details, see "7.4 Viewport" in the [report](https://github.com/Baekalfen/PyBoy/raw/master/extras/PyBoy.pdf), or the Pan Docs under [LCD Position and Scrolling](https://gbdev.io/pandocs/Scrolling.html). Example: ```python >>> pyboy.screen.get_tilemap_position() ((0, 0), (-7, 0)) ``` Returns ------- tuple: Returns the tuple of registers ((SCX, SCY), (WX - 7, WY)) """ return (self.mb.lcd.getviewport(), self.mb.lcd.getwindowpos())
Instance variables
var tilemap_position_list
-
This function provides the screen (SCX, SCY) and window (WX, WY) position for each horizontal line in the screen buffer. These parameters are often used for visual effects, and some games will reset the registers at the end of each call to
PyBoy.tick()
.See
Screen.get_tilemap_position()
for more information.Example:
>>> pyboy.tick(25) True >>> swoosh = pyboy.screen.tilemap_position_list[66:77] >>> print(*swoosh, sep=newline) # Just to pretty-print it [0, 0, -7, 0] [1, 0, -7, 0] [2, 0, -7, 0] [2, 0, -7, 0] [2, 0, -7, 0] [3, 0, -7, 0] [3, 0, -7, 0] [2, 0, -7, 0] [1, 0, -7, 0] [1, 0, -7, 0] [0, 0, -7, 0]
Returns
list:
- Nested list of SCX, SCY, WX and WY for each scanline (144x4). Returns (0, 0, 0, 0) when LCD is off.
Expand source code
@property def tilemap_position_list(self): """ This function provides the screen (SCX, SCY) and window (WX, WY) position for each horizontal line in the screen buffer. These parameters are often used for visual effects, and some games will reset the registers at the end of each call to `pyboy.PyBoy.tick()`. See `Screen.get_tilemap_position` for more information. Example: ```python >>> pyboy.tick(25) True >>> swoosh = pyboy.screen.tilemap_position_list[66:77] >>> print(*swoosh, sep=newline) # Just to pretty-print it [0, 0, -7, 0] [1, 0, -7, 0] [2, 0, -7, 0] [2, 0, -7, 0] [2, 0, -7, 0] [3, 0, -7, 0] [3, 0, -7, 0] [2, 0, -7, 0] [1, 0, -7, 0] [1, 0, -7, 0] [0, 0, -7, 0] ``` Returns ------- list: Nested list of SCX, SCY, WX and WY for each scanline (144x4). Returns (0, 0, 0, 0) when LCD is off. """ if self.mb.lcd._LCDC.lcd_enable: return [[line[0], line[1], line[2] - 7, line[3]] for line in self.mb.lcd._scanlineparameters] else: return [[0, 0, 0, 0] for line in range(144)]
var raw_buffer
-
Provides a raw, unfiltered
bytes
object with the data from the screen. CheckScreen.raw_buffer_format
to see which dataformat is used. The returned type and dataformat are subject to change. The screen buffer is row-major.Use this, only if you need to bypass the overhead of
Screen.image
orScreen.ndarray
.Example:
>>> import numpy as np >>> rows, cols = pyboy.screen.raw_buffer_dims >>> ndarray = np.frombuffer( ... pyboy.screen.raw_buffer, ... dtype=np.uint8, ... ).reshape(rows, cols, 4) # Just an example, use pyboy.screen.ndarray instead
Returns
memoryview:
- 92160 bytes memoryview of screen data.
var raw_buffer_dims
-
Returns the dimensions of the raw screen buffer. The screen buffer is row-major.
Example:
>>> pyboy.screen.raw_buffer_dims (144, 160)
Returns
tuple:
- A two-tuple of the buffer dimensions. E.g. (144, 160).
var raw_buffer_format
-
Returns the color format of the raw screen buffer. This format is subject to change.
Example:
>>> from PIL import Image >>> pyboy.screen.raw_buffer_format 'RGBA' >>> image = Image.frombuffer( ... pyboy.screen.raw_buffer_format, ... pyboy.screen.raw_buffer_dims[::-1], ... pyboy.screen.raw_buffer, ... ) # Just an example, use pyboy.screen.image instead >>> image.save('frame.png')
Returns
str:
- Color format of the raw screen buffer. E.g. 'RGBA'.
var image
-
Reference to a PIL Image from the screen buffer. Remember to copy, resize or convert this object if you intend to store it. The backing buffer will update, but it will be the same
PIL.Image
object.Convenient for screen captures, but might be a bottleneck, if you use it to train a neural network. In which case, read up on the
pyboy.api
features, Pan Docs on tiles/sprites, and join our Discord channel for more help.Example:
>>> image = pyboy.screen.image >>> type(image) <class 'PIL.Image.Image'> >>> image.save('frame.png')
Returns
PIL.Image:
- RGB image of (160, 144) pixels
var ndarray
-
References the screen data in NumPy format. Remember to copy this object if you intend to store it. The backing buffer will update, but it will be the same
ndarray
object.The format is given by
Screen.raw_buffer_format
. The screen buffer is row-major.Example:
>>> pyboy.screen.ndarray.shape (144, 160, 4) >>> # Display "P" on screen from the PyBoy bootrom >>> pyboy.screen.ndarray[66:80,64:72,0] array([[255, 255, 255, 255, 255, 255, 255, 255], [255, 0, 0, 0, 0, 0, 255, 255], [255, 0, 0, 0, 0, 0, 0, 255], [255, 0, 0, 255, 255, 0, 0, 255], [255, 0, 0, 255, 255, 0, 0, 255], [255, 0, 0, 255, 255, 0, 0, 255], [255, 0, 0, 0, 0, 0, 0, 255], [255, 0, 0, 0, 0, 0, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 255, 255, 255, 255, 255, 255, 255]], dtype=uint8)
Returns
numpy.ndarray:
- Screendata in
ndarray
of bytes with shape (144, 160, 4)
Methods
def get_tilemap_position(self)
-
These coordinates define the offset in the tile map from where the top-left corner of the screen is place. Note that the tile map defines 256x256 pixels, but the screen can only show 160x144 pixels. When the offset is closer to the right or bottom edge than 160x144 pixels, the screen will wrap around and render from the opposite site of the tile map.
For more details, see "7.4 Viewport" in the report, or the Pan Docs under LCD Position and Scrolling.
Example:
>>> pyboy.screen.get_tilemap_position() ((0, 0), (-7, 0))
Returns
tuple:
- Returns the tuple of registers ((SCX, SCY), (WX - 7, WY))
Expand source code
def get_tilemap_position(self): """ These coordinates define the offset in the tile map from where the top-left corner of the screen is place. Note that the tile map defines 256x256 pixels, but the screen can only show 160x144 pixels. When the offset is closer to the right or bottom edge than 160x144 pixels, the screen will wrap around and render from the opposite site of the tile map. For more details, see "7.4 Viewport" in the [report](https://github.com/Baekalfen/PyBoy/raw/master/extras/PyBoy.pdf), or the Pan Docs under [LCD Position and Scrolling](https://gbdev.io/pandocs/Scrolling.html). Example: ```python >>> pyboy.screen.get_tilemap_position() ((0, 0), (-7, 0)) ``` Returns ------- tuple: Returns the tuple of registers ((SCX, SCY), (WX - 7, WY)) """ return (self.mb.lcd.getviewport(), self.mb.lcd.getwindowpos())
class Sprite (mb, sprite_index)
-
This class presents an interface to the sprites held in the OAM data on the Game Boy.
The purpose is to make it easier to interpret events on the screen, in order to program a bot, or train an AI.
Sprites are used on the Game Boy for enemy and player characters, as only sprites can have transparency, and can move at pixel-precision on the screen. The other method of graphics – tile maps – can only be placed in a grid-size of 8x8 pixels precision, and can have no transparency.
Sprites on the Game Boy are tightly associated with tiles. The sprites can be seen as "upgraded" tiles, as the image data still refers back to one (or two) tiles. The tile that a sprite will show, can change between each call to
PyBoy.tick()
, so make sure to verify theSprite.tile_identifier
hasn't changed.By knowing the tile identifiers of players, enemies, power-ups and so on, you'll be able to search for them using
pyboy.sprite_by_tile_identifier
and feed it to your bot or AI.Expand source code
class Sprite: def __init__(self, mb, sprite_index): """ This class presents an interface to the sprites held in the OAM data on the Game Boy. The purpose is to make it easier to interpret events on the screen, in order to program a bot, or train an AI. Sprites are used on the Game Boy for enemy and player characters, as only sprites can have transparency, and can move at pixel-precision on the screen. The other method of graphics -- tile maps -- can only be placed in a grid-size of 8x8 pixels precision, and can have no transparency. Sprites on the Game Boy are tightly associated with tiles. The sprites can be seen as "upgraded" tiles, as the image data still refers back to one (or two) tiles. The tile that a sprite will show, can change between each call to `pyboy.PyBoy.tick`, so make sure to verify the `Sprite.tile_identifier` hasn't changed. By knowing the tile identifiers of players, enemies, power-ups and so on, you'll be able to search for them using `pyboy.sprite_by_tile_identifier` and feed it to your bot or AI. """ if not (0 <= sprite_index < SPRITES): raise PyBoyOutOfBoundsException(f"Sprite index of {sprite_index} is out of range (0-{SPRITES})") self.mb = mb self._offset = sprite_index * 4 self._sprite_index = sprite_index """ The index of the sprite itself. Beware, that this only represents the index or a "slot" in OAM memory. Many games will change the image data of the sprite in the "slot" several times per second. Returns ------- int: unsigned tile index """ # Documentation states the y coordinate needs to be subtracted by 16 self.y = self.mb.getitem(OAM_OFFSET + self._offset + 0) - 16 """ The Y-coordinate on the screen to show the Sprite. The (x,y) coordinate points to the top-left corner of the sprite. Returns ------- int: Y-coordinate """ # Documentation states the x coordinate needs to be subtracted by 8 self.x = self.mb.getitem(OAM_OFFSET + self._offset + 1) - 8 """ The X-coordinate on the screen to show the Sprite. The (x,y) coordinate points to the top-left corner of the sprite. Returns ------- int: X-coordinate """ # Sprites can only use unsigned tile indexes in the lower tile data. self.tile_identifier = self.mb.getitem(OAM_OFFSET + self._offset + 2) """ The identifier of the tile the sprite uses. To get a better representation, see the method `pyboy.api.sprite.Sprite.tiles`. For double-height sprites, this will only give the identifier of the first tile. The second tile will always be the one immediately following the first (`tile_identifier + 1`). Returns ------- int: unsigned tile index """ attr = self.mb.getitem(OAM_OFFSET + self._offset + 3) self.attr_obj_bg_priority = _bit(attr, 7) """ To better understand this values, look in the [Pan Docs: VRAM Sprite Attribute Table (OAM)](https://gbdev.io/pandocs/OAM.html). Returns ------- bool: The state of the bit in the attributes lookup. """ self.attr_y_flip = _bit(attr, 6) """ To better understand this values, look in the [Pan Docs: VRAM Sprite Attribute Table (OAM)](https://gbdev.io/pandocs/OAM.html). Returns ------- bool: The state of the bit in the attributes lookup. """ self.attr_x_flip = _bit(attr, 5) """ To better understand this values, look in the [Pan Docs: VRAM Sprite Attribute Table (OAM)](https://gbdev.io/pandocs/OAM.html). Returns ------- bool: The state of the bit in the attributes lookup. """ self.attr_palette_number = 0 """ To better understand this values, look in the [Pan Docs: VRAM Sprite Attribute Table (OAM)](https://gbdev.io/pandocs/OAM.html). Returns ------- int: The state of the bit(s) in the attributes lookup. """ self.attr_cgb_bank_number = 0 """ To better understand this values, look in the [Pan Docs: VRAM Sprite Attribute Table (OAM)](https://gbdev.io/pandocs/OAM.html). Returns ------- bool: The state of the bit in the attributes lookup. """ if self.mb.cgb: self.attr_palette_number = attr & 0b111 self.attr_cgb_bank_number = _bit(attr, 3) else: self.attr_palette_number = _bit(attr, 4) LCDC = LCDCRegister(self.mb.getitem(LCDC_OFFSET)) sprite_height = 16 if LCDC._get_sprite_height() else 8 self.shape = (8, sprite_height) """ Sprites can be set to be 8x8 or 8x16 pixels (16 pixels tall). This is defined globally for the rendering hardware, so it's either all sprites using 8x16 pixels, or all sprites using 8x8 pixels. Returns ------- (int, int): The width and height of the sprite. """ self.tiles = [Tile(self.mb, self.tile_identifier)] """ The Game Boy support sprites of single-height (8x8 pixels) and double-height (8x16 pixels). In the single-height format, one tile is used. For double-height sprites, the Game Boy will also use the tile immediately following the identifier given, and render it below the first. More information can be found in the [Pan Docs: VRAM Sprite Attribute Table (OAM)](https://gbdev.io/pandocs/OAM.html) Returns ------- list: A list of `pyboy.api.tile.Tile` object(s) representing the graphics data for the sprite """ if sprite_height == 16: self.tiles += [Tile(self.mb, self.tile_identifier + 1)] self.on_screen = -sprite_height < self.y < 144 and -8 < self.x < 160 """ To disable sprites from being rendered on screen, developers will place the sprite outside the area of the screen. This is often a good way to determine if the sprite is inactive. This check doesn't take transparency into account, and will only check the sprite's bounding-box of 8x8 or 8x16 pixels. Returns ------- bool: True if the sprite has at least one pixel on screen. """ def __eq__(self, other): return self._offset == other._offset def __repr__(self): tiles = ", ".join([str(t) for t in self.tiles]) return f"Sprite [{self._sprite_index}]: Position: ({self.x}, {self.y}), Shape: {self.shape}, Tiles: ({tiles}), On screen: {self.on_screen}"
Instance variables
var y
-
The Y-coordinate on the screen to show the Sprite. The (x,y) coordinate points to the top-left corner of the sprite.
Returns
int:
- Y-coordinate
var x
-
The X-coordinate on the screen to show the Sprite. The (x,y) coordinate points to the top-left corner of the sprite.
Returns
int:
- X-coordinate
var tile_identifier
-
The identifier of the tile the sprite uses. To get a better representation, see the method
Sprite.tiles
.For double-height sprites, this will only give the identifier of the first tile. The second tile will always be the one immediately following the first (
tile_identifier + 1
).Returns
int:
- unsigned tile index
var attr_obj_bg_priority
-
To better understand this values, look in the Pan Docs: VRAM Sprite Attribute Table (OAM).
Returns
bool:
- The state of the bit in the attributes lookup.
var attr_y_flip
-
To better understand this values, look in the Pan Docs: VRAM Sprite Attribute Table (OAM).
Returns
bool:
- The state of the bit in the attributes lookup.
var attr_x_flip
-
To better understand this values, look in the Pan Docs: VRAM Sprite Attribute Table (OAM).
Returns
bool:
- The state of the bit in the attributes lookup.
var attr_palette_number
-
To better understand this values, look in the Pan Docs: VRAM Sprite Attribute Table (OAM).
Returns
int:
- The state of the bit(s) in the attributes lookup.
var attr_cgb_bank_number
-
To better understand this values, look in the Pan Docs: VRAM Sprite Attribute Table (OAM).
Returns
bool:
- The state of the bit in the attributes lookup.
var shape
-
Sprites can be set to be 8x8 or 8x16 pixels (16 pixels tall). This is defined globally for the rendering hardware, so it's either all sprites using 8x16 pixels, or all sprites using 8x8 pixels.
Returns
(int, int): The width and height of the sprite.
var tiles
-
The Game Boy support sprites of single-height (8x8 pixels) and double-height (8x16 pixels).
In the single-height format, one tile is used. For double-height sprites, the Game Boy will also use the tile immediately following the identifier given, and render it below the first.
More information can be found in the Pan Docs: VRAM Sprite Attribute Table (OAM)
Returns
list:
- A list of
Tile
object(s) representing the graphics data for the sprite
var on_screen
-
To disable sprites from being rendered on screen, developers will place the sprite outside the area of the screen. This is often a good way to determine if the sprite is inactive.
This check doesn't take transparency into account, and will only check the sprite's bounding-box of 8x8 or 8x16 pixels.
Returns
bool:
- True if the sprite has at least one pixel on screen.
class Tile (mb, identifier)
-
The Game Boy uses tiles as the building block for all graphics on the screen. This base-class is used for
PyBoy.get_tile()
,Sprite
andTileMap
, when refering to graphics.This class is not meant to be instantiated by developers reading this documentation, but it will be created internally and returned by
Sprite.tiles
andTileMap.tile()
.The data of this class is static, apart from the image data, which is loaded from the Game Boy's memory when needed. Beware that the graphics for the tile can change between each call to
PyBoy.tick()
.Expand source code
class Tile: def __init__(self, mb, identifier): """ The Game Boy uses tiles as the building block for all graphics on the screen. This base-class is used for `pyboy.PyBoy.get_tile`, `pyboy.api.sprite.Sprite` and `pyboy.api.tilemap.TileMap`, when refering to graphics. This class is not meant to be instantiated by developers reading this documentation, but it will be created internally and returned by `pyboy.api.sprite.Sprite.tiles` and `pyboy.api.tilemap.TileMap.tile`. The data of this class is static, apart from the image data, which is loaded from the Game Boy's memory when needed. Beware that the graphics for the tile can change between each call to `pyboy.PyBoy.tick`. """ self.mb = mb if self.mb.cgb: if not (0 <= identifier < TILES_CGB): raise PyBoyOutOfBoundsException("Identifier out of range") else: if not (0 <= identifier < TILES): raise PyBoyOutOfBoundsException("Identifier out of range") self.data_address = LOW_TILEDATA + (16 * (identifier % TILES)) """ The tile data is defined in a specific area of the Game Boy. This function returns the address of the tile data corresponding to the tile identifier. It is advised to use `pyboy.api.tile.Tile.image` or one of the other `image`-functions if you want to view the tile. You can read how the data is read in the [Pan Docs: VRAM Tile Data](https://gbdev.io/pandocs/Tile_Data.html). Returns ------- int: address in VRAM where tile data starts """ if identifier < TILES: self.vram_bank = 0 else: self.vram_bank = 1 self.tile_identifier = identifier """ The Game Boy has a slightly complicated indexing system for tiles. This identifier unifies the otherwise complicated indexing system on the Game Boy into a single range of 0-383 (both included) or 0-767 for Game Boy Color. Returns ------- int: Unique identifier for the tile """ self.shape = (8, 8) """ Tiles are always 8x8 pixels. Returns ------- (int, int): The width and height of the tile. """ self.raw_buffer_format = self.mb.lcd.renderer.color_format """ Returns the color format of the raw screen buffer. Returns ------- str: Color format of the raw screen buffer. E.g. 'RGBA'. """ def image(self): """ Use this function to get an `PIL.Image` object of the tile. The image is 8x8 pixels. The format or "mode" might change at any time. Be aware, that the graphics for this tile can change between each call to `pyboy.PyBoy.tick`. Example: ```python >>> tile = pyboy.get_tile(1) >>> tile.image().save('tile_1.png') ``` Returns ------- PIL.Image : Image of tile in 8x8 pixels and RGBA colors. """ if Image is None: logger.error(f'{__name__}: Missing dependency "Pillow".') utils.PillowImportError()._raise_import_error() if utils.cython_compiled: return Image.fromarray(self._image_data().base, mode=self.raw_buffer_format) else: return Image.frombytes(self.raw_buffer_format, (8, 8), self._image_data()) def ndarray(self): """ Use this function to get an `numpy.ndarray` object of the tile. The array has a shape of (8, 8, 4) and each value is of `numpy.uint8`. The values corresponds to an image of 8x8 pixels with each sub-color in a separate cell. The format is given by `pyboy.api.tile.Tile.raw_buffer_format`. Be aware, that the graphics for this tile can change between each call to `pyboy.PyBoy.tick`. Example: ```python >>> tile1 = pyboy.get_tile(1) >>> tile1.ndarray()[:,:,0] # Upper part of "P" array([[255, 255, 255, 255, 255, 255, 255, 255], [255, 255, 255, 255, 255, 255, 255, 255], [255, 255, 255, 255, 255, 255, 255, 255], [255, 0, 0, 0, 0, 0, 255, 255], [255, 0, 0, 0, 0, 0, 0, 255], [255, 0, 0, 255, 255, 0, 0, 255], [255, 0, 0, 255, 255, 0, 0, 255], [255, 0, 0, 255, 255, 0, 0, 255]], dtype=uint8) >>> tile2 = pyboy.get_tile(2) >>> tile2.ndarray()[:,:,0] # Lower part of "P" array([[255, 0, 0, 0, 0, 0, 0, 255], [255, 0, 0, 0, 0, 0, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 255, 255, 255, 255, 255, 255, 255]], dtype=uint8) ``` Returns ------- numpy.ndarray : Array of shape (8, 8, 4) with data type of `numpy.uint8`. """ # The data is laid out as (X, red, green, blue), where X is currently always zero, but this is not guarenteed # across versions of PyBoy. return np.asarray(self._image_data()).view(dtype=np.uint8).reshape(8, 8, 4) def _image_data(self): """ Use this function to get the raw tile data. The data is a `memoryview` corresponding to 8x8 pixels in RGBA colors. Be aware, that the graphics for this tile can change between each call to `pyboy.PyBoy.tick`. Returns ------- memoryview : Image data of tile in 8x8 pixels and RGB colors. """ self.data = np.zeros((8, 8), dtype=np.uint32) for k in range(0, 16, 2): # 2 bytes for each line if self.vram_bank == 0: byte1 = self.mb.lcd.VRAM0[self.data_address + k - VRAM_OFFSET] byte2 = self.mb.lcd.VRAM0[self.data_address + k + 1 - VRAM_OFFSET] else: byte1 = self.mb.lcd.VRAM1[self.data_address + k - VRAM_OFFSET] byte2 = self.mb.lcd.VRAM1[self.data_address + k + 1 - VRAM_OFFSET] colorcode = self.mb.lcd.renderer.colorcode(byte1, byte2) for x in range(8): self.data[k // 2][x] = self.mb.lcd.BGP.getcolor((colorcode >> x * 8) & 0xFF) return self.data def __eq__(self, other): return self.data_address == other.data_address and self.vram_bank == other.vram_bank def __repr__(self): return f"Tile: {self.tile_identifier}"
Instance variables
var data_address
-
The tile data is defined in a specific area of the Game Boy. This function returns the address of the tile data corresponding to the tile identifier. It is advised to use
Tile.image()
or one of the otherimage
-functions if you want to view the tile.You can read how the data is read in the Pan Docs: VRAM Tile Data.
Returns
int:
- address in VRAM where tile data starts
var tile_identifier
-
The Game Boy has a slightly complicated indexing system for tiles. This identifier unifies the otherwise complicated indexing system on the Game Boy into a single range of 0-383 (both included) or 0-767 for Game Boy Color.
Returns
int:
- Unique identifier for the tile
var shape
-
Tiles are always 8x8 pixels.
Returns
(int, int): The width and height of the tile.
var raw_buffer_format
-
Returns the color format of the raw screen buffer.
Returns
str:
- Color format of the raw screen buffer. E.g. 'RGBA'.
Methods
def image(self)
-
Use this function to get an
PIL.Image
object of the tile. The image is 8x8 pixels. The format or "mode" might change at any time.Be aware, that the graphics for this tile can change between each call to
PyBoy.tick()
.Example:
>>> tile = pyboy.get_tile(1) >>> tile.image().save('tile_1.png')
Returns
PIL.Image :
- Image of tile in 8x8 pixels and RGBA colors.
Expand source code
def image(self): """ Use this function to get an `PIL.Image` object of the tile. The image is 8x8 pixels. The format or "mode" might change at any time. Be aware, that the graphics for this tile can change between each call to `pyboy.PyBoy.tick`. Example: ```python >>> tile = pyboy.get_tile(1) >>> tile.image().save('tile_1.png') ``` Returns ------- PIL.Image : Image of tile in 8x8 pixels and RGBA colors. """ if Image is None: logger.error(f'{__name__}: Missing dependency "Pillow".') utils.PillowImportError()._raise_import_error() if utils.cython_compiled: return Image.fromarray(self._image_data().base, mode=self.raw_buffer_format) else: return Image.frombytes(self.raw_buffer_format, (8, 8), self._image_data())
def ndarray(self)
-
Use this function to get an
numpy.ndarray
object of the tile. The array has a shape of (8, 8, 4) and each value is ofnumpy.uint8
. The values corresponds to an image of 8x8 pixels with each sub-color in a separate cell. The format is given byTile.raw_buffer_format
.Be aware, that the graphics for this tile can change between each call to
PyBoy.tick()
.Example:
>>> tile1 = pyboy.get_tile(1) >>> tile1.ndarray()[:,:,0] # Upper part of "P" array([[255, 255, 255, 255, 255, 255, 255, 255], [255, 255, 255, 255, 255, 255, 255, 255], [255, 255, 255, 255, 255, 255, 255, 255], [255, 0, 0, 0, 0, 0, 255, 255], [255, 0, 0, 0, 0, 0, 0, 255], [255, 0, 0, 255, 255, 0, 0, 255], [255, 0, 0, 255, 255, 0, 0, 255], [255, 0, 0, 255, 255, 0, 0, 255]], dtype=uint8) >>> tile2 = pyboy.get_tile(2) >>> tile2.ndarray()[:,:,0] # Lower part of "P" array([[255, 0, 0, 0, 0, 0, 0, 255], [255, 0, 0, 0, 0, 0, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 255, 255, 255, 255, 255, 255, 255]], dtype=uint8)
Returns
numpy.ndarray :
- Array of shape (8, 8, 4) with data type of
numpy.uint8
.
Expand source code
def ndarray(self): """ Use this function to get an `numpy.ndarray` object of the tile. The array has a shape of (8, 8, 4) and each value is of `numpy.uint8`. The values corresponds to an image of 8x8 pixels with each sub-color in a separate cell. The format is given by `pyboy.api.tile.Tile.raw_buffer_format`. Be aware, that the graphics for this tile can change between each call to `pyboy.PyBoy.tick`. Example: ```python >>> tile1 = pyboy.get_tile(1) >>> tile1.ndarray()[:,:,0] # Upper part of "P" array([[255, 255, 255, 255, 255, 255, 255, 255], [255, 255, 255, 255, 255, 255, 255, 255], [255, 255, 255, 255, 255, 255, 255, 255], [255, 0, 0, 0, 0, 0, 255, 255], [255, 0, 0, 0, 0, 0, 0, 255], [255, 0, 0, 255, 255, 0, 0, 255], [255, 0, 0, 255, 255, 0, 0, 255], [255, 0, 0, 255, 255, 0, 0, 255]], dtype=uint8) >>> tile2 = pyboy.get_tile(2) >>> tile2.ndarray()[:,:,0] # Lower part of "P" array([[255, 0, 0, 0, 0, 0, 0, 255], [255, 0, 0, 0, 0, 0, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 0, 0, 255, 255, 255, 255, 255], [255, 255, 255, 255, 255, 255, 255, 255]], dtype=uint8) ``` Returns ------- numpy.ndarray : Array of shape (8, 8, 4) with data type of `numpy.uint8`. """ # The data is laid out as (X, red, green, blue), where X is currently always zero, but this is not guarenteed # across versions of PyBoy. return np.asarray(self._image_data()).view(dtype=np.uint8).reshape(8, 8, 4)
class TileMap (pyboy, mb, select)
-
The Game Boy has two tile maps, which defines what is rendered on the screen. These are also referred to as "background" and "window".
Use
pyboy.tilemap_background
andpyboy.tilemap_window
to instantiate this object.This object defines
__getitem__
, which means it can be accessed with the square brackets to get a tile identifier at a given coordinate.Example:
>>> pyboy.tilemap_window[8,8] 1 >>> pyboy.tilemap_window[7:12,8] [0, 1, 0, 1, 0] >>> pyboy.tilemap_window[7:12,8:11] [[0, 1, 0, 1, 0], [0, 2, 3, 4, 5], [0, 0, 6, 0, 0]]
Each element in the matrix, is the tile identifier of the tile to be shown on screen for each position. If you need the entire 32x32 tile map, you can use the shortcut:
tilemap[:,:]
.Expand source code
class TileMap: def __init__(self, pyboy, mb, select): """ The Game Boy has two tile maps, which defines what is rendered on the screen. These are also referred to as "background" and "window". Use `pyboy.tilemap_background` and `pyboy.tilemap_window` to instantiate this object. This object defines `__getitem__`, which means it can be accessed with the square brackets to get a tile identifier at a given coordinate. Example: ``` >>> pyboy.tilemap_window[8,8] 1 >>> pyboy.tilemap_window[7:12,8] [0, 1, 0, 1, 0] >>> pyboy.tilemap_window[7:12,8:11] [[0, 1, 0, 1, 0], [0, 2, 3, 4, 5], [0, 0, 6, 0, 0]] ``` Each element in the matrix, is the tile identifier of the tile to be shown on screen for each position. If you need the entire 32x32 tile map, you can use the shortcut: `tilemap[:,:]`. """ self.pyboy = pyboy self.mb = mb self._select = select self._use_tile_objects = False self.frame_count_update = 0 self.__refresh_lcdc() self.shape = (32, 32) """ Tile maps are always 32x32 tiles. Returns ------- (int, int): The width and height of the tile map. """ def _refresh_lcdc(self): if self.frame_count_update == self.pyboy.frame_count: return 0 self.__refresh_lcdc() def __refresh_lcdc(self): """ The tile data and view that is showed on the background and window respectively can change dynamically. If you believe it has changed, you can use this method to update the tilemap from the LCDC register. """ LCDC = LCDCRegister(self.mb.getitem(LCDC_OFFSET)) if self._select == "WINDOW": self.map_offset = HIGH_TILEMAP if LCDC.windowmap_select else LOW_TILEMAP self.signed_tile_data = not bool(LCDC.tiledata_select) elif self._select == "BACKGROUND": self.map_offset = HIGH_TILEMAP if LCDC.backgroundmap_select else LOW_TILEMAP self.signed_tile_data = not bool(LCDC.tiledata_select) else: raise KeyError(f"Invalid tilemap selected: {self._select}") def search_for_identifiers(self, identifiers): """ Provided a list of tile identifiers, this function will find all occurrences of these in the tilemap and return the coordinates where each identifier is found. Example: ``` >>> pyboy.tilemap_window.search_for_identifiers([5,3]) [[[9, 11]], [[9, 9], [9, 12]]] ``` Meaning, that tile identifier `43` is found at the positions: (0,0), (2,4), and (8,7), while tile identifier `123`was not found anywhere. Args: identifiers (list): List of tile identifiers (int) Returns ------- list: list of matches for every tile identifier in the input """ # TODO: Crude implementation tilemap_identifiers = np.asarray(self[:, :], dtype=np.uint32) matches = [] for i in identifiers: matches.append([[int(y) for y in x] for x in np.argwhere(tilemap_identifiers == i)]) return matches def _tile_address(self, column, row): """ Returns the memory address in the tilemap for the tile at the given coordinate. The address contains the index of tile which will be shown at this position. This should not be confused with the actual tile data of `pyboy.api.tile.Tile.data_address`. This can be used as an global identifier for the specific location in a tile map. Be aware, that the tile index referenced at the memory address might change between calls to `pyboy.PyBoy.tick`. And the tile data for the same tile index might also change to display something else on the screen. The index might also be a signed number. Depending on if it is signed or not, will change where the tile data is read from. Use `pyboy.api.tilemap.TileMap.signed_tile_index` to test if the indexes are signed for this tile view. You can read how the indexes work in the [Pan Docs: VRAM Tile Data](https://gbdev.io/pandocs/Tile_Data.html). Args: column (int): Column in this tile map. row (int): Row in this tile map. Returns ------- int: Address in the tile map to read a tile index. """ if not 0 <= column < 32: raise IndexError("column is out of bounds. Value of 0 to 31 is allowed") if not 0 <= row < 32: raise IndexError("row is out of bounds. Value of 0 to 31 is allowed") return self.map_offset + 32 * row + column def tile(self, column, row): """ Provides a `pyboy.api.tile.Tile`-object which allows for easy interpretation of the tile data. The object is agnostic to where it was found in the tilemap. I.e. equal `pyboy.api.tile.Tile`-objects might be returned from two different coordinates in the tile map if they are shown different places on the screen. Args: column (int): Column in this tile map. row (int): Row in this tile map. Returns ------- `pyboy.api.tile.Tile`: Tile object corresponding to the tile index at the given coordinate in the tile map. """ return Tile(self.mb, self.tile_identifier(column, row)) def tile_identifier(self, column, row): """ Returns an identifier (integer) of the tile at the given coordinate in the tile map. The identifier can be used to quickly recognize what is on the screen through this tile view. This identifier unifies the otherwise complicated indexing system on the Game Boy into a single range of 0-383 (both included). You can read how the indexes work in the [Pan Docs: VRAM Tile Data](https://gbdev.io/pandocs/Tile_Data.html). Args: column (int): Column in this tile map. row (int): Row in this tile map. Returns ------- int: Tile identifier. """ self._refresh_lcdc() tile = self.mb.getitem(self._tile_address(column, row)) if self.signed_tile_data: return ((tile ^ 0x80) - 128) + LOW_TILEDATA_NTILES else: return tile def __repr__(self): self._refresh_lcdc() adjust = 4 _use_tile_objects = self._use_tile_objects self.use_tile_objects(False) return_data = ( f"Tile Map Address: {self.map_offset:#0{6}x}, " + f"Signed Tile Data: {'Yes' if self.signed_tile_data else 'No'}\n" + " " * 5 + "".join([f"{i: <4}" for i in range(32)]) + "\n" + "_" * (adjust * 32 + 2) + "\n" + "\n".join( [ f"{i: <3}| " + "".join([str(tile).ljust(adjust) for tile in line]) for i, line in enumerate(self[:, :]) ] ) ) self.use_tile_objects(_use_tile_objects) return return_data def use_tile_objects(self, switch): """ Used to change which object is returned when using the ``__getitem__`` method (i.e. `tilemap[0,0]`). Args: switch (bool): If True, accesses will return `pyboy.api.tile.Tile`-object. If False, accesses will return an `int`. """ self._use_tile_objects = switch def _fix_slice(self, addr): if addr.step is None: step = 1 else: step = addr.step if addr.start is None: start = 0 else: start = addr.start if addr.stop is None: stop = 32 else: stop = addr.stop if step < 0: raise ValueError("Reversed ranges are unsupported") elif start > stop: raise ValueError("Invalid range") return start, stop, step def __getitem__(self, xy): if isinstance(xy, (int, slice)): x = xy y = slice(None) else: x, y = xy x_slice = isinstance(x, slice) y_slice = isinstance(y, slice) if x_slice: x = self._fix_slice(x) else: assert isinstance(x, int) if y_slice: y = self._fix_slice(y) else: assert isinstance(y, int) if self._use_tile_objects: tile_fun = self.tile else: tile_fun = lambda x, y: self.tile_identifier(x, y) if x_slice and y_slice: return [[tile_fun(_x, _y) for _x in range(*x)] for _y in range(*y)] elif x_slice: return [tile_fun(_x, y) for _x in range(*x)] elif y_slice: return [tile_fun(x, _y) for _y in range(*y)] else: return tile_fun(x, y)
Instance variables
var shape
-
Tile maps are always 32x32 tiles.
Returns
(int, int): The width and height of the tile map.
Methods
def search_for_identifiers(self, identifiers)
-
Provided a list of tile identifiers, this function will find all occurrences of these in the tilemap and return the coordinates where each identifier is found.
Example:
>>> pyboy.tilemap_window.search_for_identifiers([5,3]) [[[9, 11]], [[9, 9], [9, 12]]]
Meaning, that tile identifier
43
is found at the positions: (0,0), (2,4), and (8,7), while tile identifier123
was not found anywhere.Args
identifiers
:list
- List of tile identifiers (int)
Returns
list:
- list of matches for every tile identifier in the input
Expand source code
def search_for_identifiers(self, identifiers): """ Provided a list of tile identifiers, this function will find all occurrences of these in the tilemap and return the coordinates where each identifier is found. Example: ``` >>> pyboy.tilemap_window.search_for_identifiers([5,3]) [[[9, 11]], [[9, 9], [9, 12]]] ``` Meaning, that tile identifier `43` is found at the positions: (0,0), (2,4), and (8,7), while tile identifier `123`was not found anywhere. Args: identifiers (list): List of tile identifiers (int) Returns ------- list: list of matches for every tile identifier in the input """ # TODO: Crude implementation tilemap_identifiers = np.asarray(self[:, :], dtype=np.uint32) matches = [] for i in identifiers: matches.append([[int(y) for y in x] for x in np.argwhere(tilemap_identifiers == i)]) return matches
def tile(self, column, row)
-
Provides a
Tile
-object which allows for easy interpretation of the tile data. The object is agnostic to where it was found in the tilemap. I.e. equalTile
-objects might be returned from two different coordinates in the tile map if they are shown different places on the screen.Args
column
:int
- Column in this tile map.
row
:int
- Row in this tile map.
Returns
Tile
: Tile object corresponding to the tile index at the given coordinate in the tile map.Expand source code
def tile(self, column, row): """ Provides a `pyboy.api.tile.Tile`-object which allows for easy interpretation of the tile data. The object is agnostic to where it was found in the tilemap. I.e. equal `pyboy.api.tile.Tile`-objects might be returned from two different coordinates in the tile map if they are shown different places on the screen. Args: column (int): Column in this tile map. row (int): Row in this tile map. Returns ------- `pyboy.api.tile.Tile`: Tile object corresponding to the tile index at the given coordinate in the tile map. """ return Tile(self.mb, self.tile_identifier(column, row))
def tile_identifier(self, column, row)
-
Returns an identifier (integer) of the tile at the given coordinate in the tile map. The identifier can be used to quickly recognize what is on the screen through this tile view.
This identifier unifies the otherwise complicated indexing system on the Game Boy into a single range of 0-383 (both included).
You can read how the indexes work in the Pan Docs: VRAM Tile Data.
Args
column
:int
- Column in this tile map.
row
:int
- Row in this tile map.
Returns
int:
- Tile identifier.
Expand source code
def tile_identifier(self, column, row): """ Returns an identifier (integer) of the tile at the given coordinate in the tile map. The identifier can be used to quickly recognize what is on the screen through this tile view. This identifier unifies the otherwise complicated indexing system on the Game Boy into a single range of 0-383 (both included). You can read how the indexes work in the [Pan Docs: VRAM Tile Data](https://gbdev.io/pandocs/Tile_Data.html). Args: column (int): Column in this tile map. row (int): Row in this tile map. Returns ------- int: Tile identifier. """ self._refresh_lcdc() tile = self.mb.getitem(self._tile_address(column, row)) if self.signed_tile_data: return ((tile ^ 0x80) - 128) + LOW_TILEDATA_NTILES else: return tile
def use_tile_objects(self, switch)
-
Used to change which object is returned when using the
__getitem__
method (i.e.pyboy.api.tilemap[0,0]
).Args
switch
:bool
- If True, accesses will return
Tile
-object. If False, accesses will return anint
.
Expand source code
def use_tile_objects(self, switch): """ Used to change which object is returned when using the ``__getitem__`` method (i.e. `tilemap[0,0]`). Args: switch (bool): If True, accesses will return `pyboy.api.tile.Tile`-object. If False, accesses will return an `int`. """ self._use_tile_objects = switch