Render rooms with pseudo-isometric Phaser architecture

This commit is contained in:
Admin 2026-09-22 00:12:23 +00:00
parent 0512a51c8b
commit 809db4836f

View File

@ -356,6 +356,43 @@ class HouseScene extends Phaser.Scene {
window.addEventListener('pagehide', () => this.persist(), { once: true }) window.addEventListener('pagehide', () => this.persist(), { once: true })
} }
createPolygonMask(points) {
const shape = this.make.graphics({ add: false })
shape.fillStyle(0xffffff, 1)
shape.beginPath()
shape.moveTo(points[0].x, points[0].y)
for (let i = 1; i < points.length; i += 1) {
shape.lineTo(points[i].x, points[i].y)
}
shape.closePath()
shape.fillPath()
return shape.createGeometryMask()
}
addMaskedTile(room, texture, points, depth, tileScale = 0.7, tint = null) {
const tile = this.add.tileSprite(
room.x + 7,
room.y + 7,
room.width - 14,
room.height - 14,
texture
)
.setOrigin(0, 0)
.setDepth(depth)
tile.setTileScale(tileScale, tileScale)
tile.setMask(this.createPolygonMask(points))
if (tint !== null) {
tile.setTint(tint)
}
return tile
}
drawHouse() { drawHouse() {
const graphics = this.add.graphics().setDepth(0) const graphics = this.add.graphics().setDepth(0)
@ -371,66 +408,119 @@ class HouseScene extends Phaser.Scene {
for (const room of ROOMS) { for (const room of ROOMS) {
const skin = ROOM_SKINS[room.id] const skin = ROOM_SKINS[room.id]
const inset = 7 const inset = 7
const wallTop = room.y + 8 const sideInset = Math.round(room.width * 0.18)
const wallBottom = room.walkBand.minY const wallTop = room.y + 9
const wallHeight = Math.max(36, wallBottom - wallTop) const backTop = wallTop + Math.max(10, Math.round(FLOOR_H * 0.04))
const floorTop = wallBottom - 1 const horizonY = room.walkBand.minY
const floorHeight = room.y + room.height - floorTop - 7 const bottomY = room.y + room.height - 8
const left = room.x + inset
const right = room.x + room.width - inset
const backLeft = room.x + sideInset
const backRight = room.x + room.width - sideInset
// Base room backing gives clean rounded corners behind all textures. // Base backing remains rectangular so room selection and hold feedback
// always have a clean silhouette.
graphics.fillStyle(room.color, 1) graphics.fillStyle(room.color, 1)
graphics.fillRoundedRect(room.x, room.y, room.width, room.height, 14) graphics.fillRoundedRect(room.x, room.y, room.width, room.height, 14)
// Wallpaper layer. // Back wall: deliberately narrower than the room to create depth.
const wallpaper = this.add.tileSprite( const backWall = this.add.tileSprite(
room.x + inset, backLeft,
wallTop, backTop,
room.width - inset * 2, backRight - backLeft,
wallHeight, Math.max(30, horizonY - backTop),
skin.wallpaper skin.wallpaper
) )
.setOrigin(0, 0) .setOrigin(0, 0)
.setDepth(1) .setDepth(1)
wallpaper.setTileScale(0.72, 0.72) backWall.setTileScale(0.67, 0.67)
// Separate floor layer so the character always stands on a readable surface. // Angled side walls. They use the same wallpaper with subtle opposite
const floor = this.add.tileSprite( // tints so the corner reads clearly without needing baked perspective art.
room.x + inset, this.addMaskedTile(
floorTop, room,
room.width - inset * 2, skin.wallpaper,
floorHeight, [
skin.floor { x: left, y: wallTop },
{ x: backLeft, y: backTop },
{ x: backLeft, y: horizonY },
{ x: left, y: horizonY + Math.round(FLOOR_H * 0.08) }
],
2,
0.67,
0xffeaf3
) )
.setOrigin(0, 0)
.setDepth(2)
floor.setTileScale(0.62, 0.62) this.addMaskedTile(
room,
skin.wallpaper,
[
{ x: backRight, y: backTop },
{ x: right, y: wallTop },
{ x: right, y: horizonY + Math.round(FLOOR_H * 0.08) },
{ x: backRight, y: horizonY }
],
2,
0.67,
0xf0f4ff
)
// Shared architecture layer sits above both wallpaper and floor. // Perspective floor: narrow at the back, full-width at the front.
this.addMaskedTile(
room,
skin.floor,
[
{ x: backLeft, y: horizonY },
{ x: backRight, y: horizonY },
{ x: right, y: bottomY },
{ x: left, y: bottomY }
],
3,
0.54
)
// Ceiling and skirting architecture. The central trims stay straight,
// while perspective guide lines make the side walls/floor read in 3D.
this.add.image( this.add.image(
room.x + room.width / 2, room.x + room.width / 2,
room.y + 10, backTop,
'architecture-ceilingTrim' 'architecture-ceilingTrim'
) )
.setDisplaySize(room.width - 12, 18) .setDisplaySize(backRight - backLeft + 8, 16)
.setDepth(5) .setDepth(6)
this.add.image( this.add.image(
room.x + room.width / 2, room.x + room.width / 2,
floorTop, horizonY,
'architecture-skirting' 'architecture-skirting'
) )
.setDisplaySize(room.width - 12, 14) .setDisplaySize(backRight - backLeft + 8, 13)
.setDepth(5) .setDepth(6)
this.add.image( this.add.image(
room.x + room.width / 2, room.x + room.width / 2,
room.y + room.height - 7, bottomY,
'architecture-floorEdge' 'architecture-floorEdge'
) )
.setDisplaySize(room.width - 10, 16) .setDisplaySize(room.width - 10, 15)
.setDepth(5) .setDepth(6)
graphics.lineStyle(3, 0xd2aabf, 0.95)
// Rear vertical corners.
graphics.lineBetween(backLeft, backTop, backLeft, horizonY)
graphics.lineBetween(backRight, backTop, backRight, horizonY)
// Ceiling perspective lines.
graphics.lineBetween(left, wallTop, backLeft, backTop)
graphics.lineBetween(right, wallTop, backRight, backTop)
// Floor/skirting perspective lines.
graphics.lineBetween(left, horizonY + Math.round(FLOOR_H * 0.08), backLeft, horizonY)
graphics.lineBetween(right, horizonY + Math.round(FLOOR_H * 0.08), backRight, horizonY)
graphics.lineBetween(left, bottomY, backLeft, horizonY)
graphics.lineBetween(right, bottomY, backRight, horizonY)
graphics.lineStyle(4, 0xd5b8c6, 1) graphics.lineStyle(4, 0xd5b8c6, 1)
graphics.strokeRoundedRect(room.x, room.y, room.width, room.height, 14) graphics.strokeRoundedRect(room.x, room.y, room.width, room.height, 14)
@ -440,21 +530,21 @@ class HouseScene extends Phaser.Scene {
fontSize: '16px', fontSize: '16px',
color: '#765466', color: '#765466',
fontStyle: 'bold', fontStyle: 'bold',
backgroundColor: 'rgba(255,255,255,0.72)', backgroundColor: 'rgba(255,255,255,0.78)',
padding: { x: 6, y: 3 } padding: { x: 6, y: 3 }
}).setDepth(8) }).setDepth(9)
if (room.id === 'disco') { if (room.id === 'disco') {
this.add.text(room.x + room.width - 48, room.y + 18, '♪ ✦', { this.add.text(room.x + room.width - 48, room.y + 18, '♪ ✦', {
fontSize: '18px', fontSize: '18px',
color: '#9b65c6' color: '#9b65c6'
}).setDepth(8) }).setDepth(9)
} }
if (room.id === 'garage') { if (room.id === 'garage') {
this.add.text(room.x + room.width - 44, room.y + 18, '🚗', { this.add.text(room.x + room.width - 44, room.y + 18, '🚗', {
fontSize: '18px' fontSize: '18px'
}).setDepth(8) }).setDepth(9)
} }
} }
@ -1101,7 +1191,7 @@ const game = new Phaser.Game({
}) })
window.__DOLLHOUSE__ = { window.__DOLLHOUSE__ = {
version: '0.5.0', version: '0.6.0',
game, game,
async resetAll() { async resetAll() {
await clearGameState() await clearGameState()