用html5写一个第一视角的摩托车开车游戏,画上摩托车把手,把手两侧放上可操控方向的按钮,再加上,前进和减速的按钮

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>第一视角摩托车</title>
<style>
html, body {
margin: 0;
padding: 0;
background: #000;
overflow: hidden;
user-select: none;
font-family: Arial, sans-serif;
}

#gameCanvas {
display: block;
background: linear-gradient(to bottom, #87CEEB 0%, #87CEEB 45%, #98FB98 45%, #228B22 100%);
}

#hud {
position: absolute;
top: 20px;
left: 20px;
color: white;
font-size: 20px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
background: rgba(0,0,0,0.5);
padding: 15px;
border-radius: 10px;
}

#controls {
position: absolute;
bottom: 50px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 200px;
align-items: center;
}

#speedControls {
position: absolute;
bottom: 150px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 20px;
align-items: center;
}

.control-btn {
width: 80px;
height: 80px;
border-radius: 50%;
background: rgba(255,255,255,0.3);
border: 3px solid white;
color: white;
font-size: 30px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
}

.control-btn:active {
background: rgba(255,255,255,0.5);
transform: scale(0.9);
}

.speed-btn {
width: 100px;
height: 60px;
border-radius: 30px;
font-size: 24px;
}

#startScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.8);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
}

#startBtn {
padding: 20px 40px;
font-size: 24px;
background: #ff6b6b;
color: white;
border: none;
border-radius: 10px;
cursor: pointer;
margin-top: 20px;
transition: background 0.3s ease;
}

#startBtn:hover {
background: #ff5252;
}
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>

<div id="hud">
<div>速度: <span id="speed">0</span> km/h</div>
<div>时间: <span id="time">0</span>s</div>
<div>距离: <span id="distance">0</span>m</div>
</div>

<div id="speedControls">
<button class="control-btn speed-btn" id="accelerateBtn">▲ 加速</button>
<button class="control-btn speed-btn" id="brakeBtn">▼ 减速</button>
</div>

<div id="controls">
<button class="control-btn" id="leftBtn">◀</button>
<button class="control-btn" id="rightBtn">▶</button>
</div>

<div id="startScreen">
<h1>🏍️ 摩托车竞速</h1>
<p>使用屏幕两侧按钮控制方向</p>
<p>▲▼ 按钮控制加速和减速</p>
<button id="startBtn">开始游戏</button>
</div>

    <script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');

// 设置画布大小
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);

// 游戏状态
let gameStarted = false;
let speed = 0;
let maxSpeed = 200;
let acceleration = 1.5;
let deceleration = 2;
let position = 0;
let roadCurve = 0;
let targetCurve = 0;
let leanAngle = 0;
let startTime = Date.now();

// 道路生成
const roadSegments = [];
const segmentCount = 500;
const segmentLength = 50;

// 生成随机道路
for (let i = 0; i < segmentCount; i++) {
roadSegments.push({
z: i * segmentLength,
curve: Math.sin(i * 0.02) * 200 + Math.sin(i * 0.05) * 100,
y: Math.sin(i * 0.03) * 30
});
}

// 控制状态
const controls = {
left: false,
right: false,
accelerate: false,
brake: false
};

// 设置控制按钮
const leftBtn = document.getElementById('leftBtn');
const rightBtn = document.getElementById('rightBtn');
const accelerateBtn = document.getElementById('accelerateBtn');
const brakeBtn = document.getElementById('brakeBtn');

// 方向控制
leftBtn.addEventListener('mousedown', () => controls.left = true);
leftBtn.addEventListener('mouseup', () => controls.left = false);
leftBtn.addEventListener('touchstart', (e) => { e.preventDefault(); controls.left = true; });
leftBtn.addEventListener('touchend', () => controls.left = false);

rightBtn.addEventListener('mousedown', () => controls.right = true);
rightBtn.addEventListener('mouseup', () => controls.right = false);
rightBtn.addEventListener('touchstart', (e) => { e.preventDefault(); controls.right = true; });
rightBtn.addEventListener('touchend', () => controls.right = false);

// 速度控制
accelerateBtn.addEventListener('mousedown', () => controls.accelerate = true);
accelerateBtn.addEventListener('mouseup', () => controls.accelerate = false);
accelerateBtn.addEventListener('touchstart', (e) => { e.preventDefault(); controls.accelerate = true; });
accelerateBtn.addEventListener('touchend', () => controls.accelerate = false);

brakeBtn.addEventListener('mousedown', () => controls.brake = true);
brakeBtn.addEventListener('mouseup', () => controls.brake = false);
brakeBtn.addEventListener('touchstart', (e) => { e.preventDefault(); controls.brake = true; });
brakeBtn.addEventListener('touchend', () => controls.brake = false);

// 键盘控制
document.addEventListener('keydown', (e) => {
switch(e.key) {
case 'ArrowLeft': controls.left = true; break;
case 'ArrowRight': controls.right = true; break;
case 'ArrowUp': controls.accelerate = true; break;
case 'ArrowDown': controls.brake = true; break;
}
});

document.addEventListener('keyup', (e) => {
switch(e.key) {
case 'ArrowLeft': controls.left = false; break;
case 'ArrowRight': controls.right = false; break;
case 'ArrowUp': controls.accelerate = false; break;
case 'ArrowDown': controls.brake = false; break;
}
});

// 开始游戏
document.getElementById('startBtn').addEventListener('click', () => {
document.getElementById('startScreen').style.display = 'none';
gameStarted = true;
startTime = Date.now();
gameLoop();
});

// 绘制道路
function drawRoad() {
const centerX = canvas.width / 2;
const horizon = canvas.height * 0.7;
const segmentHeight = canvas.height * 0.5 / 30;

// 找到当前位置对应的路段
const currentSegment = Math.floor(position / segmentLength);

for (let i = 1; i < 30; i++) {
const segmentIndex = (currentSegment + i) % segmentCount;
const segment = roadSegments[segmentIndex];
const prevSegment = roadSegments[(segmentIndex - 1 + segmentCount) % segmentCount];

const z = segment.z - position;
const scale = 200 / z;

const x1 = centerX + (prevSegment.curve + roadCurve) * scale;
const x2 = centerX + (segment.curve + roadCurve) * scale;
const y1 = horizon - i * segmentHeight;
const y2 = horizon - (i - 1) * segmentHeight;

const roadWidth = 400 * scale;

// 绘制路面
ctx.fillStyle = i % 2 ? '#444' : '#555';
ctx.beginPath();
ctx.moveTo(x1 - roadWidth/2, y1);
ctx.lineTo(x1 + roadWidth/2, y1);
ctx.lineTo(x2 + roadWidth/2, y2);
ctx.lineTo(x2 - roadWidth/2, y2);
ctx.fill();

// 绘制路边
ctx.fillStyle = '#228B22';
ctx.fillRect(0, y1, x1 - roadWidth/2, y2 - y1);
ctx.fillRect(x1 + roadWidth/2, y1, canvas.width - x1 - roadWidth/2, y2 - y1);

// 绘制中线
if (i % 4 === 0) {
ctx.strokeStyle = '#FFF';
ctx.lineWidth = 4 * scale;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
}
}

// 绘制摩托车把手
function drawHandlebars() {
const centerX = canvas.width / 2;
const centerY = canvas.height - 120;

ctx.save();
ctx.translate(centerX, centerY);
ctx.rotate(leanAngle * 0.02);

// 绘制把手主体
ctx.strokeStyle = '#333';
ctx.lineWidth = 12;
ctx.beginPath();
ctx.moveTo(-180, 0);
ctx.lineTo(180, 0);
ctx.stroke();

// 绘制左手把
ctx.fillStyle = '#222';
ctx.fillRect(-200, -8, 40, 16);

// 绘制右手把
ctx.fillRect(160, -8, 40, 16);

// 绘制后视镜
ctx.fillStyle = '#666';
ctx.fillRect(-220, -20, 15, 10);
ctx.fillRect(205, -20, 15, 10);

// 绘制仪表
ctx.fillStyle = '#000';
ctx.fillRect(-40, -25, 80, 30);
ctx.strokeStyle = '#0F0';
ctx.lineWidth = 2;
ctx.strokeRect(-40, -25, 80, 30);

// 速度显示
ctx.fillStyle = '#0F0';
ctx.font = '16px monospace';
ctx.textAlign = 'center';
ctx.fillText(`${speed.toFixed(0)} km/h`, 0, -8);

ctx.restore();
}

// 更新游戏状态
function update() {
if (!gameStarted) return;

// 处理输入
if (controls.accelerate) {
speed = Math.min(speed + acceleration, maxSpeed);
}
if (controls.brake) {
speed = Math.max(speed - deceleration * 2, 0);
}
if (controls.left) {
targetCurve -= 3;
leanAngle = Math.max(leanAngle - 2, -15);
} else if (controls.right) {
targetCurve += 3;
leanAngle = Math.min(leanAngle + 2, 15);
} else {
leanAngle *= 0.9;
}

targetCurve *= 0.95;
roadCurve += (targetCurve - roadCurve) * 0.1;

// 更新位置
position += speed * 0.5;
if (position >= segmentCount * segmentLength) {
position -= segmentCount * segmentLength;
}

// 更新HUD
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
document.getElementById('time').textContent = elapsed;
document.getElementById('distance').textContent = (position / 10).toFixed(0);
document.getElementById('speed').textContent = speed.toFixed(0);
}

// 渲染画面
function render() {
// 清空画布
ctx.fillStyle = '#87CEEB';
ctx.fillRect(0, 0, canvas.width, canvas.height);

// 绘制道路
drawRoad();

// 绘制摩托车把手
drawHandlebars();
}

// 游戏循环
function gameLoop() {
update();
render();
if (gameStarted) {
requestAnimationFrame(gameLoop);
}
}

// 防止触摸滚动
document.addEventListener('touchmove', (e) => {
e.preventDefault();
}, { passive: false });
</script>
</body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/97929.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/97929.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringMVC 系列博客(一):基础概念与注解开发入门

目录 一、引言 二、MVC 模式&#xff1a;SpringMVC 的设计基石 2.1 MVC 三大组件 2.2 主流 MVC 框架对比 2.3 MVC 模式的核心优势 三、SpringMVC 框架&#xff1a;是什么&#xff1f;为什么学&#xff1f; 3.1 什么是 SpringMVC&#xff1f; 3.2 为什么要学 SpringMVC&a…

Java 字符串操作教程:三个任务完整复现与解析

这次是完成一些小任务来试试身手&#xff0c;免得生疏&#xff1a; 编写程序&#xff0c;使用charAt和length方法&#xff0c;将字符串"HelloWorld"拆分为"Hello"和"World"两个子串并输出。 设计一个方法&#xff0c;利用indexOf和lastlndexOf&a…

向量技术研究报告:从数学基础到AI革命的支柱

1. 向量的数学本质与历史演变 1.1 核心定义与数学表示 向量是同时具有大小&#xff08;Magnitude&#xff09;和方向&#xff08;Direction&#xff09;的量&#xff0c;在数学上被严格定义为向量空间中的元素。与仅有大小的标量&#xff08;Scalar&#xff09;不同&#xff0c…

Qt QHorizontalStackedBarSeries详解

1、概述QHorizontalStackedBarSeries 是 Qt Charts 模块中的一个类&#xff0c;用于创建水平堆叠条形图。它继承自 QAbstractBarSeries 类&#xff0c;允许将多个数据系列堆叠在一起显示&#xff0c;每个条形由多个部分组成&#xff0c;这些部分共同构成一个完整的条形&#xf…

《股票智能查询与投资决策辅助应用项目方案》

前引&#xff1a;本股票智能查询与投资决策辅助应用通过整合多源金融数据&#xff0c;运用量化分析 机器学习技术&#xff0c;为普通投资者提供全方位股票信息服务和个性化投资建议。系统不仅解决了传统工具 “数据分散、分析复杂” 的问题&#xff0c;更通过人性化交互和直观…

从零开始构建Kubernetes Operator:一个完整的深度学习训练任务管理方案

从零开始构建Kubernetes Operator&#xff1a;一个完整的深度学习训练任务管理方案一、引言二、为什么需要Operator&#xff1f;1. Controller vs Operator&#xff1a;本质区别2. 有状态服务 vs 无状态服务的挑战三、项目架构设计3.1整体架构图3.2核心组件4.核心实现解析1. CR…

第二十二篇|新世界语学院教育数据深度解析:学制函数、能力矩阵与升学图谱

第二十二篇&#xff5c;新世界语学院教育数据深度解析&#xff1a;学制函数、能力矩阵与升学图谱 系列主题&#xff1a;500所日本语言学校结构数据工程 关键词&#xff1a;新世界语学院、东京新宿、学制函数建模、JLPT能力矩阵、升学网络、教育数据工程 一、合规与法人建模&…

Java开发工具选择指南:Eclipse、NetBeans与IntelliJ IDEA对比

在Java开发的世界里&#xff0c;选择合适的开发工具就如同挑选一把称手的禅杖&#xff0c;能助你在代码修行的路上更加得心应手。本文将为Java开发者提供一份实用的IDE选择指南&#xff0c;从功能、适用人群、性能等方面深入解析几款主流的Java开发工具&#xff0c;帮助你找到最…

iOS App 内存泄漏与性能调优实战 如何排查内存问题、优化CPU与GPU性能、降低耗电并提升流畅度(uni-app iOS开发优化指南)

在 iOS 应用开发中&#xff0c;内存泄漏 是最常见且最难排查的性能问题之一。 它会导致应用 运行越来越卡、占用内存过高、频繁崩溃&#xff0c;甚至严重消耗电池。 尤其在 uni-app 跨平台开发 中&#xff0c;JS 层和原生层的混合调用更容易隐藏内存问题&#xff1a; 对象未释放…

从源代码开始构建、部署和管理应用程序

1.创建项目目录并准备应用程序的代码及其依赖1.创建项目目录&#xff0c;并将当前目录切换到该目录[roothost1 ~]# mkdir python-web && cd python-web2.创建 app.py 文件并添加以下代码[roothost1 python-web]# vi app.py [roothost1 python-web]# cat app.py import …

Flutter-[2]第一个应用

摘要 根据官方文档搭配好环境&#xff0c;使用vscode创建完应用后&#xff0c;会遇到以下问题 设备无法选择打开了lib\main.dart右上角也没有运行按钮 环境 Windows11Flutter 3.35.4 必要设置 1. 查看是否开启Windows桌面应用开发flutter config --list输出如下: All Settings:…

QListWidget选择阻止问题解决方案

QListWidget选择阻止问题解决方案QListWidget选择阻止问题解决方案问题背景QListWidget工作机制详解1. 事件处理流程2. 关键机制说明2.1 鼠标事件与信号的分离2.2 信号阻塞的局限性2.3 断开连接方法的问题问题的根本原因1. 异步事件处理2. 多层状态管理3. 事件优先级解决方案演…

TCL华星计划投建第8.6代印刷OLED产线

近日&#xff0c;TCL科技集团股份有限公司&#xff08;000100.SZ&#xff09;发布公告&#xff0c;公司、旗下子公司TCL华星与广州市人民政府、广州经济技术开发区管理委员会共同签署项目合作协议&#xff0c;拟共同出资于广州市建设一条月加工2290mm2620mm玻璃基板能力约2.25万…

MATLAB 时间序列小波周期分析

1. 文件结构 WaveletPeriod/ ├── main_wavelet_period.m % 一键运行 ├── wavelet_power_spectrum.m % 小波功率谱 显著性 ├── period_peak_detect.m % 自动周期峰值 ├── plot_wavelet_results.m % 时频图 周期图 └── example/└── temp.csv …

如何精准配置储

当电费账单变身利润引擎&#xff0c;您的企业是否做好了准备&#xff1f;鹧鸪云储能仿真软件&#xff0c;不止于仿真——我们以智能算法为核心&#xff0c;为企业定制“高收益、高适配、可持续”的储能配置方案&#xff0c;将用电数据转化为新一轮增长动能。智慧大脑&#xff1…

Uniapp崩溃监控体系构建:内存泄漏三维定位法(堆栈/资源/线程)

在Uniapp开发中&#xff0c;内存泄漏是导致应用崩溃的核心隐患。通过堆栈分析、资源追踪和线程监控三维定位法&#xff0c;可系统化定位泄漏源。以下是完整实施方案&#xff1a;一、堆栈维度&#xff1a;泄漏对象溯源内存快照比对使用Chrome DevTools定期获取内存快照&#xff…

NLP中Subword算法:WordPiece、BPE、BBPE、SentencePiece详解以及代码实现

本文将介绍以下内容&#xff1a; 1. Subword与传统tokenization技术的对比2. WordPiece3. Byte Pair Encoding (BPE)4. Byte-level BPE(BBPE)5. SentencePiece 以及各Subword算法代码实现 一、Subword与传统tokenization技术的对比 1. 传统tokenization技术 传统tokenizatio…

十一章 无界面压测

一、采用无界面压测的原因1.节约系统资源。 2.更快捷&#xff0c;只需要启动命令即可进行压测 3.主要是用于性能压测集成.无界面压测命令参数&#xff1a; -n 表示无界面压测 -t 制定你的 jmx 脚本 -l 生成 jtl 测试报告二、注意配置文件设置:输出为xml jmeter.save.s…

从零实现 Qiankun 微前端:基座应用控制子应用路由与信息交互

随着前端业务的快速发展,单体应用模式(Monolith)越来越难以支撑复杂业务场景。微前端(Micro Frontends)应运而生,它将大型应用拆解成多个子应用(Micro App),通过主应用进行统一调度和集成。 在微前端技术栈中,Qiankun(乾坤)是一个广泛使用的解决方案,基于 single…

在业务应用中集成 go-commons,实现应用+系统双指标监控

在日常 Go 服务开发中&#xff0c;我们通常需要同时监控 业务指标&#xff08;比如 QPS、请求延迟、错误率&#xff09;&#xff0c;也需要关注 系统指标&#xff08;CPU、内存、磁盘占用情况&#xff09;。 过去这类场景通常要引入多个库&#xff1a;一个负责业务指标采集&…