单人壁球挑战赛

🎯 核心目标
用球拍控制小球,击中上方目标得分,尽可能获得高分。
🕹️ 基础操作
PC端控制:
-
← → 箭头键 或 A/D键:左右移动球拍
-
空格键:发射球/暂停游戏
-
R键:重新开始游戏
移动端控制:
-
左右箭头按钮:移动球拍
-
发射按钮:发射球/暂停游戏
-
滑动屏幕:直接移动球拍(触摸控制)
-
点击画布:发射球
🎮 游戏机制
1. 球拍与球
-
球初始停在球拍上(闪烁提示)
-
按空格键或点击发射按钮将球射出
-
球击中球拍会反弹,根据击中位置改变反弹角度
2. 目标系统
-
屏幕上方有多行多列彩色目标
-
不同颜色的目标有不同分数:
-
普通目标:10分(蓝色系)
-
金色目标:50分(金色,高分奖励)
-
红色生命目标:10分 + 恢复1条生命
-
3. 得分与升级
-
每次击中目标获得相应分数
-
每得100分:触发花朵特效庆祝
-
等级系统:每100分升1级,球速随等级提高
4. 生命与失败
-
初始生命值:3条命
-
损失生命:球掉落到底部
-
生命恢复:击中红色生命目标
-
最大生命值:最多5条命
-
游戏结束:生命值为0
🌸 特效系统
1. 花朵特效
-
每获得100分触发一次
-
屏幕上方飘落彩色花朵
-
花朵有不同形状和颜色
2. 得分动画
-
每次击中目标显示"+"分数动画
-
动画从击中位置向上飘散
⚙️ 游戏界面
1. 统计信息
-
分数:当前得分
-
生命值:剩余生命数
-
等级:当前游戏等级
2. 控制区域
-
PC端:显示键盘控制提示和操作按钮
-
移动端:显示触摸控制按钮
3. 特殊状态
-
暂停状态:可以暂停和继续游戏
-
游戏结束:显示最终得分和重玩按钮
🎵 音效系统
-
背景音乐:放松的背景音乐(可开关)
-
得分音效:击中目标时播放
-
花朵特效音效:每100分时播放
📱 设备适配
PC端优化:
-
键盘控制
-
大尺寸目标
-
完整控制面板
移动端优化:
-
触摸控制按钮
-
滑动控制
-
自适应目标大小
-
优化的画布尺寸
🏆 策略提示
控制反弹角度:球击中球拍的不同位置会改变反弹角度
优先击中特殊目标:金色目标(50分)和红色生命目标
节奏控制:不要急于发射,等待合适时机
生命管理:保持至少1条生命作为安全缓冲
利用墙壁反弹:让球从侧面墙壁反弹击中难以直接击中的目标
💡 游戏特色
-
自适应难度:球速随等级提高,挑战性逐渐增加
-
双重控制:同时支持键盘和触摸控制
-
视觉反馈:丰富的动画和特效
-
放松体验:柔和的背景音乐和视觉效果
-
跨平台:在PC和移动设备上都能良好运行
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>单人壁球挑战赛</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
-webkit-overflow-scrolling: touch;
}
html, body {
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
background: #000;
color: white;
overflow-x: hidden;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 0;
position: fixed;
width: 100%;
height: 100%;
overscroll-behavior: none;
}
/* 防止iOS橡皮筋效果 */
body {
overflow: hidden;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
/* 背景图片 */
#background-image {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('https://amitofo.icu/lianchi.jpg');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
opacity: 0.7;
z-index: -2;
}
/* 背景遮罩 */
#background-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(10, 20, 40, 0.85);
z-index: -1;
}
/* 更多游戏角标 */
.more-games-corner {
position: absolute;
top: 15px;
left: 15px;
z-index: 100;
padding: 8px 15px;
background: linear-gradient(135deg, #ff3366, #ff6633);
border-radius: 20px;
box-shadow: 0 4px 12px rgba(255, 51, 102, 0.4);
transition: all 0.3s ease;
}
.more-games-corner:hover, .more-games-corner:active {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(255, 51, 102, 0.6);
}
.more-games-link {
color: white;
text-decoration: none;
font-size: 14px;
font-weight: 600;
display: flex;
align-items: center;
gap: 6px;
}
.more-games-link i {
font-size: 16px;
}
/* 游戏容器 */
.game-container {
width: 100%;
max-width: 900px;
margin: 0 auto;
padding: 20px;
padding-top: 70px;
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
overflow: hidden;
}
/* 标题 */
.header {
text-align: center;
margin-bottom: 25px;
width: 100%;
}
h1 {
font-size: 2.8rem;
margin-bottom: 8px;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.7);
background: linear-gradient(90deg, #FFD700, #FFA500, #FF6347);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
letter-spacing: 1px;
font-weight: 800;
}
.subtitle {
font-size: 1.1rem;
opacity: 0.9;
color: #e0e0ff;
max-width: 600px;
margin: 0 auto;
line-height: 1.5;
}
/* 游戏UI */
.game-ui {
display: flex;
justify-content: space-between;
width: 100%;
margin-bottom: 20px;
flex-wrap: wrap;
gap: 15px;
}
.stats {
display: flex;
gap: 20px;
flex-wrap: wrap;
justify-content: center;
width: 100%;
}
.stat-box {
background: rgba(255, 255, 255, 0.12);
padding: 15px 20px;
border-radius: 12px;
min-width: 120px;
text-align: center;
backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
}
.stat-label {
font-size: 0.95rem;
opacity: 0.8;
margin-bottom: 5px;
color: #b0b0ff;
}
.stat-value {
font-size: 2.2rem;
font-weight: 800;
color: #FFD700;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
/* 游戏画布容器 */
.canvas-container {
width: 100%;
max-width: 800px;
overflow: hidden;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6);
border: 2px solid rgba(255, 215, 0, 0.3);
position: relative;
background-color: rgba(10, 15, 35, 0.85);
touch-action: manipulation;
height: 500px; /* 固定高度 */
margin: 0 auto;
}
/* 游戏画布 */
#gameCanvas {
display: block;
width: 100%;
height: 100%;
}
/* 控制区域 – PC端 */
.pc-controls {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 25px;
width: 100%;
max-width: 800px;
}
.control-info {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 25px;
margin-bottom: 20px;
background: rgba(0, 0, 0, 0.3);
padding: 18px;
border-radius: 12px;
width: 100%;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.control-item {
display: flex;
align-items: center;
gap: 10px;
}
.key {
background: rgba(255, 255, 255, 0.15);
padding: 8px 16px;
border-radius: 8px;
font-weight: 700;
border: 1px solid rgba(255, 255, 255, 0.3);
min-width: 50px;
text-align: center;
color: #FFD700;
}
/* 按钮 */
.buttons {
display: flex;
gap: 15px;
margin-top: 10px;
flex-wrap: wrap;
justify-content: center;
}
button {
background: linear-gradient(to bottom, #4a6ee0, #3a5ed0);
color: white;
border: none;
padding: 14px 28px;
border-radius: 10px;
font-size: 1.05rem;
cursor: pointer;
transition: all 0.2s;
font-weight: 600;
letter-spacing: 0.5px;
box-shadow: 0 4px 12px rgba(58, 94, 208, 0.4);
display: flex;
align-items: center;
gap: 8px;
}
button:hover {
background: linear-gradient(to bottom, #5a7ef0, #4a6ee0);
transform: translateY(-3px);
box-shadow: 0 6px 18px rgba(58, 94, 208, 0.6);
}
button:active {
transform: translateY(1px);
}
button#restartBtn {
background: linear-gradient(to bottom, #FF9800, #F57C00);
box-shadow: 0 4px 12px rgba(245, 124, 0, 0.4);
}
button#restartBtn:hover {
background: linear-gradient(to bottom, #FFB74D, #FF9800);
box-shadow: 0 6px 18px rgba(245, 124, 0, 0.6);
}
/* 花朵特效容器 */
#flower-effects {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 10;
overflow: hidden;
}
.flower {
position: absolute;
font-size: 24px;
opacity: 0;
animation: fall linear forwards;
}
@keyframes fall {
0% {
transform: translateY(-50px) rotate(0deg);
opacity: 0;
}
10% {
opacity: 1;
}
100% {
transform: translateY(100vh) rotate(360deg);
opacity: 0;
}
}
/* 得分动画 */
.score-popup {
position: absolute;
font-size: 28px;
font-weight: 900;
color: #FFD700;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.8);
z-index: 5;
pointer-events: none;
animation: scorePopup 1s ease-out forwards;
}
@keyframes scorePopup {
0% {
opacity: 0;
transform: translateY(0) scale(0.5);
}
50% {
opacity: 1;
transform: translateY(-30px) scale(1.2);
}
100% {
opacity: 0;
transform: translateY(-60px) scale(0.8);
}
}
/* 游戏结束界面 */
.game-over {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.92);
padding: 45px;
border-radius: 16px;
text-align: center;
display: none;
z-index: 100;
width: 90%;
max-width: 500px;
border: 2px solid #FFD700;
box-shadow: 0 0 50px rgba(255, 215, 0, 0.4);
backdrop-filter: blur(10px);
}
.game-over h2 {
font-size: 2.8rem;
color: #FFD700;
margin-bottom: 20px;
}
.game-over p {
font-size: 1.4rem;
margin-bottom: 35px;
color: #e0e0ff;
}
/* 移动端控制按钮 */
.mobile-controls {
display: none;
margin-top: 25px;
width: 100%;
max-width: 300px;
justify-content: center;
gap: 20px;
}
.mobile-btn {
width: 80px;
height: 80px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
border: 2px solid rgba(255, 215, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
font-size: 32px;
color: white;
cursor: pointer;
user-select: none;
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
touch-action: manipulation;
}
/* 音频控制 */
.audio-control {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 50;
background: rgba(0, 0, 0, 0.7);
border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
}
/* PC端专用样式 */
@media (min-width: 769px) {
.mobile-controls {
display: none !important;
}
.pc-controls {
display: flex !important;
}
}
/* 移动端样式 */
@media (max-width: 768px) {
.pc-controls {
display: none !important; /* 移动端隐藏控制区域 */
}
.mobile-controls {
display: flex;
}
h1 {
font-size: 2.2rem;
}
.subtitle {
font-size: 1rem;
padding: 0 10px;
}
.game-ui {
flex-direction: column;
align-items: center;
gap: 15px;
}
.stats {
width: 100%;
justify-content: center;
}
.stat-box {
min-width: 100px;
padding: 12px 15px;
}
.stat-value {
font-size: 1.8rem;
}
.more-games-corner {
top: 10px;
left: 10px;
padding: 6px 12px;
}
.more-games-link {
font-size: 13px;
}
/* 移动端调整画布高度 */
.canvas-container {
height: 400px;
}
/* 移动端游戏容器调整 */
.game-container {
padding: 15px;
padding-top: 60px;
}
}
@media (max-width: 480px) {
.game-container {
padding: 15px;
padding-top: 60px;
}
.stat-box {
min-width: 85px;
padding: 10px 12px;
}
.stat-label {
font-size: 0.85rem;
}
.stat-value {
font-size: 1.6rem;
}
.game-over {
padding: 30px 20px;
width: 95%;
}
.game-over h2 {
font-size: 2.2rem;
}
.game-over p {
font-size: 1.2rem;
}
/* iPhone竖屏优化 */
.canvas-container {
height: 350px;
}
h1 {
font-size: 1.8rem;
}
.subtitle {
font-size: 0.9rem;
margin-bottom: 10px;
}
.game-ui {
margin-bottom: 10px;
}
}
/* iPhone竖屏特殊优化 */
@media (max-width: 480px) and (max-height: 850px) {
.game-container {
padding-top: 50px;
}
.canvas-container {
height: 300px;
}
.header {
margin-bottom: 15px;
}
.stat-box {
padding: 8px 10px;
min-width: 70px;
}
.stat-value {
font-size: 1.4rem;
}
.mobile-controls {
margin-top: 15px;
}
.mobile-btn {
width: 70px;
height: 70px;
font-size: 28px;
}
}
/* iPhone小屏幕特殊优化 */
@media (max-width: 375px) and (max-height: 670px) {
.canvas-container {
height: 280px;
}
.game-container {
padding-top: 40px;
padding: 10px;
padding-top: 40px;
}
h1 {
font-size: 1.6rem;
}
.stat-box {
padding: 6px 8px;
min-width: 65px;
}
.stat-label {
font-size: 0.75rem;
}
.stat-value {
font-size: 1.2rem;
}
.mobile-btn {
width: 65px;
height: 65px;
font-size: 24px;
}
}
/* 横屏优化 */
@media (orientation: landscape) and (max-height: 600px) {
.game-container {
padding-top: 15px;
}
.header {
margin-bottom: 10px;
}
h1 {
font-size: 1.8rem;
}
.subtitle {
font-size: 0.9rem;
margin-bottom: 10px;
}
.game-ui {
margin-bottom: 10px;
}
.stat-box {
padding: 8px 12px;
min-width: 80px;
}
.stat-value {
font-size: 1.4rem;
}
.canvas-container {
height: 280px;
}
.mobile-controls {
margin-top: 15px;
}
}
</style>
</head>
<body>
<!– 背景图片 –>
<div id="background-image"></div>
<div id="background-overlay"></div>
<!– 花朵特效容器 –>
<div id="flower-effects"></div>
<!– 更多游戏角标 –>
<div class="more-games-corner">
<a href="https://game.amitofo.icu" class="more-games-link">
<i class="fas fa-gamepad"></i>
<span>更多游戏</span>
</a>
</div>
<!– 音频控制 –>
<div class="audio-control" id="audioToggle">
<i class="fas fa-volume-up" id="audioIcon"></i>
</div>
<!– 游戏主容器 –>
<div class="game-container">
<div class="header">
<h1>单人壁球挑战赛</h1>
<p class="subtitle">按空格键从球拍发射球,击中目标得分!每100分触发花朵特效。</p>
</div>
<div class="game-ui">
<div class="stats">
<div class="stat-box">
<div class="stat-label">分数</div>
<div id="score" class="stat-value">0</div>
</div>
<div class="stat-box">
<div class="stat-label">生命值</div>
<div id="lives" class="stat-value">3</div>
</div>
<div class="stat-box">
<div class="stat-label">等级</div>
<div id="level" class="stat-value">1</div>
</div>
</div>
</div>
<!– 游戏画布容器 –>
<div class="canvas-container">
<canvas id="gameCanvas" width="800" height="500"></canvas>
</div>
<!– 移动端控制按钮 –>
<div class="mobile-controls">
<div class="mobile-btn" id="mobileLeft">
<i class="fas fa-arrow-left"></i>
</div>
<div class="mobile-btn" id="mobileSpace">
<i class="fas fa-arrow-up"></i>
<span style="font-size: 12px; margin-left: 5px;">发射</span>
</div>
<div class="mobile-btn" id="mobileRight">
<i class="fas fa-arrow-right"></i>
</div>
</div>
<!– 游戏控制按钮 – PC端显示 –>
<div class="pc-controls">
<div class="control-info">
<div class="control-item">
<div class="key">← →</div>
<span>移动球拍</span>
</div>
<div class="control-item">
<div class="key">空格</div>
<span>发射球/暂停</span>
</div>
<div class="control-item">
<div class="key">R</div>
<span>重新开始</span>
</div>
</div>
<div class="buttons">
<button id="startBtn">
<i class="fas fa-play"></i>
<span>开始游戏</span>
</button>
<button id="pauseBtn">
<i class="fas fa-pause"></i>
<span>暂停游戏</span>
</button>
<button id="restartBtn">
<i class="fas fa-redo"></i>
<span>重新开始</span>
</button>
</div>
</div>
<!– 游戏结束界面 –>
<div id="gameOver" class="game-over">
<h2>游戏结束</h2>
<p>最终得分: <span id="finalScore">0</span></p>
<button id="playAgainBtn">
<i class="fas fa-redo"></i>
<span>再来一局</span>
</button>
</div>
</div>
<!– 背景音乐 –>
<audio id="backgroundMusic" loop>
<source src="https://amitofo.icu/beijing.ogg" type="audio/ogg">
</audio>
<!– 得分音效 –>
<audio id="scoreSound">
<source src="https://amitofo.icu/xiaochu.mp3" type="audio/mpeg">
</audio>
<!– 新的击球得分音效 –>
<audio id="winSound" preload="auto">
<source src="https://amitofo.icu/win.mp3" type="audio/mpeg">
</audio>
<script>
// 获取Canvas元素和上下文
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// 游戏状态
let gameRunning = false;
let gamePaused = false;
let score = 0;
let lives = 3;
let level = 1;
let animationId = null;
let ballOnPaddle = true; // 球是否在球拍上(等待发射)
let lastScoreForEffect = 0; // 上次触发特效的分数
let isMobile = false; // 是否是移动设备
// 球拍属性
const paddle = {
width: 100,
height: 15,
x: canvas.width / 2 – 50,
y: canvas.height – 30,
speed: 8,
color: '#4a6ee0',
borderColor: '#5a7ef0'
};
// 球属性
const ball = {
x: canvas.width / 2,
y: canvas.height – 45, // 初始位置在球拍上方
radius: 10,
speedX: 0, // 初始速度为0,等待发射
speedY: 0,
color: '#FFD700',
borderColor: '#FFA500'
};
// 目标属性 – 根据设备类型调整
let targets = [];
let targetRows = 5;
let targetCols = 10;
let targetWidth = 70;
let targetHeight = 20;
let targetPadding = 5;
let targetOffsetTop = 50;
let targetOffsetLeft = 25;
// 音频元素
const backgroundMusic = document.getElementById('backgroundMusic');
const scoreSound = document.getElementById('scoreSound');
const winSound = document.getElementById('winSound');
let musicEnabled = true;
// 花朵颜色数组
const flowerColors = ['#FF3366', '#FF6633', '#FF33CC', '#33FF99', '#33CCFF', '#FFCC00', '#9966FF'];
const flowerIcons = ['❀', '✿', '❁', '🌸', '🌺', '🌼', '🌻'];
// 检测设备类型
function detectDevice() {
isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
return isMobile;
}
// 根据屏幕尺寸调整目标参数(优化版本)
function adjustTargetParameters() {
const canvasWidth = canvas.width;
const canvasHeight = canvas.height;
if (isMobile && window.innerWidth < 768) {
// 移动端竖屏调整
if (canvasWidth < 400) {
targetCols = 6;
targetRows = 4;
targetWidth = Math.max(40, (canvasWidth – targetOffsetLeft * 2 – targetPadding * (targetCols – 1)) / targetCols);
targetHeight = 15;
targetPadding = 3;
targetOffsetTop = 40;
targetOffsetLeft = (canvasWidth – (targetCols * targetWidth + (targetCols – 1) * targetPadding)) / 2;
} else {
targetCols = 8;
targetRows = 5;
targetWidth = Math.max(45, (canvasWidth – targetOffsetLeft * 2 – targetPadding * (targetCols – 1)) / targetCols);
targetHeight = 18;
targetPadding = 4;
targetOffsetTop = 45;
targetOffsetLeft = (canvasWidth – (targetCols * targetWidth + (targetCols – 1) * targetPadding)) / 2;
}
// 确保目标不会太小或太大
targetWidth = Math.min(Math.max(targetWidth, 40), 70);
targetHeight = Math.min(Math.max(targetHeight, 12), 20);
// 确保目标不会超出屏幕
const totalWidth = targetCols * targetWidth + (targetCols – 1) * targetPadding;
if (totalWidth > canvasWidth – 10) {
targetWidth = (canvasWidth – 10 – (targetCols – 1) * targetPadding) / targetCols;
}
} else {
// PC端默认参数
targetCols = 10;
targetRows = 5;
targetWidth = 70;
targetHeight = 20;
targetPadding = 5;
targetOffsetTop = 50;
targetOffsetLeft = 25;
// 如果画布较小,适当调整
if (canvasWidth < 600) {
targetCols = 8;
targetWidth = Math.max(50, (canvasWidth – targetOffsetLeft * 2 – targetPadding * (targetCols – 1)) / targetCols);
}
}
}
// 初始化目标
function initTargets() {
targets = [];
adjustTargetParameters();
for (let r = 0; r < targetRows; r++) {
for (let c = 0; c < targetCols; c++) {
// 随机生成特殊目标
let isSpecial = Math.random() < 0.1; // 10%概率生成特殊目标
let color, points, isLife;
if (isSpecial) {
// 随机选择特殊目标类型
if (Math.random() < 0.5) {
color = '#FFD700'; // 金色目标 – 高分
points = 50;
isLife = false;
} else {
color = '#FF5252'; // 红色目标 – 恢复生命
points = 10;
isLife = true;
}
} else {
// 普通目标
const hue = 200 + (r * 20); // 不同行的颜色不同
color = `hsl(${hue}, 70%, 60%)`;
points = 10;
isLife = false;
}
targets.push({
x: c * (targetWidth + targetPadding) + targetOffsetLeft,
y: r * (targetHeight + targetPadding) + targetOffsetTop,
width: targetWidth,
height: targetHeight,
color: color,
points: points,
isLife: isLife,
visible: true
});
}
}
}
// 初始化游戏
function initGame() {
score = 0;
lives = 3;
level = 1;
ballOnPaddle = true;
lastScoreForEffect = 0;
// 重置球拍位置
paddle.x = canvas.width / 2 – paddle.width / 2;
// 根据设备调整球拍大小
if (isMobile) {
paddle.width = Math.min(120, canvas.width * 0.2);
paddle.speed = 6;
} else {
paddle.width = 100;
paddle.speed = 8;
}
// 调整球的大小
ball.radius = isMobile ? Math.min(10, canvas.width * 0.02) : 10;
// 重置球位置和速度(在球拍上等待发射)
ball.x = paddle.x + paddle.width / 2;
ball.y = paddle.y – ball.radius;
ball.speedX = 0;
ball.speedY = 0;
// 初始化目标
initTargets();
// 更新UI
updateUI();
// 隐藏游戏结束界面
document.getElementById('gameOver').style.display = 'none';
// 播放背景音乐
if (musicEnabled) {
backgroundMusic.volume = 0.5;
backgroundMusic.play().catch(e => console.log("自动播放被阻止,需要用户交互"));
}
}
// 绘制球拍
function drawPaddle() {
// 绘制球拍阴影
ctx.shadowColor = 'rgba(0, 0, 0, 0.3)';
ctx.shadowBlur = 5;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
// 绘制球拍主体
ctx.fillStyle = paddle.color;
ctx.fillRect(paddle.x, paddle.y, paddle.width, paddle.height);
// 绘制球拍边框
ctx.strokeStyle = paddle.borderColor;
ctx.lineWidth = 2;
ctx.strokeRect(paddle.x, paddle.y, paddle.width, paddle.height);
// 绘制球拍内部细节
ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
ctx.fillRect(paddle.x + 5, paddle.y + 3, paddle.width – 10, paddle.height – 6);
// 重置阴影
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
}
// 绘制球
function drawBall() {
// 如果球在球拍上等待发射,绘制特殊效果
if (ballOnPaddle) {
// 绘制脉动效果
const pulseSize = Math.sin(Date.now() / 200) * 2;
// 绘制球阴影
ctx.shadowColor = 'rgba(255, 215, 0, 0.5)';
ctx.shadowBlur = 10 + pulseSize * 3;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// 绘制球体
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius + pulseSize, 0, Math.PI * 2);
ctx.fillStyle = '#FFD700';
ctx.fill();
// 绘制球边框
ctx.strokeStyle = '#FFA500';
ctx.lineWidth = 2;
ctx.stroke();
// 只在PC端显示提示文字
if (!isMobile) {
ctx.font = "14px Arial";
ctx.fillStyle = "#FFFFFF";
ctx.textAlign = "center";
ctx.fillText("按空格发射", ball.x, ball.y – ball.radius – 15);
}
// 重置阴影
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
} else {
// 正常绘制球
ctx.shadowColor = 'rgba(0, 0, 0, 0.4)';
ctx.shadowBlur = 8;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.fillStyle = ball.color;
ctx.fill();
ctx.strokeStyle = ball.borderColor;
ctx.lineWidth = 2;
ctx.stroke();
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
}
// 绘制球高光
ctx.beginPath();
ctx.arc(ball.x – ball.radius/3, ball.y – ball.radius/3, ball.radius/3, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255, 255, 255, 0.6)';
ctx.fill();
}
// 绘制目标
function drawTargets() {
targets.forEach(target => {
if (!target.visible) return;
// 绘制目标阴影
ctx.shadowColor = 'rgba(0, 0, 0, 0.3)';
ctx.shadowBlur = 3;
ctx.shadowOffsetX = 1;
ctx.shadowOffsetY = 1;
// 绘制目标主体
ctx.fillStyle = target.color;
ctx.fillRect(target.x, target.y, target.width, target.height);
// 绘制目标边框
ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
ctx.lineWidth = 1;
ctx.strokeRect(target.x, target.y, target.width, target.height);
// 特殊目标添加额外效果
if (target.points === 50) {
// 金色目标的特殊效果
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
ctx.fillRect(target.x + 5, target.y + 3, target.width – 10, target.height – 6);
} else if (target.isLife) {
// 生命目标的特殊效果
ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
ctx.beginPath();
ctx.arc(target.x + target.width/2, target.y + target.height/2, target.height/3, 0, Math.PI * 2);
ctx.fill();
}
// 重置阴影
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
});
}
// 绘制背景
function drawBackground() {
// 清除画布,使用半透明黑色实现渐隐效果
ctx.fillStyle = 'rgba(10, 15, 35, 0.2)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制网格线
ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
ctx.lineWidth = 1;
// 垂直线
for (let x = 0; x < canvas.width; x += 40) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
ctx.stroke();
}
// 水平线
for (let y = 0; y < canvas.height; y += 40) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
}
// 创建得分动画
function createScoreAnimation(x, y, points) {
const canvasContainer = document.querySelector('.canvas-container');
const scorePopup = document.createElement('div');
scorePopup.className = 'score-popup';
scorePopup.textContent = `+${points}`;
// 根据设备调整字体大小
const fontSize = isMobile ? '20px' : '28px';
scorePopup.style.fontSize = fontSize;
// 计算相对于容器的位置
const rect = canvas.getBoundingClientRect();
const containerRect = canvasContainer.getBoundingClientRect();
const relativeX = x * (rect.width / canvas.width);
const relativeY = y * (rect.height / canvas.height);
scorePopup.style.left = `${relativeX}px`;
scorePopup.style.top = `${relativeY}px`;
canvasContainer.appendChild(scorePopup);
// 动画结束后移除元素
setTimeout(() => {
if (scorePopup.parentNode) {
scorePopup.parentNode.removeChild(scorePopup);
}
}, 1000);
}
// 发射球
function launchBall() {
if (ballOnPaddle && gameRunning && !gamePaused) {
ballOnPaddle = false;
// 设置球的初始速度
const baseSpeed = isMobile ? 4 : 5;
ball.speedX = (Math.random() > 0.5 ? 1 : -1) * baseSpeed;
ball.speedY = -baseSpeed;
}
}
// 更新球的位置
function updateBall() {
if (ballOnPaddle) {
// 球在球拍上,跟随球拍移动
ball.x = paddle.x + paddle.width / 2;
ball.y = paddle.y – ball.radius;
} else {
// 移动球
ball.x += ball.speedX;
ball.y += ball.speedY;
// 检测与墙壁的碰撞
if (ball.x + ball.radius > canvas.width || ball.x – ball.radius < 0) {
ball.speedX = -ball.speedX;
}
// 检测与天花板的碰撞
if (ball.y – ball.radius < 0) {
ball.speedY = -ball.speedY;
}
// 检测与球拍的碰撞
if (
ball.y + ball.radius > paddle.y &&
ball.y – ball.radius < paddle.y + paddle.height &&
ball.x > paddle.x &&
ball.x < paddle.x + paddle.width
) {
// 根据击中球拍的位置调整反弹角度
let hitPoint = (ball.x – paddle.x) / paddle.width;
const angleMultiplier = isMobile ? 8 : 10;
ball.speedX = (hitPoint – 0.5) * angleMultiplier;
ball.speedY = -Math.abs(ball.speedY); // 确保球向上反弹
}
// 检测球是否落到底部
if (ball.y + ball.radius > canvas.height) {
lives–;
updateUI();
if (lives <= 0) {
gameOver();
} else {
// 重置球的位置到球拍上
ballOnPaddle = true;
ball.x = paddle.x + paddle.width / 2;
ball.y = paddle.y – ball.radius;
ball.speedX = 0;
ball.speedY = 0;
}
}
// 检测与目标的碰撞
targets.forEach(target => {
if (!target.visible) return;
if (
ball.x + ball.radius > target.x &&
ball.x – ball.radius < target.x + target.width &&
ball.y + ball.radius > target.y &&
ball.y – ball.radius < target.y + target.height
) {
// 击中目标
target.visible = false;
ball.speedY = -ball.speedY;
// 更新分数
score += target.points;
// 创建得分动画
createScoreAnimation(ball.x, ball.y – 20, target.points);
// 播放击球得分音效
if (musicEnabled) {
winSound.currentTime = 0;
winSound.play().catch(e => console.log("得分音效播放失败"));
}
// 检查是否需要触发花朵特效
if (Math.floor(score / 100) > Math.floor(lastScoreForEffect / 100)) {
createFlowerEffect();
lastScoreForEffect = score;
// 播放额外的得分音效
if (musicEnabled) {
scoreSound.currentTime = 0;
scoreSound.play().catch(e => console.log("音效播放失败"));
}
}
// 如果是生命目标,增加生命值
if (target.isLife) {
lives = Math.min(lives + 1, 5); // 最多5条命
}
// 更新等级
let newLevel = Math.floor(score / 100) + 1;
if (newLevel > level) {
level = newLevel;
// 每升一级,球速增加
const speedIncrease = isMobile ? 1.05 : 1.1;
ball.speedX *= speedIncrease;
ball.speedY *= speedIncrease;
}
updateUI();
// 检查是否所有目标都被击中
if (targets.every(t => !t.visible)) {
// 所有目标被击中,重新生成
initTargets();
// 奖励额外生命
lives = Math.min(lives + 1, 5);
updateUI();
}
}
});
}
}
// 创建花朵特效
function createFlowerEffect() {
const flowerContainer = document.getElementById('flower-effects');
const flowerCount = isMobile ? 20 : 30; // 移动端减少花朵数量
for (let i = 0; i < flowerCount; i++) {
const flower = document.createElement('div');
flower.className = 'flower';
// 随机选择花朵图标和颜色
const iconIndex = Math.floor(Math.random() * flowerIcons.length);
const colorIndex = Math.floor(Math.random() * flowerColors.length);
flower.textContent = flowerIcons[iconIndex];
flower.style.color = flowerColors[colorIndex];
flower.style.left = Math.random() * 100 + 'vw';
flower.style.fontSize = (isMobile ? 16 : 20) + Math.random() * (isMobile ? 15 : 20) + 'px';
// 随机动画参数
const duration = 3 + Math.random() * 4; // 3-7秒
const delay = Math.random() * 2; // 0-2秒延迟
const horizontalMove = (Math.random() – 0.5) * 100; // 水平移动距离
flower.style.animation = `fall ${duration}s linear ${delay}s forwards`;
flower.style.transform = `translateX(${horizontalMove}px)`;
flowerContainer.appendChild(flower);
// 动画结束后移除花朵元素
setTimeout(() => {
if (flower.parentNode) {
flower.parentNode.removeChild(flower);
}
}, (duration + delay) * 1000);
}
}
// 更新UI
function updateUI() {
document.getElementById('score').textContent = score;
document.getElementById('lives').textContent = lives;
document.getElementById('level').textContent = level;
}
// 游戏结束
function gameOver() {
gameRunning = false;
cancelAnimationFrame(animationId);
document.getElementById('finalScore').textContent = score;
document.getElementById('gameOver').style.display = 'block';
}
// 游戏主循环
function gameLoop() {
// 绘制游戏背景
drawBackground();
// 绘制游戏元素
drawTargets();
drawPaddle();
drawBall();
// 更新游戏状态
if (!gamePaused && gameRunning) {
updateBall();
}
// 继续游戏循环
if (gameRunning) {
animationId = requestAnimationFrame(gameLoop);
}
}
// 键盘控制
const keys = {};
window.addEventListener('keydown', (e) => {
keys[e.key] = true;
// 空格键发射球或暂停游戏
if (e.key === ' ' && gameRunning) {
if (ballOnPaddle) {
launchBall();
} else {
togglePause();
}
e.preventDefault(); // 防止空格键滚动页面
}
// R键重新开始
if (e.key === 'r' || e.key === 'R') {
restartGame();
}
});
window.addEventListener('keyup', (e) => {
keys[e.key] = false;
});
// 处理球拍移动
function handlePaddleMovement() {
if (keys['ArrowLeft'] || keys['Left'] || keys['a'] || keys['A']) {
paddle.x = Math.max(0, paddle.x – paddle.speed);
}
if (keys['ArrowRight'] || keys['Right'] || keys['d'] || keys['D']) {
paddle.x = Math.min(canvas.width – paddle.width, paddle.x + paddle.speed);
}
// 每帧调用
if (gameRunning && !gamePaused) {
requestAnimationFrame(handlePaddleMovement);
}
}
// 切换暂停状态
function togglePause() {
gamePaused = !gamePaused;
// PC端更新按钮文本
if (!isMobile) {
document.getElementById('pauseBtn').innerHTML = gamePaused ?
'<i class="fas fa-play"></i><span>继续游戏</span>' :
'<i class="fas fa-pause"></i><span>暂停游戏</span>';
}
if (!gamePaused && gameRunning) {
handlePaddleMovement();
}
}
// 开始游戏
function startGame() {
if (!gameRunning) {
initGame();
gameRunning = true;
gamePaused = false;
if (!isMobile) {
document.getElementById('pauseBtn').innerHTML = '<i class="fas fa-pause"></i><span>暂停游戏</span>';
}
gameLoop();
handlePaddleMovement();
}
}
// 暂停游戏
function pauseGame() {
if (gameRunning) {
togglePause();
}
}
// 重新开始游戏
function restartGame() {
gameRunning = false;
gamePaused = false;
cancelAnimationFrame(animationId);
startGame();
}
// 音频控制
document.getElementById('audioToggle').addEventListener('click', function() {
musicEnabled = !musicEnabled;
const icon = document.getElementById('audioIcon');
if (musicEnabled) {
icon.className = 'fas fa-volume-up';
backgroundMusic.volume = 0.5;
backgroundMusic.play().catch(e => console.log("音频播放需要用户交互"));
} else {
icon.className = 'fas fa-volume-mute';
backgroundMusic.pause();
}
});
// 移动端控制
document.getElementById('mobileLeft').addEventListener('touchstart', function(e) {
e.preventDefault();
keys['ArrowLeft'] = true;
});
document.getElementById('mobileLeft').addEventListener('touchend', function(e) {
e.preventDefault();
keys['ArrowLeft'] = false;
});
document.getElementById('mobileRight').addEventListener('touchstart', function(e) {
e.preventDefault();
keys['ArrowRight'] = true;
});
document.getElementById('mobileRight').addEventListener('touchend', function(e) {
e.preventDefault();
keys['ArrowRight'] = false;
});
document.getElementById('mobileSpace').addEventListener('touchstart', function(e) {
e.preventDefault();
if (gameRunning) {
if (ballOnPaddle) {
launchBall();
} else {
togglePause();
}
}
});
// 事件监听 – PC端按钮
document.getElementById('startBtn').addEventListener('click', startGame);
document.getElementById('pauseBtn').addEventListener('click', pauseGame);
document.getElementById('restartBtn').addEventListener('click', restartGame);
document.getElementById('playAgainBtn').addEventListener('click', restartGame);
// 窗口调整大小时调整Canvas
function resizeCanvas() {
const container = document.querySelector('.canvas-container');
const containerWidth = Math.min(container.clientWidth, 800);
const containerHeight = container.clientHeight;
// 设置Canvas尺寸
canvas.width = containerWidth;
canvas.height = containerHeight;
// 重新调整球拍位置
paddle.y = canvas.height – 30;
// 根据设备调整球拍宽度
if (isMobile) {
paddle.width = Math.min(120, containerWidth * 0.2);
paddle.speed = 6;
} else {
paddle.width = 100;
paddle.speed = 8;
}
// 调整球的大小
ball.radius = isMobile ? Math.min(10, containerWidth * 0.02) : 10;
// 如果球在球拍上,调整球的位置
if (ballOnPaddle) {
ball.x = paddle.x + paddle.width / 2;
ball.y = paddle.y – ball.radius;
}
// 如果游戏运行中,重新初始化目标以适应新尺寸
if (gameRunning) {
initTargets();
}
}
// 初始调整Canvas大小
window.addEventListener('load', () => {
// 检测设备类型
detectDevice();
// 初始化Canvas
resizeCanvas();
// 自动开始游戏
setTimeout(() => {
startGame();
}, 500);
// 预加载音频
winSound.load();
// 尝试播放背景音乐(需要用户交互)
document.addEventListener('click', function initAudio() {
if (musicEnabled) {
backgroundMusic.volume = 0.5;
backgroundMusic.play().catch(e => console.log("音频播放需要用户交互"));
}
document.removeEventListener('click', initAudio);
});
});
window.addEventListener('resize', resizeCanvas);
// 触摸设备支持
let touchStartX = 0;
canvas.addEventListener('touchstart', (e) => {
if (!gameRunning || gamePaused) return;
e.preventDefault();
const touch = e.touches[0];
const rect = canvas.getBoundingClientRect();
touchStartX = touch.clientX – rect.left;
}, { passive: false });
canvas.addEventListener('touchmove', (e) => {
if (!gameRunning || gamePaused) return;
e.preventDefault();
const touch = e.touches[0];
const rect = canvas.getBoundingClientRect();
const touchX = touch.clientX – rect.left;
// 计算移动距离并更新球拍位置
const deltaX = touchX – touchStartX;
paddle.x += deltaX;
// 限制球拍在画布内
if (paddle.x < 0) paddle.x = 0;
if (paddle.x + paddle.width > canvas.width) paddle.x = canvas.width – paddle.width;
// 更新起始触摸位置
touchStartX = touchX;
// 如果球在球拍上,更新球的位置
if (ballOnPaddle) {
ball.x = paddle.x + paddle.width / 2;
}
}, { passive: false });
// 点击画布发射球(移动端)
canvas.addEventListener('touchend', (e) => {
if (!gameRunning || gamePaused) return;
e.preventDefault();
// 移动端点击发射球
if (ballOnPaddle) {
launchBall();
}
}, { passive: false });
// 防止页面滚动
document.addEventListener('touchmove', function(e) {
if (gameRunning) {
e.preventDefault();
}
}, { passive: false });
// 防止双击缩放
let lastTouchEnd = 0;
document.addEventListener('touchend', function(event) {
const now = (new Date()).getTime();
if (now – lastTouchEnd <= 300) {
event.preventDefault();
}
lastTouchEnd = now;
}, false);
</script>
</body>
</html>
网硕互联帮助中心
![基于python的人脸检测识别录像系统[python]-计算机毕业设计源码+LW文档-网硕互联帮助中心](https://www.wsisp.com/helps/wp-content/uploads/2026/02/20260210120457-698b1ee9bfff8-220x150.jpg)




评论前必须登录!
注册