贪吃蛇游戏源码
//定义食物类food class Food { //定义一个属性表示食物对应的元素 element: HTMLElement; constructor() { //获取页面中的food元素并赋值给element this.element = document.getElementById('food'); } //定义一个获取食物X轴坐标的方法 get X() { return this.element.offsetLeft; } //定义一个获取食物Y轴坐标的方法 get Y() { return this.element.offsetTop; } //修改食物位置的方法 change() { // left:0-290,top:0-290 需要被10整除,蛇移动一次就是一格(10)。 // let left = Math.round(Math.random() * 29) * 10; let top = Math.round(Math.random() * 29) * 10; this.element.style.left = `${left}px` this.element.style.top = `${top}px` } } export default Food
//游戏控制器,控制其他所有类 import Snake from "./Snake"; import Food from "./Food"; import ScorePanel from "./ScorePanel"; class GameControl { //定义三个属性 //蛇 snake: Snake; food: Food; scorePanel: ScorePanel; //创建一个属性存储蛇的移动放心 direction: string = ''; //创建一个属性记录游戏是否结束 isLive: boolean = true; constructor() { this.snake = new Snake(); this.food = new Food(); this.scorePanel = new ScorePanel(10,2); this.init() } //游戏初始化方法调用后游戏就开始 init() { //绑定键盘按下的事件 document.addEventListener('keydown', this.keydowHandler.bind(this)) this.run() } //创建一个键盘按下的响应函数 keydowHandler(e) { console.log(e.key); //需要检验key的值是否合法(用户是否按了正确的案件) this.direction = e.key; } //创建一个控制蛇移动的方法 run() { /* 根据方向(this.direction)来改变蛇的偏移量 向上TOP减少 向下TOP增加 向左left减少 向右left增加 */ //获取蛇现在的坐标 let x = this.snake.X; let y = this.snake.Y; //计算坐标的变化 switch (this.direction) { case 'ArrowUp': case 'Up': //向上移动TOP减小 y -= 10; break; case 'ArrowDown': case 'Down': //向下移动TOP增加 y += 10; break; case 'ArrowLeft': case 'Left': //向左移动x减小 x -= 10; break; case 'ArrowRight': case 'Right': //向右移动x增加 x += 10; break; } //检查蛇是否吃到了食物 this.checkEat(x,y) //修改蛇的位置 try { this.snake.X = x; this.snake.Y = y; } catch (e) { //进入catch,说明除了异常,游戏结束,弹出一个信息提示 alert(e.message + 'GAME 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) { console.log('吃到食物了'); this.food.change();//改变食物的位置 this.scorePanel.addScore();//增加分数 this.snake.addBody();//增加蛇的长度 } } } export default GameControl
//定义记分牌类 class ScorePanel { maxLevel:number//限制最大等级 upScore:number//多少分升级 //score和level用来记录分数和等级 private _score: number = 0; private _level: number = 1; //分数和等级所在元素 scoreEle: HTMLElement; levelEle: HTMLElement; constructor(maxlevel:number = 10,upScore:number=10) { this.maxLevel = maxlevel; this.upScore = upScore this.scoreEle = document.getElementsByClassName('score')[0] as HTMLAreaElement; this.levelEle = document.getElementsByClassName('level')[0] as HTMLAreaElement; } get score() { return this._score } get level() { return this._level } //设置一个加分的方法 addScore(){ let score = ++this._score; this.scoreEle.innerHTML = `${score}` //判断分数是多少,来判断是否升级 if(this._score %this.upScore ===0){ this.levelUp() } } //提升等级的方法 levelUp(){ if(this._level <this.maxLevel){ this.levelEle.innerHTML = `${++this._level}` } } } export default ScorePanel
class Snake { //表示蛇头的元素 head: HTMLElement; //表示身体的元素 bodies: HTMLCollection; //获取蛇的容器 element: HTMLElement constructor() { this.head = document.querySelector('#snake > div') this.bodies = document.getElementById('snake').getElementsByTagName('div') this.element = document.getElementById('snake'); } //获取蛇的坐标(蛇头坐标) get X() { return this.head.offsetLeft; } get Y() { return this.head.offsetTop; } set X(value: number) { if (this.X == value) { return; } //X的值的合法区间0-290 if (value < 0 || value > 290) { //蛇撞墙的处理 throw new Error('蛇撞墙了') } //修改X时,是在修改水平坐标,蛇在左右移动,蛇向左移动时,不能向右移动,反之亦然 if (this.bodies[1]) { //判断是否有第二截身体 if (value === (this.bodies[1] as HTMLElement).offsetLeft) { //判断即将改变的蛇头的坐标是否和第二截身体的坐标相同,相同就发生了掉头 //console.log('发生了掉头'); //如果发生了掉头,让蛇向反方向继续移动 if(value>this.X){ //如果value大于X,则说明蛇在向右走 value = this.X-10 }else{ value = this.X+10 } } } //移动身体 this.moveBody() this.head.style.left = `${value}px`; try { this.checkHeadBody() } catch (error) { alert(error.message+'GAME OVER !') } } set Y(value: number) { if (this.Y == value) { return; } if (value < 0 || value > 290) { //蛇撞墙的处理 throw new Error('蛇撞墙了') } //修改Y时,是在修改蛇的垂直坐标,蛇在上下移动,蛇向上移动时,不能向下移动,反之亦然 if (this.bodies[1]) { //判断是否有第二截身体 if (value === (this.bodies[1] as HTMLElement).offsetTop) { //判断即将改变的蛇头的坐标是否和第二截身体的坐标相同,相同就发生了掉头 //console.log('发生了掉头'); //如果发生了掉头,让蛇向反方向继续移动 if(value>this.Y){ //如果value大于Y,则说明蛇在向上走 value = this.Y-10 }else{ value = this.Y+10 } } } //移动身体 this.moveBody() this.head.style.top = `${value}px`; try { this.checkHeadBody() } catch (error) { alert(error.message+'GAME OVER !') } } //蛇增加身体的方法 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
<!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="main"> <!-- 舞台 --> <div class="stage"> <!-- 蛇容器 --> <div id="snake"> <!-- 蛇的各个部分 --> <div></div> </div> <div id="food"> <!-- 添加4个div设置蛇的样式 --> <div></div> <div></div> <div></div> <div></div> </div> </div> <!-- 计分板 --> <div class="score-panel"> <div> SCORE:<span class="score">0</span> </div> <div> LEVEL:<span class="level">1</span> </div> </div> </div> </body> </html>
import './style/index.less' import GameControl from './moduls/GameControl' new GameControl();
@bgcl: #b7d4a8; * { margin: 0; padding: 0; //改变盒子模型的计算方式 box-sizing: border-box; } .main { width: 360px; height: 420px; background-color: @bgcl; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); border: solid 10px #000000; border-radius: 5%; } .stage { width: 304px; height: 304px; border: 2px solid black; margin: 20px 18px; //开启相对定位 position: relative; #snake>div { width: 10px; height: 10px; background-color: #000000; border: 1px solid @bgcl; //开启绝对定位 position: absolute; } } #food{ width: 10px; height: 10px; border: 1px solid @bgcl; position: absolute; left: 200px; top: 100px; display: flex; flex-flow: row wrap; justify-content: space-between; align-content: space-between; &>div{ width: 3px; height: 3px; background-color: #000000; transform: rotate(45deg); } } .score-panel { display: flex; width: 85%; margin: 10px auto; justify-content: space-between; }
{ "name": "part3", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --mode production", "dev": "webpack --mode development", "start": "webpack serve --open " }, "author": "", "license": "ISC", "devDependencies": { "@babel/core": "^7.20.7", "@babel/preset-env": "^7.20.2", "babel-loader": "^9.1.0", "clean-webpack-plugin": "^4.0.0", "core-js": "^3.26.1", "css-loader": "^6.7.3", "html-webpack-plugin": "^5.5.0", "less": "^4.1.3", "less-loader": "^11.1.0", "style-loader": "^3.3.1", "ts-loader": "^9.4.2", "typescript": "^4.9.4", "webpack": "^5.75.0", "webpack-cli": "^5.0.1", "webpack-dev-server": "^4.11.1" }, "dependencies": { "-": "^0.0.1", "D": "^1.0.0", "postcss": "^8.4.21", "postcss-loader": "^7.0.2", "postcss-preset-env": "^7.8.3" } }
{ "compilerOptions": { "module": "ES2015", "target": "ES6", // "sourceMap": false, // "strict": true }, "exclude": [ "node_modules" ] }
//引入path模块,管理路径 // import { Configuration } from 'webpack';//运行webpack时注释掉 /** * @type {Configuration} */ const path = require('path'); //引入html-webpack-plugin const HTMLWEBPACKPLUGIN = require('html-webpack-plugin') const { CleanWebpackPlugin } = require("clean-webpack-plugin") //webpack中的所有配置信息都应写在module.exports中 module.exports = { //指定入口文件 entry: './src/index.ts', //指定打包文件所在目录 output: { //指定打包文件的目录 // path:'./dist' path: path.resolve(__dirname, 'dist'), //打包后文件的文件名 filename: "bundle.js", environment: { //配置打包的环境 arrowFunction: false,//告诉webpack不使用箭头函数 const:false,//告诉webpack不使用const } }, //指定webpack打包时要使用的模块 module: { //指定加载的规则 rules: [ { //指定规则生效的文件 test: /\.ts$/, //要使用的loader,执行顺序从后往前 use: [ {//配置babel //指定加载器 loader: "babel-loader", //设置babel options: { //设置预定义环境 presets: [ [ //指定环境插件 "@babel/preset-env", //配置信息 { //要兼容的目标浏览器 targets: { "chrome": "88", "ie": "11" }, "corejs": "3",//指定core.js的版本 // //使用core.js的方式 "useBuiltIns": "usage",//"usage"表示按需加载 }, ] ] } }, "ts-loader" ], //指定要排除的文件 exclude: /node_modules/, }, //设置less文件的处理 { test: /\.less$/, use: [ "style-loader", "css-loader", { loader:"postcss-loader", options:{ postcssOptions:{ plugins:[ [ "postcss-preset-env", { browsers:'last 2 versions' } ] ] } } }, "less-loader" ] } ] }, //配置webpack的插件 plugins: [ new CleanWebpackPlugin(), new HTMLWEBPACKPLUGIN({ // title:'这是一个自定义的title'//用来配置自动生成的html文件的title template: "./src/index.html"//用来配置自动生成的html文件的模板 }) ], mode: 'development', resolve: { //设置引用模块 extensions: ['.ts', '.js']//告诉webpack哪些后缀的文件可以被当做模块引用 } }