likes
comments
collection
share

人家用ts看电影,我用ts玩贪食蛇!

作者站长头像
站长
· 阅读数 10

界面展示

人家用ts看电影,我用ts玩贪食蛇!

环境搭建

  1. 小游戏的第一步就是地图和基础资源搭建了
  2. 这里包括HTML代码全部都是由ts进行注入的。
//引入less布局
import "./public/style/index.less";
//修改页面title
document.title = "贪吃蛇";
//声明函数 将px转为rem
function getPosition(position: number) {
  let size = getComputedStyle(window.document.documentElement)["fontSize"];
 return  position/ parseInt(size.replace(/px/, ""));
    
}
//获取body注入主体
let body: HTMLElement = document.body;
//一格为0.5rem
body.innerHTML += `
<!--- 主窗口 --->
<div class="outWin">
 <div class="win">
 <div class="snake"></div>
 </div>
 <div class="foot">
     <p class="score">SCORE
     <p id="score"></p>
     <p class="level">
     LEVEL
     <p id="level"></p>
     </p>
 </div>
</div>`;

积分和等级类

  1. 在这里我定义了两个参数一个是积分,一个是等级,
  2. 同时对他们进行setter来控制数据修改的同时修改dom
 //定义积分类
class ScoreLevel {
   _score: number;
   _level: number;
  constructor() {
    this.score = 0;
    this.level = 1;
  }
  set score(value: number) {
    this._score = value;
    let score: HTMLElement = document.querySelector("#score");
    score.innerText = this._score.toString();
    if (this._score == this._level * 5) {
      this._score = 0;
      this.level = this._level +=1; 
    }
  }
  set level(value: number) {
    this._level = value;
    let level: HTMLElement = document.querySelector("#level");
    level.innerText = this._level.toString();
  }
  addScore() {
    this.score = this._score + 1;
  }
}

Food类

  1. 这里使用了getter来获取food的x和y。
  2. 在构造函数中完成了对food的初始化。
//定义food类
class Food {
  element: HTMLElement;
  constructor() {
    let win = document.querySelector(".win");
    let food=document.createElement("div")
    food.innerHTML=`
    <div></div>
    <div></div>
    <div></div>
    <div></div>`
    food.className="food"
    win.appendChild(food); 
    this.element = document.querySelector(".food");
    //最大9.5rem 最小0px
    this.element.style.left = `${Math.random() * 9.5}rem`;
    this.element.style.top = `${Math.random() * 9.5}rem`;
  }
  get X() {
    return getPosition(this.element.offsetLeft);
  }
  get Y() {
    return getPosition(this.element.offsetTop);
  }
}

snake类

  1. Snake类主要有一个body list组成。
  2. 其所有函数都是为了获取body list或修改body的数据。
  3. 并且snake类也同样在造函数中完成了对snake的初始化。
//snake类
class Snake {
  bodyList:HTMLElement[];
  constructor() {
    let snake = document.querySelector(".snake");
    let head = document.createElement("div");
    this.bodyList=[];
    head.className = "head";
    this.bodyList.push(head);
    head.style.top="0";
    head.style.left="0";
    snake.appendChild(head);
    this.addBody();
  }
  get headX() {
    return getPosition(this.bodyList[0].offsetLeft);
  }
  set headX(value:number){
    this.bodyList[0].style.left=this.headX+value+"rem"
  }
  get headY() {
    return getPosition(this.bodyList[0].offsetTop);
  }
  set headY(value:number){
    this.bodyList[0].style.top=this.headY+value+"rem"
  }
  addBody() {
    let body:HTMLElement = document.createElement("div");
    body.className = "body";
    this.bodyList.push(body);
    let snake = document.querySelector(".snake");
    snake.append(body);
  }
}

游戏控制类

  1. 这个类也是最重要的一个类。
  2. 主要作用是监视键键盘事件,并在键盘事件中根据键盘按键。来进行x值和y值的函数调用。 3.在调用中监视Head的x值和y值是否越界或是否与food重叠
//GameControl
class GameControl {
  snake: Snake;
  food: Food;
  scoreLevel: ScoreLevel;
  timerX:any
  timerY:any
  audio:HTMLAudioElement
  constructor() {
    this.food = new Food();
    this.scoreLevel = new ScoreLevel();
    this.snake = new Snake();
    this.audio=document.querySelector("audio")
    document.addEventListener("keydown", this.keyDown.bind(this))
  }
  keyDown(event: KeyboardEvent) {
    let key = event.key;
    switch (key) {
      case "ArrowUp":
        this.moveY(-0.5);
        break;
      case "ArrowDown":
        this.moveY(0.5);
        break;
      case "ArrowLeft":
        this.moveX(-0.5);
        break;
      case "ArrowRight":
        this.moveX(0.5);
        break;
    }
  }
  moveX(num:number){
    this.gameTest();
    clearInterval(this.timerY)
    clearInterval(this.timerX)
    this.timerX=setInterval(()=>{
        this.snake.headX=num;
        this.gameTest();
        this.moveBody();
    },240-20*this.scoreLevel._level) 
  }
  moveY(num:number){
   clearInterval(this.timerY)
   clearInterval(this.timerX)
    this.timerY=setInterval(()=>{
      this.snake.headY=num;
      this.gameTest();
      this.moveBody();
    },240-20*this.scoreLevel._level)
  }
  moveBody(){
    for(let i=this.snake.bodyList.length-1;i>0;i--){
      this.snake.bodyList[i].style.left= this.snake.bodyList[i-1].style.left
      this.snake.bodyList[i].style.top= this.snake.bodyList[i-1].style.top
    }
  }
  getFood(){
    if(Math.abs(this.snake.headX-this.food.X)<0.5&&Math.abs(this.snake.headY-this.food.Y)<0.5){
       this.scoreLevel.addScore()
       this.food.element.remove();
        this.food=new Food()
       this.snake.addBody()
    }
  }
 async outWin(){
    if(this.snake.headX<0||this.snake.headX>9.5||this.snake.headY<0||this.snake.headY>10){
       await this.audio.play();
       window.alert("GameOver")
       window.location.reload();
      clearInterval(this.timerX)
      clearInterval(this.timerY)
    }
  }

 gameTest(){
       this.getFood();
       this.outWin();
 }
}

难点解析与思考

  1. 整体的逻辑都非常简单。
  2. 唯一的难点是在控制蛇身体移动的函数
  3. 这里采用了for循环,用遍历的形式前一项div的位置给予后一项div

less代码

@media screen and(max-device-width:600px){
    html{
        font-size:12px;
    }
}
@media screen and(min-device-width:600px) and (max-device-width:1200px) {
    html{
        font-size:24px;
    }
}
@media screen and(min-device-width:1200px) and (max-device-width:1800px) {
    html{
        font-size:36px;
    }
}
@media screen and (min-device-width:1800px) {
    html{
        font-size:42px;
    }
}
body{
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
}
.outWin{
    border-radius: 3px;
    background-color: rgb(139, 190, 139);
    height:14rem;
    width:12rem;
    border:0.3rem black solid;
    padding: 0.3rem;
    display: flex;
    flex-direction: column;
     align-items: center;
    .win{
        height:10rem;
        width:10rem;
        border:0.1rem black solid; 
        position: relative;
        .snake{
            // position: absolute;
            &> div{
                position: absolute;
                width: 0.25rem;
                height: 0.25rem;
                background-color: black;  
             }
        } 
    }
    .foot{
        display: flex;
        flex-direction: row;
        width: 100%;
        justify-content: space-around;
        align-items: flex-end;
        p{
            display: flex;
            flex-direction: row;
        }
    }
}
@keyframes foodFlashing {
    from {opacity: 0;}
    to {opacity: 1;}
  }
.food{
    width:0.25rem;
    height: 0.25rem;
    position:absolute;
    display: flex;
    flex-direction: row;
    margin:0.15rem;
    animation: foodFlashing 1s infinite;
    justify-content: space-around;
    align-items: center;
    flex-wrap: wrap;
    div{
        background-color: black;
        width: 0.1rem;
        height:0.1rem;
        border-radius: 5%;
    }
}