Render rooms with pseudo-isometric Phaser architecture
This commit is contained in:
parent
0512a51c8b
commit
809db4836f
166
src/main.js
166
src/main.js
@ -356,6 +356,43 @@ class HouseScene extends Phaser.Scene {
|
||||
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() {
|
||||
const graphics = this.add.graphics().setDepth(0)
|
||||
|
||||
@ -371,66 +408,119 @@ class HouseScene extends Phaser.Scene {
|
||||
for (const room of ROOMS) {
|
||||
const skin = ROOM_SKINS[room.id]
|
||||
const inset = 7
|
||||
const wallTop = room.y + 8
|
||||
const wallBottom = room.walkBand.minY
|
||||
const wallHeight = Math.max(36, wallBottom - wallTop)
|
||||
const floorTop = wallBottom - 1
|
||||
const floorHeight = room.y + room.height - floorTop - 7
|
||||
const sideInset = Math.round(room.width * 0.18)
|
||||
const wallTop = room.y + 9
|
||||
const backTop = wallTop + Math.max(10, Math.round(FLOOR_H * 0.04))
|
||||
const horizonY = room.walkBand.minY
|
||||
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.fillRoundedRect(room.x, room.y, room.width, room.height, 14)
|
||||
|
||||
// Wallpaper layer.
|
||||
const wallpaper = this.add.tileSprite(
|
||||
room.x + inset,
|
||||
wallTop,
|
||||
room.width - inset * 2,
|
||||
wallHeight,
|
||||
// Back wall: deliberately narrower than the room to create depth.
|
||||
const backWall = this.add.tileSprite(
|
||||
backLeft,
|
||||
backTop,
|
||||
backRight - backLeft,
|
||||
Math.max(30, horizonY - backTop),
|
||||
skin.wallpaper
|
||||
)
|
||||
.setOrigin(0, 0)
|
||||
.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.
|
||||
const floor = this.add.tileSprite(
|
||||
room.x + inset,
|
||||
floorTop,
|
||||
room.width - inset * 2,
|
||||
floorHeight,
|
||||
skin.floor
|
||||
// Angled side walls. They use the same wallpaper with subtle opposite
|
||||
// tints so the corner reads clearly without needing baked perspective art.
|
||||
this.addMaskedTile(
|
||||
room,
|
||||
skin.wallpaper,
|
||||
[
|
||||
{ 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(
|
||||
room.x + room.width / 2,
|
||||
room.y + 10,
|
||||
backTop,
|
||||
'architecture-ceilingTrim'
|
||||
)
|
||||
.setDisplaySize(room.width - 12, 18)
|
||||
.setDepth(5)
|
||||
.setDisplaySize(backRight - backLeft + 8, 16)
|
||||
.setDepth(6)
|
||||
|
||||
this.add.image(
|
||||
room.x + room.width / 2,
|
||||
floorTop,
|
||||
horizonY,
|
||||
'architecture-skirting'
|
||||
)
|
||||
.setDisplaySize(room.width - 12, 14)
|
||||
.setDepth(5)
|
||||
.setDisplaySize(backRight - backLeft + 8, 13)
|
||||
.setDepth(6)
|
||||
|
||||
this.add.image(
|
||||
room.x + room.width / 2,
|
||||
room.y + room.height - 7,
|
||||
bottomY,
|
||||
'architecture-floorEdge'
|
||||
)
|
||||
.setDisplaySize(room.width - 10, 16)
|
||||
.setDepth(5)
|
||||
.setDisplaySize(room.width - 10, 15)
|
||||
.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.strokeRoundedRect(room.x, room.y, room.width, room.height, 14)
|
||||
@ -440,21 +530,21 @@ class HouseScene extends Phaser.Scene {
|
||||
fontSize: '16px',
|
||||
color: '#765466',
|
||||
fontStyle: 'bold',
|
||||
backgroundColor: 'rgba(255,255,255,0.72)',
|
||||
backgroundColor: 'rgba(255,255,255,0.78)',
|
||||
padding: { x: 6, y: 3 }
|
||||
}).setDepth(8)
|
||||
}).setDepth(9)
|
||||
|
||||
if (room.id === 'disco') {
|
||||
this.add.text(room.x + room.width - 48, room.y + 18, '♪ ✦', {
|
||||
fontSize: '18px',
|
||||
color: '#9b65c6'
|
||||
}).setDepth(8)
|
||||
}).setDepth(9)
|
||||
}
|
||||
|
||||
if (room.id === 'garage') {
|
||||
this.add.text(room.x + room.width - 44, room.y + 18, '🚗', {
|
||||
fontSize: '18px'
|
||||
}).setDepth(8)
|
||||
}).setDepth(9)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1101,7 +1191,7 @@ const game = new Phaser.Game({
|
||||
})
|
||||
|
||||
window.__DOLLHOUSE__ = {
|
||||
version: '0.5.0',
|
||||
version: '0.6.0',
|
||||
game,
|
||||
async resetAll() {
|
||||
await clearGameState()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user