这个简单的教程介绍了如何使用Three.js创建俄罗斯方块游戏。 每天分享最新的软件开发、Devops、敏捷软件、测试和项目管理最新、最热门的文章,每天花三分钟学习什么都不为过。 称赞、评论、关注。 你的支持是我最大的动力。 下面的嘀嘀打车介绍了自动化测试和谷歌云相关的视频课程,请看。
想想怎么在俄罗斯方块上玩吧。 方块运动时,我们可以自由地转换和旋转它。 创建块的立方体必须清晰相连,并且在代码中的表示也必须直观。 另一方面,如果尝试完成一个片段(在2D中为1行),则立方体被删除,此时它们的原点块不重要。 实际上,这并不重要。 ——一个块中的一些箱子可能会被删除,其他的箱子可能不会被删除。
追踪一个箱子的起源需要不断分割和合并几何图形,相信我,那将会是疯狂的混乱。 在原始二维俄罗斯块中,方形颜色可能是原始块中的指示器。 但是,3D需要一种快速的方法来显示z轴,颜色非常适合它。
在我们的游戏中,立方体在动态时连接,在非动态时连接。
要添加静态块,请从移动块接触地板(或其他块)的那一刻开始。 移动块(具有多个立方体的合并几何体)将转换为不再移动的静态孤立立方体。 将这些立方体保存为3D阵列很有用。
Tetris.staticBlocks=[]; Tetris.zColors=[0x6666ff,0x66ffff,0xcc68EE,0x 666633,0x 66 ff 66,0x 9966 ff,0x 00 ff 66,0x 003399,0x 3300000 z ) ) if ( Tetris.static blocks [ x ]===undefined ) Tetris.static blocks [ if ( Tetris.static blocks [ x ] [ y ]===var mesh=three.scene utils.createmultimaterialobject ( new three.cube geometry ( tetris.blocksize,Tetris.block size, block size [ new three.meshbasicmaterial ] { color:0x 000000,shading: THREE.FlatShading,wireframe: true, transparent:true } mesh.position.x=( x-Tetris.bounding boxconfig.splitx/2 ) Tetris.block size Tetris.block sizze mesh.position.y=( y-Tetris.boundingboxconfig.splity/2 ) Tetris.block size Tetris.block size/2; mesh.position.z=( z-Tetris.boundingboxconfig.splitz/2 ) Tetris.block size Tetris.block size/2; mesh.overdraw=true; Tetris.Scene.add(mesh; Tetris.static blocks [ x ] [ y ] [ z ]=mesh; (; 这里有很多需要说明的事情。
颜色和材料Tetris.zColors包含显示立方体在z轴上的位置的颜色列表。 我想要漂亮的立方体,应该有颜色和轮廓的框。 我用在Three.js教程中不太流行的东西——multiMaterials。 Three.js SceneUtils包含接受几何图形和材质阵列的函数(请注意括号[] )。 当您查看Three.js的源代码时:
createmultimaterialobject:function ( geometry,materials ) { var i,il=materials.length,group=new THREE.Object3D for(I=0; i il; I ) var object=new three.mesh ( geometry,materials[ i ] ); group.add(object; } return group; },这是一个非常简单的hack,可以为每个材料创建网格。 使用纯WebGL有更好的方法来获得同样的结果。 fe两次调用draw,一次使用gl.LINES,第二次使用gl.something。 (时间 -此函数的典型用途不是不同类型的绘制,例如同时合并纹理和材质。
在3D空间中的位置现在为什么这个位置看起来是这样?
mesh.position.x=( x-Tetris.boundingboxconfig.splitx/2 ) Tetris.block size Tetris.block size/2; 我们将( 0,0,0 )点放置在init上板块的中心。 这不是个好位置。 这意味着一些立方体具有负位置,而另一些立方体具有正位置。 在我们的示例中,建议指定对象的角。 还希望将框的位置视为1到6或至少0到5的离散值。 Three.js (以及WebGL、OpenGL和所有其他内容)使用自己的单位。 这些单位与米或像素相同。 还记得为配置输入值吗
Tetris.block size=boundingboxconfig.width/boundingboxconfig.splitx; 那个负责转换。 总结:
//transform0- 5to-3-2 ( x-Tetris.boundingboxconfig.splitx/2 )/scaletothree.jsunits * Tetris.block size/( ) tetris.blocksize ) 65 nota corner-wehavetoshiftpositiontetris.block size/2卓越测试我们的游戏仍然非常静态,但可以打开控制台运行。
var i=0,j=0,k=0,interval=setinterval(function ) ) if ) I==6) ) I=0; j; ( if ) j==6) j=0; k; ( if ) k==6) clearinterval )间隔; 返回; }Tetris.addstaticblock(I,j,k ); I; 应该用立方体填充(,30 )板子的动画。
保持分数保持分数的小实用函数:
Tetris.currentPoints=0; tetris.addpoints=function(n ) { Tetris.currentPoints =n; Tetris.points DOM.innerhtml=Tetris.current points; cufon.replace(&; #039; #points&; #039; ); }首先,创建一个新文件以保存块对象并将其包含在index.html中。 该文件如下
window.Tetris=window.Tetris|| { }; //equivalent to if (! window.tetris(window.tetris={} ); 这样,即使文件分析顺序受到某种干扰,也不会复盖现有对象或使用未定义的变量。 在这种情况下,var Tetris={};
我们主文件的“”声明。 在继续之前需要效用函数。
Tetris.Utils={}; Tetris.utils.clone vector=function ( v ) return ( y: v.y,y:v.y,z: v.z} ) ); (; 要理解我们为什么需要它,我们必须谈谈JS的变量。 使用数字时,总是按值传递。 这意味着写作:
var a=5; var b=a; 把数字5放在b里,但无论如何都与a无关。 但是,在使用对象时:
vara=(x:5 ); var b=a; b是对对象的引用。 使用bx=6; 写入a引用的同一对象。 所以需要制作向量副本的方法。 简单的v1=v2意味着我们的记忆中只有一个向量。 但是,直接访问向量的数字部分进行克隆时,有两个向量,它们的操作是独立的。 最后的准备是定义形状。
Tetris.Block={}; Tetris.Block.shapes=[ [ {x: 0,y: 0,z:0 ],{x: 1,y: 1,z:0 ],{x: 1,z: 0} z: 0}、{x: 1,y: 0,z: 0}、{x: 0,z: 0}、{x:1} {x: 0,y: 2,z: 0}、{x: 1,y: 1,z: 0} 这非常重要,在下一节中说明。
该形状生成三个值:基本形状、位置和旋转。 在这方面,我们应该事先考虑我们想如何检测冲突。 根据我的经验,我知道游戏中的碰撞检测总是或多或少是假的。 所有与性能相关的——几何形状都被简化,特定情况下的碰撞首先被排除,部分碰撞完全不被考虑,碰撞响应几乎总是不准确的。 没关系——如果那个看起来很自然的话,没人会注意到吧。 我们大大节省了宝贵的CPU周期。 那么,俄罗斯方块最简单的冲突检测是什么? 所有形状都是轴对齐的立方体,中心位于指定的一组点上。 我有99%的信心在板上的每个位置留下一组值[FREE,MOVING,STATIC]是处理它的最佳方法。 这样,如果我们想移动形状,而且它所需的空间已经占用了——,我们就会发生冲突。 复杂性: o (形状中立方体的数量)=O ) )1)。 嘘! 现在,我知道轮换非常复杂,应该尽量避免。 这就是我们保持方块基本形状为旋转形式的原因。 这样可以只应用位置并快速检查是否存在碰撞。 我们的情况实际上并不重要,但在更复杂的游戏中会这样。 没有可以用懒惰的方法编程的小游戏。 位置和旋转——都在Three.js中使用。 但是,问题是Three.js和我们的开发板使用了不同的单元。 为了简化代码,单独保存位置。 旋转在任何地方都是一样的,所以使用内置的。 首先,随机取形,制作复印件。 这是需要cloneVector函数的理由。
Tetris.Block.position={}; Tetris.Block.generate=function ( ) vargeometry,tmpGeometry; vartype=math.floor ( math.random ( * ( Tetris.block.shapes.length ) ); this.blockType=type; Tetris.Block.shape=[]; for(varI=0; I Tetris.block.shapes [ type ].length; I ( { Tetris.block.shape [ I ]=Tetris.utils.clone vector ( Tetris.block.shapes [ type ] ) ) ); }现在需要把所有立方体作为一个形式连接起来。 有Three.js函数。 需要几何图形和网格,并将它们合并在一起。 这里实际发生的是内部顶点数组的合并。 考虑合并几何的位置。 所以,第一个立方体必须为( 0,0,0 )。 网格有位置,但几何中没有。 始终被视为( 0,0,0 )。 虽然可以创建两个网格的连接函数,但是比保持形状更复杂,对吧?
geometry=new three.cube geometry ( Tetris.blockSize、Tetris.blockSize、Tetris.block size ); for(varI=1; i Tetris.Block.shape.length; I ) tmp geometry=new three.mesh ( new three.cube geometry ( Tetris.blockSize,Tetris.blockSize,Tetris.block size ) ) tmp geometry.position.x=Tetris.block size * Tetris.block.shape [ I ].x; tmp geometry.position.y=Tetris.block size * Tetris.block.shape [ I ].y; three.geometry utils.merge ( geometry,tmpGeometry; }可以通过组合几何体来使用教程前面的双材质技术。
Tetris.block.mesh=three.scene utils.createmultimaterialobject ( geometry,[ new three.meshbasicmaterial ( color:) ) 我们必须设定我们街区的初始位置和旋转。 x、y是棋盘的中心,z是任意数字。
//initialpositiontetris.block.position={ x:math.floor ( Tetris.boundingboxconfig.splitx/2 )-1, y:math.Tetris.block.mesh.position.x=( Tetris.block.position.x-Tetris.boundingboxconfig.splitx/2 ) 2 ) Tetris.Tetris.block.mesh.position.y=( Tetris.block.position.y-Tetris.boundingboxconfig.splity/2 ) tetris.) boundingboxconfig.splity ) Tetris.block.mesh.position.z=( Tetris.block.position.) 2 ) Tetris.boundingboxconfig.splitz/2 ) Tetris.block.mesh.rotation={ x:0,y: 0,z: 0}; Tetris.block.mesh.overdraw=true; Tetris.scene.add ( Tetris.block.mesh ); (; //end of Tetris.Block.generate ( )如果需要,可以从控制台调用Tetris.Block.generate ( )。
移动方形很简单。 对于旋转,使用Three.js内部。 需要将角度转换为弧度。
Tetris.block.rotate=function(x,y,z ) ) Tetris.block.mesh.rotation.x=x * math.pi/180; Tetris.block.mesh.rotation.y=y * math.pi/180; Tetris.block.mesh.rotation.z=z * math.pi/180; (; 地方也很简单。 Three.js需要考虑块大小的位置,但复制不需要。 我们的娱乐有简单的地板安全检查; 稍后将被删除。
Tetris.block.move=function(x,y,z ) Tetris.block.mesh.position.x=x * Tetris.block size; Tetris.Block.position.x =x; Tetris.block.mesh.position.y=y * Tetris.block size; Tetris.Block.position.y =y; Tetris.block.mesh.position.z=z * Tetris.block size; Tetris.Block.position.z =z; if(Tetris.block.position.z==0) Tetris.Block.hitBottom; (; 再次点击创建hitBottom有什么用? 你还记得吗? 块生命周期结束后,必须将其转换为静态立方体,然后从场景中删除以生成新的。
Tetris.block.hit bottom=function ( ( Tetris.block.petrify ); Tetris.scene.remove object ( Tetris.block.mesh ); Tetris.Block.generate (; (; generate (和removeObject )已经是Three.js用于删除未使用的网格的函数。 幸运的是,以前创建了静态立方体的函数,现在将在petrify ( ) )中使用。
Tetris.Block.petrify=function ( ) { var shape=Tetris.Block.shape; for(varI=0; i shape.length; I ( Tetris.addstaticblock ( Tetris.block.position.x shape [ I ].x,Tetris.block.position.y shape [ I ].y,} ) ; Tetris.Block.shape的简称-它:使用提高代码的亮度和性能,每次都在适当的时候使用该技术。 通过这个函数,可以知道保持旋转形状和分离位置是个好主意的理由。 多亏了这个,我们的代码变得容易阅读,通过碰撞检测变得更重要。
连接好点,现在我们有方块需要的所有功能,在需要的地方把它们挂钩吧。 由于必须首先生成块,因此将Tetris.start ( )更改为:
Tetris.start=function ( ) document.getelementbyid ) &; #039; menu&; #039; ).style.display=&; #039; none&; #039; Tetris.points DOM=document.getelementbyid ( & amp; #039; points ); Tetris.points DOM.style.display=& amp; #039; 块& amp; #039; Tetris.Block.generate (; //add this line Tetris.animate (; (; 因为每走一步都应该让积木往前走一步,所以在Tetris.animate ( )中采取行动,将其更改为:
while ( Tetris.cumulatedframetimetetris.gamesteptime ( Tetris.cumulatedframetime-=Tetris.gamesteptime; Tetris.block.move ( 0,0,-1); 老实说,我讨厌键盘事件。 密钥代码是无意义的,在keydown和keypress中是不同的。 没有轮询键盘状态的好方法,第二次按键事件比第二次重复10倍后等。 考虑到键盘操作较多的真正的游戏,大多数情况下都会构建所有像说唱一样的胡说八道。 试试KeyboardJS。 那太好了。 用vanilla JS展示总体思路。 为了调试它,我使用了console.log(keycode )。 找到正确的代码是很大的帮助。
window.addevent监听器( & amp; #039; keydown&; #039;function ) event ) varkey=event.which event.which:event.keycode; switch(key ) ) case38://up ) arrow ) Tetris.block.move ( 0,1,0 ); 黑; case40//down(arrow ) tetris.block.move(0,- 1,0 ); 黑; case37//left(arrow ) Tetris.block.move (-1,0,0 ); 黑; case39//right(arrow ) tetris.block.move ) 1、0、0 ); 黑; case 32//space Tetris.block.move ( 0,-1); 黑; case87://up(w ) Tetris.block.rotate ( 90,0,0 ); 黑; case83//down(s ) Tetris.block.rotate (-90,0,0 ); 黑; case65//left(a ) tetris.block.rotate ) 0、0和90 ); 黑; case68://right(d ) Tetris.block.rotate ( 0,0,-90 ); 黑; case81//(q ) Tetris.block.rotate ( 0,90,0 ); 黑; case69//(e ) Tetris.block.rotate(0,- 90,0 ); 黑; },false; 如果你现在要玩游戏,你应该可以移动盒子并旋转它。 没有碰撞检测,但如果碰撞地面,则会被去除,新的方块会出现在船上。 静态版本可能会以不同的方式旋转,因为不会将旋转应用于已保存的形状。
板对象从新类开始,存储三维空间信息。 “const”、“enum”的值几乎不需要。 这些实际上既不是const也不是enum。 因为JS里没有那样的东西。 但是,JS 1.8.5有一个新功能- freeze。 可以创建对象并保护其免受进一步修改。 所有可能运行WebGL的浏览器都广泛支持,并提供类似枚举的对象。
window.Tetris=window.Tetris|| { }; Tetris.Board={}; Tetris.Board.COLLISION={NONE:0,WALL:1,GROUND:2}; object.freeze ( Tetris.board.collision ); Tetris.Board.FIELD={EMPTY:0,ACTIVE:1,PETRIFIED:2}; object.freeze ( Tetris.board.field ); willusefieldenumtostorestateofourboardinfieldsarray.ongamestartweneedtoinitializeitasempty.Tetris.board.fields=[ ] x _x; x({Tetris.board.fields[x]=[] ); for(vary=0; y _y; y ( { Tetris.board.fields [ x ] [ y ]=[ ] ); for(varz=0; z _z; z ( { Tetris.board.fields [ x ] [ y ] [ z ]=Tetris.board.field.empty; } }; Tetris.Board.init ( )必须在游戏中的任何块出现之前调用。 从Tetris.init调用。 因为可以很容易地将板大小作为参数提供。
//addanywhereintetris.init Tetris.board.init ( boundingboxconfig.splitx,boundingBoxConfig.splitY,boundingboxcong
Tetris.Block.petrify=function ( ) { var shape=Tetris.Block.shape; for(varI=0; i shape.length; I ( Tetris.addstaticblock ( Tetris.block.position.x shape [ I ].x,Tetris.block.position.y shape [ I ].y,y ) ; 碰撞检测俄罗斯方块有两种主要的碰撞类型。 第一种是墙壁碰撞,当一个活动块在x/y轴上移动或旋转时,它会在一个级别上碰撞墙壁或另一个块。 第二种是地面碰撞,其中一个块在z轴上移动并碰撞地板或另一个块,在其生命周期结束时发生。 我们从墙壁的碰撞开始。 这很简单。 为了使代码更好,我重新使用了速记。
Tetris.board.test collision=function ( ground _ check ) { var x,y,z,I; //shorthandsvarfields=Tetris.board.fields; var posx=Tetris.block.position.x,posy=Tetris.Block.position.y,posz=Tetris.Block.position.z,shape Fe i shape.length; I ) )/4 wallsdetectionforeverypartoftheshapeif ( ) shape[I].xPOSX )0| ) shape[I].yposy )0| ) shape [ I ] }现在如何处理块与块的冲突? 我们已经在数组中保存了石化块,所以可以检查块是否与现有立方体相交。 你可能想知道为什么testCollision有ground_check作为参数。 这是一个简单的观察结果,它检测块和块的碰撞的方式与地面和墙壁的碰撞几乎相同。 唯一的不同在于,z轴上的移动会导致地面碰撞。
if(fields[shape].xposx ) [ shape [ I ].yposy ] [ shape [ I ].zpo SZ-1 ]==Tetris.board.field.petrififificaty 这意味着我们的移动区块下没有立方体,但它到达地面,无论如何都应该被石化。
if ( ) shape[I].zposz )=0) return Tetris.board.collision.ground; }; }; 碰撞反应没那么差,对吧? 现在,用我们掌握的信息做点什么吧。 我们从最简单的地方开始,检测丢失的游戏。 要执行此操作,请在创建新块后立即测试是否存在冲突。 如果撞到地上,就没有再玩的意义了。 计算块的位置,然后将其添加到Tetris.Block.generate :
Tetris.board.test collision ( true )==Tetris.board.collision.ground ) { Tetris.gameOver=true; Tetris.points DOM.innerhtml=& amp; #039; gameover&; #039; cufon.replace(&; #039; #points&; #039; ); 运动也很简单。 改变位置后,称为碰撞检测,将关于z轴运动的信息作为参数传递。 如果发生墙壁碰撞,就不能移动。 你应该恢复原状。 我们可以添加一些行并减去位置,但我很懒,喜欢再次调用move函数,但我会使用倒置的参数。 它永远不会与z轴移动一起使用,所以可以将零作为z传递。 如果形状碰到地面,我们已经有了应该调用的函数hitBottom ( )。 从游戏中删除活动形状,修改棋盘状态,然后创建新形状。
//addinsteadofgroundleveldetectionfrompart3var collision=Tetris.board.test collision (=0); if ( collision==Tetris.board.collision.wall ) tetris.block.move(-x,-y,0 ); //laziness ftw } if ( collision==Tetris.board.collision.ground ) { Tetris.Block.hitBottom ); }如果此时运行游戏,您会发现旋转的形状不是永久的。 如果撞到地面,就会恢复最初的旋转。 这是因为将旋转应用于Three.js网格,例如Tetris.Block.mesh.rotation,但不用于获取基于立方体形状表示的坐标。 为了解决这个问题,我们需要前面的快速数学课。
3D数学免责声明:数学可怕或没有时间时,实际上可以跳过这一部分。 虽然了解引擎内部发生了什么很重要,但稍后将为此使用Three.js函数。 考虑表示3D空间中位置的3要素向量。 要在欧氏空间中变换这样的向量,必须添加另一个向量。 可以表示如下。
\ [\begin { matrix } x\y\z\\ end { matrix }\begin { matrix }\deltax\\ deltay\deltay\deltay 围绕一个轴的旋转会影响三个坐标中的两个(如果你不相信,请检查它),而且方程不是很简单。 幸运的是,大多数计算机生成的图形都使用了Three.js、WebGL、OpenGL和GPU本身等方法。 如果你还记得高中,把向量乘以矩阵就可以得到另一个向量。 在此基础上进行了许多转变。 最简单的是中性转换。 使用单位矩阵。 它只给出一般概念,并用作其他转换的基础。
\ [\begin { matrix } x\y\z\w\\ end { matrix }\begin { matrix } 100\\ 000\\ 000矢量启用翻译
\ [\begin { matrix } x\y\z\w\\ end { matrix }\*\begin { matrix } 10\deltax\01 \解决数值错误,配额缩放也很简单:
\ [\begin { matrix } x\y\z\w\\ end { matrix }\begin { matrix } sx00\\ 0sy0\ x轴的情况
\ [\begin { matrix } 100\0 cos\alpha-sin\alpha0\0sin\alpha cos\alpha0\ 0001 end { matrix }
\ [\begin { matrix } cos\alpha0sin\alpha0\ 010\- sin\alpha0cos\alpha0\ 00001 end { matrix }
\ [\begin { matrix } cos\alpha-sin\alpha0\ sin\alpha cos\alpha0\ 001\\ 00001 end { matrix }所有三个轴简单地变换表示位置的向量。 幸运的是,很多时候你不需要在数学库中工作。 Three.js已经内置了数学库,所以使用它。
若要在要旋转的Three.js中旋转形状,必须创建一个旋转矩阵,并将其与形状的每个向量相乘。 再次使用“cloneVector”以确保创建的形状独立于存储为图案的形状。
//append to Tetris.Block.rotate ( ) varrotation matrix=new three.matrix4); rotation matrix.setrotationfromeuler ( Tetris.block.mesh.rotation ); for(varI=0; i Tetris.Block.shape.length; I ( { Tetris.block.shape [ I ]=rotation matrix.multiply vector3( Tetris.utils.clone vector ( Tetris.block.shapes Tetris.utils.round vector ( Tetris.block.shape [ I ] ); }旋转矩阵和我们棋盘上的显示有问题。 字段表示为以整数索引的数组,矩阵向量乘法的结果可能是浮点数。 JavaScript对浮点数不太好。 几乎可以确定会产生1.000001和2.999998这样的位置。 所以需要舍入函数。
Tetris.utils.round vector=function ( v ) v.x=math.round ) v.x; v.y=math.round(v.y ); v.z=math.round(v.z ); (; 当我们旋转我们的形状时,检查是否有碰撞非常简单。 通过再次调用此函数,使用了相同的技术撤消了旋转,但使用了反转参数。 请注意,如果撤消移动,冲突将永远不会发生。 如果需要,可以添加参数,以避免在不需要时重新检查。
//appendtotetris.block.rotate ( if ) Tetris.board.test collision ( false )==Tetris.board.collision.wall/要检查切片是否已完成,请计算占用字段的最大数量,并检查在z轴上移动的每个切片是否已满。 这样就可以改变基板的大小。 这个功能应该还有效。 这样考虑一下你所有的功能。 如果有什么变化,让代码变得灵活。
Tetris.board.check completed=function ( { varx,y,z,x2,y2,z2,fields=Tetris.Board.fields; var rebuild=false; var sum,expected=fields [0].length * fields.length,bonus=0; for(z=0; z fields[0][0].length; z ) { sum=0; for(y=0; y fields[0].length; y ) for(x=0; x fields.length; x ( ) if ) fields [ x ] [ y ] [ z ]==Tetris.board.field.petrified ] sum; }切片已满后,必须将其删除并移动所有后续切片。 缩小z一次,以避免跳过移位的切片。 为了让游戏更有趣,一次完成多个切片就可以获得奖励积分。
if(sum==expected ) { bonus =1 bonus; //1,3,7,15 . for ( y2=0; y2 fields[0].length; y2 ) for ) x2=0; x2 fields.length; x2 ) for(Z2=Z; z2 fields[0][0].length-1; z2 ) Tetris.board.fields [ x2 ] [ y2 ] [ z2 ]=fields [ x2 ] [ y2 ] [ z2 ] [ Z1 ]; //shift } Tetris.board.fields [ x2 ] [ y2 ] [ fields [0] [0].length-1 ]=Tetris.board.field.empty; } } rebuild=true; z—-; }if(Bonus ) Tetris.add points ( 1000 * bonus ); }现在,即使处理了板信息,也需要更改Three.js中的几何体。 上一个周期做不到。 因为一次完成多个切片可以多次重建几何体。 在循环检查中,每个Tetris.Board.fields和相应的Tetris.staticBlocks根据需要添加和删除几何图形。
if(rebuild ) for ) varz=0; z fields[0][0].length-1; z ) for(vary=0; y fields[0].length; y ) for(varx=0; x fields.length; x ( ) if ) fields [ x ] [ y ] [ z ]==Tetris.board.field.petrified! Tetris.static blocks [ x ] [ y ] [ z ] ( Tetris.addstaticblock ( x,y,z ) ); } if ( fields [ x ] [ y ] [ z ]==Tetris.board.field.empty Tetris.static blocks [ x ] [ y ] [ z ] { Tetris.scene.} } } } } }}; 音频API使用HTML5添加音频很简单。 让我们从向index.html添加元素开始。
在JS中使用这些文件也很简单。 首先,创建一个对象来保存你的声音:
//before Tetris.init ( ) Tetris.sounds={}; 要调用音频API,必须获取这些DOM元素。
//in Tetris.init ( Tetris.sounds [ & amp; #039; theme&; #039; ]=document.getelementbyid ( audio _ theme ); tetris.sounds[&; #039; collision& #039; ]=document.getelementbyid ( audio _ collision ); tetris.sounds[&; #039; 移动和映射; #039; ]=document.getelementbyid ( audio _ move ); tetris.sounds[&; #039; gameover&; #039; ]=document.getelementbyid ( audio _ game over & amp; #039; ); tetris.sounds[&; #039; score&; #039; ]=document.getelementbyid ( audio _ score ); 有几种方法。 您也可以创建自己的音频播放器,但对于我们的目的( play ( )和pause ) )就足够了。 也许你能猜到应该在哪里添加音乐:
tetris.sounds[&; #039; theme&; #039; ].play(-tetris.init ) ),初始化声音对象后。 tetris.sounds[&; #039; theme&; #039; ].pause(-tetris.start ) )。 ELSE{Tetris.sounds[&; #039; 移动和映射; #039; ].play (; ( Tetris.Block.move ) )那么,如果没有地面碰撞的话。 俄罗斯方块. sounds[“冲突”].play (; 在-Tetris.block.move ( )中,如果地面发生碰撞。 Tetris.sounds[&; #039; score&; #039; ].play (; 位于俄罗斯方块. addPoints ( )。 Tetris.sounds[&; #039; gameo]
ver”].play(); – 在 Tetris.Block.generate() 中,我们测试输掉的游戏。结论
这就是所有的人!我们的俄罗斯方块现在功能齐全。我希望这是一种学习 Three.js 的有趣方式。这里没有涉及许多主题,例如高级几何图形、着色器、灯光、骨骼动画等。我只是想表明创建游戏并不总是需要它们。如果你想了解更多,你可能应该从现在开始使用纯 WebGL。您可以从本教程开始。另请查看Brandon Jones的“Building the Game” 。