Avant de commencer le texte, je veux dire quelque chose. La soi-disant autosuffisance dans le titre fait référence au développement de ce jeu sans référence à aucune idée de conception. Vous pourriez être confus si vous faites référence à d'excellentes idées. , n'obtiendriez-vous pas deux fois le résultat avec la moitié de l'effort ? Bien sûr, il y a des avantages à faire référence et non. Je parle seulement des avantages de ne pas faire référence lorsque je termine enfin un travail après des efforts minutieux et des dizaines de bugs. modifications, je peux fièrement dire aux autres : « Regardez, j'ai développé ce jeu ! » Bien sûr, la créativité ne m'appartient pas, mais cela n'affecte pas ma « vanité ». Ne serait-il pas intéressant de m'approprier un jeu classique ? pour le comprendre et l'intégrer dans le jeu ? , quand je regarde en arrière et que je regarde les idées des autres, je me demande parfois : « Pourquoi n'y ai-je pas pensé avant ? », « Alors ce problème peut être résolu comme ça », « Cette idée de design est bien meilleure que la mienne ! », etc. sont bien mieux que de simplement regarder les idées des autres et de bloquer votre propre réflexion dès le début, n'est-ce pas ?
D'accord, le texte commence~
Si vous voulez d'abord voir l'effet, sautez et essayez !
Tetris, l'interface principale du jeu doit être composée de blocs un par un, comme indiqué ci-dessous. Bien entendu, ces grilles ne sont pas visibles dans le produit fini. Ceci est juste pour aider à comprendre le principal. la taille de l'interface est de 400 × 500, définissez la taille de chaque brique (grille) sur 20 × 20, il y a alors 20 briques dans chaque rangée et 25 briques dans chaque colonne. Code associé :
brickWidth = 20, //砖块大小 width = 400, height = 500; //画布宽高,20X25
En ce qui concerne la grille de l'interface principale, nous devons mentionner une variable< très importante. 🎜> Oui, c'est BOARD, un tableau bidimensionnel Visuellement parlant, sa taille est de 20×26. La valeur stockée est 0 ou 1. 0 signifie qu'il n'y a pas de brique à cette position, et 1. signifie qu'il y a une brique à cet endroit. Les blocs jouent un rôle important dans certaines des décisions suivantes. Les étudiants qui font attention au jeu peuvent découvrir pourquoi il est 20×26 au lieu de 20×25 correspondant à la grille de l'interface principale. réglez-le également sur 20× au début. 25. Plus tard, j'ai remarqué que si j'ajoute une ligne et que la valeur de cette ligne est 1, on peut facilement juger si la brique a touché le bas de l'interface principale. Code associé :
// 初始化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; } } }
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() { …… } //变换砖头朝向
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] ] //次行为翻转的情况];
Retournement horizontal de l'image ), la deuxième dimension est la direction (haut, gauche, bas, droite), et la troisième dimension est la répartition des quatre briques de la forme I. définir chaque nouvelle forme objet dans Dans un tableau 4×4, par exemple, This.embattle[0][0] de Tu est [0,4,5,8], et le nombre est l'emplacement de la brique, comme indiqué ci-dessous :
Ainsi, pour déterminer la position et l'apparence d'une forme, isOverturn est nécessaire pour déterminer si elle est retournée, une direction est nécessaire pour déterminer sa direction , et originX et originY sont nécessaires pour déterminer la position du "tableau". Ensuite, les quatre méthodes de prototype de Brick sont expliquées respectivement.Brick.prototype.draw
ctx.fillStyle = 'rgb('+Math.floor(Math.random()*256)+','+Math.floor(Math.random()*256)+', '+Math.floor(Math.random()*256)+')'; 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方法的结果是什么了吗?事实上,它不应该成功改变方向的,对吧?还有其他一些卡位的情况。
Les 4 méthodes de prototype de Brick sont présentées ici. Maintenant, si je souhaite afficher la forme suivante dans l'interface d'information à droite, le moyen le plus direct est d'instancier un objet via le constructeur de la forme. Pour l'empêcher d'appeler automatiquement autoMove, j'ai ajouté le constructeur isModel. pour déterminer s'il est utilisé pour les invites.
Il existe également des événements clés, la fonction NextBrick et supprimerObj. C'est facile à comprendre. Fonction NextBrick.
De plus, je ne suis pas sûr que deleteObj ait réellement réussi à amener le GC à recycler l'objet.
De plus, je voulais à l'origine ajouter une fonction de niveau, car la vitesse (variable de vitesse) peut être réglée librement, j'ai donc mis cette fonction de côté.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!