This document describes the mapping system in Fluffy-Swizz Interactive, including how maps are created, loaded, and managed during gameplay.
The mapping system provides the game environment where gameplay takes place. It handles tile-based maps, collision detection with map elements, and map transitions.
The game supports several map types:
- Tiled JSON Maps - Created with the Tiled Map Editor
- Programmatically Generated Maps - Created at runtime
- Array-Based Maps - Defined using 2D arrays
The mapping system is managed by the TileMapManager class.
Methods:
constructor(scene)- Initializes the map manager for a scenepreloadMaps()- Preloads all configured mapscreateMapFromTiled(key, options)- Creates a map from Tiled JSON datacreateMapFromArray(data, tilesetKey, options)- Creates a map from a 2D arraygenerateRandomMap(width, height, options)- Generates a random mapgenerateSequentialMap(width, height)- Generates a sequential map for testingswitchMap(key, options)- Switches to a new mapgetAvailableMaps()- Returns a list of all available mapsgetMapDimensions()- Returns dimensions of the current mapgetTileAt(x, y)- Gets tile at world coordinatessetTileAt(x, y, index)- Sets tile at world coordinatesgetMapProperty(name)- Gets a custom property from the mapdestroy()- Cleans up all map resources
Properties:
scene- Reference to the current scenecurrentMap- Current active mapcurrentMapKey- Key of the current mapmaps- Collection of loaded mapsmapConfig- Configuration for mapslayers- Map layers (ground, walls, objects, etc.)collisionLayers- Layers that have collision enabled
Maps are configured with specific properties:
// Example map configuration
this.mapConfig = {
'arena': {
key: 'arena',
file: 'assets/maps/arena.json',
tilesets: ['scifi_tiles'],
collisionLayers: ['walls'],
spawnPoints: {
player: { x: 512, y: 384 },
enemies: [
{ x: 200, y: 200 },
{ x: 800, y: 200 },
{ x: 200, y: 600 },
{ x: 800, y: 600 }
]
}
},
'laboratory': {
key: 'laboratory',
file: 'assets/maps/laboratory.json',
tilesets: ['scifi_tiles', 'lab_tiles'],
collisionLayers: ['walls', 'equipment'],
spawnPoints: {
player: { x: 100, y: 100 },
enemies: [
{ x: 300, y: 300 },
{ x: 500, y: 300 },
{ x: 700, y: 300 }
]
}
},
// Additional maps...
};Maps are preloaded during the Preloader scene:
// In TileMapManager.js
preloadMaps() {
// Preload all map files
for (const mapKey in this.mapConfig) {
const map = this.mapConfig[mapKey];
// Load map file if not already loaded
if (map.file && !this.scene.cache.tilemap.exists(map.key)) {
this.scene.load.tilemapTiledJSON(map.key, map.file);
}
// Load tilesets if not already loaded
for (const tilesetKey of map.tilesets) {
if (!this.scene.textures.exists(tilesetKey)) {
this.scene.load.image(
tilesetKey,
`assets/tilesets/${tilesetKey}.png`
);
}
}
}
}Maps can be created from Tiled JSON data:
// In TileMapManager.js
createMapFromTiled(key, options = {}) {
// Get map configuration
const mapConfig = this.mapConfig[key];
if (!mapConfig) {
console.error(`Map configuration not found for key: ${key}`);
return null;
}
// Create tilemap from loaded JSON
const map = this.scene.make.tilemap({ key: mapConfig.key });
// Add tilesets
const tilesets = [];
for (const tilesetKey of mapConfig.tilesets) {
tilesets.push(map.addTilesetImage(tilesetKey, tilesetKey));
}
// Create layers
const layers = {};
for (const layerData of map.layers) {
const layer = map.createLayer(
layerData.name,
tilesets,
0, 0
);
// Set layer properties
if (options.scale) {
layer.setScale(options.scale);
}
// Set collision if this is a collision layer
if (mapConfig.collisionLayers.includes(layerData.name)) {
layer.setCollisionByProperty({ collides: true });
// Optionally show debug graphics
if (options.debug) {
const debugGraphics = this.scene.add.graphics();
layer.renderDebug(debugGraphics, {
tileColor: null,
collidingTileColor: new Phaser.Display.Color(243, 134, 48, 128),
faceColor: new Phaser.Display.Color(40, 39, 37, 255)
});
}
}
// Store layer reference
layers[layerData.name] = layer;
}
// Store map data
this.currentMap = map;
this.currentMapKey = key;
this.layers = layers;
// Set world bounds based on map size
this.scene.physics.world.setBounds(
0, 0,
map.widthInPixels * (options.scale || 1),
map.heightInPixels * (options.scale || 1)
);
// Return the created map
return {
map: map,
layers: layers,
config: mapConfig
};
}Maps can also be created programmatically:
// In TileMapManager.js
generateRandomMap(width, height, options = {}) {
// Create empty tilemap
const map = this.scene.make.tilemap({
tileWidth: 32,
tileHeight: 32,
width: width,
height: height
});
// Add tileset
const tileset = map.addTilesetImage(
options.tilesetKey || 'scifi_tiles'
);
// Create layers
const groundLayer = map.createBlankLayer('ground', tileset);
const wallsLayer = map.createBlankLayer('walls', tileset);
// Fill ground layer with floor tiles
const floorTile = options.floorTile || 1;
groundLayer.fill(floorTile);
// Generate random walls
const wallTile = options.wallTile || 2;
const wallDensity = options.wallDensity || 0.1;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
// Add border walls
if (x === 0 || y === 0 || x === width - 1 || y === height - 1) {
wallsLayer.putTileAt(wallTile, x, y);
continue;
}
// Random internal walls
if (Math.random() < wallDensity) {
wallsLayer.putTileAt(wallTile, x, y);
}
}
}
// Set collision on walls layer
wallsLayer.setCollisionByExclusion([-1]);
// Store map data
this.currentMap = map;
this.currentMapKey = 'random';
this.layers = {
ground: groundLayer,
walls: wallsLayer
};
// Set world bounds
this.scene.physics.world.setBounds(
0, 0,
map.widthInPixels,
map.heightInPixels
);
// Return the created map
return {
map: map,
layers: this.layers,
config: {
key: 'random',
spawnPoints: this.generateSpawnPoints(width, height, wallsLayer)
}
};
}The game can switch between maps during gameplay:
// In TileMapManager.js
switchMap(key, options = {}) {
// Clean up current map if it exists
if (this.currentMap) {
// Destroy all layers
for (const layerName in this.layers) {
this.layers[layerName].destroy();
}
// Clear references
this.layers = {};
}
// Create new map
const mapData = this.createMapFromTiled(key, options);
// Emit map change event
this.scene.events.emit('map-changed', {
key: key,
map: mapData.map,
spawnPoints: mapData.config.spawnPoints
});
return mapData;
}The mapping system integrates with Phaser's physics for collision detection:
// In Game.js
setupMapCollision() {
// Get collision layers
const collisionLayers = [];
for (const layerName of this.mapManager.mapConfig[this.currentMapKey].collisionLayers) {
collisionLayers.push(this.mapManager.layers[layerName]);
}
// Set up player collision with map
for (const layer of collisionLayers) {
this.physics.add.collider(this.player, layer);
}
// Set up enemy collision with map
for (const layer of collisionLayers) {
this.physics.add.collider(this.enemyGroup, layer);
}
// Set up bullet collision with map
for (const layer of collisionLayers) {
this.physics.add.collider(
this.bulletPool.getGroup(),
layer,
(bullet) => {
// Create impact effect
this.spritePool.createHitEffect(bullet.x, bullet.y, {
tint: 0xCCCCCC,
scale: 0.3
});
// Release bullet back to pool
this.bulletPool.releaseBullet(bullet);
},
null,
this
);
}
}Maps define spawn points for players and enemies:
// Example of using spawn points
setupPlayer() {
// Get player spawn point from current map
const spawnPoint = this.mapManager.currentMap.config.spawnPoints.player;
// Create player at spawn point
this.player = new Player(
this,
spawnPoint.x,
spawnPoint.y,
this.gameMode
);
}
spawnEnemies() {
// Get enemy spawn points from current map
const spawnPoints = this.mapManager.currentMap.config.spawnPoints.enemies;
// Spawn enemies at each point
for (const spawnPoint of spawnPoints) {
this.enemyFactory.createEnemy(
'basic',
spawnPoint.x,
spawnPoint.y
);
}
}Maps can include interactive tiles with special properties:
// In TileMapManager.js
setupInteractiveTiles() {
// Check if we have an 'interactive' layer
if (!this.layers.interactive) return;
// Get all tiles with the 'interactive' property
const interactiveTiles = this.layers.interactive.filterTiles(
tile => tile.properties.interactive
);
// Set up each interactive tile
for (const tile of interactiveTiles) {
// Get tile properties
const props = tile.properties;
// Set up based on interaction type
switch (props.interactionType) {
case 'health':
this.setupHealthTile(tile);
break;
case 'teleport':
this.setupTeleportTile(tile);
break;
case 'switch':
this.setupSwitchTile(tile);
break;
// Additional interaction types...
}
}
}
setupHealthTile(tile) {
// Create a physics body for this tile
this.scene.physics.add.existing(tile, true);
// Set up overlap with player
this.scene.physics.add.overlap(
this.scene.player,
tile,
() => {
// Heal the player
this.scene.player.heal(tile.properties.healAmount || 20);
// Play healing sound
this.scene.soundManager.playSound('health_pickup');
// Create healing effect
this.scene.spritePool.createHealEffect(
tile.pixelX + 16,
tile.pixelY + 16
);
// Disable the tile temporarily
tile.properties.active = false;
tile.alpha = 0.3;
// Reactivate after cooldown
this.scene.time.delayedCall(
tile.properties.cooldown || 30000,
() => {
tile.properties.active = true;
tile.alpha = 1;
}
);
},
() => tile.properties.active !== false
);
}Maps can include destructible elements:
// In TileMapManager.js
setupDestructibles() {
// Check if we have a 'destructibles' layer
if (!this.layers.destructibles) return;
// Get all tiles with the 'destructible' property
const destructibleTiles = this.layers.destructibles.filterTiles(
tile => tile.properties.destructible
);
// Set up each destructible tile
for (const tile of destructibleTiles) {
// Set health property if not already set
if (tile.properties.health === undefined) {
tile.properties.health = 100;
}
// Create a physics body for this tile
this.scene.physics.add.existing(tile, true);
// Set up collision with bullets
this.scene.physics.add.overlap(
this.scene.bulletPool.getGroup(),
tile,
(tile, bullet) => {
// Reduce tile health
tile.properties.health -= bullet.damage;
// Create impact effect
this.scene.spritePool.createHitEffect(
bullet.x,
bullet.y,
{
tint: 0xCCCCCC,
scale: 0.3
}
);
// Release bullet back to pool
this.scene.bulletPool.releaseBullet(bullet);
// Check if tile is destroyed
if (tile.properties.health <= 0) {
// Play destruction sound
this.scene.soundManager.playSound('tile_destroy');
// Create destruction effect
this.scene.spritePool.createDeathEffect(
tile.pixelX + 16,
tile.pixelY + 16,
{
tint: 0xAAAAAA,
scale: 0.8,
lifespan: 1000
}
);
// Remove the tile
this.layers.destructibles.removeTileAt(
tile.x,
tile.y
);
// Potentially spawn pickup
if (Math.random() < 0.3) {
this.scene.spritePool.createXPPickup(
tile.pixelX + 16,
tile.pixelY + 16,
{ value: 5 }
);
}
}
}
);
}
}The game can transition between maps during gameplay:
// In Game.js
transitionToMap(mapKey) {
// Show transition effect
this.cameras.main.fade(500, 0, 0, 0);
// Wait for fade to complete
this.cameras.main.once('camerafadeoutcomplete', () => {
// Switch to new map
const mapData = this.mapManager.switchMap(mapKey);
// Move player to spawn point
const spawnPoint = mapData.config.spawnPoints.player;
this.player.setPosition(spawnPoint.x, spawnPoint.y);
// Set up collisions for new map
this.setupMapCollision();
// Spawn enemies for new map
this.spawnEnemies();
// Fade back in
this.cameras.main.fadeIn(500);
});
}The camera follows the player and is constrained by map boundaries:
// In Game.js
setupCamera() {
// Set camera bounds to match map size
this.cameras.main.setBounds(
0, 0,
this.mapManager.currentMap.widthInPixels,
this.mapManager.currentMap.heightInPixels
);
// Set camera to follow player
this.cameras.main.startFollow(
this.player,
true,
0.1, 0.1 // Lerp factor for smooth following
);
// Set camera zoom
this.cameras.main.setZoom(1);
// Optional: Add camera effects
this.cameras.main.setBackgroundColor('#111111');
// Add camera shake on player damage
this.events.on('player-damage', () => {
this.cameras.main.shake(100, 0.01);
});
}The mapping system uses culling to improve performance:
// In TileMapManager.js
optimizeMapRendering() {
// Enable culling on all layers
for (const layerName in this.layers) {
this.layers[layerName].setCullPadding(2);
this.layers[layerName].setSkipCull(false);
}
// Set render order to optimize for top-down view
this.currentMap.setRenderOrder(Phaser.Tilemaps.RENDER_ORDER_RIGHT_DOWN);
}The mapping system supports animated tiles:
// In TileMapManager.js
setupAnimatedTiles() {
// Find tiles with animation properties
for (const layerName in this.layers) {
const layer = this.layers[layerName];
// Get all tiles with animation data
const animatedTiles = layer.filterTiles(
tile => tile.properties.animated
);
// Set up each animated tile
for (const tile of animatedTiles) {
const props = tile.properties;
// Create animation if it doesn't exist
const animKey = `tile_anim_${props.animationKey}`;
if (!this.scene.anims.exists(animKey)) {
this.scene.anims.create({
key: animKey,
frames: this.scene.anims.generateFrameNumbers(
props.spritesheet,
{
start: props.startFrame,
end: props.endFrame
}
),
frameRate: props.frameRate || 10,
repeat: -1
});
}
// Replace tile with sprite
const sprite = this.scene.add.sprite(
tile.pixelX + 16,
tile.pixelY + 16,
props.spritesheet
);
// Play animation
sprite.play(animKey);
// Set sprite depth to match layer
sprite.setDepth(layer.depth);
// Remove the original tile
layer.removeTileAt(tile.x, tile.y);
}
}
}This documentation is maintained by the Fluffy-Swizz Interactive development team.