Compare commits
2 commits
4bf8897370
...
3412eb2cc5
| Author | SHA1 | Date | |
|---|---|---|---|
| 3412eb2cc5 | |||
| cfc4a915be |
2 changed files with 293 additions and 1 deletions
171
README.md
Normal file
171
README.md
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
# 3D Coin Collector Game
|
||||
|
||||
A fun and engaging 3D game built with Three.js where you control a player to collect coins while avoiding obstacles. Test your reflexes and see how high you can score!
|
||||
|
||||
## 🎮 Game Overview
|
||||
|
||||
Navigate your character (a blue cube) through a 3D arena to collect golden coins while avoiding dangerous red obstacles. Each coin collected increases your score, but colliding with obstacles reduces your health. The game ends when your health reaches zero.
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- **3D Graphics**: Beautiful 3D environment with shadows, lighting, and fog effects
|
||||
- **Smooth Controls**: Responsive keyboard and touch controls
|
||||
- **Dynamic Gameplay**: Moving obstacles that bounce off boundaries
|
||||
- **Animated Coins**: Coins rotate and float with smooth animations
|
||||
- **Health System**: Start with 100 health, lose 1 point per obstacle collision
|
||||
- **Score Tracking**: Earn 10 points for each coin collected
|
||||
- **Camera Follow**: Third-person camera that follows your player
|
||||
- **Mobile Support**: Full touch control support for smartphones and tablets
|
||||
- **Responsive Design**: Adapts to different screen sizes
|
||||
|
||||
## 🎯 How to Play
|
||||
|
||||
1. **Objective**: Collect as many yellow coins as possible while avoiding red obstacles
|
||||
2. **Scoring**: Each coin collected gives you 10 points
|
||||
3. **Health**: You start with 100 health. Each collision with a red obstacle reduces health by 1
|
||||
4. **Game Over**: When your health reaches 0, the game ends and displays your final score
|
||||
5. **Restart**: Click "Play Again" to start a new game
|
||||
|
||||
## 🕹️ Controls
|
||||
|
||||
### Desktop
|
||||
- **W** or **↑ Arrow Key**: Move forward
|
||||
- **S** or **↓ Arrow Key**: Move backward
|
||||
- **A** or **← Arrow Key**: Move left
|
||||
- **D** or **→ Arrow Key**: Move right
|
||||
|
||||
### Mobile/Touch Devices
|
||||
- **Touch and Drag**: Touch anywhere on the screen and drag to move your character
|
||||
- Drag up to move forward
|
||||
- Drag down to move backward
|
||||
- Drag left to move left
|
||||
- Drag right to move right
|
||||
- Movement is relative to the center of the screen
|
||||
|
||||
## 🎨 Game Elements
|
||||
|
||||
### Player
|
||||
- **Appearance**: Blue cube with metallic material
|
||||
- **Behavior**: Rotates while moving, stays within arena boundaries
|
||||
- **Collision**: Pushed back when hitting obstacles
|
||||
|
||||
### Coins
|
||||
- **Appearance**: Golden spheres with emissive glow
|
||||
- **Behavior**: Rotate continuously and float up and down
|
||||
- **Value**: 10 points each
|
||||
- **Respawn**: A new coin spawns immediately after collection
|
||||
|
||||
### Obstacles
|
||||
- **Appearance**: Red-orange boxes
|
||||
- **Behavior**: Move randomly and bounce off arena boundaries
|
||||
- **Damage**: -1 health per collision
|
||||
- **Avoidance**: Stay away to preserve your health!
|
||||
|
||||
## 🛠️ Technical Details
|
||||
|
||||
### Built With
|
||||
- **Three.js r128**: 3D graphics library
|
||||
- **Vanilla JavaScript**: No frameworks required
|
||||
- **HTML5 Canvas**: WebGL rendering
|
||||
- **CSS3**: Modern styling and responsive design
|
||||
|
||||
### Game Architecture
|
||||
- **Object-Oriented Design**: Uses ES6 classes with inheritance
|
||||
- **GameObject Base Class**: Common functionality for all game entities
|
||||
- **Modular Classes**: Player, Coin, and Obstacle extend GameObject
|
||||
- **Game Loop**: Smooth 60 FPS animation using requestAnimationFrame
|
||||
|
||||
### Features Implementation
|
||||
- **Shadow Mapping**: PCF soft shadows for realistic lighting
|
||||
- **Fog Effect**: Atmospheric depth with distance fog
|
||||
- **Collision Detection**: Distance-based collision system
|
||||
- **Boundary Constraints**: Prevents player and obstacles from leaving the arena
|
||||
- **Touch Event Handling**: Full support for multi-touch devices
|
||||
|
||||
## 📋 Requirements
|
||||
|
||||
- Modern web browser with WebGL support (Chrome, Firefox, Safari, Edge)
|
||||
- JavaScript enabled
|
||||
- Internet connection (for Three.js CDN)
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
1. **Clone or Download** this repository
|
||||
2. **Open** `index.html` in your web browser
|
||||
3. **Start Playing** immediately - no installation required!
|
||||
|
||||
### Local Server (Optional)
|
||||
For best performance, you can run a local server:
|
||||
|
||||
```bash
|
||||
# Using Python 3
|
||||
python -m http.server 8000
|
||||
|
||||
# Using Python 2
|
||||
python -m SimpleHTTPServer 8000
|
||||
|
||||
# Using Node.js (with http-server)
|
||||
npx http-server
|
||||
```
|
||||
|
||||
Then open `http://localhost:8000` in your browser.
|
||||
|
||||
## 📱 Mobile Compatibility
|
||||
|
||||
The game is fully optimized for mobile devices:
|
||||
- Touch controls work on all touch-enabled devices
|
||||
- Responsive UI that adapts to screen size
|
||||
- Optimized performance for mobile browsers
|
||||
- No joystick clutter - clean touch interface
|
||||
|
||||
## 🎮 Game Mechanics
|
||||
|
||||
### Movement
|
||||
- Player moves at a constant speed of 0.15 units per frame
|
||||
- Movement is constrained within the 30x30 arena
|
||||
- Player rotates while moving for visual feedback
|
||||
|
||||
### Coin Collection
|
||||
- Coins spawn randomly across the arena
|
||||
- 10 coins are present at the start
|
||||
- New coins spawn immediately after collection
|
||||
- Coins have a collision radius of 0.8 units
|
||||
|
||||
### Obstacle Behavior
|
||||
- 8 obstacles spawn at game start
|
||||
- Each obstacle moves in a random direction
|
||||
- Obstacles bounce off arena boundaries
|
||||
- Obstacles have a collision radius of 1.5 units
|
||||
- Spawn away from player's starting position
|
||||
|
||||
### Camera System
|
||||
- Third-person camera follows the player
|
||||
- Camera positioned 15 units behind the player
|
||||
- Always looks at the player position
|
||||
- Smooth camera movement
|
||||
|
||||
## 🏆 Tips for High Scores
|
||||
|
||||
1. **Stay Mobile**: Keep moving to avoid obstacles
|
||||
2. **Plan Your Route**: Look ahead to plan coin collection paths
|
||||
3. **Avoid Corners**: Obstacles can trap you in corners
|
||||
4. **Watch Your Health**: Health is displayed in the top-left corner
|
||||
5. **Practice**: The more you play, the better you'll get at avoiding obstacles!
|
||||
|
||||
## 🐛 Known Issues
|
||||
|
||||
None at the moment! If you encounter any issues, please report them.
|
||||
|
||||
## 📝 License
|
||||
|
||||
This project is open source and available for educational purposes.
|
||||
|
||||
## 🙏 Acknowledgments
|
||||
|
||||
- Three.js community for the excellent 3D library
|
||||
- Built as a learning project for 3D web game development
|
||||
|
||||
---
|
||||
|
||||
**Enjoy the game and try to beat your high score!** 🎉
|
||||
|
||||
123
index.html
123
index.html
|
|
@ -72,6 +72,11 @@
|
|||
text-align: center;
|
||||
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
#instructions {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -87,7 +92,7 @@
|
|||
</div>
|
||||
|
||||
<div id="instructions">
|
||||
<p><strong>Controls:</strong> WASD or Arrow Keys to move | Collect yellow coins | Avoid red obstacles!</p>
|
||||
<p><strong>Controls:</strong> WASD or Arrow Keys to move | Touch and drag to move (mobile) | Collect yellow coins | Avoid red obstacles!</p>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||
|
|
@ -319,6 +324,12 @@
|
|||
this.keys = {};
|
||||
this.coins = [];
|
||||
this.obstacles = [];
|
||||
this.touchActive = false;
|
||||
this.touchStartX = 0;
|
||||
this.touchStartY = 0;
|
||||
this.touchCurrentX = 0;
|
||||
this.touchCurrentY = 0;
|
||||
this.touchId = null;
|
||||
|
||||
this.init();
|
||||
this.setupEventListeners();
|
||||
|
|
@ -427,6 +438,111 @@
|
|||
});
|
||||
window.addEventListener('resize', () => this.onWindowResize());
|
||||
document.getElementById('restartBtn').addEventListener('click', () => this.restart());
|
||||
|
||||
// Touch event listeners
|
||||
this.setupTouchControls();
|
||||
}
|
||||
|
||||
setupTouchControls() {
|
||||
const canvas = this.renderer.domElement;
|
||||
|
||||
// Prevent default touch behaviors
|
||||
canvas.addEventListener('touchstart', (e) => {
|
||||
e.preventDefault();
|
||||
this.handleTouchStart(e);
|
||||
}, { passive: false });
|
||||
|
||||
canvas.addEventListener('touchmove', (e) => {
|
||||
e.preventDefault();
|
||||
this.handleTouchMove(e);
|
||||
}, { passive: false });
|
||||
|
||||
canvas.addEventListener('touchend', (e) => {
|
||||
e.preventDefault();
|
||||
this.handleTouchEnd(e);
|
||||
}, { passive: false });
|
||||
|
||||
canvas.addEventListener('touchcancel', (e) => {
|
||||
e.preventDefault();
|
||||
this.handleTouchEnd(e);
|
||||
}, { passive: false });
|
||||
}
|
||||
|
||||
handleTouchStart(e) {
|
||||
if (this.touchActive) return;
|
||||
|
||||
const touch = e.touches[0];
|
||||
const rect = this.renderer.domElement.getBoundingClientRect();
|
||||
|
||||
// Use center of screen as reference point
|
||||
this.touchStartX = rect.left + rect.width / 2;
|
||||
this.touchStartY = rect.top + rect.height / 2;
|
||||
|
||||
this.touchId = touch.identifier;
|
||||
this.touchCurrentX = touch.clientX;
|
||||
this.touchCurrentY = touch.clientY;
|
||||
this.touchActive = true;
|
||||
}
|
||||
|
||||
handleTouchMove(e) {
|
||||
if (!this.touchActive) return;
|
||||
|
||||
const touch = Array.from(e.touches).find(t => t.identifier === this.touchId);
|
||||
if (!touch) return;
|
||||
|
||||
this.touchCurrentX = touch.clientX;
|
||||
this.touchCurrentY = touch.clientY;
|
||||
|
||||
this.updateTouchMovement();
|
||||
}
|
||||
|
||||
handleTouchEnd(e) {
|
||||
if (!this.touchActive) return;
|
||||
|
||||
// Check if the touch that ended is our tracked touch
|
||||
const touch = Array.from(e.changedTouches).find(t => t.identifier === this.touchId);
|
||||
if (!touch) return;
|
||||
|
||||
this.touchActive = false;
|
||||
this.touchId = null;
|
||||
this.touchCurrentX = this.touchStartX;
|
||||
this.touchCurrentY = this.touchStartY;
|
||||
this.updateTouchMovement();
|
||||
}
|
||||
|
||||
updateTouchMovement() {
|
||||
if (!this.touchActive) {
|
||||
// Reset all movement keys
|
||||
this.keys['w'] = false;
|
||||
this.keys['s'] = false;
|
||||
this.keys['a'] = false;
|
||||
this.keys['d'] = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const deltaX = this.touchCurrentX - this.touchStartX;
|
||||
const deltaY = this.touchCurrentY - this.touchStartY;
|
||||
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
|
||||
// Normalize movement direction
|
||||
const threshold = 10; // Minimum movement to register
|
||||
if (distance < threshold) {
|
||||
this.keys['w'] = false;
|
||||
this.keys['s'] = false;
|
||||
this.keys['a'] = false;
|
||||
this.keys['d'] = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate direction
|
||||
const normalizedX = deltaX / distance;
|
||||
const normalizedY = deltaY / distance;
|
||||
|
||||
// Update movement keys based on direction
|
||||
this.keys['w'] = normalizedY < -0.3;
|
||||
this.keys['s'] = normalizedY > 0.3;
|
||||
this.keys['a'] = normalizedX < -0.3;
|
||||
this.keys['d'] = normalizedX > 0.3;
|
||||
}
|
||||
|
||||
updatePlayer() {
|
||||
|
|
@ -516,6 +632,11 @@
|
|||
animate() {
|
||||
requestAnimationFrame(() => this.animate());
|
||||
|
||||
// Update touch movement if active
|
||||
if (this.touchActive) {
|
||||
this.updateTouchMovement();
|
||||
}
|
||||
|
||||
this.updatePlayer();
|
||||
this.updateCoins();
|
||||
this.updateObstacles();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue