feat: sync illustrated forest asset integration

This commit is contained in:
Admin 2026-09-19 06:17:04 +00:00
parent 3d35489209
commit c3a5aecdbd

View File

@ -15,12 +15,44 @@ class ForestScene extends Phaser.Scene {
this.touchPointers = { left: new Set(), right: new Set() };
}
preload() {
this.cameras.main.setBackgroundColor('#10251d');
const cx = this.scale.width / 2;
const cy = this.scale.height / 2;
const label = this.add.text(cx, cy - 28, 'Growing the forest…', {
fontFamily: 'Georgia, serif',
fontSize: '20px',
color: '#e7ead9'
}).setOrigin(0.5);
const track = this.add.rectangle(cx, cy + 12, 220, 8, 0x294235).setOrigin(0.5);
const fill = this.add.rectangle(cx - 110, cy + 12, 0, 8, 0xb6c97a).setOrigin(0, 0.5);
this.load.on('progress', value => {
fill.width = 220 * value;
});
this.load.on('complete', () => {
label.destroy();
track.destroy();
fill.destroy();
});
this.load.image('player-sheet', '/assets/player-sheet.png');
this.load.image('flora-sheet', '/assets/flora-sheet.png');
this.load.image('fauna-sheet', '/assets/fauna-sheet.png');
this.load.image('terrain-sheet', '/assets/terrain-sheet.png');
}
create() {
this.physics.world.setBounds(0, 0, WORLD_WIDTH, WORLD_HEIGHT);
this.cameras.main.setBounds(0, 0, WORLD_WIDTH, WORLD_HEIGHT);
this.cameras.main.setBackgroundColor('#8fc7a2');
this.createTextures();
this.createArtFrames();
this.createAnimations();
this.createBackdrop();
this.createWorld();
this.createPlayer();
@ -70,6 +102,105 @@ class ForestScene extends Phaser.Scene {
});
}
createArtFrames() {
const addFrame = (textureKey, name, x, y, width, height) => {
this.textures.get(textureKey).add(name, 0, x, y, width, height);
};
[
['idle0', 0, 70, 145, 185],
['idle1', 130, 70, 150, 185],
['idle2', 260, 70, 150, 185],
['idle3', 385, 70, 170, 185],
['run0', 575, 75, 165, 180],
['run1', 715, 75, 165, 180],
['run2', 855, 75, 165, 180],
['run3', 995, 75, 165, 180],
['run4', 1130, 75, 175, 180],
['run5', 1270, 75, 178, 180],
['jump0', 0, 315, 190, 195],
['jump1', 175, 315, 200, 195],
['fall0', 470, 315, 170, 195],
['fall1', 625, 315, 175, 195],
['land0', 955, 325, 225, 185],
['land1', 1185, 325, 205, 185]
].forEach(([name, x, y, w, h]) => addFrame('player-sheet', name, x, y, w, h));
addFrame('terrain-sheet', 'ground-main', 10, 18, 500, 132);
addFrame('terrain-sheet', 'log-platform', 10, 315, 290, 105);
addFrame('terrain-sheet', 'root-platform', 995, 650, 430, 205);
addFrame('flora-sheet', 'bush', 5, 18, 260, 165);
addFrame('flora-sheet', 'fern-cluster', 450, 0, 390, 180);
addFrame('flora-sheet', 'bluebells', 0, 170, 365, 150);
addFrame('flora-sheet', 'mush-red', 0, 310, 185, 220);
addFrame('flora-sheet', 'mush-brown', 370, 310, 215, 220);
addFrame('flora-sheet', 'mush-orange', 545, 315, 145, 215);
addFrame('flora-sheet', 'mush-purple', 670, 310, 210, 220);
addFrame('flora-sheet', 'mush-blue', 930, 310, 310, 220);
addFrame('flora-sheet', 'acorn-art', 10, 525, 90, 120);
const frogFrames = [
[40, 500, 165, 150], [200, 500, 190, 150], [390, 500, 190, 150],
[570, 500, 220, 150], [810, 500, 175, 150]
];
frogFrames.forEach(([x, y, w, h], i) => addFrame('fauna-sheet', `frog${i}`, x, y, w, h));
const hedgehogFrames = [
[0, 45, 155, 145], [145, 45, 155, 145], [285, 45, 160, 145],
[425, 45, 160, 145], [565, 45, 160, 145], [700, 45, 155, 145]
];
hedgehogFrames.forEach(([x, y, w, h], i) => addFrame('fauna-sheet', `hedgehog${i}`, x, y, w, h));
}
createAnimations() {
const frames = (texture, names) => names.map(frame => ({ key: texture, frame }));
this.anims.create({
key: 'squirrel-idle',
frames: frames('player-sheet', ['idle0', 'idle1', 'idle2', 'idle3']),
frameRate: 4,
repeat: -1
});
this.anims.create({
key: 'squirrel-run',
frames: frames('player-sheet', ['run0', 'run1', 'run2', 'run3', 'run4', 'run5']),
frameRate: 12,
repeat: -1
});
this.anims.create({
key: 'squirrel-jump',
frames: frames('player-sheet', ['jump0', 'jump1']),
frameRate: 8,
repeat: 0
});
this.anims.create({
key: 'squirrel-fall',
frames: frames('player-sheet', ['fall0', 'fall1']),
frameRate: 5,
repeat: -1
});
this.anims.create({
key: 'frog-hop',
frames: frames('fauna-sheet', ['frog0', 'frog1', 'frog2', 'frog3', 'frog4']),
frameRate: 7,
repeat: -1,
repeatDelay: 1400
});
this.anims.create({
key: 'hedgehog-walk',
frames: frames('fauna-sheet', ['hedgehog0', 'hedgehog1', 'hedgehog2', 'hedgehog3', 'hedgehog4', 'hedgehog5']),
frameRate: 8,
repeat: -1
});
}
createTextures() {
const g = this.make.graphics({ add: false });
@ -234,11 +365,16 @@ class ForestScene extends Phaser.Scene {
createWorld() {
this.platforms = this.physics.add.staticGroup();
const ground = this.add.rectangle(WORLD_WIDTH / 2, GROUND_Y + 46, WORLD_WIDTH, 100, 0x263c2d);
const ground = this.add.rectangle(WORLD_WIDTH / 2, GROUND_Y + 46, WORLD_WIDTH, 100, 0x263c2d, 0);
this.physics.add.existing(ground, true);
this.platforms.add(ground);
this.add.rectangle(WORLD_WIDTH / 2, GROUND_Y + 2, WORLD_WIDTH, 20, 0x55714a);
for (let x = 215; x < WORLD_WIDTH + 220; x += 430) {
this.add.image(x, GROUND_Y - 5, 'terrain-sheet', 'ground-main')
.setOrigin(0.5, 0)
.setScale(0.9)
.setDepth(3);
}
const branchPositions = [
[500, 545], [720, 505], [900, 465],
@ -250,24 +386,45 @@ class ForestScene extends Phaser.Scene {
];
branchPositions.forEach(([x, y], index) => {
const branch = this.platforms.create(x, y, 'branch');
branch.setScale(index % 3 === 0 ? 1.35 : 1);
branch.refreshBody();
const scale = index % 3 === 0 ? 0.62 : 0.55;
const visual = this.add.image(x, y + 15, 'terrain-sheet', 'log-platform')
.setScale(scale)
.setDepth(6);
const collider = this.add.rectangle(
x,
y,
Math.max(130, visual.displayWidth * 0.82),
16,
0x000000,
0
);
this.physics.add.existing(collider, true);
this.platforms.add(collider);
});
for (let x = 90; x < WORLD_WIDTH - 120; x += 105) {
for (let x = 120; x < WORLD_WIDTH - 120; x += 180) {
const n = Math.sin(x * 0.013) + Math.cos(x * 0.021);
if (n > -0.15) {
this.add.image(x, GROUND_Y - 10, 'fern')
if (n > -0.3) {
this.add.image(x, GROUND_Y - 2, 'flora-sheet', 'fern-cluster')
.setOrigin(0.5, 1)
.setScale(0.65 + ((x % 70) / 160))
.setAlpha(0.85);
.setScale(0.2 + ((x % 90) / 900))
.setAlpha(0.96)
.setDepth(5);
}
if (x % 315 < 130) {
this.add.image(x + 34, GROUND_Y - 5, 'flower')
if (x % 540 < 190) {
this.add.image(x + 65, GROUND_Y - 2, 'flora-sheet', 'bluebells')
.setOrigin(0.5, 1)
.setScale(0.7 + ((x % 50) / 170));
.setScale(0.2)
.setDepth(5);
}
if (x % 900 < 190) {
this.add.image(x - 65, GROUND_Y - 3, 'flora-sheet', 'bush')
.setOrigin(0.5, 1)
.setScale(0.34)
.setDepth(4);
}
}
@ -296,13 +453,14 @@ class ForestScene extends Phaser.Scene {
}
createPlayer() {
this.player = this.physics.add.sprite(160, 520, 'squirrel');
this.player.setScale(1.12);
this.player = this.physics.add.sprite(160, 520, 'player-sheet', 'idle0');
this.player.setScale(0.5);
this.player.setCollideWorldBounds(true);
this.player.setDragX(900);
this.player.setMaxVelocity(330, 760);
this.player.body.setSize(34, 47);
this.player.body.setOffset(12, 7);
this.player.body.setSize(64, 105, true);
this.player.body.setOffset(40, 62);
this.player.play('squirrel-idle');
}
createCollectibles() {
@ -314,28 +472,34 @@ class ForestScene extends Phaser.Scene {
[3370, 345], [3610, 555], [3800, 440], [4050, 555], [4230, 365],
[4470, 555], [4680, 460], [4910, 555], [5100, 370]
].forEach(([x, y], i) => {
const acorn = this.acornGroup.create(x, y, 'acorn');
const acorn = this.acornGroup.create(x, y, 'flora-sheet', 'acorn-art');
acorn.setScale(0.42);
acorn.setBounce(0.2);
acorn.body.setSize(55, 70, true);
acorn.setData('phase', i * 0.4);
});
this.mushroomGroup = this.physics.add.group({ allowGravity: true });
const mushrooms = [
[720, 575, 'mushroom-red', 'Scarlet bonnet'],
[1700, 360, 'mushroom-purple', 'Amethyst veil'],
[2860, 575, 'mushroom-gold', 'Honey lantern'],
[4170, 370, 'mushroom-red', 'Foxfire cap'],
[4860, 575, 'mushroom-purple', 'Moon gill']
[720, 575, 'mush-red', 'Scarlet bonnet'],
[1700, 360, 'mush-purple', 'Amethyst veil'],
[2860, 575, 'mush-orange', 'Honey lantern'],
[4170, 370, 'mush-brown', 'Foxfire cap'],
[4860, 575, 'mush-blue', 'Moon gill']
];
mushrooms.forEach(([x, y, texture, name]) => {
const mushroom = this.mushroomGroup.create(x, y, texture);
mushrooms.forEach(([x, y, frame, name], index) => {
const mushroom = this.mushroomGroup.create(x, y, 'flora-sheet', frame);
const scale = frame === 'mush-blue' ? 0.2 : 0.24;
mushroom.setScale(scale);
mushroom.setData('name', name);
mushroom.setBounce(0.1);
mushroom.body.setSize(mushroom.width * 0.52, mushroom.height * 0.62, true);
this.tweens.add({
targets: mushroom,
y: y - 6,
duration: 900 + (x % 400),
scaleX: scale * 1.04,
scaleY: scale * 1.04,
duration: 900 + index * 120,
yoyo: true,
repeat: -1,
ease: 'Sine.inOut'
@ -362,15 +526,31 @@ class ForestScene extends Phaser.Scene {
});
[1120, 2600, 3980].forEach((x, i) => {
const frog = this.add.image(x, GROUND_Y - 8, 'frog').setOrigin(0.5, 1);
const frog = this.add.sprite(x, GROUND_Y - 2, 'fauna-sheet', 'frog0')
.setOrigin(0.5, 1)
.setScale(0.32)
.setDepth(7);
frog.play('frog-hop');
frog.anims.setProgress((i * 0.27) % 1);
});
[1450, 3450].forEach((x, i) => {
const hedgehog = this.add.sprite(x, GROUND_Y - 3, 'fauna-sheet', 'hedgehog0')
.setOrigin(0.5, 1)
.setScale(0.34)
.setDepth(7)
.setFlipX(i % 2 === 1);
hedgehog.play('hedgehog-walk');
this.tweens.add({
targets: frog,
y: GROUND_Y - 20,
duration: 220,
delay: 2300 + i * 900,
targets: hedgehog,
x: x + (i % 2 === 0 ? 190 : -190),
duration: 5200,
yoyo: true,
repeat: -1,
repeatDelay: 3000 + i * 500
ease: 'Sine.inOut',
onYoyo: () => hedgehog.toggleFlipX(),
onRepeat: () => hedgehog.toggleFlipX()
});
});
@ -507,8 +687,7 @@ class ForestScene extends Phaser.Scene {
if (!this.player?.body) return;
if (this.player.body.blocked.down || this.player.body.touching.down) {
this.player.setVelocityY(-520);
this.player.setAngularVelocity(this.player.flipX ? -25 : 25);
this.time.delayedCall(180, () => this.player?.setAngularVelocity(0));
this.player.play('squirrel-jump', true);
}
}
@ -588,8 +767,17 @@ class ForestScene extends Phaser.Scene {
const grounded = this.player.body.blocked.down || this.player.body.touching.down;
if (grounded) {
this.player.setAngle(0);
if (Math.abs(this.player.body.velocity.x) > 35) {
this.player.play('squirrel-run', true);
} else {
this.player.setAngle(Phaser.Math.Clamp(this.player.body.velocity.x * 0.025, -8, 8));
this.player.play('squirrel-idle', true);
}
} else if (this.player.body.velocity.y < 0) {
this.player.play('squirrel-jump', true);
this.player.setAngle(Phaser.Math.Clamp(this.player.body.velocity.x * 0.012, -5, 5));
} else {
this.player.play('squirrel-fall', true);
this.player.setAngle(Phaser.Math.Clamp(this.player.body.velocity.x * 0.012, -5, 5));
}
this.acornGroup.children.iterate(acorn => {