feat: Implement jewel selection and swapping mechanics with visual feedback

- Increased starting rows from 2 to 5 for larger initial grid
- Added selectedJewel and swapWithJewel properties for tracking selections
- Enhanced world bounds to provide better collision detection area
- Implemented click handling for jewels with selection/deselection logic
- Added adjacent jewel checking and swapping functionality with animations
- Included visual feedback through scaling tweens when selecting jewels
- Implemented isSwapping flag to prevent concurrent interactions during swaps
- Disabled interactivity during swap animations and re-enabled afterward
- Removed gravity from arcade physics for puzzle game behavior

The changes implement core gameplay mechanics for a match-three style puzzle game where players can select jewels, check adjacency, and swap positions with visual feedback.
This commit is contained in:
Brian Fertig 2025-08-23 13:44:24 -06:00
parent 31c7eaefb6
commit e9cb2a939d
2 changed files with 161 additions and 8 deletions

View File

@ -12,9 +12,13 @@ export class GameScene extends Phaser.Scene {
};
this.numberOfJewels = 5;
this.startRows = 2;
this.startRows = 5;
this.level = 1;
this.levelTime = 60;
// Add selectedJewel property
this.selectedJewel = null;
this.swapWithJewel = null;
}
preload() {
@ -26,23 +30,18 @@ export class GameScene extends Phaser.Scene {
create() {
this.makeGrid();
this.physics.world.setBounds(this.grid.getBounds().x, this.grid.getBounds().y, this.grid.getBounds().width, this.grid.getBounds().height);
this.physics.world.setBounds(this.grid.getBounds().x - 50, this.grid.getBounds().y - 50, this.grid.getBounds().width + 100, this.grid.getBounds().height + 100);
this.jewels = this.physics.add.group({
collideWorldBounds: true,
});
this.physics.add.collider(this.jewels, this.jewels);
this.createStart();
this.createJewel(this.getJewelAtPosition(1, 7), 3, 3);
}
update(time, delta) {
}
selectJewel(x, y) {
}
makeGrid() {
this.grid = this.add.rectangle(
this.gridConfig.leftPadding + this.gridConfig.allPadding,
@ -62,9 +61,18 @@ export class GameScene extends Phaser.Scene {
'jewels',
type
);
jewel.setOrigin(0.5);
jewel.setDisplaySize(this.gridConfig.jewelWidth, this.gridConfig.jewelHeight);
jewel.jewelType = type;
this.jewels.add(jewel);
// Add click event to the jewel
jewel.setInteractive();
jewel.on('pointerdown', () => {
this.handleJewelClick(jewel.x, jewel.y);
});
return jewel;
}
createStart() {
@ -119,4 +127,149 @@ export class GameScene extends Phaser.Scene {
// Return the jewel type or null if not found
return jewelAtPosition ? jewelAtPosition.jewelType : null;
}
// New function to handle jewel clicks
handleJewelClick(x, y) {
// Find which jewel was clicked
const clickedJewel = this.getJewelAtWorldPosition(x, y);
if (!clickedJewel || this.isSwapping) {
return;
}
// If no jewel is currently selected
if (!this.selectedJewel) {
this.selectJewel(clickedJewel);
return;
}
// If clicking on the already selected jewel, deselect it
if (clickedJewel === this.selectedJewel) {
this.deselectJewel();
return;
}
// Check if clicked jewel is adjacent to selected jewel
const isAdjacent = this.isAdjacent(this.selectedJewel, clickedJewel);
if (isAdjacent) {
// Mark for swap
this.selectJewelForSwap(clickedJewel);
} else {
// Deselect current and select new jewel
this.deselectJewel();
this.selectJewel(clickedJewel);
}
}
// Helper function to get jewel at world position
getJewelAtWorldPosition(x, y) {
let closestJewel = null;
let minDistance = Infinity;
this.jewels.children.iterate((jewel) => {
if (jewel) {
const distance = Phaser.Math.Distance.Between(jewel.x, jewel.y, x, y);
if (distance < minDistance && distance < 50) { // 50 is half of jewel width/height
minDistance = distance;
closestJewel = jewel;
}
}
});
return closestJewel;
}
// Helper function to check if two jewels are adjacent (horizontally or vertically)
isAdjacent(jewel1, jewel2) {
const col1 = Math.floor((jewel1.x - this.gridConfig.leftPadding) / this.gridConfig.jewelWidth);
const row1 = Math.floor(jewel1.y / this.gridConfig.jewelHeight);
const col2 = Math.floor((jewel2.x - this.gridConfig.leftPadding) / this.gridConfig.jewelWidth);
const row2 = Math.floor(jewel2.y / this.gridConfig.jewelHeight);
// Check if jewels are adjacent (same row and adjacent columns, or same column and adjacent rows)
return (row1 === row2 && Math.abs(col1 - col2) === 1) || (col1 === col2 && Math.abs(row1 - row2) === 1);
}
// Helper function to select a jewel for swap
selectJewelForSwap(jewel) {
this.swapWithJewel = jewel;
this.tweens.add({
targets: this.selectedJewel,
scale: 1,
duration: 200
});
this.swapJewel();
}
// Helper function to select a jewel for swap
selectJewel(jewel) {
this.selectedJewel = jewel;
this.tweens.add({
targets: jewel,
scale: 1.2,
duration: 200
});
}
// Helper function to deselect a jewel
deselectJewel() {
if (this.selectedJewel) {
this.tweens.add({
targets: this.selectedJewel,
scale: 1,
duration: 200
});
this.selectedJewel = null;
}
}
// Modified swap function to disable clicks during execution
swapJewel() {
// Set swapping flag to prevent new clicks from being processed
this.isSwapping = true;
// Disable all jewel interactivity temporarily
this.jewels.children.iterate((jewel) => {
if (jewel) {
jewel.disableInteractive();
}
});
// Perform the swap animation here - currently empty
const fromX = this.selectedJewel.x;
const fromY = this.selectedJewel.y;
const toX = this.swapWithJewel.x;
const toY = this.swapWithJewel.y;
this.tweens.add({
targets: this.selectedJewel,
x: toX,
y: toY,
duration: 300
});
this.tweens.add({
targets: this.swapWithJewel,
x: fromX,
y: fromY,
duration: 300
});
this.selectedJewel = null;
this.swapWithJewel = null;
// After swap completes, re-enable interactivity and reset flag
this.time.delayedCall(500, () => { // Adjust delay as needed for animation duration
this.jewels.children.iterate((jewel) => {
if (jewel) {
jewel.setInteractive();
}
});
this.isSwapping = false;
});
// Check for Matches
}
}

View File

@ -17,7 +17,7 @@ const GAME_CONFIG = {
physics: {
default: 'arcade',
arcade: {
gravity: { y: 100 },
gravity: { y: 0 },
debug: false
}
}