>  기사  >  웹 프론트엔드  >  HTML5 Tetris(그림 및 텍스트) 직접 생성

HTML5 Tetris(그림 및 텍스트) 직접 생성

黄舟
黄舟원래의
2017-04-01 11:24:014491검색

본문을 시작하기 전에 먼저 말씀드리고 싶은 것이 있습니다. 소위 자급자족이라는 말은 어떠한 디자인 아이디어도 참고하지 않고 이 게임을 개발하는 것을 의미합니다. , 반만 노력하면 두 배의 결과를 얻을 수 있지 않을까? 물론, 참고하는 것과 그렇지 않은 것 모두 장단점이 있지만, 고생 끝에 작품을 완성했을 때와 수십 번의 버그에 대해서만 이야기하겠습니다. "보세요, 제가 이 게임을 개발했습니다! "물론 창의력은 내 것이 아니지만 이것이 내 "허영심"에 영향을 미치지는 않습니다. 내 자신의 클래식 게임을 제공하는 것이 흥미롭지 않을까요? 이해하고 게임에 접목시키는 걸까, 다른 사람의 생각을 뒤돌아 보면 '나는 왜 예전에는 이런 생각을 못했지?', '그럼 이 문제는 이렇게 풀 수 있지 않을까' 하는 생각이 들 때가 있습니다. “이 디자인 아이디어는 내 것보다 훨씬 낫습니다!” 등은 다른 사람의 아이디어를보고 처음부터 자신의 생각을 차단하는 것보다 훨씬 낫습니다.

자, 본문 시작합니다~

효과를 먼저 보고 싶다면 점프해 보세요!

테트리스의 메인 게임 인터페이스는 아래와 같이 하나씩 블록으로 구성되어야 합니다. 물론 이러한 그리드는 완성품에서는 볼 수 없습니다. 인터페이스 크기는 400×500이고 각 브릭(그리드)의 크기를 20×20으로 설정하면 각 행에 20개의 브릭이 있고 각 열에 25개의 브릭이 있습니다. 관련 코드:

brickWidth = 20,    //砖块大小
width = 400, height = 500;  //画布宽高,20X25

메인 인터페이스의 그리드와 관련하여 매우 중요한 변수를 언급해야 합니다. . 2차원 배열인 BOARD입니다. 시각적으로 크기는 20×26입니다. 저장된 값은 0 또는 1입니다. 0은 이 위치에 벽돌이 없음을 의미합니다. 이 위치의 벽돌은 다음 판단에서 중요한 역할을 합니다. 신중한 게이머는 그것이 기본 인터페이스 그리드에 해당하는 20×25 대신 20×26인 이유를 알 수 있습니다. . 그리고 나중에 행을 추가하고 이 행의 값이 모두 1이면 벽돌이 기본 인터페이스 하단에 닿았는지 쉽게 판단할 수 있다는 사실을 알아냈습니다. 관련 코드 :

// 初始化BOARD,注意纵向有26个,最后一排用来判断是否触底
for(i=0;i<20;i++){
    BOARD[i] = [];
    for(j=0;j<26;j++) {
        if(j==25) {
            BOARD[i][j] = 1
        } else {
            BOARD[i][j] = 0;
        }
    }
}


다음으로 4개의 벽돌로 이루어진 '모양'을 보면 5개가 있습니다 설명을 위해 이름을 Tian(들판), Chu(괭이), Tu(팽대기), Thunder(번개), Line(가로줄 하나), 하하, 재미있는 이름이었는데 못 찾았네요. 영어 이름.

먼저 브릭 클래스 Brick을 정의합니다:

function Brick() { }

그 아래에는 여러 프로토타입 변수와 메서드가 있습니다:

Brick.prototype.embattle = null;    //砖块的布局(需重载)
Brick.prototype.isOverturn = 0; //是否翻转
Brick.prototype.originX = 9;    //砖头的绘制起点X
Brick.prototype.originY = -3;    //砖头的绘制起点Y
Brick.prototype.direction = 0;  //砖头朝向
Brick.prototype.autoMoveTimer = null;   //自动移动计时器
Brick.prototype.draw = function() { …… }    //画砖块的方法
Brick.prototype.move = function(moveX, moveY) { …… }    //移动的方法
Brick.prototype.autoMove = function() { …… }    //自动移动的方法
Brick.prototype.change = function() { …… }    //变换砖头朝向

Brick의 하위 클래스는 Tian, ​​​​Chu, Tu입니다. , Thunder , 5번째 줄, 각 하위 클래스는 Brick의 embattle 변수를 오버로드합니다. embattle은 영어로 embattle을 의미합니다. 우선 학생들은 내 생각을 이해해야 합니다. Tu의 embattle을 예로 들어 보겠습니다. 코드는 다음과 같습니다.

this.embattle = [
    [ [0,4,5,8], [1,4,5,6], [1,4,5,9], [0,1,2,5] ],  //布局表为4X4表格,数字为砖头位置
    [ [0,4,5,8], [1,4,5,6], [1,4,5,9], [0,1,2,5] ]   //次行为翻转的情况];

embattle은 3차원 배열입니다(시각적으로 말하면). , 그림의 가로 뒤집기)와 같습니다. 2차원은 방향(위, 왼쪽, 아래쪽, 오른쪽)이고, 3차원은 제가 정의한 모양의 벽돌 4개 분포입니다. 4의 각각의 새로운 모양 객체 ×4 배열에서 예를 들어 Tu의 this.embattle[0][0]은 [0,4,5,8]이고 숫자는

따라서 모양의 위치와 모양을 결정하려면 isOverturn이 뒤집혔는지 확인하는 데 필요하고, 방향을 결정하려면 방향이 필요합니다. , "배열"의 위치를 ​​결정하려면 OriginX 및 OriginY가 필요합니다.

다음으로 브릭의 4가지 프로토타입 방식을 각각 설명합니다.

Brick.prototype.draw

ctx.fillStyle = &#39;rgb(&#39;+Math.floor(Math.random()*256)+&#39;,&#39;+Math.floor(Math.random()*256)+&#39;,
&#39;+Math.floor(Math.random()*256)+&#39;)&#39;;
for(i=0;i<4;i++) {
    tmp = this.embattle[this.isOverturn][this.direction][i];
    ctx.fillRect((this.originX+tmp%4)*brickWidth, (this.originY+Math.floor(tmp/4))*brickWidth, brickWidth, brickWidth);
    ctx.strokeRect((this.originX+tmp%4)*brickWidth+1, (this.originY+Math.floor(tmp/4))*brickWidth+1, brickWidth-2, brickWidth-2);   //注意+1和减2
}

有上面说的确定形状的位置和样子的方法,之后就是纯粹canvas画图,4个砖块一个一个地画,不要看代码很长其实就是那么一点点,originX、originY和砖块在阵中的位置就可以确定画砖块的起点了。注意到代码的注释了没有,画边框的时候,它是从起点向外面画的,就像我把一个塑料袋套在另一个塑料袋的外面,为了以后的清除的方便且不影响其他的砖块,把边框画进fillRect的领土,就像我现在把这个塑料袋不套在外面而是放进这另一个塑料袋里面一样,就这个意思。

Brick.prototype.move

这是最长的一个了,移动的时候,moveX和moveY表示横纵的增量,没有同时非0的情况(这是人为的设定,要么横向移动要么纵向移动),当然要判断即将移动到的位置是否违规

横向:

如果阵贴靠主界面左侧则不能向左移即moveX不能为-1

(this.originX==0 && moveX==-1)

判断右边时比较麻烦,因为不能直接用阵来判断是否贴靠右侧(看前面的图就知道阵的右边和下边可能没有砖块的),这时要一个个地判断4个砖块是否有至少有一个在最右,这时不能向右移动

|| (this.originX+tmp[0]%4==19 && moveX==1)
|| (this.originX+tmp[1]%4==19 && moveX==1)
|| (this.originX+tmp[2]%4==19 && moveX==1)
|| (this.originX+tmp[3]%4==19 && moveX==1)

最后还要判断即将到达的位置是否已经有砖块了。

|| (BOARD[this.originX+tmp[0]%4+moveX][this.originY+Math.floor(tmp[0]/4)]==1)
|| (BOARD[this.originX+tmp[1]%4+moveX][this.originY+Math.floor(tmp[1]/4)]==1)
|| (BOARD[this.originX+tmp[2]%4+moveX][this.originY+Math.floor(tmp[2]/4)]==1)
|| (BOARD[this.originX+tmp[3]%4+moveX][this.originY+Math.floor(tmp[3]/4)]==1)

纵向:

即将到达的位置是否已经有砖块了,注意到下面的代码的&& moveX==0,原来是没有的,后来发现每次砖块怎么刚刚靠上下面堆着的砖块就不能再移动了,原来横向移动的时候也进行了这个判断,即刚刚靠上下面的砖块,如果这时想左右移动,但下方有砖块,但是问题来了,下面有没有砖块跟我左右移动有什么关系呢?是吧。

if((as==1 || bs==1 || cs==1 || ds==1) && moveX==0) { …… }

纵向终止判断里面主要做了几件事:清除autoMoveTimer,设置BOARD在该形状当前位置的值为1,有可以消除的整行就消除,加分改分,判断胜利/失败,删除当前对象,召唤下一个形状。

横纵都没违规时:

这时,把该形状前一个位置的砖块清除,更新originX和originY,再画出来。

for(i=0;i<4;i++) {
    tmp = this.embattle[this.isOverturn][this.direction][i];
    ctx.clearRect((this.originX+tmp%4)*brickWidth, (this.originY+Math.floor(tmp/4))*brickWidth, brickWidth, brickWidth);
}
this.originX += moveX;
this.originY += moveY;
this.draw();

Brick.prototype.autoMove

只做一件事,设置计时器,定时向下移动。

var status, self = this;this.autoMoveTimer = setInterval(function() {
    status = self.move(0,1);
},speed);

Brick.prototype.change

改变形状的朝向,很好办啊,不是有embattle数组了吗?当然没有那么简单,不只是换个数组这么简单。要考虑改变方向之后占用的位置是否已经有砖块了,如果形状是贴着主界面右边界就更糟糕了,比如原来是竖着的Line,改变其方向变为横,占用阵的0、1、2、3,如果Line贴着右边界,originX为19,变为横向,占用阵的0、1、2、3,后面三个砖块已经溢出了主界面。

解决方案是:如果有越界的砖块就把阵往左挪一挪,直到不再越界。

while(ox+tmp[0]%4 > 19 || ox+tmp[1]%4 > 19 || ox+tmp[2]%4 > 19 || ox+tmp[3]%4 > 19) {
    ox -= 1;
}

最后,如果都没事,就可以清除原位置,画出改变方向之后的形状了。

并不是太完美,因为有些卡位的情况没考虑进来,什么是卡位,看下图,你知道Line实例调用change方法的结果是什么了吗?事实上,它不应该成功改变方向的,对吧?还有其他一些卡位的情况。

브릭의 4가지 프로토타입 방식을 소개합니다. 이제 오른쪽 정보 인터페이스에 다음 도형을 표시하려는 경우 가장 직접적인 방법은 도형의 생성자 를 통해 개체를 인스턴스화하는 것입니다. 자동으로 autoMove를 호출하는 것을 방지하기 위해 생성자 isModel을 추가했습니다. 프롬프트에 사용되는지 여부를 결정합니다.

핵심 이벤트 모니터링, NextBrick 기능, 삭제Obj도 있으니 직접 보시면 이해하기 쉽습니다. NextBrick 기능.

또한 deleteObj가 실제로 GC에서 객체 재활용을 성공적으로 수행했는지 확실하지 않습니다.

그리고 원래는 레벨 기능을 추가하고 싶었는데 속도(속도변수)를 자유롭게 설정할 수 있어서 이 기능은 따로 남겨두었습니다.

위 내용은 HTML5 Tetris(그림 및 텍스트) 직접 생성의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.