用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
英文地址,請注明出處:http://en.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;一個負責業務指標采集&…