Asset Loading
Relevant source files
The following files were used as context for generating this wiki page:The asset loading system in souless is a decentralized process managed within the preload lifecycle hook of Phaser Scenes. It uses a combination of aggregator loaders and static methods on entity classes to load necessary game assets like spritesheets, images, and UI elements. This approach ensures that assets are loaded on-demand by the scenes that require them, promoting modularity and maintainability.
The core principle is that each major entity (like a character or enemy) is responsible for declaring its own assets. These declarations are then aggregated by specialized loader classes, which are invoked from the active scene's preload method. This keeps the scene's preload method clean and delegates loading logic to the appropriate modules.
Architecture Overview
Asset loading is initiated within a Phaser Scene's preload method, typically in GameScene.js. The scene calls high-level aggregator loaders, which in turn delegate the loading task to more specific classes or static methods.
The general flow is as follows:
- A
Phaser.Scene(e.g.,GameScene) begins itspreloadphase. - The scene calls the
preloadmethod of aggregator loaders likeCharacterAssetLoaderandEnemyAssetLoader. - These aggregators then call the static
preloadmethod on individual entity classes (e.g.,Monk.preload(scene),Slime.preload(scene)). - The entity's static
preloadmethod reads asset paths from configuration files (e.g.,characterData.js,enemiesData.js) and uses the scene'sthis.loadobject to load the assets. - Scene-specific assets like map layers and UI elements are loaded directly within the scene's
preloadmethod.
This flow is illustrated in the diagram below.
This diagram shows the delegation of asset loading from the GameScene to various loaders and finally to the individual entity classes.
Sources: src/scenes/GameScene.js:120-155, src/utils/CharacterAssetLoader.js:13-23, src/utils/EnemyAssetLoader.js:6-11, src/entities/enemies/base/EnemyBase.js:292-306
Key Loader Components
The system is composed of several key loader classes that handle different types of assets.
| Loader Class | Responsibility | Source File |
|---|---|---|
CharacterAssetLoader | Aggregates and loads all playable character spritesheets and their skill icons by calling their respective preload methods. | src/utils/CharacterAssetLoader.js |
EnemyAssetLoader | Aggregates and loads spritesheets for standard, non-boss enemies. | src/utils/EnemyAssetLoader.js |
EnemyBase | Provides a static preload method to load assets for any given enemy type (including bosses) based on configuration in enemiesData.js. | src/entities/enemies/base/EnemyBase.js |
SkillIconLoader | Loads all skill icon images for all characters. Its preload method is called by CharacterAssetLoader. | src/utils/CharacterAssetLoader.js:22 (usage), src/scenes/UITestScene.js:40 (usage) |
GameScene / MainMenuScene | Orchestrate the loading process and handle scene-specific assets like map backgrounds and UI elements. | src/scenes/GameScene.js, src/scenes/MainMenuScene.js |
Character Asset Loading
CharacterAssetLoader is a central point for loading all assets related to playable characters. It imports all character classes (e.g., Monk, Assassin, Bladekeeper) and calls the static preload method on each one. It also triggers the loading of skill icons via SkillIconLoader.
// src/utils/CharacterAssetLoader.js:13-23
export default class CharacterAssetLoader {
static preload(scene) {
Soul.preload(scene);
Monk.preload(scene);
Assassin.preload(scene);
Bladekeeper.preload(scene);
Fireknight.preload(scene);
Ranger.preload(scene);
Mauler.preload(scene);
Princess.preload(scene);
SkillIconLoader.preload(scene);
}
}
Sources: src/utils/CharacterAssetLoader.js:13-23
The actual asset paths are defined in characterData.js. Each character has a skills object where each skill contains an icon property with the path to the image file.
// src/config/characterData.js:23-26
skills: {
attack: {
type: 'instant',
icon: 'assets/skills/monk/attack.png',
Sources: src/config/characterData.js:23-26
Enemy Asset Loading
Enemy assets are loaded in two ways:
- Standard Enemies:
EnemyAssetLoadercalls the staticpreloadmethod for a predefined list of common enemies likeSlimeandCanine. - All Enemies (including Bosses): The static method
EnemyBase.preload(scene, enemyType)provides a generic way to load assets for any enemy. It reads the configuration fromenemiesData.jsfor the specifiedenemyType, finds all asset paths, and loads them as spritesheets.
The GameScene uses EnemyAssetLoader for common enemies and calls EnemyBase.preload() directly for bosses defined in the map configuration.
// src/entities/enemies/base/EnemyBase.js:292-306
static preload(scene, enemyType) {
const data = EnemiesData[enemyType];
if (!data) {
console.error(`Enemy data not found: ${enemyType}`);
return;
}
const { frameWidth, frameHeight } = data.sprite;
const assets = data.assets;
// 모든 assets 키(idle, hit, death, attack 등)를 순회하며 로드
Object.entries(assets).forEach(([key, path]) => {
scene.load.spritesheet(`${enemyType}_${key}`, path, { frameWidth, frameHeight });
});
}
Sources: src/entities/enemies/base/EnemyBase.js:292-306
The asset paths are defined in enemiesData.js under the assets key for each enemy.
// src/config/enemiesData.js:23-29
assets: {
idle: '/assets/enemy/slime/Slime_Spiked_Idle.png',
hit: '/assets/enemy/slime/Slime_Spiked_Hit.png',
death: '/assets/enemy/slime/Slime_Spiked_Death.png',
attack: '/assets/enemy/slime/Slime_Spiked_Hit.png',
},
Sources: src/config/enemiesData.js:23-29
Scene-Specific Asset Loading
Scenes like GameScene, MainMenuScene, and UITestScene are responsible for loading assets unique to them.
GameScene: Loads map layer images based on the configuration inmapData.jsand portal effect images.MainMenuScene: Loads its own background and title images.UITestScene: Loads UI-specific spritesheets, such as the skill UI frame.
This is handled directly in each scene's preload method. For example, GameScene loads its map assets like this:
// src/scenes/GameScene.js:125-128
loadMapAssets() {
this.mapModel = new MapModel(this, this.currentMapKey, this.mapConfig, true);
this.mapModel.preload();
this.mapConfig.layers.forEach((layer) => {
this.load.image(layer.key, layer.path);
});
}
Sources: src/scenes/GameScene.js:125-131
Loading Sequence
The following sequence diagram illustrates the call chain for asset loading when GameScene is created.
This diagram shows how GameScene orchestrates the loading by calling static methods on loader and entity classes, which then use the scene's loader instance to perform the actual loading operation.
Sources: src/scenes/GameScene.js:120-155, src/utils/CharacterAssetLoader.js, src/entities/enemies/base/EnemyBase.js:292-306
Summary
The asset loading strategy in souless is robust and scalable. By delegating loading responsibilities to the entities themselves and using aggregator modules, the system avoids monolithic loader files and keeps scene code focused on orchestration. Configuration files like characterData.js and enemiesData.js serve as a single source of truth for asset paths, making it easy to manage and update game assets. This modular approach ensures that as the project grows, the asset management system remains organized and easy to extend.