js html5 css俄罗斯方块游戏再现

yizhihongxing

以下是详细的js html5 css俄罗斯方块游戏再现攻略:

1.前置知识准备

在开始实现俄罗斯方块游戏前,需要掌握HTML5、CSS、JavaScript等前端技术。特别是JavaScript中的面向对象编程、事件响应等知识。同时,也需要掌握Canvas绘图技术。

2.实现思路

俄罗斯方块游戏的基本思路是:方块下落、方块移动、方块旋转、方块消除等操作。因此,可以将方块对象以及相关操作封装成一个类,通过定时器模拟下落及用户键盘事件来控制方块的移动、旋转等操作。同时,还需要考虑到边界判断、方块落地后是否可以消除等问题。

3.实现步骤

3.1. 构造方块类

在JavaScript中定义一个方块类,该类包含方块的形状、位置、大小信息,以及方块移动、旋转、下落等方法。其中,方块的形状通过二维数组表示,只需要定义7种常见形状的方块类型。具体示例如下:

方块类定义

/**
 * 方块类
 */
class Block {
    constructor(type, x, y, size) {
        this.type = type;  //方块类型
        this.x = x;  //方块左上角x坐标
        this.y = y;  //方块左上角y坐标
        this.size = size;  //方块大小
        this.shape = BLOCK_TYPE[type];  //方块形状
    }

    // 方块下落
    down(){
      this.y += this.size;
    }

    // 方块向左移动
    left(){
      this.x -= this.size;
    }

    // 方块向右移动
    right(){
      this.x += this.size;
    }

    // 方块旋转操作
    rotate(){
      let newShape = rotateShape(this.shape);
      // 判断旋转后是否超过画布边界
      if(isExceed(this.x, this.y, newShape)){
           return;
      }
      this.shape = newShape;
    }

    //绘制方块
    draw(ctx){
        ctx.fillStyle = BLOCK_COLOR[this.type];
        for(let i = 0; i < this.shape.length; i++){
            for(let j = 0; j < this.shape[i].length; j++){
                if(this.shape[i][j] > 0){//绘制实心方块
                    ctx.fillRect(this.x + j*this.size, this.y + i*this.size, this.size, this.size);
                }
            }
        }
    }
}

//7种常见方块的形状
const BLOCK_TYPE = [
  [
      [1, 1],
      [1, 1]
  ],
  [
      [1, 0, 0],
      [1, 1, 1]
  ],
  [
      [0, 0, 1],
      [1, 1, 1]
  ],
  [
      [1, 1, 0],
      [0, 1, 1]
  ],
  [
      [0, 1, 1],
      [1, 1, 0]
  ],
  [
      [1, 1, 1, 1]
  ],
  [
      [1, 1, 1],
      [0, 1, 0]
  ]
];

//方块颜色
const BLOCK_COLOR = [
    "#fff", "#f00", "#0f0", "#00f", "#ff0", "#f0f", "#0ff"
];

//方块旋转算法,实现方块顺时针旋转功能
const rotateShape = shape => {
  let newShape = [];
  for(let i = 0; i < shape[0].length; i++){  //先遍历数组列数
      newShape.push([])
      for(let j = shape.length-1; j >= 0 ; j--){  //在遍历行数,并倒序扫描
          newShape[i].push(shape[j][i]);
      }
  }
  return newShape;
}

//是否超出边界判断, x:方块左上角横坐标,y:方块左上角纵坐标,shape:方块形状数组
const isExceed = (x, y, shape) => {
    for(let i = 0; i < shape.length; i++){
        for(let j = 0; j < shape[i].length; j++){
            if(shape[i][j] > 0){  //只判断实心方块
                let newX = x + j*BLOCK_SIZE;
                let newY = y + i*BLOCK_SIZE;
                if(newX < 0 || newX >= WIDTH || newY < 0 || newY >= HEIGHT){
                    return true;
                }
            }
        }
    }
    return false;
}

3.2. 定义游戏操作类

游戏操作类包含游戏的初始化、开始、暂停、易位等操作。其中,开始操作用于启动方块下落的定时器;易位则用于在方块下落后快速移动方块到底部。具体实现如下:

游戏操作类定义

/**
 * 游戏操作类
 */
class Game {
    constructor(ctx){
        this.ctx = ctx;
        this.block = null;  //当前活动方块
        this.fallTime = 500;  //方块下落时间间隔
        this.canMove = true;  //是否可以移动方块
        this.canRotate = true;  //是否可以旋转方块
        this.status = "init";  //游戏状态
        this.score = 0;  //游戏分数
        this.map = this.createMap();  //游戏地图
    }

    //初始化游戏
    init(){
        this.ctx.clearRect(0, 0, WIDTH, HEIGHT);
        this.fallTime = 500;
        this.score = 0;
        this.map = this.createMap();
        this.block = new Block(this.getRandomType(), 4*BLOCK_SIZE, 0, BLOCK_SIZE);
        this.drawMap();
        this.drawBlock();
    }

    //开始游戏
    start(){
        if(this.status == "init" || this.status == "end"){
            this.status = "running";
            this.timer = setInterval(()=>{
                this.block.down();
                if(this.isDead()){  //游戏结束
                    this.status = "end";
                    clearInterval(this.timer);
                    alert('game over! score=' + this.score);
                    return;
                }
                if(this.isCollision()){  //方块落地
                    this.mapBlock();  
                    this.eliminate();
                    this.block = new Block(this.getRandomType(), 4*BLOCK_SIZE, 0, BLOCK_SIZE); //新建下一个活动方块
                }
                this.drawMap();
                this.drawBlock();
            }, this.fallTime);
        }else if(this.status == "pause"){
            this.status = "running";
            this.timer = setInterval(()=>{
                this.block.down();
                if(this.isDead()){
                    this.status = "end";
                    clearInterval(this.timer);
                    alert('game over! score=' + this.score);
                    return;
                }
                if(this.isCollision()){
                    this.mapBlock();
                    this.eliminate();
                    this.block = new Block(this.getRandomType(), 4*BLOCK_SIZE, 0, BLOCK_SIZE);
                }
                this.drawMap();
                this.drawBlock();
            }, this.fallTime);
        }
    }

    //暂停游戏
    pause(){
        if(this.status == "running"){
            clearInterval(this.timer);
            this.status = "pause";
        }
    }

    //方块快速移动到底部
    moveDown(){
        if(this.canMove){  //控制移动频率
            this.canMove = false;
            clearInterval(this.timer);
            while(!this.isCollision()){
                this.block.down();
            }
            this.mapBlock();
            this.eliminate();
            this.block = new Block(this.getRandomType(), 4*BLOCK_SIZE, 0, BLOCK_SIZE);
            this.drawMap();
            this.drawBlock();
            this.canMove = true;
        }
    }

    //方块向左移动
    moveLeft(){
        if(this.canMove){
            this.canMove = false;
            this.block.left();
            if(this.isExceed() || this.isCollision()){
                this.block.right();  //操作无效时复原
            }else{
                this.drawMap();
                this.drawBlock(); 
            }
            this.canMove = true;
        }
    }

    //方块向右移动
    moveRight(){
        if(this.canMove){
            this.canMove = false;
            this.block.right();
            if(this.isExceed() || this.isCollision()){
                this.block.left();
            }else{
                this.drawMap();
                this.drawBlock(); 
            }
            this.canMove = true;
        }
    }

    //方块顺时针旋转
    rotate(){
        if(this.canRotate){
            this.canRotate = false;
            this.block.rotate();
            if(this.isExceed() || this.isCollision()){
                this.block.rotate();
                this.block.rotate();
                this.block.rotate();
            }else{
                this.drawMap();
                this.drawBlock(); 
            }
            this.canRotate = true;
        }
    }

    // 画方块
    drawBlock() {
        this.block.draw(this.ctx);
    }

    // 画地图
    drawMap(){
        this.ctx.clearRect(0,0,WIDTH,HEIGHT);
        for(let i=0; i<this.map.length; i++){
            for(let j=0; j<this.map[i].length; j++){
                if(this.map[i][j] > 0){
                    this.ctx.fillStyle = BLOCK_COLOR[this.map[i][j]];
                    this.ctx.fillRect(j*BLOCK_SIZE, i*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
                }
            }
        }
    }

    //落地,固定方块
    mapBlock(){
        for(let i=0; i<this.block.shape.length; i++){
            for(let j=0; j<this.block.shape[i].length; j++){
                if(this.block.shape[i][j] > 0){
                    let x = this.block.x/BLOCK_SIZE + j;
                    let y = this.block.y/BLOCK_SIZE + i;
                    this.map[y][x] = this.block.type;
                }
            }
        }
    }

    //消除一行方块
    eliminate(){
        for(let i=this.map.length-1; i>=0; i--){
            let flag = true;
            for(let j=0; j<this.map[i].length; j++){
                if(this.map[i][j] == 0){
                    flag = false;
                    break;
                }
            }
            if(flag){
                for(let k=i-1; k>=0; k--){
                    for(let j=0; j<this.map[k].length; j++){
                        this.map[k+1][j] = this.map[k][j];
                    }
                }
                this.score += 10;
                this.fallTime -= 10;  //方块下落速度逐渐变快
                this.drawMap();
            }
        }
    }

    //是否死亡
    isDead(){
        for(let i=0; i<this.block.shape.length; i++){
            for(let j=0; j<this.block.shape[i].length; j++){
                if(this.block.shape[i][j] > 0){
                    let x = Math.floor(this.block.x/BLOCK_SIZE) + j;
                    let y = Math.floor(this.block.y/BLOCK_SIZE) + i;
                    if(y < 0 || this.map[y][x] > 0){
                        return true;
                    }
                }
            }
        }
        return false;
    }

    //是否发生碰撞
    isCollision(){
        if(this.block.y + this.block.shape.length*this.block.size > HEIGHT){  //是否触底
            return true;
        }
        for(let i=0; i<this.block.shape.length; i++){
            for(let j=0; j<this.block.shape[i].length; j++){
                if(this.block.shape[i][j] > 0){
                    let x = Math.floor(this.block.x/BLOCK_SIZE) + j;
                    let y = Math.floor(this.block.y/BLOCK_SIZE) + i;
                    if(x < 0 || x >= this.map[0].length || y >= this.map.length || this.map[y][x] > 0){
                        return true;
                    }
                }
            }
        }
        return false;
    }

    //是否超出边界
    isExceed(){
        return isExceed(this.block.x, this.block.y, this.block.shape);
    }

    //生成随机方块
    getRandomType(){
        return Math.floor(Math.random()*BLOCK_TYPE.length);
    }

    //生成游戏地图
    createMap(){
        const map = [];
        for(let i=0; i<ROW; i++){
            map[i] = [];
            for(let j=0; j<COLUMN; j++){
                map[i][j] = 0;
            }
        }
        return map;
    }
}

3.3. 完成键盘事件响应

通过添加键盘事件监听器,实现对方块下落、移动、旋转等操作。具体实现如下:

键盘事件响应

/**
 * 添加键盘事件监听器
 */
window.addEventListener('keydown', function(e) {
    switch (e.key) {
        case "ArrowLeft"://方块左移
            game.moveLeft();
            break;
        case "ArrowRight"://方块右移
            game.moveRight();
            break;
        case "ArrowDown"://方块快速下落
            game.moveDown();
            break;
        case "ArrowUp"://方块旋转
            game.rotate();
            break;
    }
});

/**
 * 点击“开始/重新开始”按钮
 */
btnStart.addEventListener("click", function(){
    game.init();
    game.start();
})

/**
 * 点击“暂停”按钮
 */
btnPause.addEventListener("click", function(){
    game.pause();
})

/**
 * 点击“易位”按钮
 */
btnMove.addEventListener("click", function(){
    game.moveDown();
})

4.示例运行和演示

4.1. 示例1

本案例中实现的俄罗斯方块游戏在线运行地址如下:

https://htmlpreview.github.io/?https://github.com/aitiger/russian-block/blob/master/index.html

在页面中,点击“开始/重新开始”按钮,即可开始游戏。

4.2. 示例2

也可以在本地计算机上运行该游戏。具体操作如下:

  1. 下载源代码:
git clone https://github.com/aitiger/russian-block.git

​2. 进入项目目录:

cd russian-block

​3. 直接用浏览器打开index.html文件即可开始游戏。

5.总结

通过上述步骤,即可实现一个简单的俄罗斯方块游戏。在此基础上,可以进一步优化游戏界面和交互,如添加游戏难度、音效等。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:js html5 css俄罗斯方块游戏再现 - Python技术站

(0)
上一篇 2023年6月9日
下一篇 2023年6月9日

相关文章

  • 学习js在线html(富文本,所见即所得)编辑器

    学习使用JS在线HTML编辑器,主要涉及以下几个步骤: 第一步:准备项目 创建项目文件夹,命名为“html_editor”,在该文件夹下新建index.html、main.js、style.css三个文件。 在index.html文件中添加必要的HTML结构,主要包括一个textarea元素和一个用于显示编辑结果的div元素。 在main.js文件中编写Ja…

    css 2023年6月10日
    00
  • Bootstrap CSS布局之代码

    我们来详细讲解一下Bootstrap CSS布局之代码的完整攻略。 什么是Bootstrap Bootstrap是一个流行的CSS框架,旨在使响应式设计和前端开发变得更加容易。它是由Twitter开发的,现在已经成为了一个由全球社区维护的开源项目。 Bootstrap主要提供了很多巧妙的CSS布局、JavaScript插件、表单控件、图标、字体等常用界面组件…

    css 2023年6月11日
    00
  • android计算pad或手机的分辨率/像素/密度/屏幕尺寸/DPI值的方法

    让我来详细讲解一下“android计算pad或手机的分辨率/像素/密度/屏幕尺寸/DPI值的方法”的完整攻略。 1. 分辨率和像素 在Android设备上,分辨率和像素是经常被用到的术语。分辨率可以理解为屏幕分辨率,是指屏幕上横向和纵向的像素点数。例如,720×1280像素的屏幕分辨率意味着宽度为720像素,高度为1280像素。 那么像素是什么呢? 像素是显…

    css 2023年6月9日
    00
  • 深入解析CSS的Sass框架中混合宏的使用

    深入解析CSS的Sass框架中混合宏的使用 什么是Sass框架? Sass(Syntactically Awesome StyleShets)是一种CSS的预处理语言,它增加了许多CSS没有的特性,例如嵌套选择器,变量,Mixin宏等。Sass可以帮助开发者简化CSS的编码和维护,并且提高CSS代码的重用性。 Sass中Mixin宏的介绍 Mixin宏是Sa…

    css 2023年6月10日
    00
  • JS实现div内部的文字或图片自动循环滚动代码

    实现div内部的文字或图片自动循环滚动,可以使用JavaScript来实现。下面是实现的完整攻略: 1. 准备工作 首先需要一个包含滚动内容的div容器,例如: <div id="scroll-container"> <ul> <li><img src="img1.jpg" a…

    css 2023年6月10日
    00
  • html直接引用vue和element-ui的方法

    当我们想要在HTML页面中使用Vue.js和Element UI时,可以通过以下两种方法引入它们: 一、通过CDN引入 我们可以通过使用CDN引入Vue.js和Element UI,如下所示: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"&…

    css 2023年6月9日
    00
  • 使用字符代替圆角尖角研究心得分享

    使用字符代替圆角尖角研究心得分享 在 web 开发中,经常需要使用到各种图形元素,其中包括线条、箭头、圆角、尖角等。本文将分享使用字符代替圆角和尖角的实用技巧。 圆角处理技巧 当需要实现圆角时,可以使用 CSS 的 border-radius 属性,但在某些情况下,比如需要实现自定义的圆角形状或者背景色与边框色不同时,使用字符代替圆角也是一个不错的方案。 示…

    css 2023年6月10日
    00
  • 本文的主角 vertical-align使用介绍

    vertical-align 是 CSS 中的一个属性,用于控制元素的垂直对齐方式。在 Web 开发中,垂直对齐是一个常见的问题,vertical-align 属性可以帮助我们解决这个问题。下面是一个完整攻略,包含了 vertical-align 属性的使用介绍和两个示例说明。 vertical-align 属性的使用介绍 vertical-align 属性…

    css 2023年5月18日
    00
合作推广
合作推广
分享本页
返回顶部