使用React来实现一个简单的贪吃蛇游戏

github地址:https://github.com/YangLin2510/snake_game

最近学习了一下React前端开发框架,可以基于组件来开发可复用的前端界面。

写一个小项目来综合练习下

创建项目

用create-react-app 命令行工具创建项目: (create-react-app 命令可以通过 npm 安装)

create-react-app snake_game

项目目录结构如下:


src 目录

在src下建一个snake_game目录里面并建一个snake_game.js 文件

页面

先实现一个静态页面

                    |
                    |得分:0
                    |
                    |
 游戏区              |-----------
                    |
                    | 暂停
                    |  
                    |
                    |
                    |

class Game extends Component {
    render(props){
        var trs = this.refresh();
        var pasuse_start_btn  = <button onClick={this.pause}>暂停</button>;
        if(!this.state.is_runing){
           pasuse_start_btn =<button onClick={this.start}>开始</button>;
        }

        return (
          <div className="game">
            <div className="game_name">贪吃蛇</div>
            <div className="game_menu"><a href="#">游戏</a><a href="#">帮助</a></div>
            <div className="game_div">

              <div className="game_background">
                <table border="1">

                  {trs}

                </table>
              </div>

              <div className="game_info">
                <h4>得分: {this.state.score} </h4>
                <h4>控制 W:上 S:下 A:左 D:右</h4>
                {pasuse_start_btn}
                <button onClick={this.init}>重新开始</button>
              </div>
            </div>
          </div>
        );
     }
}

还有对应的css, 放到单独的样式文件

.game {
   background-color: white;
   width: 60%;
   padding:16px 20%;
}

.game_name {
   text-align: center;
   font-weight: bold;
}

.game_menu a {
   padding-right: 16px;
}

.game_background {
   width: 60%;
   float: left;
}
.game_background table {
  border-collapse: collapse;
}
.game_background table td{
   width: 32px;
   height: 32px;
   padding: 0;
   margin: 0;
}

.game_info {
  width: 40%;
  float: left;
}

.game_background table td img {
  margin: 0;
  padding: 0;
}

.red {
  background-color: red;
  width: 100%;
  height:100%;
}

.black {
  background-color: black;
  width: 100%;
  height:100%;
}


组件的内部状态

 var snake = [{"x":1,"y":1}];
 var next_red = {"x":1,"y":3};
 this.state = {"score":0,"snake":snake,"red_node_position":next_red,"direction":direction.right,size:{"col":15,"row":20},"i":"","is_runing":true};    

snake: 贪吃蛇,用一个位置数组标识

score: 分数

red_node_position: 贪吃蛇食物的位置

direction:前进的方向

size: 背景的大小 初始为 15 * 20

is_running: 是否是暂停状态


实现贪吃蛇的前进

1.贪吃蛇的第一个元素上下左右移动一位

2.后面的元素依次占据它前面元素的位置

3.如果碰到了随机生成的点,则在移动之后再在末尾加一个元素。

   move(d){
       var snake = this.state.snake;
       var s;
       var first = {"x":snake[0].x,"y":snake[0].y};
       var get_red = false;
       var last_node={};
       var game_over_flag = false;

       if(d == direction.right){
         first.y +=1;
       }
       else if(d == direction.left){
         first.y -=1;
       }
       else if(d == direction.down){
         first.x +=1;
       }
       else if (d == direction.up) {
         first.x -=1;
       }

       if(snake.length >1 && first.x == snake[1].x && first.y == snake[1].y){//前进方向不能逆行
          return ;
       }

       if(first.x == this.state.red_node_position.x && first.y == this.state.red_node_position.y){//碰到红点
          get_red = true;
          this.setState({"score":this.state.score+1})
          var last_node_index = snake.length-1;
          last_node.x = snake[last_node_index].x;
          last_node.y = snake[last_node_index].y;
       }

       for(s in snake){
          var next_first = {"x":snake[s].x,"y":snake[s].y};

          snake[s].x = first.x;
          snake[s].y = first.y;

          first = next_first;
       }

       if(get_red){//追加一个元素
         get_red = false;
         var i = snake.length;
         snake[i] = {"x":last_node.x,"y":last_node.y};
          this.next_red();
       }
       this.setState(snake);
       this.game_over_check();
     }

游戏结束检测

判断条件,snake 数组中有元素超出了边界或者snake元素存在重复(碰撞)

 game_over_check(){
       //游戏结束监测
       //1.超出边界
       if(this.state.snake[0].x >= this.state.size.col || this.state.snake[0].x < 0 || this.state.snake[0].y < 0 || this.state.snake[0].y >= this.state.size.row){
          this.init();
       }
       //2.和自己冲突
       for(var s=1;s < this.state.snake.length && this.state.snake.length>1;s++){
           if(this.state.snake[s].x == this.state.snake[0].x && this.state.snake[s].y == this.state.snake[0].y){
               this.init();
           }
       }
     }

生成下一个食物点

随机生成一个点,必须要去掉贪吃蛇占据的位置

先把所有不包括贪吃蛇位置的元素放到一个数组,然后随机选择一个。

 next_red(){
        //生成下一个红点
        var nodes = new Array();
        for(let c = 0;c < this.state.size.col;c ++){
           for(let r =0;r < this.state.size.row;r ++){
              if(this.get_status(c,r)==0){
                var n = {"x":c,"y":r};
                nodes.push(n);
              }
           }
        }

        var next_index = Math.round(Math.random()*nodes.length-1);
        this.setState({"red_node_position":nodes[next_index]})
     }

刷新界面

根据现在的内部数据生成界面

 /* 刷新界面 */
     refresh(){
          var background = new Array();
          for(let c = 0;c<this.state.size.col;c++){
             background[c] = new Array();
             for(let r = 0;r < this.state.size.row;r++){
                background[c][r] = this.get_status(c,r);
             }
          }

          return background.map(function(value,index,array){
              var tds = value.map(function(v,i,a){
                 if(v==0){
                   return <td></td>
                 }
                 else if(v==1){
                   return <td>{black}</td>
                 }
                 else if (v ==2) {
                   return <td>{red}</td>
                 }
              });
              return <tr>{tds}</tr>;
          });
     }

暂停和开始

开始: 创建一个定时器,每隔一秒钟调用一次move

暂停:  去掉定时器

  start(){
       var i = setInterval(function(){
         this.move(this.state.direction);
       }.bind(this),600);

       this.setState({"i":i,"is_runing":true});
     }

     pause(){
       var  i = this.state.i;
       window.clearInterval(i);
       this.setState({"is_runing":false});
     }

游戏控制和初始化

componentDidMount 生命周期函数会在界面加载完成后自动调用。

如果按下了wsad键,则根据按键改变前进的方向。

调用start()函数开始游戏

  componentDidMount(){
        document.onkeydown = function(e){
          this.pause();
          var keyNum = window.event ? e.keyCode : e.which;
          if(keyNum == 87){
            this.move(direction.up);
            this.setState({"direction":direction.up});
          }
          else if (keyNum == 83) {
            this.move(direction.down);
            this.setState({"direction":direction.down});
          }
          else if (keyNum == 65) {
            this.move(direction.left);
            this.setState({"direction":direction.left});
          }
          else if (keyNum == 68) {
            this.move(direction.right);
            this.setState({"direction":direction.right});
          }
          this.start();
        }.bind(this);

        this.start();
     }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值