Archery Game Blog
A blog about the Archery Castle game
In this game, the player controls an archer who must shoot arrows at targets. The player can move using the WASD keys, and can shoot arrows by pressing the space bar. The target will move back and forth across the screen, and the player must time their shots to hit the target. The game will keep track of the player’s score, which increases each time they hit the target. The game ends when the player reaches 30 points.
flowchart TD
A[Game Starts] --> B[Explore Map]
B --> C{Interact w/ Villager NPC?}
C -->|No| B
C -->|Yes| D[NPC Asks to Start Game]
D --> E{Player Choice}
E -->|Nevermind| B
E -->|Start| F[Remove Barrier, Start Target Movement, Destroy NPC, Enable Player Attacks]
F --> G{Player Presses Space?}
G --> |True| H{Spawn Arrow}
G --> |False| I
H --> I{Arrow Obj Exists?}
I --> |True| J[Move arrow up screen]
J --> K{Arrow hits target?}
K --> |True| L[Arrow sticks to target, hits remaining decreases by 1]
L --> M{Hits remaining <= 0?}
M --> |True| N[Show end screen, stop game]
M --> |False| O{Arrow is offscreen?}
O --> |True| P[Destroy Arrow]
O --> |False| Q[Draw all]
P --> Q
Q --> G
Key Features
- OOP (e.g. Inheritance)
- The FightingPlayer class inherits from the Player class, and the Projectile class inherits from the Character class.
- Collision Detection
- Custom error detection for when the arrow hits the target, and when the arrow goes offscreen.
- Data Structures
- Arrays (for example, for projectiles)
- Objects (sprite data)
- Game Loop
- Handles movement, drawing, and collision detection every frame
- Event Handling
- Keyboard input for player movement and shooting
- Conditionals, Loops, other simple programming concepts
File Structure
All code is in /assets/js/castleGame.
/assets/js/castleGame/GameLevelArchery> main file/assets/js/castleGame/castleArchery.md> Markdown file to view the game/assets/js/castleGame/custom/EndScreen.js> Displays an end screen upon winning/assets/js/castleGame/custom/FightingPlayer.js> A special kind ofPlayerthat can attack/assets/js/castleGame/custom/Projectile.js> Used for the arrows, implements animation, control, collision detection
Challenge
Archery Game
Code example: FightingPlayer.js
import Player from '../essentials/Player.js';
import Projectile from './Projectile.js';
/**
* A version of the Player class with added functionality for shooting projectiles (arrows).
* @param data - Initial data for the player character.
* @param gameEnv - The game environment object, providing access to game state and resources.
*
* This class extends the basic Player class to allow for SPACE to create arrows
*/
class FightingPlayer extends Player {
// Construct the class, with a list of stored projectiles
constructor(data = null, gameEnv = null) {
super(data, gameEnv);
this.projectiles = [];
this.lastAttackTime = Date.now();
this.attackCooldown = 500; // 500ms between shots
this.currentDirection = 'right'; // track facing direction
// Bind attack to spacebar
if (typeof window !== 'undefined') {
this._attackHandler = (event) => {
if (event.code === 'Space' || event.key === ' ') {
this.attack();
}
};
window.addEventListener('keydown', this._attackHandler);
}
}
// Update spook and the projectiles
update(...args) {
super.update(...args); // Do normal player updating
// Track facing direction based on movement
if (this.velocity.x > 0) this.currentDirection = 'right';
else if (this.velocity.x < 0) this.currentDirection = 'left';
// Update and clean up projectiles
this.projectiles = this.projectiles.filter(p => !p.revComplete);
this.projectiles.forEach(p => p.update());
}
This code includes the constructor (which calls the parent class’s constructor and then its own additions). It also includes a feature to update projectiles every tick.
// Execute an attack
attack() {
// Don't allow shooting until the game has started
if (typeof window !== 'undefined' && !window.archeryGameStarted) {
return;
}
const now = Date.now();
if (now - this.lastAttackTime < this.attackCooldown) return;
// Calculate target point in direction player is facing
const facingRight = this.currentDirection === 'right';
// Shoot arrow 500 pixels in facing direction
const targetX = this.position.x + (facingRight ? 500 : -500);
const targetY = this.position.y;
// Create arrow projectile
this.projectiles.push(
new Projectile(
this.gameEnv,
targetX,
targetY,
// Offset source position to start at player center
this.position.x + this.width/2,
this.position.y + this.height/2,
"PLAYER" // Special type for player projectiles
)
);
this.lastAttackTime = now;
}
This is the main addition to the class. This code creates a new projectile every time attack() is called.
// Clean up event listeners when destroyed
destroy() {
if (typeof window !== 'undefined' && this._attackHandler) {
window.removeEventListener('keydown', this._attackHandler);
}
super.destroy();
}
}
export default FightingPlayer;
This code wraps up the fighting player with a destroy function.
CS111 Requirements
| Category | Concept | How the Game Implements It | Code Evidence | Lesson / HW Links | |
|---|---|---|---|---|---|
| Control Structures | Iteration | Loops are used to update projectiles and find targets | this.projectiles.forEach(p => p.update()), filtering arrays, looping through targets |
Homework | |
| Conditionals | Used for collisions, movement, and game state logic | if (window.archeryGameStarted), if (xDiff <= ...), if (hitsRemaining <= 0) |
Homework | ||
| Nested Conditions | Complex logic combining movement, collisions, and game state | Target movement + bounce + speed increase inside nested if blocks |
Homework | ||
| Data Types | Numbers | Used for position, velocity, timing, and scoring | position.x += velocity.x, speed += 0.5, Date.now() |
Lesson | — |
| Strings | Used for sprite paths, dialogue, UI text | "Start the game?", image paths, commentary text |
Homework | ||
| Booleans | Track game state and object states | archeryGameStarted, this.stuck, this.revComplete |
Homework | ||
| Arrays | Store collections of objects like projectiles and game objects | this.projectiles = [], gameEnv.gameObjects.find(...) |
Hacks / Homework | ||
| Objects (JSON) | Used for configuration of sprites and game entities | sprite_data_mc, target_data, barrier_data |
— | ||
| Operators | Mathematical | Physics and movement calculations | +, -, *, / in velocity, distance, and speed updates |
Homework | |
| String Operations | UI text and path construction | path + "/images/...", template literals for time display |
Homework | ||
| Boolean Expressions | Complex logic for gameplay conditions | And/or/not in collision checks and state gating | Homework |
CS111 Explanation
-
Iteration (loops) Using loops to continuously update objects like projectiles and to process collections of game objects. Homework
this.projectiles = this.projectiles.filter(p => !p.revComplete); this.projectiles.forEach(p => p.update());Looping through enemies to find the closest target:
for (const target of targets) { const dx = target.position.x - this.position.x; const dy = target.position.y - this.position.y; const dist = Math.sqrt(dx * dx + dy * dy); } -
Conditionals (if/else) Uses conditionals to control gameplay, such as preventing actions before the game starts or handling movement. Homework
if (!window.archeryGameStarted) { return; }if (this.position.x <= 0) { this.velocity.x = this.speed; } else if (this.position.x + this.width >= this.gameEnv.innerWidth) { this.velocity.x = -this.speed; } -
Nested Conditions (complex logic) Combines multiple conditions to handle collision and win conditions. Conditionals HW
if (!this.stuck && xDiff <= TARGET_SIZE/2.0 && yDiff <= TARGET_SIZE/2.0) { this.stuck = true; nearestTarget.hitsRemaining -= 1; if (nearestTarget.hitsRemaining <= 0){ showEndScreen(this.gameEnv); } }
🔢 Data Types
-
Numbers Used for positions, velocities, speed, timing, and score tracking.
this.speed = 10; this.hitsRemaining = 30; const timeTaken = Date.now() / 1000.0; -
Strings Used for dialogue, UI text, IDs, and file paths. Homework
id: 'Knight', greeting: "Hi, I am a Knight.", img.src = path + '/images/sorcerers/archeryWinScreen.png'; -
Booleans Used for game state and logic flags. Homework
window.archeryGameStarted = false; this.stuck = false; if (this.stuck && this.stuckTarget) { // follow target } -
Arrays Used to store collections like projectiles and game objects. Hacks and homework
this.projectiles = []; this.projectiles.push(new Projectile(...)); -
Objects (JSON / structured data) Defines game entities using structured configuration objects.
const sprite_data_mc = { id: 'Knight', SCALE_FACTOR: 7, INIT_POSITION: { x: 0.5 * width, y: 0.75 * height } };
➗ Operators
-
Mathematical Operators Used for movement, physics, and collision calculations. HW
this.position.x += this.velocity.x; const dist = Math.sqrt(dx * dx + dy * dy); this.speed += 0.5; -
String Operations Used to dynamically build paths and UI text. Homework
const image_src_floor = path + "/images/castleGame/grassBackground.png"; timeLabel.textContent = `Time taken: ${formattedTime}`; -
Boolean Expressions (logical operators) Used to combine conditions for gameplay logic. Homework
if (this.position.x <= 0 || this.position.x > this.gameEnv.innerWidth) { this.revComplete = true; }