赞
踩
用typescript做一个贪吃蛇的小游戏
将前面配置好的tsconfig.json,webpack.config.js,package.json文件(文末)复制粘贴到新文件中并根据项目做简单修改
npm -i
npm i -D less less-loader css-loader style-loader
npm i -D postcss postcss-loader postcss-preset-env
至此,项目结构搭建完成
输入npm start开启监听便于实时更新代码
- <!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 id="main">
- <!-- 设置游戏的舞台 -->
- <div id="stage">
- <!-- 设置蛇 -->
- <div id="snake">
- <!-- snake内部的div,表示蛇的各部分 -->
- <div>
-
- </div>
- </div>
- <!--设置食物-->
- <div id="food">
- <!-- 添加4个小div,设置食物的样式 -->
- <div></div>
- <div></div>
- <div></div>
- <div></div>
- </div>
- </div>
- <!-- 设置游戏的积分牌 -->
- <div id="score-panel">
- <div>
- SCORE:<span id="score">0</span>
- </div>
- <div>
- level:<span id="level">1</span>
- </div>
- </div>
- </div>
- <!-- <script src="../dist/bundle.js"></script> -->
- </body>
- </html>
-
-
-
-
-
-
index.ts文件引入less样式
- //设置变量
- @bg-color:yellow;
-
- //清除默认样式
- *{
- margin:0;
- padding: 0;
- //改变盒子模型的计算方式
- box-sizing: border-box;
- }
-
- body{
- font:bold 20px "Courier";
- }
-
- //设置主窗口的样式
- #main{
- width: 360px;
- height: 420px;
- //设置背景颜色
- background-color:@bg-color;
- margin: 100px auto;
- border: 10px solid blue;
- //设置圆角
- border-radius: 20px;
-
- //开启弹性盒模型
- display: flex;
- //设置主轴的方向
- flex-flow: column;
- //设置辅轴的对齐方式
- align-items: center;
- //设置主轴的对齐方式
- justify-content: space-around;
-
- //游戏舞台
- #stage{
- width: 304px;
- height: 304px;
- border: 2px solid black;
- //开启相对定位
- position: relative;
-
- //设置蛇的样式
- #snake{
- &>div{
- width: 10px;
- height: 10px;
- background-color: red;
- border: 1px solid @bg-color;
- //开启绝对定位 使蛇可以移动
- position: absolute;
- }
- }
-
- //设置食物
- #food{
- width: 10px;
- height: 10px;
- position: absolute;
- left:40px;
- top:100px;
- //background-color: red;
- display: flex;
- //设置横轴为主轴,wrap表示自动换行
- flex-flow: row wrap;
- //设置主轴和侧轴的空白空间分配到元素之间
- justify-content: space-between;
- align-content: space-between;
-
- &>div{
- width: 5px;
- height: 5px;
- background-color: green;
- border: 1px solid yellow;
- //使4gediv旋转45度
- transform: rotate(45deg);
- }
- }
- }
-
- //记分牌
- #score-panel{
- width: 300px;
- display: flex;
- //设置主轴的对齐方式
- justify-content: space-between;
- }
- }
-
按不同功能分成多个模块,便于代码的编写及修改。
将Food.ts,ScorePanel.ts,Snake.ts模块导入到GameControl.ts模块,由该模块来统一整合。
index.ts文件引入各模块
- //定义类
- 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(){
- //生成一个随机的位置
- //食物的位置最小是0,最大是290 300-10=290
- //蛇移动一次就是一格,一格的大小就是10,所以就要求食物的坐标必须是10的倍数
- //Math.round表示四舍五入
- //Math.floor(Math.random()*30)*10;
- //Math.floor表示向下取整
- let top=Math.round(Math.random()*29)*10;//既包括0,又包括290,而且都是10的倍数
- let left=Math.round(Math.random()*29)*10;
- this.element.style.left=left+'px';
- this.element.style.top=top+'px';
- }
- }
-
- //测试代码
- // const food=new Food()
- // console.log(food.X,food.Y);
- // food.change();
- // console.log(food.X,food.Y);
-
- export default Food;
- //定义表示记分牌的类
- class ScorePanel{
- //score和level用来记录分数和等级
- score=0;
- level=1;
- //分数和等级所在的元素,在构造函数中进行初始化
- scoreEle:HTMLElement;
- levelEle:HTMLElement;
- //设置变量限制等级
- maxLevel:number;
- //设置一个变量表示多少分时升级
- upScore:number;
- constructor(maxLevel:number=10,upScore:number=10){//不传默认10,穿了多少就多少
- this.scoreEle=document.getElementById("score")!;
- this.levelEle=document.getElementById("level")!;
- this.maxLevel=maxLevel;
- this.upScore=upScore;
- }
-
- //设置一个加分的方法
- addScore(){
- //使分数自增
- //this.score++;
- this.scoreEle.innerHTML=++this.score+'';//装换成字符串,innerHTML接收字符串
- //判断分数是多少
- if(this.score%this.upScore===0){
- this.levelUp();
- }
- }
- //提升等级的方法
- levelUp(){
- if(this.level<=this.maxLevel){
- this.levelEle.innerHTML=++this.level+'';
- }
- }
- }
-
- //测试代码
- //const scorePanel=new ScorePanel(1,0);
- // scorePanel.addScore();
- // scorePanel.addScore();
- // scorePanel.addScore();
- // for(let i=0;i<9;i++)
- // {
- // scorePanel.addScore();
- // }
-
- export default ScorePanel;//改为默认模块暴露出去
- class Snake{
- //表示蛇头的元素
- head:HTMLElement;
- //蛇的身体(包括蛇头)
- bodies:HTMLCollection;
- //获取蛇的容器
- element:HTMLElement;
- constructor(){
- this.element=document.getElementById("snake")!;
- this.head=document.querySelector("#snake>div")!as HTMLElement;
- this.bodies=this.element.getElementsByTagName("div");
- }
- //获取蛇的坐标(蛇头坐标)
- get X(){
- return this.head.offsetLeft;
- }
- //获取蛇的Y轴坐标
- 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]&&(this.bodies[1]as HTMLElement).offsetLeft===value){
- //console.log("发生了水平掉头");
- //如果发生了掉头,则应让蛇向反方向移动
- 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;
- }
- //y的值的合法范围0-290
- if(value<0||value>290){
- //进入判断说明蛇撞墙了
- throw new Error("蛇撞墙了");
- }
- //修改y时,是在修改垂直坐标,蛇在上下移动,蛇在向上移动式时,不能向下掉头,反之亦然
- if(this.bodies[1]&&(this.bodies[1]as HTMLElement).offsetTop===value){
- //console.log("发生了垂直掉头");
- //如果发生了掉头,则应让蛇向反方向移动
- if(value>this.Y){
- //如果新值大于旧值,则说明蛇在向下走(实际是蛇向上走),此时发生掉头.但实际上应该使蛇继续向上走
- value=this.Y-10;
- }else{
- value=this.Y+10;
- }
- }
- //移动身体
- this.moveBody();
- this.head.style.top=value+"px";
- //检查有没有撞到自己
- this.checkHeadBody();
- }
-
- //蛇增加身体的方法
- addBody(){
- //向element中添加一个div
- this.element.insertAdjacentHTML("beforeend","<div></div>");
- }
- //添加一个蛇身体移动的方法
- moveBody(){
- //将后边的身体设置为前边身体的位置
- // 第4节=第3节的位置
- // 第3节=第2节的位置
- // 第2节=第1节的位置
- //遍历获取所有的身体
- for(let i=this.bodies.length-1;i>0;i--)
- {
- //获取前边身体的位置
- let X=(this.bodies[i-1]as HTMLElement).offsetLeft;//没加类型断言报错是因为bodies类型是Element,另外一个是htmlelement
- 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++){
- let bd=this.bodies[i]as HTMLElement;
- if(this.X===bd.offsetLeft && this.Y===bd.offsetTop){
- //进入判断说明蛇头撞到了身体,游戏结束
- throw new Error("撞到自己了");
- }
- }
- }
- }
-
- export default Snake;
- //引入其他类
- import Snake from "./snake";
- import Food from "./Food";
- import ScorePanel from "./ScorePanel";
-
- //游戏的控制器,控制其他所有的类
- class GameControl{
- //定义三个属性
- //蛇
- snake:Snake;
- //食物
- food:Food;
- //记分牌
- scorepanel:ScorePanel;
-
- //创建一个属性来存储蛇的移动方向(也就是按键的方向)
- direction:string='';
- //创建一个属性用来记录游戏是否结束
- isLive=true;
-
- constructor(){
- this.snake=new Snake();
- this.food=new Food();
- this.scorepanel=new ScorePanel();
-
- this.init();
- }
-
- //游戏的初始化方法,调用后游戏即开始
- init(){
- //绑定键盘按下的事件
- document.addEventListener('keydown',this.keydownHandle.bind(this));//加了bind使得括号里的this指向keydownHandle
- //调用run方法。使蛇移动,没开定时器时,蛇只动一次,因为只调用一次
- this.run();
- }
-
- /*
- ArrowUp Up
- ArrowDown Down
- ArrowLeft Left
- ArrowRight Right
- */
- //创建一个键盘按下的响应函数
- keydownHandle(event:KeyboardEvent){
- //console.log(this);没加bind之前 this指向是document
- //需要检查event.key的值是否合法(用户是否按了正确的按键)
- //修改direction属性
- this.direction=event.key;
- }
-
- //创建一个控制蛇移动的方法
- run(){
- //根据方向(this.direction)来使蛇的位置改变
- /*
- 向上:top减少
- 向下:top增加
- 向左: left减少
- 向右;left增加
- */
- //获取蛇现在的坐标
- let X=this.snake.X;
- let Y=this.snake.Y;
-
- //根据按键方向来修改x值和y值
- switch(this.direction){
- case "ArrowUp":
- case "Up":
- Y-=10;
- break;
- case "ArrowDown":
- case "Down":
- Y+=10;
- break;
- case "ArrowLeft":
- case "Left":
- X-=10;
- break;
- case "ArrowRight":
- case "Right":
- X+=10;
- break;
- }
-
- //检查蛇是否吃到了食物
- this.checkEat(X,Y);
-
-
- //修改蛇的x值和y值
- try{
- this.snake.X=X;
- this.snake.Y=Y;
- }catch(e:any){
- //进入到catch,说明出现了异常,游戏结束,弹出一个提示信息
- alert(e.message+"GAME OVER");
- //将isLive设置为false
- 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;
至此,项目完成,输入npm run build即可运行代码
注:所需的配置文件如下
tsconfig.json
- {
- "compilerOptions": {
- "module": "es2015",
- "target": "es2015",
- "strict": true,
- "noEmitOnError": true
- }
- }
package.json
- {
- "name": "part2",
- "version": "1.0.0",
- "description": "",
- "main": "index.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1",
- "build": "webpack --mode development",
- "start": "webpack serve --open --mode development "
- },
- "keywords": [],
- "author": "",
- "license": "ISC",
- "devDependencies": {
- "@babel/core": "^7.19.3",
- "@babel/preset-env": "^7.19.4",
- "babel-loader": "^8.2.5",
- "clean-webpack-plugin": "^4.0.0",
- "core-js": "^3.25.5",
- "css-loader": "^6.7.1",
- "html-webpack-plugin": "^5.5.0",
- "less": "^4.1.3",
- "less-loader": "^11.1.0",
- "postcss": "^8.4.18",
- "postcss-loader": "^7.0.1",
- "postcss-preset-env": "^7.8.2",
- "style-loader": "^3.3.1",
- "ts-loader": "^9.4.1",
- "typescript": "^4.8.4",
- "webpack": "^5.74.0",
- "webpack-cli": "^4.10.0",
- "webpack-dev-server": "^4.11.1"
- }
- }
webpack.config.js
- //引入一个包
- const path=require("path");
- //引入html插件
- const HTMLWebpackPlugin=require('html-webpack-plugin');
- //引入clean插件
- const {CleanWebpackPlugin}=require('clean-webpack-plugin');
- const { resolve } = require("path");
-
- //webpack中的所有的配置信息都应该写在module.exports中
- module.exports={
- //指定入口文件
- entry:"./src/index.ts",
-
- //指定打包文件所在的目录
- output:{
- //指定打包文件的目录
- path:path.resolve(__dirname,"dist"),
- //打包后文件的文件
- filename:"bundle.js",
-
- //告诉webpack不使用箭头函数
- environment:{
- arrowFunction:false,
- const:false
- }
- },
-
- //指定webpack打包时要使用的模块
- module:{
- //指定要加载的规则
- rules:[
- {
- //test指定规则生效的文件
- test:/\.ts$/,
- //要使用的loader
- use:[
- //配置babel
- {
- //指定加载器
- loader:'babel-loader',
- //设置babel
- options:{
- //设置预定义的环境
- presets:[
- [
- //指定环境的插件
- "@babel/preset-env",
- //配置信息
- {
- //要兼容的目标浏览器
- targets:{
- "edge":"106",
- "ie":"11",
- "chrome":"99"
- },
- //指定corejs的版本
- "corejs":"3",
- //使用corejs的方式 "usage"表示按需加载
- "useBuiltIns":"usage"
- }
- ]
- ]
- }
- },
- 'ts-loader',
- ],
- //要排除的文件
- exclude:/node_modules/
- },
- //设置less文件的处理
- {
- test:/\.less$/,
- use:[
- "style-loader",
- "css-loader",
- //引入postcss
- {
- loader:"postcss-loader",
- options:{
- postcssOptions:{
- plugins:[
- [
- 'postcss-preset-env',
- {
- browsers:"last 2 versions"//兼容两种最新的浏览器
- }
- ]
- ]
- }
- }
- },
- "less-loader"//loader执行顺序从下往上
- ]
- }
- ]
- },
-
- //配置webpack插件
- plugins:[
- new CleanWebpackPlugin(),
- new HTMLWebpackPlugin({
- //title:"这是一个自定义的title"
- template:"./src/index.html"//以他作为模板生成js文件
- }),
- ],
-
- //用来设置引用的模块
- resolve:{
- extensions:['.ts','.js']//以.ts ,.js结尾的文件都可以作为模块来使用
- }
- }
做了小练习来熟悉typescript,对typescript语法及使用有了进一步的了解,提高了编程能力。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。