typescript 做一個貪吃蛇小游戲
搭建環境
創建 tscofig.json 檔案
配置如下
{
"compilerOptions": {
"target": "es2015",
"module": "es2015",
"strict": true,
"outDir": "./dist",
"noEmitOnError": true
}
}
創建 webpack.config.js 檔案
-
安裝 webpack
npm i webpack webpack-cli -D -
配置
配置 webpack 專案工程化,配置專案運行打包,兼容,處理 .ts .css .less html 檔案 -
安裝插件
npm i html-webpack-plugin webpack-dev-server -Dwebpack-dev-server 作用
webpack 內部服務器,可以在開發階段專案自動運行,比如修改了代碼,會檢測到改動并且自動運行,不需要每次都手動運行查看,利于開發效率
html-webpack-plugin
html 插件,可用來提供 html 模板,打包后的 dist 檔案中的 html 會根據這個模板生成
配置如下
const path = require('path')
// html 插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
const htmlPlugin = new HtmlWebpackPlugin({
template: './src/index.html',
filename: './index.html'
})
module.exports = {
mode: 'development',
entry: './src/index.ts',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
environment: {
// 兼容 IE ,禁止使用箭頭函式
arrowFunction: false,
// 兼容 IE ,禁止使用es6語法
const: false
}
},
module: {
rules: [{
test: /\.ts$/,
use: [
// 配置babel
{
// 指定加載器
loader: 'babel-loader',
// 設定 babel
options: {
// 設定預定義環境
presets: [
[
// 指定環境插件
"@babel/preset-env",
// 配置資訊
{
// 要兼容的目標瀏覽器
targets: {
"chrome": "58"
},
// 指定 corejs 版本
"corejs": "3",
// 使用 corejs 的方式"usage"表示按需加載
"useBuiltIns": "usage"
}
]
]
}
}, 'ts-loader'
],
exclude: /node_modules/
},
{
test: /\.less$/,
use: [
"style-loader",
"css-loader",
// 引入 postcss
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
"postcss-preset-env",
{
browsers: 'last 2 versions'
}
]
]
}
}
},
"less-loader"
]
}
]
},
plugins: [htmlPlugin],
resolve: {
extensions: ['.ts', '.js']
}
}
創建 package.json 檔案
在專案名稱是英文的情況下使用
npm init -y
如果專案名稱不是英文,使用如下命令,給它設定一個英文名稱
npm init
安裝專案所需的全部開發依賴,如下

配置就說這么多,下面開始專案代碼
采用的是結構與資料分離
先布局基本樣式
在專案根目錄下創建 src 檔案夾,在src 檔案夾下創建 index.html 和 index.ts 檔案
./src/index.html
<!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>貪吃蛇</title>
</head>
<body>
<div class="box">
<div class="top">
// 蛇
<div id="snake">
// 蛇的每一節
<div></div>
</div>
// 食物
<div id="food"></div>
</div>
// 記分牌
<div class="bottom">
<div>SCORE:<span id="score">3</span></div>
<div>LEVEL:<span id="level">1</span></div>
</div>
</div>
</body>
</html>
在 src 目錄下創建 style 檔案夾存放樣式檔案 index.less
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font: bold 20px "Courier";
}
.box {
width: 360px;
height: 420px;
background-color: #b7d4a8;
border: 10px solid #000;
margin: 100px auto;
border-radius: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-evenly;
}
.top {
width: 304px;
height: 304px;
border: 2px solid #000;
position: relative;
}
.bottom {
width: 304px;
display: flex;
justify-content: space-between;
}
#snake {
position: relative;
}
#snake div {
width: 10px;
height: 10px;
background-color: black;
border: 1px solid #b7d4a8;
position: absolute;
top: 0;
left: 0;
}
#food {
width: 10px;
height: 10px;
background-color: black;
position: absolute;
border: 1px solid #b7d4a8;
}
在 index.ts 檔案中引入樣式
import './style/index.less'
- 不要忘記安裝 less

開始業務邏輯代碼
在 src 檔案下創建 modules 檔案夾,存放每個類
Food.ts , 控制食物隨機出現的位置
class Food {
// 定義一個屬性表示事物所對應的元素
element: HTMLElement
constructor() {
// !感嘆號的意思是不用管
this.element = document.getElementById('food')!
}
// 定義一個獲取食物X軸坐標的方法
get X() {
return this.element.offsetLeft
}
// 定義一個獲取食物Y軸坐標的方法
get Y() {
return this.element.offsetTop
}
// 修改十五位置的方法
change() {
// 生成一個亂數
// 食物的位置最小時0,最大是290
// Math.round(Math.random() * 29) * 10
this.element.style.top = Math.round(Math.random() * 29) * 10 + 'px'
this.element.style.left = Math.round(Math.random() * 29) * 10 + 'px'
}
}
// let food = new Food()
// food.change()
// console.log(food.X, food.Y)
export default Food
創建 ScorePanel.ts,控制記分牌的變化
// 定義記分牌
class ScorePanel {
// score, level 記錄分數和等級
score = 0
level = 1
// 分數和等級初始化
scoreEle: HTMLElement
levelEle: HTMLElement
// shezhibianliang
maxLevel: number
maxScore: number
constructor(maxLevel: number = 10, maxScore: number = 10) {
this.scoreEle = document.getElementById('score')!
this.levelEle = document.getElementById('level')!
this.maxLevel = maxLevel
this.maxScore = maxScore
}
// 設定加分的方法
addScore() {
this.score++
this.scoreEle.innerHTML = this.score + ''
if (this.score % this.maxScore === 0) {
this.levelUp()
}
}
// 提升等級的方法
levelUp() {
if (this.level < this.maxLevel) {
this.level++
this.levelEle.innerHTML = this.level + ''
}
}
}
export default ScorePanel
創建 Snake.ts,控制蛇的長度/位置/是否撞墻或者撞到自己
class Snake {
// 表示蛇頭的元素
head: HTMLElement
// 蛇的身體
bodies: HTMLCollection
element: HTMLElement
constructor() {
this.element = document.getElementById('snake')!
this.head = document.querySelector('#snake > div')!
this.bodies = this.element.getElementsByTagName('div')
}
// 獲取蛇的坐標
get X() {
return this.head.offsetLeft
}
get Y() {
return this.head.offsetTop
}
// 設定蛇頭的坐標
set X(value: number) {
if (this.X === value) return
// 判斷是否撞墻 X值的合法范圍
if (value < 0 || value > 290) {
// 蛇撞墻了拋出例外
throw new Error("蛇撞墻了");
}
// 修改水平座標蛇向右走不能往左走
if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === value) {
// 如果發生了掉頭讓蛇繼續移動
if (value > this.X) {
value = this.X -10
} else {
value = this.X + 10
}
}
// 移動身體
this.moveBody()
this.head.style.left = value + 'px'
// 檢查有沒有撞到自己
this.checkHeadBody()
}
set Y(value: number) {
if (this.Y === value) return
if (value < 0 || value > 290) {
throw new Error("蛇撞墻了");
}
// 修改水平座標蛇向右走不能往左走
if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === value) {
// 如果發生了掉頭讓蛇繼續移動
if (value > this.Y) {
value = this.Y -10
} else {
value = this.Y + 10
}
}
// 移動身體
this.moveBody()
this.head.style.top = value + 'px'
// 檢查有沒有撞到自己
this.checkHeadBody()
}
addBody() {
this.element.insertAdjacentHTML('beforeend', '<div></div>')
}
moveBody() {
// 從后往前改,將后面的身體設定位前面的身體位置
// 遍歷獲取所有的身體
for (let i = this.bodies.length - 1; i > 0; i--) {
let X = (this.bodies[i - 1] as HTMLElement).offsetLeft;
let Y = (this.bodies[i - 1] as HTMLElement).offsetTop;
(this.bodies[i] as HTMLElement).style.left = X + 'px';
(this.bodies[i] as HTMLElement).style.top = Y + 'px';
}
}
checkHeadBody() {
for (let i = 1; i < this.bodies.length; i++){
if (this.X === (this.bodies[i] as HTMLElement).offsetLeft && this.Y === (this.bodies[i] as HTMLElement).offsetTop) {
throw new Error("撞到自己了");
}
}
}
}
export default Snake
創建 GameControl.ts,控制蛇的移動,把蛇、食物、記分牌聯系到一塊
import Snake from "./snake";
import Food from "./food";
import ScorePanel from "./ScorePanel";
class GameControl {
// 定義三個屬性
// 蛇
snake: Snake
food: Food
scorePanel: ScorePanel
// c創建一個屬性來存盤蛇的移動方向
direction: string = 'Right'
// 創建一個屬性用來記錄游戲是否結束
isLive: boolean = true
constructor() {
this.snake = new Snake()
this.food = new Food()
this.scorePanel = new ScorePanel()
this.init()
}
// 游戲初始化方法
init() {
// 系結鍵盤事件
// 修改this指向
document.addEventListener('keydown', this.keydownHandler.bind(this))
// 呼叫 run 方法
this.run()
}
// 創建鍵盤按下的函式
keydownHandler(e: KeyboardEvent) {
// 修改 dirention 的值
this.direction = e.key
}
// 創建控制蛇移動的方法
run() {
// 根據方向來改變蛇的位置
// 獲取蛇現在的坐標
let X = this.snake.X
let Y = this.snake.Y
switch (this.direction) {
case "ArrowUp":
case "Up":
Y -= 10
break;
case "ArrowDown":
case "Down":
Y += 10
break;
case "ArrowRight":
case "Right":
X += 10
break;
case "ArrowLeft":
case "Left":
X -= 10
break;
default:
break;
}
this.checkEat(X, Y)
try {
this.snake.X = X
this.snake.Y = Y
} catch (e) {
alert(' OVER!')
this.isLive = false
}
this.isLive && setTimeout(this.run.bind(this), 300 - (this.scorePanel.level - 1) * 30);
}
// 定義一個方法判斷吃到食物
checkEat(X: Number, Y: number) {
if (X === this.food.X && Y === this.food.Y) {
this.food.change()
this.scorePanel.addScore()
this.snake.addBody()
}
}
}
export default GameControl
在 src 下的 index.ts 中引入 GameControl 并且實體化,就可以運行代碼
import GameControl from './modules/GameControl'
new GameControl()
gitee 專案地址:
https://gitee.com/jinyang465/snake-demo
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/377229.html
標籤:其他
