JSFiddle - React, Tailwind, and code Playground
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS Character Movement</title>
</head>
<body>
<h1>Character Movement Demo</h1>
<canvas id="gameCanvas" width="800" height="600"></canvas>
</body>
</html>
CSS
body {
display: flex;
flex-direction: column; /* Stack title and canvas vertically */
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0; /* Light grey background */
font-family: sans-serif;
}
canvas {
border: 1px solid black;
background-color: #aaeebb; /* Light green 'grass' */
/* Optional: Crispier pixels for pixel art */
image-rendering: pixelated;
image-rendering: crisp-edges;
}
h1 {
margin-bottom: 20px;
}
JavaScript
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// --- Configuration ---
const TILE_SIZE = 32;
const CHARACTER_SCALE = 2;
const CHARACTER_SPEED = 3;
const ANIMATION_SPEED = 8;
const PLAYER_MAX_HEALTH = 100;
const ENEMY_SPEED = 1.5;
const ENEMY_SIZE = 25 * CHARACTER_SCALE;
const ENEMY_SPAWN_RATE = 120;
const ENEMY_HEALTH = 1;
const ENEMY_DAMAGE = 10;
// --- Fireball Config ---
const BASE_FIREBALL_SPEED = 6;
const BASE_FIREBALL_SIZE = 10 * CHARACTER_SCALE;
const BASE_FIREBALL_DAMAGE = 1;
const FIREBALL_COOLDOWN = 20; // Cooldown *after* shooting
// --- Charge Shot Config ---
const MAX_CHARGE_TIME = 90; // Frames (e.g., 90 frames = 1.5 seconds at 60fps)
const MAX_FIREBALL_SIZE_MULTIPLIER = 3; // Max size is BASE * this multiplier
const MAX_FIREBALL_DAMAGE_MULTIPLIER = 5; // Max damage is BASE * this multiplier
const CHARGE_INDICATOR_MAX_RADIUS = 20 * CHARACTER_SCALE;
// --- Assets ---
const spriteSheet = new Image();
spriteSheet.src = 'https://opengameart.org/sites/default/files/styles/medium/public/Green-Cap-Character-16x18.png'; // <<< REPLACE
// --- Sprite Sheet Configuration ---
const SPRITE_FRAME_WIDTH = 16;
const SPRITE_FRAME_HEIGHT = 18;
const ANIMATION_FRAMES = 4;
const ANIMATION_MAP = { idleDown: 0, walkDown: 0, walkLeft: 1, walkRight: 2, walkUp: 3 };
// --- Game State ---
let character = {
x: canvas.width / 2 - (SPRITE_FRAME_WIDTH * CHARACTER_SCALE) / 2,
y: canvas.height / 2 - (SPRITE_FRAME_HEIGHT * CHARACTER_SCALE) / 2,
width: SPRITE_FRAME_WIDTH, height: SPRITE_FRAME_HEIGHT,
scaledWidth: SPRITE_FRAME_WIDTH * CHARACTER_SCALE,
scaledHeight: SPRITE_FRAME_HEIGHT * CHARACTER_SCALE,
dx: 0, dy: 0,
isMoving: false, direction: 'down', currentFrame: 0, animationTimer: 0,
health: PLAYER_MAX_HEALTH, maxHealth: PLAYER_MAX_HEALTH,
shootCooldownTimer: 0, canShoot: true,
// --- New Charging State ---
isCharging: false,
chargeTimer: 0,
};
const keysPressed = {
ArrowUp:...