Add comprehensive mobile support with touch controls and responsive design
Implemented full mobile optimization to make the game mobile-ready:
**Core Mobile Support:**
- Updated viewport meta tag to prevent zoom and improve touch responsiveness
- Added mobile web app meta tags for iOS/Android standalone mode
- Enhanced CSS to prevent text selection, pull-to-refresh, and tap highlights
- Configured Phaser scale system with min/max bounds (320x240 to 1920x1080)
- Added fullscreen support for game container
**Touch Controls:**
- Added mobile device detection to HuntingScene
- Automatically default to touch controls on mobile devices
- Hide keyboard control instructions on mobile
- Only hide cursor on desktop (preserve default cursor on mobile)
- Adjusted messaging for mobile ("Tap to shoot" vs "Click to shoot")
**Touch Feedback:**
- Added pointerdown/pointerup handlers to all interactive buttons
- Buttons now scale down when pressed (0.95x) for tactile feedback
- Maintained hover effects for desktop compatibility
- Updated IntroScene "SET SAIL" button with touch feedback
- Updated MapScene location markers and close button
- Updated TransitionScene continue button
- All touch feedback works on both touch and mouse inputs
**Backward Compatibility:**
- All desktop functionality preserved
- Hover effects still work on desktop
- Keyboard controls available on desktop
- Touch and mouse inputs work seamlessly together
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
17
index.html
17
index.html
@@ -2,9 +2,19 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-fullscreen">
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<title>Whale Hunting - A Point & Click Adventure</title>
|
<title>Whale Hunting - A Point & Click Adventure</title>
|
||||||
<style>
|
<style>
|
||||||
|
* {
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -14,9 +24,14 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
font-family: 'Courier New', monospace;
|
font-family: 'Courier New', monospace;
|
||||||
|
overscroll-behavior: none;
|
||||||
|
touch-action: none;
|
||||||
}
|
}
|
||||||
#game-container {
|
#game-container {
|
||||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
|
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
|
||||||
|
touch-action: none;
|
||||||
|
max-width: 100vw;
|
||||||
|
max-height: 100vh;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
14
src/main.js
14
src/main.js
@@ -21,7 +21,19 @@ const config = {
|
|||||||
},
|
},
|
||||||
scale: {
|
scale: {
|
||||||
mode: Phaser.Scale.FIT,
|
mode: Phaser.Scale.FIT,
|
||||||
autoCenter: Phaser.Scale.CENTER_BOTH
|
autoCenter: Phaser.Scale.CENTER_BOTH,
|
||||||
|
width: 800,
|
||||||
|
height: 600,
|
||||||
|
min: {
|
||||||
|
width: 320,
|
||||||
|
height: 240
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
width: 1920,
|
||||||
|
height: 1080
|
||||||
|
},
|
||||||
|
fullscreenTarget: 'game-container',
|
||||||
|
expandParent: false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ export default class HuntingScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
|
// Detect mobile device
|
||||||
|
this.isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||||
|
|
||||||
|
// Default to touch on mobile, keyboard on desktop
|
||||||
|
this.useKeyboard = !this.isMobile;
|
||||||
|
|
||||||
// Ocean background
|
// Ocean background
|
||||||
this.add.rectangle(400, 300, 800, 600, 0x1e5a8e);
|
this.add.rectangle(400, 300, 800, 600, 0x1e5a8e);
|
||||||
this.createOceanWaves();
|
this.createOceanWaves();
|
||||||
@@ -38,7 +44,8 @@ export default class HuntingScene extends Phaser.Scene {
|
|||||||
this.setupOceanMovement();
|
this.setupOceanMovement();
|
||||||
|
|
||||||
// Message display
|
// Message display
|
||||||
this.showMessage('Hunt whales! Click or press SPACE to shoot harpoon.');
|
const shootMessage = this.isMobile ? 'Hunt whales! Tap to shoot harpoon.' : 'Hunt whales! Click or press SPACE to shoot harpoon.';
|
||||||
|
this.showMessage(shootMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
@@ -89,7 +96,8 @@ export default class HuntingScene extends Phaser.Scene {
|
|||||||
this.shootHarpoon();
|
this.shootHarpoon();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Tab to toggle control mode
|
// Tab to toggle control mode (desktop only)
|
||||||
|
if (!this.isMobile) {
|
||||||
this.tabKey.on('down', () => {
|
this.tabKey.on('down', () => {
|
||||||
this.useKeyboard = !this.useKeyboard;
|
this.useKeyboard = !this.useKeyboard;
|
||||||
this.controlModeText.setText(`Controls: ${this.useKeyboard ? 'KEYBOARD' : 'MOUSE'} (TAB to switch)`);
|
this.controlModeText.setText(`Controls: ${this.useKeyboard ? 'KEYBOARD' : 'MOUSE'} (TAB to switch)`);
|
||||||
@@ -99,9 +107,12 @@ export default class HuntingScene extends Phaser.Scene {
|
|||||||
this.input.setDefaultCursor('none');
|
this.input.setDefaultCursor('none');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Start with mouse control (hide cursor)
|
// Start with mouse control (hide cursor on desktop only)
|
||||||
this.input.setDefaultCursor('none');
|
if (!this.isMobile) {
|
||||||
|
this.input.setDefaultCursor(this.useKeyboard ? 'default' : 'none');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCrosshairMouse() {
|
updateCrosshairMouse() {
|
||||||
@@ -190,12 +201,14 @@ export default class HuntingScene extends Phaser.Scene {
|
|||||||
});
|
});
|
||||||
this.statsText.setScrollFactor(0);
|
this.statsText.setScrollFactor(0);
|
||||||
|
|
||||||
// Control mode indicator (fixed to screen)
|
// Control mode indicator (fixed to screen, desktop only)
|
||||||
|
if (!this.isMobile) {
|
||||||
this.controlModeText = this.add.text(400, 15, 'Controls: MOUSE (TAB to switch)', {
|
this.controlModeText = this.add.text(400, 15, 'Controls: MOUSE (TAB to switch)', {
|
||||||
fontSize: '16px',
|
fontSize: '16px',
|
||||||
fill: '#ffff00'
|
fill: '#ffff00'
|
||||||
}).setOrigin(0.5, 0);
|
}).setOrigin(0.5, 0);
|
||||||
this.controlModeText.setScrollFactor(0);
|
this.controlModeText.setScrollFactor(0);
|
||||||
|
}
|
||||||
|
|
||||||
// Return button (fixed to screen)
|
// Return button (fixed to screen)
|
||||||
const returnBtn = this.add.rectangle(750, 30, 80, 35, 0x8B0000);
|
const returnBtn = this.add.rectangle(750, 30, 80, 35, 0x8B0000);
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export default class IntroScene extends Phaser.Scene {
|
|||||||
fontStyle: 'bold'
|
fontStyle: 'bold'
|
||||||
}).setOrigin(0.5);
|
}).setOrigin(0.5);
|
||||||
|
|
||||||
// Button hover effects
|
// Button hover effects (desktop)
|
||||||
buttonBg.on('pointerover', () => {
|
buttonBg.on('pointerover', () => {
|
||||||
buttonBg.setFillStyle(0x4a90c4);
|
buttonBg.setFillStyle(0x4a90c4);
|
||||||
});
|
});
|
||||||
@@ -57,8 +57,17 @@ export default class IntroScene extends Phaser.Scene {
|
|||||||
buttonBg.setFillStyle(0x2d5f8e);
|
buttonBg.setFillStyle(0x2d5f8e);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Button click handler
|
// Touch feedback (works on both touch and mouse)
|
||||||
buttonBg.on('pointerdown', () => {
|
buttonBg.on('pointerdown', () => {
|
||||||
|
buttonBg.setFillStyle(0x4a90c4);
|
||||||
|
buttonBg.setScale(0.95);
|
||||||
|
buttonText.setScale(0.95);
|
||||||
|
});
|
||||||
|
|
||||||
|
buttonBg.on('pointerup', () => {
|
||||||
|
buttonBg.setFillStyle(0x2d5f8e);
|
||||||
|
buttonBg.setScale(1.0);
|
||||||
|
buttonText.setScale(1.0);
|
||||||
this.startGame();
|
this.startGame();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -56,12 +56,20 @@ export default class MapScene extends Phaser.Scene {
|
|||||||
closeButton.setInteractive({ useHandCursor: true });
|
closeButton.setInteractive({ useHandCursor: true });
|
||||||
closeButton.setStrokeStyle(2, 0xffffff);
|
closeButton.setStrokeStyle(2, 0xffffff);
|
||||||
|
|
||||||
this.add.text(750, 50, 'CLOSE', {
|
const closeText = this.add.text(750, 50, 'CLOSE', {
|
||||||
fontSize: '16px',
|
fontSize: '16px',
|
||||||
fill: '#fff'
|
fill: '#fff'
|
||||||
}).setOrigin(0.5);
|
}).setOrigin(0.5);
|
||||||
|
|
||||||
|
// Touch feedback (works on both touch and mouse)
|
||||||
closeButton.on('pointerdown', () => {
|
closeButton.on('pointerdown', () => {
|
||||||
|
closeButton.setScale(0.95);
|
||||||
|
closeText.setScale(0.95);
|
||||||
|
});
|
||||||
|
|
||||||
|
closeButton.on('pointerup', () => {
|
||||||
|
closeButton.setScale(1.0);
|
||||||
|
closeText.setScale(1.0);
|
||||||
this.returnToShip();
|
this.returnToShip();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -100,7 +108,7 @@ export default class MapScene extends Phaser.Scene {
|
|||||||
align: 'center'
|
align: 'center'
|
||||||
}).setOrigin(0.5);
|
}).setOrigin(0.5);
|
||||||
|
|
||||||
// Hover effect
|
// Hover effect (desktop)
|
||||||
marker.on('pointerover', () => {
|
marker.on('pointerover', () => {
|
||||||
marker.setScale(1.1);
|
marker.setScale(1.1);
|
||||||
this.showMessage(description);
|
this.showMessage(description);
|
||||||
@@ -110,7 +118,17 @@ export default class MapScene extends Phaser.Scene {
|
|||||||
marker.setScale(1);
|
marker.setScale(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
marker.on('pointerdown', onClick);
|
// Touch feedback (works on both touch and mouse)
|
||||||
|
marker.on('pointerdown', () => {
|
||||||
|
marker.setScale(0.9);
|
||||||
|
text.setScale(0.9);
|
||||||
|
});
|
||||||
|
|
||||||
|
marker.on('pointerup', () => {
|
||||||
|
marker.setScale(1);
|
||||||
|
text.setScale(1);
|
||||||
|
onClick();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
createInventoryDisplay() {
|
createInventoryDisplay() {
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export default class TransitionScene extends Phaser.Scene {
|
|||||||
fontStyle: 'bold'
|
fontStyle: 'bold'
|
||||||
}).setOrigin(0.5);
|
}).setOrigin(0.5);
|
||||||
|
|
||||||
// Hover effect
|
// Hover effect (desktop)
|
||||||
continueBtn.on('pointerover', () => {
|
continueBtn.on('pointerover', () => {
|
||||||
continueBtn.setFillStyle(0x4a90c4);
|
continueBtn.setFillStyle(0x4a90c4);
|
||||||
});
|
});
|
||||||
@@ -79,7 +79,18 @@ export default class TransitionScene extends Phaser.Scene {
|
|||||||
continueBtn.setFillStyle(0x2d5f8e);
|
continueBtn.setFillStyle(0x2d5f8e);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Touch feedback (works on both touch and mouse)
|
||||||
continueBtn.on('pointerdown', () => {
|
continueBtn.on('pointerdown', () => {
|
||||||
|
continueBtn.setFillStyle(0x4a90c4);
|
||||||
|
continueBtn.setScale(0.95);
|
||||||
|
btnText.setScale(0.95);
|
||||||
|
});
|
||||||
|
|
||||||
|
continueBtn.on('pointerup', () => {
|
||||||
|
continueBtn.setFillStyle(0x2d5f8e);
|
||||||
|
continueBtn.setScale(1.0);
|
||||||
|
btnText.setScale(1.0);
|
||||||
|
|
||||||
// Deduct fuel (if any cost)
|
// Deduct fuel (if any cost)
|
||||||
if (this.fuelCost > 0) {
|
if (this.fuelCost > 0) {
|
||||||
this.inventory.fuel -= this.fuelCost;
|
this.inventory.fuel -= this.fuelCost;
|
||||||
|
|||||||
Reference in New Issue
Block a user