
创建一个基于HTML5 Canvas的2D平台跳跃游戏,模拟OpenClaw的核心玩法。这个游戏将包含角色控制、平台跳跃、收集物品和敌人等元素。
设计思路
这个游戏将包含以下核心功能:
- 使用键盘控制角色移动和跳跃
- 物理引擎(重力、碰撞检测)
- 收集物品系统
- 简单的敌人AI
- 关卡设计和进度
下面是完整的实现代码:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OpenClaw - 平台跳跃游戏</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
color: white;
overflow: hidden;
}
.game-container {
position: relative;
width: 800px;
height: 500px;
background: rgba(0, 0, 0, 0.7);
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
overflow: hidden;
}
#gameCanvas {
display: block;
background: linear-gradient(to bottom, #87CEEB, #1E90FF);
border-radius: 10px;
}
.game-ui {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
z-index: 10;
}
.score, .lives {
background: rgba(0, 0, 0, 0.6);
padding: 8px 15px;
border-radius: 20px;
font-weight: bold;
font-size: 18px;
display: flex;
align-items: center;
}
.score span, .lives span {
margin-left: 5px;
color: #ffcc00;
}
.controls {
position: absolute;
bottom: 10px;
left: 0;
right: 0;
text-align: center;
background: rgba(0, 0, 0, 0.6);
padding: 10px;
border-radius: 5px;
font-size: 14px;
}
.screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, 0.8);
z-index: 20;
border-radius: 10px;
}
.screen h1 {
font-size: 48px;
margin-bottom: 20px;
color: #ffcc00;
text-shadow: 0 0 10px rgba(255, 204, 0, 0.5);
}
.screen p {
font-size: 18px;
margin: 10px 0;
max-width: 80%;
text-align: center;
line-height: 1.5;
}
.btn {
margin-top: 30px;
padding: 12px 30px;
background: linear-gradient(45deg, #ff8c00, #ffcc00);
border: none;
border-radius: 30px;
color: #1a2a6c;
font-size: 18px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 5px 15px rgba(255, 140, 0, 0.4);
}
.btn:hover {
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(255, 140, 0, 0.6);
}
.btn:active {
transform: translateY(1px);
}
.hidden {
display: none;
}
.game-over {
color: #ff4444;
}
.level-complete {
color: #44ff44;
}
</style>
</head>
<body>
<div class="game-container">
<canvas id="gameCanvas" width="800" height="500"></canvas>
<div class="game-ui">
<div class="score">分数: <span id="scoreValue">0</span></div>
<div class="lives">生命: <span id="livesValue">3</span></div>
</div>
<div class="controls">
使用方向键移动,空格键跳跃
</div>
<div id="startScreen" class="screen">
<h1>OpenClaw</h1>
<p>扮演勇敢的爪爪,穿越危险的平台,收集宝藏,击败敌人!</p>
<p>使用方向键移动,空格键跳跃</p>
<button class="btn" id="startBtn">开始游戏</button>
</div>
<div id="gameOverScreen" class="screen hidden">
<h1 class="game-over">游戏结束</h1>
<p>最终得分: <span id="finalScore">0</span></p>
<button class="btn" id="restartBtn">重新开始</button>
</div>
<div id="levelCompleteScreen" class="screen hidden">
<h1 class="level-complete">关卡完成!</h1>
<p>得分: <span id="levelScore">0</span></p>
<button class="btn" id="nextLevelBtn">下一关</button>
</div>
</div>
<script>
// 游戏主类
class Game {
constructor() {
this.canvas = document.getElementById('gameCanvas');
this.ctx = this.canvas.getContext('2d');
this.width = this.canvas.width;
this.height = this.canvas.height;
// 游戏状态
this.isRunning = false;
this.level = 1;
this.score = 0;
this.lives = 3;
// 游戏对象
this.player = null;
this.platforms = [];
this.collectibles = [];
this.enemies = [];
// 键盘状态
this.keys = {
left: false,
right: false,
up: false
};
// 绑定UI元素
this.scoreValue = document.getElementById('scoreValue');
this.livesValue = document.getElementById('livesValue');
// 绑定屏幕
this.startScreen = document.getElementById('startScreen');
this.gameOverScreen = document.getElementById('gameOverScreen');
this.levelCompleteScreen = document.getElementById('levelCompleteScreen');
// 绑定按钮
document.getElementById('startBtn').addEventListener('click', () => this.start());
document.getElementById('restartBtn').addEventListener('click', () => this.restart());
document.getElementById('nextLevelBtn').addEventListener('click', () => this.nextLevel());
// 键盘事件
window.addEventListener('keydown', (e) => this.handleKeyDown(e));
window.addEventListener('keyup', (e) => this.handleKeyUp(e));
// 初始化游戏
this.init();
}
init() {
this.showScreen(this.startScreen);
}
start() {
this.hideAllScreens();
this.isRunning = true;
this.score = 0;
this.lives = 3;
this.level = 1;
this.updateUI();
this.loadLevel(this.level);
this.gameLoop();
}
restart() {
this.start();
}
nextLevel() {
this.level++;
this.hideAllScreens();
this.isRunning = true;
this.loadLevel(this.level);
this.gameLoop();
}
loadLevel(level) {
// 重置游戏对象
this.platforms = [];
this.collectibles = [];
this.enemies = [];
// 创建玩家
this.player = new Player(100, 300, 40, 60);
// 根据关卡生成不同的布局
if (level === 1) {
// 平台
this.platforms.push(new Platform(0, 450, 800, 50)); // 地面
this.platforms.push(new Platform(200, 380, 150, 20));
this.platforms.push(new Platform(450, 320, 150, 20));
this.platforms.push(new Platform(100, 250, 120, 20));
this.platforms.push(new Platform(350, 180, 150, 20));
this.platforms.push(new Platform(600, 250, 120, 20));
// 收集品
this.collectibles.push(new Collectible(250, 340, 'coin'));
this.collectibles.push(new Collectible(500, 280, 'coin'));
this.collectibles.push(new Collectible(150, 210, 'gem'));
this.collectibles.push(new Collectible(400, 140, 'gem'));
this.collectibles.push(new Collectible(650, 210, 'coin'));
// 敌人
this.enemies.push(new Enemy(450, 300, 40, 40, 450, 550));
} else if (level === 2) {
// 更复杂的关卡
this.platforms.push(new Platform(0, 450, 800, 50));
this.platforms.push(new Platform(150, 400, 100, 20));
this.platforms.push(new Platform(350, 350, 100, 20));
this.platforms.push(new Platform(550, 300, 100, 20));
this.platforms.push(new Platform(250, 250, 100, 20));
this.platforms.push(new Platform(450, 200, 100, 20));
this.platforms.push(new Platform(650, 150, 100, 20));
// 收集品
this.collectibles.push(new Collectible(200, 360, 'gem'));
this.collectibles.push(new Collectible(400, 310, 'coin'));
this.collectibles.push(new Collectible(600, 260, 'gem'));
this.collectibles.push(new Collectible(300, 210, 'coin'));
this.collectibles.push(new Collectible(500, 160, 'gem'));
this.collectibles.push(new Collectible(700, 110, 'coin'));
// 敌人
this.enemies.push(new Enemy(350, 330, 40, 40, 350, 450));
this.enemies.push(new Enemy(550, 280, 40, 40, 550, 650));
}
}
updateUI() {
this.scoreValue.textContent = this.score;
this.livesValue.textContent = this.lives;
}
handleKeyDown(e) {
if (e.code === 'ArrowLeft') this.keys.left = true;
if (e.code === 'ArrowRight') this.keys.right = true;
if (e.code === 'Space') this.keys.up = true;
}
handleKeyUp(e) {
if (e.code === 'ArrowLeft') this.keys.left = false;
if (e.code === 'ArrowRight') this.keys.right = false;
if (e.code === 'Space') this.keys.up = false;
}
gameLoop() {
if (!this.isRunning) return;
// 更新游戏状态
this.update();
// 渲染游戏
this.render();
// 继续循环
requestAnimationFrame(() => this.gameLoop());
}
update() {
// 更新玩家
this.player.update(this.keys, this.platforms);
// 更新敌人
this.enemies.forEach(enemy => enemy.update());
// 检查收集品碰撞
this.collectibles = this.collectibles.filter(collectible => {
if (this.checkCollision(this.player, collectible)) {
this.score += collectible.value;
this.updateUI();
return false; // 移除收集品
}
return true;
});
// 检查敌人碰撞
this.enemies.forEach(enemy => {
if (this.checkCollision(this.player, enemy)) {
// 如果玩家从上方踩到敌人
if (this.player.velocityY > 0 &&
this.player.y + this.player.height < enemy.y + enemy.height / 2) {
this.score += 100;
this.updateUI();
// 移除敌人
this.enemies = this.enemies.filter(e => e !== enemy);
} else {
// 玩家受伤
this.lives--;
this.updateUI();
// 重置玩家位置
this.player.x = 100;
this.player.y = 300;
this.player.velocityY = 0;
if (this.lives <= 0) {
this.gameOver();
}
}
}
});
// 检查是否收集完所有物品
if (this.collectibles.length === 0) {
this.levelComplete();
}
// 检查玩家是否掉出屏幕
if (this.player.y > this.height) {
this.lives--;
this.updateUI();
this.player.x = 100;
this.player.y = 300;
this.player.velocityY = 0;
if (this.lives <= 0) {
this.gameOver();
}
}
}
render() {
// 清空画布
this.ctx.clearRect(0, 0, this.width, this.height);
// 绘制背景
this.drawBackground();
// 绘制平台
this.platforms.forEach(platform => platform.draw(this.ctx));
// 绘制收集品
this.collectibles.forEach(collectible => collectible.draw(this.ctx));
// 绘制敌人
this.enemies.forEach(enemy => enemy.draw(this.ctx));
// 绘制玩家
this.player.draw(this.ctx);
}
drawBackground() {
// 天空渐变
const gradient = this.ctx.createLinearGradient(0, 0, 0, this.height);
gradient.addColorStop(0, '#87CEEB');
gradient.addColorStop(1, '#1E90FF');
this.ctx.fillStyle = gradient;
this.ctx.fillRect(0, 0, this.width, this.height);
// 云朵
this.ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
this.drawCloud(100, 80, 30);
this.drawCloud(300, 120, 40);
this.drawCloud(600, 60, 35);
this.drawCloud(500, 150, 25);
}
drawCloud(x, y, size) {
this.ctx.beginPath();
this.ctx.arc(x, y, size, 0, Math.PI * 2);
this.ctx.arc(x + size, y, size * 0.8, 0, Math.PI * 2);
this.ctx.arc(x - size, y, size * 0.8, 0, Math.PI * 2);
this.ctx.arc(x + size * 0.5, y - size * 0.5, size * 0.7, 0, Math.PI * 2);
this.ctx.arc(x - size * 0.5, y - size * 0.5, size * 0.7, 0, Math.PI * 2);
this.ctx.fill();
}
checkCollision(rect1, rect2) {
return rect1.x < rect2.x + rect2.width &&
rect1.x + rect1.width > rect2.x &&
rect1.y < rect2.y + rect2.height &&
rect1.y + rect1.height > rect2.y;
}
gameOver() {
this.isRunning = false;
document.getElementById('finalScore').textContent = this.score;
this.showScreen(this.gameOverScreen);
}
levelComplete() {
this.isRunning = false;
document.getElementById('levelScore').textContent = this.score;
this.showScreen(this.levelCompleteScreen);
}
showScreen(screen) {
this.hideAllScreens();
screen.classList.remove('hidden');
}
hideAllScreens() {
this.startScreen.classList.add('hidden');
this.gameOverScreen.classList.add('hidden');
this.levelCompleteScreen.classList.add('hidden');
}
}
// 玩家类
class Player {
constructor(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.velocityX = 0;
this.velocityY = 0;
this.speed = 5;
this.jumpPower = 12;
this.gravity = 0.5;
this.isGrounded = false;
}
update(keys, platforms) {
// 水平移动
this.velocityX = 0;
if (keys.left) this.velocityX = -this.speed;
if (keys.right) this.velocityX = this.speed;
// 跳跃
if (keys.up && this.isGrounded) {
this.velocityY = -this.jumpPower;
this.isGrounded = false;
}
// 应用重力
this.velocityY += this.gravity;
// 更新位置
this.x += this.velocityX;
this.y += this.velocityY;
// 边界检查
if (this.x < 0) this.x = 0;
if (this.x + this.width > 800) this.x = 800 - this.width;
// 平台碰撞检测
this.isGrounded = false;
for (let platform of platforms) {
if (this.checkPlatformCollision(platform)) {
// 从上方碰撞
if (this.velocityY > 0 &&
this.y + this.height > platform.y &&
this.y + this.height < platform.y + platform.height) {
this.y = platform.y - this.height;
this.velocityY = 0;
this.isGrounded = true;
}
// 从下方碰撞
else if (this.velocityY < 0 &&
this.y < platform.y + platform.height &&
this.y > platform.y) {
this.y = platform.y + platform.height;
this.velocityY = 0;
}
// 从左侧碰撞
else if (this.velocityX > 0 &&
this.y + this.height > platform.y &&
this.y < platform.y + platform.height) {
this.x = platform.x - this.width;
}
// 从右侧碰撞
else if (this.velocityX < 0 &&
this.y + this.height > platform.y &&
this.y < platform.y + platform.height) {
this.x = platform.x + platform.width;
}
}
}
}
checkPlatformCollision(platform) {
return this.x < platform.x + platform.width &&
this.x + this.width > platform.x &&
this.y < platform.y + platform.height &&
this.y + this.height > platform.y;
}
draw(ctx) {
// 绘制身体
ctx.fillStyle = '#FF6B6B';
ctx.fillRect(this.x, this.y, this.width, this.height);
// 绘制眼睛
ctx.fillStyle = 'white';
ctx.fillRect(this.x + 10, this.y + 15, 8, 8);
ctx.fillRect(this.x + 22, this.y + 15, 8, 8);
// 绘制瞳孔
ctx.fillStyle = 'black';
ctx.fillRect(this.x + 12, this.y + 17, 4, 4);
ctx.fillRect(this.x + 24, this.y + 17, 4, 4);
// 绘制嘴巴
ctx.fillStyle = 'black';
ctx.fillRect(this.x + 15, this.y + 35, 10, 3);
// 绘制爪子
ctx.fillStyle = '#333';
ctx.fillRect(this.x + 5, this.y + 50, 8, 8);
ctx.fillRect(this.x + 27, this.y + 50, 8, 8);
}
}
// 平台类
class Platform {
constructor(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
draw(ctx) {
// 绘制平台
ctx.fillStyle = '#8B4513';
ctx.fillRect(this.x, this.y, this.width, this.height);
// 绘制平台边缘
ctx.fillStyle = '#A0522D';
ctx.fillRect(this.x, this.y, this.width, 5);
// 绘制平台细节
ctx.fillStyle = '#654321';
for (let i = 10; i < this.width; i += 20) {
ctx.fillRect(this.x + i, this.y + 8, 5, 5);
}
}
}
// 收集品类
class Collectible {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
this.width = 20;
this.height = 20;
this.value = type === 'coin' ? 50 : 100;
this.animationFrame = 0;
}
draw(ctx) {
this.animationFrame += 0.1;
const offset = Math.sin(this.animationFrame) * 3;
if (this.type === 'coin') {
// 绘制金币
ctx.fillStyle = '#FFD700';
ctx.beginPath();
ctx.arc(this.x + 10, this.y + 10 + offset, 8, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#FFA500';
ctx.beginPath();
ctx.arc(this.x + 10, this.y + 10 + offset, 5, 0, Math.PI * 2);
ctx.fill();
} else {
// 绘制宝石
ctx.fillStyle = '#00FFFF';
ctx.beginPath();
ctx.moveTo(this.x + 10, this.y + offset);
ctx.lineTo(this.x + 20, this.y + 10 + offset);
ctx.lineTo(this.x + 10, this.y + 20 + offset);
ctx.lineTo(this.x, this.y + 10 + offset);
ctx.closePath();
ctx.fill();
ctx.fillStyle = '#008B8B';
ctx.beginPath();
ctx.moveTo(this.x + 10, this.y + 5 + offset);
ctx.lineTo(this.x + 15, this.y + 10 + offset);
ctx.lineTo(this.x + 10, this.y + 15 + offset);
ctx.lineTo(this.x + 5, this.y + 10 + offset);
ctx.closePath();
ctx.fill();
}
}
}
// 敌人类
class Enemy {
constructor(x, y, width, height, minX, maxX) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.minX = minX;
this.maxX = maxX;
this.speed = 2;
this.direction = 1; // 1表示向右,-1表示向左
}
update() {
this.x += this.speed * this.direction;
if (this.x >= this.maxX) {
this.direction = -1;
} else if (this.x <= this.minX) {
this.direction = 1;
}
}
draw(ctx) {
// 绘制身体
ctx.fillStyle = '#8B0000';
ctx.fillRect(this.x, this.y, this.width, this.height);
// 绘制眼睛
ctx.fillStyle = 'yellow';
ctx.fillRect(this.x + 10, this.y + 10, 8, 8);
ctx.fillRect(this.x + 22, this.y + 10, 8, 8);
// 绘制尖刺
ctx.fillStyle = '#333';
for (let i = 0; i < 4; i++) {
ctx.beginPath();
ctx.moveTo(this.x + i * 10, this.y);
ctx.lineTo(this.x + i * 10 + 5, this.y - 5);
ctx.lineTo(this.x + i * 10 + 10, this.y);
ctx.closePath();
ctx.fill();
}
}
}
// 启动游戏
window.onload = () => {
new Game();
};
</script>
</body>
</html>
游戏特点
-
- 完整的游戏流程:
- 开始界面
- 游戏主界面
- 关卡完成界面
- 游戏结束界面
-
- 核心游戏机制:
- 平台跳跃物理系统
- 碰撞检测(平台、收集品、敌人)
- 敌人AI(简单巡逻)
- 收集系统(金币和宝石)
- 生命值和分数系统
-
- 视觉效果:
- 渐变背景和云朵
- 角色和敌人动画
- 收集品浮动效果
- 简洁的UI界面
-
- 关卡设计:
- 两个不同难度的关卡
- 每个关卡有不同的平台布局、收集品和敌人
游戏玩法
- 1.使用方向键(左右箭头)控制角色移动
- 2.使用空格键跳跃
- 3.收集所有物品(金币和宝石)完成关卡
- 4.避开敌人或从上方踩它们
- 5.小心不要掉出屏幕
您可以直接将此代码复制到HTML文件中,在浏览器中打开即可游玩。游戏完全自包含,不需要任何外部资源。