系列文章目錄
JS:經(jīng)典小游戲:像素鳥
JS:經(jīng)典小游戲:貪吃蛇
JS:經(jīng)典小游戲:掃雷
目錄
系列文章目錄
像素鳥
1.游戲介紹
2.代碼分析
3.代碼實(shí)現(xiàn)
3.1 隨機(jī)生成水管
3.2?當(dāng)水管超過游戲顯示區(qū)域時,刪除這一對水管
3.3 讓小鳥不斷下降
3.4 通過鼠標(biāo)點(diǎn)擊事件來讓小鳥上升
3.5 檢測小鳥是否撞上了水管
總結(jié)
附錄:素材文件
附錄:完整代碼
像素鳥
1.游戲介紹
像素鳥曾經(jīng)是一個火爆一時的小游戲,我們嘗試用JavaScript來實(shí)現(xiàn)像素鳥的核心功能。
操作方式:鼠標(biāo)點(diǎn)擊
勝利條件:無
失敗條件:小鳥碰到了屏幕上下的水管就算失敗
2.代碼分析
需求:
- 在屏幕上下兩側(cè)隨機(jī)生成高度不一的水管
- 水管會不斷的從右向左移動 來營造出小鳥向右飛行的感覺
- 小鳥會不斷下降,玩家點(diǎn)擊鼠標(biāo)來讓小鳥上升
- 當(dāng)小鳥碰到任一水管時,游戲結(jié)束
實(shí)現(xiàn)效果圖:
3.代碼實(shí)現(xiàn)
我們分析之后就會發(fā)現(xiàn),整個游戲其實(shí)很簡單,我們只需要通過CSS3的過渡和動畫屬性就能夠?qū)崿F(xiàn)小鳥和水管的運(yùn)動,而判斷和小鳥不斷下降都可以通過定時器來完成。
3.1 隨機(jī)生成水管
需求:
? ?1. 每過1秒出現(xiàn)一對 上下 高度隨機(jī)的管子
? ?2. 管子自右向左移動 (通過動畫實(shí)現(xiàn))
const time_wait = 1000; // 1s生成一對水管
let pipe = []; // 記錄所有水管的dom對象
let i = 0; // 分辨水管的下標(biāo)是偶數(shù)還是奇數(shù)
// 生成水管的函數(shù) 水管的高度是隨機(jī)的
function render_pipe() {
pipe[i] = document.createElement("div");
// 高度120 - 205 最高205 兩根水管都205px的話 還能留出來90px讓小鳥上升
let random_height = Math.floor(Math.random() * 86 + 120);
pipe[i].classList.add("pipe");
pipe[i].style.height = random_height + "px";
// 偶數(shù)的水管在上面 奇數(shù)的在下面
if (i % 2 == 0) {
pipe[i].style.top = 0;
} else {
pipe[i].style.bottom = 0;
// 顛倒底部的水管
pipe[i].style.transform = 'rotate(180deg)'
}
// i 的值會在執(zhí)行完這行代碼后 +1
container.appendChild(pipe[i++]);
}
// 每過1s渲染2根水管 一上一下
let add_pipe = setInterval(function () {
render_pipe();
render_pipe();
}, time_wait);
?通過CSS3動畫來讓水管向左移動
.pipe {
position: absolute;
right: 0;
width: 60px;
background: url(./assets/pipetop.png) no-repeat;
background-size: 100% 100%;
/* 水管的移動是通過動畫來完成的,動畫執(zhí)行完畢后停留到終點(diǎn)位置 */
animation: move 5s linear forwards;
}
@keyframes move {
/* 0%{
right: 0;
} */
to {
right: 100%;
}
}
3.2?當(dāng)水管超過游戲顯示區(qū)域時,刪除這一對水管
// 當(dāng)水管到屏幕外面時,刪除節(jié)點(diǎn) 并增加分?jǐn)?shù)
let scope_point = 0;
let delete_pipe = setTimeout(() => {
setInterval(() => {
// 刪除第一個節(jié)點(diǎn)和它的兄弟
let pipe_first = document.querySelector(".pipe");
container.removeChild(pipe_first.nextSibling);
container.removeChild(pipe_first);
// 分?jǐn)?shù)+1 有補(bǔ)零
let scope_point_string = "000";
if (scope_point <= 100) {
scope_point_string = "00" + ++scope_point;
}
scope.innerText = scope_point_string;
}, time_wait);
}, 5000);
3.3 讓小鳥不斷下降
通過定時器操作小鳥的dom對象,可以很簡單就能讓小鳥實(shí)現(xiàn)不斷下降的功能
// 原版的像素鳥小鳥下降速度會越來越快 小鳥在多次點(diǎn)擊后上升的速度也會越來越快
let speed = 10 // 所以提供一個共用變量給下降和上升,它用來控制小鳥上下跳動的距離
// 小鳥移動的函數(shù)
function bird_fly() {
speed += 2
// 當(dāng)speed是正數(shù)的時候 小鳥就會下降 當(dāng)speed是負(fù)數(shù)時 小鳥就會上升
bird.setAttribute("style", `top:${bird.offsetTop + speed}px`);
}
// 小鳥不停的飛行......
let time_down = setInterval(bird_fly, 100);
現(xiàn)在小鳥的移動還有點(diǎn)僵硬,我們可以通過過渡屬性來讓小鳥的移動更加順滑,代碼非常簡單,只需要給小鳥增加如下樣式即可:
transition: all 0.1s;
3.4 通過鼠標(biāo)點(diǎn)擊事件來讓小鳥上升
鼠標(biāo)點(diǎn)擊游戲任一區(qū)域,即可實(shí)現(xiàn)小鳥的上升,但是上升是通過speed變量間接控制小鳥的飛行的
// 點(diǎn)擊一下 小鳥上升
container.addEventListener("click", function () {
// 通過控制speed來間接的讓小鳥上升 這樣不僅代碼更少 而且游戲手感更接近原版
speed -= 7;
});
3.5 檢測小鳥是否撞上了水管
在完成水管的渲染、移動 和小鳥的下降和上升之后,現(xiàn)在要做的就是判斷小鳥是否撞上了水管。
思路是:
1.計(jì)算出水管在游戲開始多長時間后會到達(dá)小鳥的面前
2.在水管即將到達(dá)小鳥面前的時候 獲得要被檢測的水管,pipe1與pipe2
3.每過50ms檢查一下 小鳥是否撞上了pipe1 或者pipe2
4.水管是每過1s渲染出來一對,所以我們也每過1s切換一下被檢測水管的下標(biāo)
這種實(shí)現(xiàn)方式可以不用遍歷所有的水管,只需要每隔一段時間切換下標(biāo),這個時間間隔和生成水管的節(jié)奏保持一致即可。
// 邏輯:
// 1.游戲開始1s后生成第一對水管
// 2.計(jì)算得出第一對水管3.25s后到達(dá)小鳥的右側(cè)
// 3.游戲開始4.25s后開始檢測小鳥和水管的位置
// 4.游戲開始4.25s后每過1s,更換需要檢測的水管
// 1.游戲開始1s后開始確定水管移動的速度
setTimeout(() => {
let pipe_speed = container.offsetWidth / 5; // 水管動畫持續(xù)時間5s
// 移動距離為1000px 即速度為 200px/s
// 拿路程/速度 即可獲得水管移動到小鳥面前的時間
let test_time = (container.offsetWidth - bird.offsetLeft -
bird.offsetWidth - pipe[0].offsetWidth) / pipe_speed;
//console.log(test_time); //3.25s
//2.也就是說 水管出現(xiàn)并移動3.25s后 第一對水管到達(dá)小鳥的右側(cè)
// 3.再過3.25s后,開始檢測距離
setTimeout(() => {
get_testPipe()
distance = setInterval(() => {
test_distance()
}, 50)
}, test_time * 1000)
}, time_wait);
// 4.在水管生成后獲取小鳥和水管的一些固定屬性
setTimeout(() => {
// 獲取水管的寬度
pipe_width = pipe[0].offsetWidth
// 獲取小鳥的寬高
bird_width = bird.offsetWidth
bird_height = bird.offsetHeight
}, time_wait)
// 5.檢測小鳥是否撞到了被檢測的水管
function test_distance() {
// 獲取被檢測的水管與上邊界的高度
let height_up = pipe[test_pipe1].offsetHeight
let height_down = pipe[test_pipe2].offsetTop;
// 獲取水管現(xiàn)在的水平位置
let pipe_level = pipe[test_pipe1].offsetLeft
// 獲取小鳥現(xiàn)在的水平位置和垂直位置
let bird_level = bird.offsetLeft
let b_top = bird.offsetTop;
// 頂部與上水管相比較 或 底部與下水管相比較 且水平距離上水管與小鳥也在同一位置
if ((b_top < height_up || (b_top + bird_height > height_down))
&& (bird_level + bird_width >= pipe_level)
&& bird_level <= pipe_level + pipe_width)
{
clearInterval(distance);
clearInterval(time_down);
clearInterval(add_pipe);
clearInterval(delete_pipe);
alert(`碰! ${scope_point}分`);
// 游戲結(jié)束,刷新頁面
location.reload();
}
}
};
總結(jié)
幾個注意點(diǎn):
- 看似是小鳥在向前飛行,實(shí)際上是水管在向后移動,以減少內(nèi)存的消耗。
- 對于水管的顯示,使用背景圖片的background-size屬性,設(shè)置100% 100% 即可讓背景圖片完全覆蓋隨機(jī)高度的div。
- 對于水管的移動,使用css3的動畫屬性,這可以很方便的控制水管移動的距離、速度與方向。
- 對于小鳥的移動,我們通過一個公有變量來控制小鳥下降與上升的距離,這樣既可以實(shí)現(xiàn)上升與下降越來越快的功能,也最符合原版像素鳥的手感,同時代碼量也是最少的。
- 關(guān)于判斷小鳥和水管的位置,我們需要有一個清晰的思路:水管到達(dá)小鳥面前時開始檢測位置;水管經(jīng)過小鳥之后更換被檢測的水管;檢測距離時既要檢測上下,也要檢測左右距離。
OK,以上就是經(jīng)典小游戲 像素鳥的JavaScript實(shí)現(xiàn)思路了,希望能幫助到大家,有不懂的歡迎在評論區(qū)留言。
最后,附上JS代碼量,我可不是標(biāo)題黨。
附錄:素材文件
關(guān)于素材:因?yàn)檫@里無法直接上傳文件,所以素材圖片還請移步我的gitee倉庫中獲取。
素材與代碼倉庫:
JS小游戲素材倉庫https://gitee.com/wen-muchun/js-games
附錄:完整代碼
由于缺少背景圖片,所以直接復(fù)制的話是沒辦法看到柱子和背景圖的。
需要去我上面的倉庫里面下載一下素材圖片,或者在css里面把背景圖片替換為背景顏色,就可以運(yùn)行了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
.container {
position: relative;
width: 1000px;
height: 500px;
margin: 100px auto;
background: url(./assets/bgi.jpg) no-repeat center;
background-size: cover;
overflow: hidden;
}
.container *,
.hd * {
/* 用這行代碼禁止用戶選中文字 */
user-select: none;
}
.hd {
position: absolute;
width: 100%;
padding-top: 20px;
text-align: right;
color: #333;
font-size: 16px;
z-index: 1;
}
.bird {
width: 40px;
height: 40px;
background: url(./assets/PixelBird.png) center no-repeat;
background-size: 100%;
position: absolute;
left: 200px;
top: 40%;
font-size: 14px;
transition: all 0.1s;
}
.pipe {
position: absolute;
right: 0;
width: 60px;
background: url(./assets/pipetop.png) no-repeat;
background-size: 100% 100%;
/* 水管的移動是通過動畫來完成的,動畫執(zhí)行完畢后停留到終點(diǎn)位置 */
animation: move 5s linear forwards;
}
@keyframes move {
/* 0%{
right: 0;
} */
to {
right: 100%;
}
}
.scope {
padding-right: 20px;
font-size: 18px;
}
</style>
</head>
<body>
<div class="container">
<div class="hd">
<span class="scope">000</span>
</div>
<div class="bird"></div>
</div>
<script>
// 游戲操作:使用鼠標(biāo)左鍵點(diǎn)擊屏幕,來讓小鳥飛得更高
// 當(dāng)頁面加載完畢后 執(zhí)行下面操作
window.onload = function () {
// 獲取dom元素
const container = document.querySelector(".container");
const bird = document.querySelector(".bird");
const scope = document.querySelector(".scope");
// 原版的像素鳥小鳥下降速度會越來越快 小鳥在多次點(diǎn)擊后上升的速度也會越來越快
let speed = 10 // 所以提供一個共用變量給下降和上升,它用來控制小鳥上下跳動的距離
// 小鳥移動的函數(shù)
function bird_fly() {
speed += 2
// 當(dāng)speed是正數(shù)的時候 小鳥就會下降 當(dāng)speed是負(fù)數(shù)時 小鳥就會上升
bird.setAttribute("style", `top:${bird.offsetTop + speed}px`);
}
// 小鳥不停的飛行......
let time_down = setInterval(bird_fly, 100);
// 點(diǎn)擊一下 小鳥上升
container.addEventListener("click", function () {
// 通過控制speed來間接的讓小鳥上升 這樣不僅代碼更少 而且游戲手感更接近原版 (原版的小鳥就非常難以操控)
speed -= 7;
});
// 隨機(jī)出現(xiàn)的水管
// 1. 每過1秒出現(xiàn)一對 上下 高度隨機(jī)的水管
// 2. 水管自右向左移動 通過動畫實(shí)現(xiàn)
// 3. 判斷水管和小鳥的距離 每100ms判斷一次 根據(jù)雙方高度的值來判斷
const time_wait = 1000; // 1s生成一對水管
let pipe = []; // 記錄所有水管的dom對象
let i = 0; // 分辨水管的下標(biāo)是偶數(shù)還是奇數(shù)
// 生成水管的函數(shù) 水管的高度是隨機(jī)的
function render_pipe() {
pipe[i] = document.createElement("div");
// 高度120 - 205 最高205 兩根水管都205px的話 還能留出來90px讓小鳥上升
let random_height = Math.floor(Math.random() * 86 + 120);
pipe[i].classList.add("pipe");
pipe[i].style.height = random_height + "px";
// 偶數(shù)的水管在上面 奇數(shù)的在下面
if (i % 2 == 0) {
pipe[i].style.top = 0;
} else {
pipe[i].style.bottom = 0;
// 顛倒底部的水管
pipe[i].style.transform = 'rotate(180deg)'
}
// i 的值會在執(zhí)行完這行代碼后 +1
container.appendChild(pipe[i++]);
}
// 每過1s渲染2根水管 一上一下
let add_pipe = setInterval(function () {
render_pipe();
render_pipe();
}, time_wait);
// 當(dāng)水管到屏幕外面時,刪除節(jié)點(diǎn) 并增加分?jǐn)?shù)
let scope_point = 0;
let delete_pipe = setTimeout(() => {
setInterval(() => {
// 刪除第一個節(jié)點(diǎn)和它的兄弟
let pipe_first = document.querySelector(".pipe");
container.removeChild(pipe_first.nextSibling);
container.removeChild(pipe_first);
// 分?jǐn)?shù)+1 有補(bǔ)零
let scope_point_string = "000";
if (scope_point <= 100) {
scope_point_string = "00" + ++scope_point;
}
scope.innerText = scope_point_string;
}, time_wait);
}, 5000);
// 獲得是哪一對水管需要被判斷 它們與小鳥的距離
let test_pipe1, test_pipe2; // 記錄當(dāng)前被檢測位置的水管的下標(biāo)
function get_testPipe() {
// 開始是0和1
test_pipe1 = 0;
test_pipe2 = 1;
// 然后每過1s更換一對要檢測的水管
setInterval(() => {
test_pipe1 += 2;
test_pipe2 += 2;
}, time_wait);
}
// 邏輯:
// 1.游戲開始1s后生成第一對水管
// 2.計(jì)算得出第一對水管3.25s后到達(dá)小鳥的右側(cè)
// 3.游戲開始4.25s后開始檢測小鳥和水管的位置
// 4.游戲開始4.25s后每過1s,更換需要檢測的水管
// 1.游戲開始1s后開始確定水管移動的速度
setTimeout(() => {
let pipe_speed = container.offsetWidth / 5; // 水管動畫持續(xù)時間5s 移動距離為1000px 即速度為 200px/s
let test_time = (container.offsetWidth - bird.offsetLeft - bird.offsetWidth - pipe[0].offsetWidth) / pipe_speed;
//console.log(test_time); //3.25s 2.也就是說 水管出現(xiàn)并移動3.25s后 第一對水管到達(dá)小鳥的右側(cè)
// 3.再過3.25s后,開始檢測距離
setTimeout(() => {
get_testPipe()
distance = setInterval(() => {
test_distance()
}, 50)
}, test_time * 1000)
}, time_wait);
// 4.在水管生成后獲取小鳥和水管的一些固定屬性
setTimeout(() => {
// 獲取水管的寬度
pipe_width = pipe[0].offsetWidth
// 獲取小鳥的寬高
bird_width = bird.offsetWidth
bird_height = bird.offsetHeight
}, time_wait)
// 5.檢測小鳥是否撞到了被檢測的水管
function test_distance() {
// 獲取被檢測的水管與上邊界的高度
let height_up = pipe[test_pipe1].offsetHeight
let height_down = pipe[test_pipe2].offsetTop;
// 獲取水管現(xiàn)在的水平位置
let pipe_level = pipe[test_pipe1].offsetLeft
// 獲取小鳥現(xiàn)在的水平位置和垂直位置
let bird_level = bird.offsetLeft
let b_top = bird.offsetTop;
// 頂部與上水管相比較 或 底部與下水管相比較 且水平距離上水管與小鳥也在同一位置
if ((b_top < height_up || (b_top + bird_height > height_down)) && (bird_level + bird_width >= pipe_level) && bird_level <= pipe_level + pipe_width) {
clearInterval(distance);
clearInterval(time_down);
clearInterval(add_pipe);
clearInterval(delete_pipe);
alert(`碰! ${scope_point}分`);
// 游戲結(jié)束,刷新頁面
location.reload();
}
}
};
</script>
</body>
</html>
想要快速體驗(yàn)效果的可以復(fù)制上面的代碼在一個 txt 文件中,然后修改css樣式,并將txt后綴改為html即可運(yùn)行。文章來源:http://www.zghlxwxcb.cn/news/detail-473239.html
注意:小鳥、城市背景、水管 這3處都要修改哦文章來源地址http://www.zghlxwxcb.cn/news/detail-473239.html
background: url(./assets/bgi.jpg) no-repeat center;
// 修改為下面這種就可以了
background: #409eff no-repeat center;
到了這里,關(guān)于原生JS:100行js代碼帶你實(shí)現(xiàn)【像素鳥】小游戲(完整代碼+素材圖片)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!