I'll help you create a 2D Apex Legends-inspired game with characters from both mobile and PC versions. We'll use HTML5 Canvas for rendering and JavaScript for game logic.
I'll add a home screen with character selection before starting the game. Here's the updated version:
I'll create a menu that closely resembles Apex Legends' actual menu with the character selection carousel, background video, and proper styling. Here's the updated version:
This version includes:
The menu now closely resembles the actual Apex Legends interface with the character selection carousel and overall aesthetic.
Here's the most authentic Apex Legends menu recreation I can create for your 2D game:
This version includes:
Authentic Apex Legends menu layout with:
Character selection system with:
Game-ready structure that transitions to the 2D game view
The menu now looks and feels much more like the actual Apex Legends interface with all the key visual elements and interactions.
I'll create a simplified top-down version of Apex Legends with basic movement, shooting, and character selection. Here's the full implementation:
<!DOCTYPE html>
<html>
<head>
<title>Apex Legends 2D</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #111;
font-family: Arial, sans-serif;
}
#gameContainer {
position: relative;
width: 100vw;
height: 100vh;
}
#gameCanvas {
background: #333;
}
#menu {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.8);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
z-index: 10;
}
.character {
width: 100px;
height: 100px;
margin: 10px;
background-size: cover;
cursor: pointer;
border: 2px solid #333;
transition: all 0.3s;
}
.character:hover {
transform: scale(1.1);
border-color: #e6242b;
}
.character.selected {
border-color: #e6242b;
box-shadow: 0 0 10px #e6242b;
}
.character-container {
display: flex;
margin: 20px 0;
}
.start-btn {
padding: 10px 30px;
background: #e6242b;
color: white;
border: none;
border-radius: 5px;
font-size: 1.2rem;
cursor: pointer;
<!DOCTYPE html>
<html>
<head>
<title>Apex Legends 2D</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #111;
font-family: Arial, sans-serif;
}
#gameContainer {
position: relative;
width: 100vw;
height: 100vh;
}
#gameCanvas {
background: #333;
}
#menu {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.8);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
z-index: 10;
}
.character {
width: 100px;
height: 100px;
margin: 10px;
background-size: cover;
cursor: pointer;
border: 2px solid #333;
transition: all 0.3s;
}
.character:hover {
transform: scale(1.1);
border-color: #e6242b;
}
.character.selected {
border-color: #e6242b;
box-shadow: 0 0 10px #e6242b;
}
.character-container {
display: flex;
margin: 20px 0;
}
.start-btn {
padding: 10px 30px;
background: #e6242b;
color: white;
border: none;
border-radius: 5px;
font-size: 1.2rem;
cursor: pointer;
Here's a complete top-down Apex Legends 2D game with character selection, movement, shooting, and basic gameplay:
<jitProject id="apex-legends-2d-topdown" title="Apex Legends 2D Top-Down"> <jitFile type="file" filePath="index.html"> <!DOCTYPE html> <html> <head> <title>Apex Legends 2D</title> <style> body { margin: 0; overflow: hidden; background: #111; font-family: 'Arial', sans-serif; } #gameContainer { position: relative; width: 100vw; height: 100vh; } #gameCanvas { background: #222; display: none; } #menu { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.9); display: flex; flex-direction: column; justify-content: center; align-items: center; color: white; z-index: 10; } .title { font-size: 3rem; color: #e6242b; margin-bottom: 2rem; text-shadow: 0 0 10px rgba(230, 36, 43, 0.7); } .character-container { display: flex; flex-wrap: wrap; justify-content: center; max-width: 600px; margin: 2rem 0; } .character { width: 100px; height: 100px; margin: 10px; background-size: cover; background-position: center; border-radius: 5px; cursor: pointer; border: 2px solid #333; transition: all 0.3s; position: relative; } .character:hover { transform: scale(1.1); border-color: #e6242b; } .character.selected { border-color: #e6242b; box-shadow: 0 0 15px #e6242b; } .character-name { position: absolute; bottom: -25px; width: 100%; text-align: center; font-size: 0.9rem; color: #fff; } .start-btn { padding: 12px 40px; background: linear-gradient(to right, #e6242b, #ff5e00); color: white; border: none; border-radius: 5px; font-size: 1.2rem; cursor: pointer; transition: all 0.3s; margin-top: 2rem; text-transform: uppercase; letter-spacing: 1px; } .start-btn:hover { transform: translateY(-3px); box-shadow: 0 5px 15px rgba(230, 36, 43, 0.4); } .health-bar { position: absolute; top: 10px; left: 10px; width: 200px; height: 20px; background: #333; border: 2px solid #444; z-index: 100; } .health-fill { height: 100%; background: #e6242b; width: 100%; transition: width 0.3s; } .ammo-counter { position: absolute; top: 40px; left: 10px; color: white; font-size: 1.2rem; z-index: 100; } </style> </head> <body> <div id="gameContainer"> <div id="menu"> <h1 class="title">APEX LEGENDS 2D</h1> <div class="character-container" id="characterSelect"> <!-- Characters will be added by JavaScript --> </div> <button class="start-btn" id="startBtn">Drop In</button> </div> <canvas id="gameCanvas"></canvas> <div class="health-bar"> <div class="health-fill" id="healthBar"></div> </div> <div class="ammo-counter" id="ammoCounter">30/120</div> </div> <script src="game.js"></script> </body> </html> </jitFile> <jitFile type="file" filePath="game.js"> // Game constants const PLAYER_SPEED = 5; const BULLET_SPEED = 10; const PLAYER_SIZE = 30; const ENEMY_SIZE = 30; const BULLET_SIZE = 5; const MAP_SIZE = 2000;// Game state const game = { canvas: null, ctx: null, player: null, enemies: [], bullets: [], lastShot: 0, fireRate: 200, // ms between shots ammo: 30, maxAmmo: 30, totalAmmo: 120, selectedLegend: null, camera: { x: 0, y: 0 }, keys: { w: false, a: false, s: false, d: false }, mouse: { x: 0, y: 0, isDown: false } };
// Legends data const legends = { wraith: { name: "Wraith", color: "#8e44ad", speed: 1.1, health: 100, image: "https://media.contentapi.ea.com/content/dam/apex-legends/images/2019/01/legends-character-tiles/apex-grid-tile-legends-wraith.png.adapt.crop16x9.652w.png" }, bloodhound: { name: "Bloodhound", color: "#3498db", speed: 1.0, health: 100, image: "https://media.contentapi.ea.com/content/dam/apex-legends/images/2019/01/legends-character-tiles/apex-grid-tile-legends-bloodhound.png.adapt.crop16x9.652w.png" }, fade: { name: "Fade", color: "#f39c12", speed: 1.2, health: 100, image: "https://media.contentapi.ea.com/content/dam/apex-legends/images/2022/06/apex-grid-tile-legends-fade.png.adapt.crop16x9.652w.png" }, rhapsody: { name: "Rhapsody", color: "#e74c3c", speed: 1.0, health: 100, image: "https://media.contentapi.ea.com/content/dam/apex-legends/images/2022/06/apex-grid-tile-legends-rhapsody.png.adapt.crop16x9.652w.png" } };
// Player class class Player { constructor(x, y, legend) { this.x = x; this.y = y; this.size = PLAYER_SIZE; this.color = legend.color; this.speed = PLAYER_SPEED * legend.speed; this.health = legend.health; this.maxHealth = legend.health; this.legend = legend; this.angle = 0; }
draw(ctx, camera) {
ctx.save();
ctx.translate(this.x - camera.x, this.y - camera.y);
ctx.rotate(this.angle);
// Draw player body
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(0, 0, this.size / 2, 0, Math.PI * 2);
ctx.fill();
// Draw player direction indicator
ctx.fillStyle = '#fff';
ctx.beginPath();
ctx.moveTo(this.size / 2 - 5, 0);
ctx.lineTo(this.size / 2 - 15, -5);
ctx.lineTo(this.size / 2 - 15, 5);
ctx.closePath();
ctx.fill();
ctx.restore();
}
update(mouse) {
// Calculate angle to mouse
this.angle = Math.atan2(mouse.y - (this.y - game.camera.y), mouse.x - (this.x - game.camera.x));
}
draw(ctx, camera) {
ctx.save();
ctx.translate(this.x - camera.x, this.y - camera.y);
ctx.rotate(this.angle);
// Draw player body
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(0, 0, this.size / 2, 0, Math.PI * 2);
ctx.fill();
// Draw player direction indicator
ctx.fillStyle = '#fff';
ctx.beginPath();
ctx.moveTo(this.size / 2 - 5, 0);
ctx.lineTo(this.size / 2 - 15, -5);
ctx.lineTo(this.size / 2 - 15, 5);
ctx.closePath();
ctx.fill();
ctx.restore();
}
update(mouse) {
// Calculate angle to mouse
this.angle = Math.atan2(mouse.y - (this.y - game.camera.y), mouse.x - (this.x - game.camera.x));
}
}
// Enemy class class Enemy { constructor(x, y, legend) { this.x = x; this.y = y; this.size = ENEMY_SIZE; this.color = legend.color; this.speed = PLAYER_SPEED * 0.7 * legend.speed; this.health = legend.health; this.legend = legend; this.angle = 0; this.targetX = x; this.targetY = y; this.targetUpdateTime = 0; }
draw(ctx, camera) {
ctx.save();
ctx.translate(this.x - camera.x, this.y - camera.y);
ctx.rotate(this.angle);
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(0, 0, this.size / 2, 0, Math.PI * 2);
ctx.fill();
// Health bar
ctx.fillStyle = '#333';
ctx.fillRect(-this.size/2, -this.size/2 - 10, this.size, 5);
ctx.fillStyle = '#e6242b';
ctx.fillRect(-this.size/2, -this.size/2 - 10, this.size * (this.health / this.legend.health), 5);
ctx.restore();
}
update(player) {
// Simple AI: move toward player
const dx = player.x - this.x;
const dy = player.y - this.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
this.angle = Math.atan2(dy, dx);
this.x += (dx / distance) * this.speed;
this.y += (dy / distance) * this.speed;
}
}
draw(ctx, camera) {
ctx.save();
ctx.translate(this.x - camera.x, this.y - camera.y);
ctx.rotate(this.angle);
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(0, 0, this.size / 2, 0, Math.PI * 2);
ctx.fill();
// Health bar
ctx.fillStyle = '#333';
ctx.fillRect(-this.size/2, -this.size/2 - 10, this.size, 5);
ctx.fillStyle = '#e6242b';
ctx.fillRect(-this.size/2, -this.size/2 - 10, this.size * (this.health / this.legend.health), 5);
ctx.restore();
}
update(player) {
// Simple AI: move toward player
const dx = player.x - this.x;
const dy = player.y - this.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
this.angle = Math.atan2(dy, dx);
this.x += (dx / distance) * this.speed;
this.y += (dy / distance) * this.speed;
}
}
}
// Bullet class class Bullet { constructor(x, y, angle, isPlayer) { this.x = x; this.y = y; this.size = BULLET_SIZE; this.speed = BULLET_SPEED; this.angle = angle; this.isPlayer = isPlayer; this.color = isPlayer ? '#fff' : '#e6242b'; }
draw(ctx, camera) {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x - camera.x, this.y - camera.y, this.size, 0, Math.PI * 2);
ctx.fill();
}
update() {
this.x += Math.cos(this.angle) * this.speed;
this.y += Math.sin(this.angle) * this.speed;
}
draw(ctx, camera) {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x - camera.x, this.y - camera.y, this.size, 0, Math.PI * 2);
ctx.fill();
}
update() {
this.x += Math.cos(this.angle) * this.speed;
this.y += Math.sin(this.angle) * this.speed;
}
}
// Initialize game function init() { game.canvas = document.getElementById('gameCanvas'); game.ctx = game.canvas.getContext('2d');
// Set canvas size
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Setup character selection
setupCharacterSelection();
// Setup controls
setupControls();
// Start button
document.getElementById('startBtn').addEventListener('click', startGame);
// Set canvas size
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Setup character selection
setupCharacterSelection();
// Setup controls
setupControls();
// Start button
document.getElementById('startBtn').addEventListener('click', startGame);
}
function resizeCanvas() { game.canvas.width = window.innerWidth; game.canvas.height = window.innerHeight; }
function setupCharacterSelection() { const container = document.getElementById('characterSelect'); container.innerHTML = '';
for (const [key, legend] of Object.entries(legends)) {
const charElement = document.createElement('div');
charElement.className = 'character';
charElement.style.backgroundImage = `url('${legend.image}')`;
charElement.dataset.legend = key;
const nameElement = document.createElement('div');
nameElement.className = 'character-name';
nameElement.textContent = legend.name;
charElement.appendChild(nameElement);
charElement.addEventListener('click', () => selectCharacter(key));
container.appendChild(charElement);
}
for (const [key, legend] of Object.entries(legends)) {
const charElement = document.createElement('div');
charElement.className = 'character';
charElement.style.backgroundImage = `url('${legend.image}')`;
charElement.dataset.legend = key;
const nameElement = document.createElement('div');
nameElement.className = 'character-name';
nameElement.textContent = legend.name;
charElement.appendChild(nameElement);
charElement.addEventListener('click', () => selectCharacter(key));
container.appendChild(charElement);
}
}
function selectCharacter(legendKey) { game.selectedLegend = legendKey;
// Update UI
const characters = document.querySelectorAll('.character');
characters.forEach(char => {
char.classList.toggle('selected', char.dataset.legend === legendKey);
});
// Update UI
const characters = document.querySelectorAll('.character');
characters.forEach(char => {
char.classList.toggle('selected', char.dataset.legend === legendKey);
});
}
function setupControls() { // Keyboard controls window.addEventListener('keydown', (e) => { if (e.key.toLowerCase() === 'w') game.keys.w = true; if (e.key.toLowerCase() === 'a') game.keys.a = true; if (e.key.toLowerCase() === 's') game.keys.s = true; if (e.key.toLowerCase() === 'd') game.keys.d = true; });
window.addEventListener('keyup', (e) => {
if (e.key.toLowerCase() === 'w') game.keys.w = false;
if (e.key.toLowerCase() === 'a') game.keys.a = false;
if (e.key.toLowerCase() === 's') game.keys.s = false;
if (e.key.toLowerCase() === 'd') game.keys.d = false;
});
// Mouse controls
game.canvas.addEventListener('mousemove', (e) => {
game.mouse.x = e.clientX;
game.mouse.y = e.clientY;
});
game.canvas.addEventListener('mousedown', () => {
game.mouse.isDown = true;
});
game.canvas.addEventListener('mouseup', () => {
game.mouse.isDown = false;
});
// Touch controls for mobile
game.canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
const touch = e.touches[0];
game.mouse.x = touch.clientX;
game.mouse.y = touch.clientY;
});
game.canvas.addEventListener('touchstart', () => {
game.mouse.isDown = true;
});
game.canvas.addEventListener('touchend', () => {
game.mouse.isDown = false;
});
window.addEventListener('keyup', (e) => {
if (e.key.toLowerCase() === 'w') game.keys.w = false;
if (e.key.toLowerCase() === 'a') game.keys.a = false;
if (e.key.toLowerCase() === 's') game.keys.s = false;
if (e.key.toLowerCase() === 'd') game.keys.d = false;
});
// Mouse controls
game.canvas.addEventListener('mousemove', (e) => {
game.mouse.x = e.clientX;
game.mouse.y = e.clientY;
});
game.canvas.addEventListener('mousedown', () => {
game.mouse.isDown = true;
});
game.canvas.addEventListener('mouseup', () => {
game.mouse.isDown = false;
});
// Touch controls for mobile
game.canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
const touch = e.touches[0];
game.mouse.x = touch.clientX;
game.mouse.y = touch.clientY;
});
game.canvas.addEventListener('touchstart', () => {
game.mouse.isDown = true;
});
game.canvas.addEventListener('touchend', () => {
game.mouse.isDown = false;
});
}
function startGame() { if (!game.selectedLegend) { alert('Please select a legend first!'); return; }
// Hide menu
document.getElementById('menu').style.display = 'none';
document.getElementById('gameCanvas').style.display = 'block';
// Create player
const legend = legends[game.selectedLegend];
game.player = new Player(MAP_SIZE / 2, MAP_SIZE / 2, legend);
// Create enemies
createEnemies();
// Start game loop
gameLoop();
// Hide menu
document.getElementById('menu').style.display = 'none';
document.getElementById('gameCanvas').style.display = 'block';
// Create player
const legend = legends[game.selectedLegend];
game.player = new Player(MAP_SIZE / 2, MAP_SIZE / 2, legend);
// Create enemies
createEnemies();
// Start game loop
gameLoop();
}
function createEnemies() { game.enemies = [];
// Create 5 enemies with random legends
const legendKeys = Object.keys(legends);
for (let i = 0; i < 5; i++) {
const randomLegendKey = legendKeys[Math.floor(Math.random() * legendKeys.length)];
const legend = legends[randomLegendKey];
// Random position around the map
const x = Math.random() * MAP_SIZE;
const y = Math.random() * MAP_SIZE;
game.enemies.push(new Enemy(x, y, legend));
}
// Create 5 enemies with random legends
const legendKeys = Object.keys(legends);
for (let i = 0; i < 5; i++) {
const randomLegendKey = legendKeys[Math.floor(Math.random() * legendKeys.length)];
const legend = legends[randomLegendKey];
// Random position around the map
const x = Math.random() * MAP_SIZE;
const y = Math.random() * MAP_SIZE;
game.enemies.push(new Enemy(x, y, legend));
}
}
function gameLoop() { update(); render(); requestAnimationFrame(gameLoop); }
function update() { if (!game.player) return;
// Update player movement
let moveX = 0;
let moveY = 0;
if (game.keys.w) moveY -= game.player.speed;
if (game.keys.s) moveY += game.player.speed;
if (game.keys.a) moveX -= game.player.speed;
if (game.keys.d) moveX += game.player.speed;
// Normalize diagonal movement
if (moveX !== 0 && moveY !== 0) {
moveX *= 0.7071; // 1/sqrt(2)
moveY *= 0.7071;
}
// Update player position with bounds checking
game.player.x = Math.max(PLAYER_SIZE/2, Math.min(MAP_SIZE - PLAYER_SIZE/2, game.player.x + moveX));
game.player.y = Math.max(PLAYER_SIZE/2, Math.min(MAP_SIZE - PLAYER_SIZE/2, game.player.y + moveY));
// Update camera to follow player
game.camera.x = game.player.x - game.canvas.width / 2;
game.camera.y = game.player.y - game.canvas.height / 2;
// Keep camera within map bounds
game.camera.x = Math.max(0, Math.min(MAP_SIZE - game.canvas.width, game.camera.x));
game.camera.y = Math.max(0, Math.min(MAP_SIZE - game.canvas.height, game.camera.y));
// Update player angle based on mouse
game.player.update(game.mouse);
// Shooting
const now = Date.now();
if (game.mouse.isDown && now - game.lastShot > game.fireRate && game.ammo > 0) {
game.ammo--;
updateAmmoUI();
// Create bullet from player position in direction of mouse
const bullet = new Bullet(
game.player.x,
game.player.y,
game.player.angle,
true
);
game.bullets.push(bullet);
game.lastShot = now;
}
// Reload if no ammo and mouse released
if (!game.mouse.isDown && game.ammo === 0 && game.totalAmmo > 0) {
const reloadAmount = Math.min(game.maxAmmo, game.totalAmmo);
game.ammo = reloadAmount;
game.totalAmmo -= reloadAmount;
updateAmmoUI();
}
// Update bullets
for (let i = game.bullets.length - 1; i >= 0; i--) {
const bullet = game.bullets[i];
bullet.update();
// Remove bullets that are out of bounds
if (bullet.x < 0 || bullet.x > MAP_SIZE || bullet.y < 0 || bullet.y > MAP_SIZE) {
// Update player movement
let moveX = 0;
let moveY = 0;
if (game.keys.w) moveY -= game.player.speed;
if (game.keys.s) moveY += game.player.speed;
if (game.keys.a) moveX -= game.player.speed;
if (game.keys.d) moveX += game.player.speed;
// Normalize diagonal movement
if (moveX !== 0 && moveY !== 0) {
moveX *= 0.7071; // 1/sqrt(2)
moveY *= 0.7071;
}
// Update player position with bounds checking
game.player.x = Math.max(PLAYER_SIZE/2, Math.min(MAP_SIZE - PLAYER_SIZE/2, game.player.x + moveX));
game.player.y = Math.max(PLAYER_SIZE/2, Math.min(MAP_SIZE - PLAYER_SIZE/2, game.player.y + moveY));
// Update camera to follow player
game.camera.x = game.player.x - game.canvas.width / 2;
game.camera.y = game.player.y - game.canvas.height / 2;
// Keep camera within map bounds
game.camera.x = Math.max(0, Math.min(MAP_SIZE - game.canvas.width, game.camera.x));
game.camera.y = Math.max(0, Math.min(MAP_SIZE - game.canvas.height, game.camera.y));
// Update player angle based on mouse
game.player.update(game.mouse);
// Shooting
const now = Date.now();
if (game.mouse.isDown && now - game.lastShot > game.fireRate && game.ammo > 0) {
game.ammo--;
updateAmmoUI();
// Create bullet from player position in direction of mouse
const bullet = new Bullet(
game.player.x,
game.player.y,
game.player.angle,
true
);
game.bullets.push(bullet);
game.lastShot = now;
}
// Reload if no ammo and mouse released
if (!game.mouse.isDown && game.ammo === 0 && game.totalAmmo > 0) {
const reloadAmount = Math.min(game.maxAmmo, game.totalAmmo);
game.ammo = reloadAmount;
game.totalAmmo -= reloadAmount;
updateAmmoUI();
}
// Update bullets
for (let i = game.bullets.length - 1; i >= 0; i--) {
const bullet = game.bullets[i];
bullet.update();
// Remove bullets that are out of bounds
if (bullet.x < 0 || bullet.x > MAP_SIZE || bullet.y < 0 || bullet.y > MAP_SIZE) {