Add terrain object generation and day/night cycle functionality

- Added new tile properties for forestTree, palmTree, and boulder counts in game-map.json and game-map.tmx
- Implemented ObjectManager class to handle dynamic object spawning based on tile properties
- Added CycleManager class to manage day/night cycle transitions and visual effects
- Enhanced player collision detection with both map layers and spawned objects
- Updated Game scene to initialize and update new managers
- Adjusted player body size for better collision handling
This commit is contained in:
Brian Fertig 2025-08-16 21:19:19 -06:00
parent 13abba7061
commit 67c207e54e
9 changed files with 402 additions and 4 deletions

View File

@ -307,6 +307,129 @@
"type":"bool", "type":"bool",
"value":true "value":true
}] }]
},
{
"id":1,
"properties":[
{
"name":"forestTree",
"type":"int",
"value":40
}]
},
{
"id":2,
"properties":[
{
"name":"forestTree",
"type":"int",
"value":2
}]
},
{
"id":3,
"properties":[
{
"name":"forestTree",
"type":"int",
"value":5
},
{
"name":"palmTree",
"type":"int",
"value":2
}]
},
{
"id":4,
"properties":[
{
"name":"forestTree",
"type":"int",
"value":0
}]
},
{
"id":7,
"properties":[
{
"name":"forestTree",
"type":"int",
"value":8
}]
},
{
"id":8,
"properties":[
{
"name":"forestTree",
"type":"int",
"value":15
},
{
"name":"palmTree",
"type":"int",
"value":2
}]
},
{
"id":10,
"properties":[
{
"name":"palmTree",
"type":"int",
"value":15
}]
},
{
"id":11,
"properties":[
{
"name":"palmTree",
"type":"int",
"value":10
}]
},
{
"id":12,
"properties":[
{
"name":"boulder",
"type":"int",
"value":2
}]
},
{
"id":13,
"properties":[
{
"name":"boulder",
"type":"int",
"value":8
}]
},
{
"id":14,
"properties":[
{
"name":"boulder",
"type":"int",
"value":20
}]
},
{
"id":16,
"properties":[
{
"name":"boulder",
"type":"int",
"value":15
},
{
"name":"forestTree",
"type":"int",
"value":2
}]
}], }],
"tilewidth":100 "tilewidth":100
}], }],

BIN
assets/images/objects.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

View File

@ -10,6 +10,69 @@
<property name="collides" type="bool" value="true"/> <property name="collides" type="bool" value="true"/>
</properties> </properties>
</tile> </tile>
<tile id="1">
<properties>
<property name="forestTree" type="int" value="40"/>
</properties>
</tile>
<tile id="2">
<properties>
<property name="forestTree" type="int" value="2"/>
</properties>
</tile>
<tile id="3">
<properties>
<property name="forestTree" type="int" value="5"/>
<property name="palmTree" type="int" value="2"/>
</properties>
</tile>
<tile id="4">
<properties>
<property name="forestTree" type="int" value="0"/>
</properties>
</tile>
<tile id="7">
<properties>
<property name="forestTree" type="int" value="8"/>
</properties>
</tile>
<tile id="8">
<properties>
<property name="forestTree" type="int" value="15"/>
<property name="palmTree" type="int" value="2"/>
</properties>
</tile>
<tile id="10">
<properties>
<property name="palmTree" type="int" value="15"/>
</properties>
</tile>
<tile id="11">
<properties>
<property name="palmTree" type="int" value="10"/>
</properties>
</tile>
<tile id="12">
<properties>
<property name="boulder" type="int" value="2"/>
</properties>
</tile>
<tile id="13">
<properties>
<property name="boulder" type="int" value="8"/>
</properties>
</tile>
<tile id="14">
<properties>
<property name="boulder" type="int" value="20"/>
</properties>
</tile>
<tile id="16">
<properties>
<property name="boulder" type="int" value="15"/>
<property name="forestTree" type="int" value="2"/>
</properties>
</tile>
</tileset> </tileset>
<layer id="1" name="main" width="60" height="40"> <layer id="1" name="main" width="60" height="40">
<data encoding="csv"> <data encoding="csv">

BIN
raw/items.psd Normal file

Binary file not shown.

BIN
raw/objects.psd Normal file

Binary file not shown.

74
src/cycle.js Normal file
View File

@ -0,0 +1,74 @@
export class CycleManager {
constructor(scene) {
this.scene = scene;
this.currentCycle = 'day';
this.cycleTimer = 0;
this.cycleDuration = 120; // 3 minutes
this.cycles = ['day', 'evening', 'night', 'morning'];
// Cycle tint values
this.tintValues = {
'day': 0xffffff, // White (no tint)
'evening': 0xffb085, // Orange tint
'night': 0x222244, // Dark blue tint
'morning': 0xffffaa // Light yellow tint
};
// Cycle tint values
this.playerTintValues = {
'day': 0xffffff, // White (no tint)
'evening': 0xffccaa, // Orange tint
'night': 0x8888ff, // Dark blue tint
'morning': 0xffffdd // Light yellow tint
};
this.cycleText = null;
}
init() {
// Create cycle display text in upper right corner
this.cycleText = this.scene.add.text(
40,
40,
'Day',
{ fontSize: '24px', fill: '#ffffff' }
).setShadow(3,3, '#333', 5).setScrollFactor(0);
}
update(delta) {
// Handle cycle timing
this.cycleTimer += delta / 1000; // Convert ms to seconds
if (this.cycleTimer >= this.cycleDuration) {
this.nextCycle();
this.cycleTimer = 0;
}
}
nextCycle() {
const currentIndex = this.cycles.indexOf(this.currentCycle);
const nextIndex = (currentIndex + 1) % this.cycles.length;
this.currentCycle = this.cycles[nextIndex];
// Update display text
if (this.cycleText) {
this.cycleText.setText(this.currentCycle.charAt(0).toUpperCase() + this.currentCycle.slice(1));
}
// Apply tint to the terrain layer
this.applyTint(this.scene.mainLayer);
}
applyTint(layer) {
if (layer && this.tintValues[this.currentCycle]) {
layer.setTint(this.tintValues[this.currentCycle]);
this.scene.player.setTint(this.playerTintValues[this.currentCycle]);
this.scene.objects.setTint(this.playerTintValues[this.currentCycle]);
}
}
getCurrentCycle() {
return this.currentCycle;
}
}

102
src/objects.js Normal file
View File

@ -0,0 +1,102 @@
export class ObjectManager {
constructor(scene) {
this.scene = scene;
this.objects = [];
}
init() {
// Parse main layer for object properties and create objects
this.parseLayerForObjects();
}
parseLayerForObjects() {
const map = this.scene.mainLayer.tilemap;
const layerData = this.scene.mainLayer.layer;
// Iterate through all tiles in the layer
for (let y = 0; y < layerData.height; y++) {
for (let x = 0; x < layerData.width; x++) {
const tile = this.scene.mainLayer.getTileAt(x, y);
if (tile && tile.properties) {
// Check for object-related properties
this.createObjectFromTile(tile, x*100+50, y*100+50);
}
}
}
}
createObjectFromTile(tile, x, y) {
// Example: Look for a "type" property to determine what object to create
const objectType = tile.properties;
Object.keys(objectType).forEach(key => {
const rand = Phaser.Math.Between(1,300);
if (key === 'palmTree' && rand <= objectType[key]) {
const palmTree = this.scene.physics.add.sprite(x, y, 'objects', 0);
this.scene.objects.add(palmTree);
palmTree.setImmovable(true).setSize(60,100);
} else if (key === 'forestTree' && rand <= objectType[key]) {
const forestRand = Phaser.Math.Between(1,4);
const forestTree = this.scene.physics.add.sprite(x, y, 'objects', forestRand);
this.scene.objects.add(forestTree);
forestTree.setImmovable(true).setSize(50,100);
} else if (key === 'boulder' && rand <= objectType[key]) {
const boulder = this.scene.physics.add.sprite(x, y, 'objects', 20);
this.scene.objects.add(boulder);
boulder.setImmovable(true);
}
});
}
createEnemy(x, y, properties) {
// Placeholder for enemy creation logic
console.log(`Creating enemy at (${x}, ${y}) with properties:`, properties);
// Example implementation:
// const enemy = this.scene.physics.add.sprite(
// x * tileWidth + tileWidth / 2,
// y * tileHeight + tileHeight / 2,
// 'enemy-sprite'
// );
// this.objects.push(enemy);
}
createCollectible(x, y, properties) {
// Placeholder for collectible creation logic
console.log(`Creating collectible at (${x}, ${y}) with properties:`, properties);
// Example implementation:
// const collectible = this.scene.physics.add.sprite(
// x * tileWidth + tileWidth / 2,
// y * tileHeight + tileHeight / 2,
// 'collectible-sprite'
// );
// this.objects.push(collectible);
}
createPlatform(x, y, properties) {
// Placeholder for platform creation logic
console.log(`Creating platform at (${x}, ${y}) with properties:`, properties);
// Example implementation:
// const platform = this.scene.physics.add.sprite(
// x * tileWidth + tileWidth / 2,
// y * tileHeight + tileHeight / 2,
// 'platform-sprite'
// );
// platform.setImmovable(true);
// this.objects.push(platform);
}
update(delta) {
// Update all created objects
this.objects.forEach(object => {
if (object.update) {
object.update(delta);
}
});
}
}

View File

@ -6,6 +6,7 @@ export class Player extends Phaser.GameObjects.Sprite {
scene.add.existing(this); scene.add.existing(this);
scene.physics.world.enable(this); scene.physics.world.enable(this);
this.body.setCollideWorldBounds(true); this.body.setCollideWorldBounds(true);
this.body.setSize(50,100);
// Set player properties // Set player properties
this.speed = 200; this.speed = 200;
@ -15,7 +16,6 @@ export class Player extends Phaser.GameObjects.Sprite {
// Add input listener for mouse clicks // Add input listener for mouse clicks
scene.input.on('pointerdown', (pointer) => { scene.input.on('pointerdown', (pointer) => {
console.log(pointer);
this.moveToPoint(pointer.worldX, pointer.worldY); this.moveToPoint(pointer.worldX, pointer.worldY);
}); });

View File

@ -1,8 +1,13 @@
import { Player } from '../player.js'; import { Player } from '../player.js';
import { CycleManager } from '../cycle.js';
import { ObjectManager } from '../objects.js';
export class Game extends Phaser.Scene { export class Game extends Phaser.Scene {
constructor() { constructor() {
super({ key: 'Game' }); super({ key: 'Game' });
this.cycleManager = null;
this.objectManager = null;
this.mainLayer = null;
} }
preload() { preload() {
@ -12,6 +17,12 @@ export class Game extends Phaser.Scene {
frameHeight: 100 frameHeight: 100
}); });
// Load objects sprite
this.load.spritesheet('objects', 'assets/images/objects.png', {
frameWidth: 100,
frameHeight: 100
});
// Load tilemap // Load tilemap
this.load.tilemapTiledJSON('game-map', 'assets/game-map.json'); this.load.tilemapTiledJSON('game-map', 'assets/game-map.json');
// Load tileset image // Load tileset image
@ -26,7 +37,7 @@ export class Game extends Phaser.Scene {
const terrainTileset = map.addTilesetImage('terrain', 'terrain-tileset'); const terrainTileset = map.addTilesetImage('terrain', 'terrain-tileset');
// Create layer named "main" // Create layer named "main"
const mainLayer = map.createLayer('main', terrainTileset, 0, 0) this.mainLayer = map.createLayer('main', terrainTileset, 0, 0)
.setCollisionByProperty({ collides: true }); .setCollisionByProperty({ collides: true });
// Set world bounds based on tilemap dimensions // Set world bounds based on tilemap dimensions
@ -34,21 +45,46 @@ export class Game extends Phaser.Scene {
const worldHeight = map.heightInPixels; const worldHeight = map.heightInPixels;
this.physics.world.setBounds(0, 0, worldWidth, worldHeight); this.physics.world.setBounds(0, 0, worldWidth, worldHeight);
// Initialize object manager
this.objects = this.physics.add.group();
this.objectManager = new ObjectManager(this);
this.objectManager.init();
// Create player at center of screen // Create player at center of screen
this.player = new Player(this, 1600, 3100); this.player = new Player(this, 1600, 3100);
// Physics Collisions // Physics Collisions
this.physics.add.collider(this.player, mainLayer); this.physics.add.collider(this.player, this.mainLayer);
this.physics.add.collider(this.player, this.objects);
// Make camera follow the player // Make camera follow the player
this.cameras.main.startFollow(this.player); this.cameras.main.startFollow(this.player);
this.cameras.main.setBounds(0, 0, worldWidth, worldHeight); this.cameras.main.setBounds(0, 0, worldWidth, worldHeight);
// Initialize cycle manager
this.cycleManager = new CycleManager(this);
this.cycleManager.init();
} }
update() { update(time, delta) {
// Update player // Update player
if (this.player) { if (this.player) {
this.player.update(); this.player.update();
} }
// Update cycle manager
if (this.cycleManager) {
this.cycleManager.update(delta);
// Apply tint to main layer when needed
if (this.mainLayer && this.cycleManager.getCurrentCycle() !== 'day') {
this.cycleManager.applyTint(this.mainLayer);
}
}
// Update object manager
if (this.objectManager) {
this.objectManager.update(delta);
}
} }
} }