游戏开发入门之俄罗斯方块
2014-02-15 13:09
489 查看
程序分析
俄罗斯方块是由多种类型的方块与游戏边界背景组成,根据面向对象的方法,把整个程序分隔成两部分--游戏主体、形状两个对象。其中游戏主体处理的事情包括:
绘制游戏界面与边界。
容纳方块与形状并绘制。
控制器:监听键盘事件,并将之转换成对形状对象的控制,如变形、左移、右移、下移以及直接落地操作。
游戏规则控制:包括形状对象的生成,形状对象每次下落的时间间隔,边界检测(形状对象不能移除游戏边界之外)。
形状对象处理的事情:
根据指定的方块排列坐标,生成对应数量的方块以及各方块的坐标信息。
处理对象移动或变形操作对各方块坐标的影响,并使得游戏主体可以根据这些坐标正确绘制方块信息。
确定了程序的分割及功能逻辑划分下面要就要进行编码了。
程序代码
形状对象:Shapevar Shape = function(x, y, gc){//形状/方块对象,提供方块初始位置 this.gc = gc; this.x = x; this.y = y; }; Shape.prototype = { init : function(boxes, centerIndex){//初始化方块内容(提供方块各格子与初始位置的偏移量, 以及变型时中心位置(或传入false等于不可变型)) var me = this, gc = me.gc; me.boxes = boxes.slice(); this.turnAble = true; if(centerIndex === false){ this.turnAble = false; }else{ this.centerIndex = (typeof centerIndex == 'undefined') ? 1 : centerIndex; if(this.centerIndex >= boxes.length){ this.centerIndex = boxes.length >= 2 ? 1 : 0; } } var x = me.x, y = me.y; me.nodes = []; for(var i = 0; i < me.boxes.length; i++){ var node = {x : x + me.boxes[i].x, y : y + me.boxes[i].y, dom : gc.createBox()}; me.nodes.push(node); } }, putIn : function($p){ for(var i = 0; i < this.nodes.length; i++){ $p.appendChild(this.nodes[i].dom); } }, turn : function(flag){//逆时针旋转, flag = true时顺时针旋转 var me = this, nodes = me.nodes, gc = me.gc, centerBox = 1; if(me.turnAble){ var cx = nodes[centerBox].x, cy = nodes[centerBox].y; for(var i = 0; i < nodes.length; i++){ var rx = nodes[i].x - cx, ry = nodes[i].y - cy; rx = !!flag ? rx : -rx; ry = !!flag ? -ry : ry; nodes[i].x = cx + ry; nodes[i].y = cy + rx; } var crossInfo = me.detectCross(); if(crossInfo){ me.move(-crossInfo.x, -crossInfo.y, true); } } }, detectCross : function(){//检测是否跃出当前游戏网格(旋转后可能会有此问题), 返回超出范围的最大值,x轴和y轴 var me = this, nodes = me.nodes, gc = me.gc; var crossX = 0, crossY = 0; function absMax(a1, a2){return Math.abs(a1) > Math.abs(a2) ? a1 : a2;} for(var i = 0; i < nodes.length; i++){ var node = nodes[i]; var absX = 0, absY = 0; absX = node.x < gc.w && node.x >= 0 ? 0 : node.x % (gc.w - 1); absY = node.y >= 0 && node.y < gc.h ? 0 : node.y % (gc.h - 1); crossX = absMax(absX, crossX); crossY = absMax(absY, crossY); } return crossX != 0 || crossY != 0 ? {x : crossX, y : crossY} : false; }, moveAble : function(x, y){ var me = this, nodes = me.nodes, gc = me.gc, maxX = gc.w, maxY = gc.h; var flag = true; for(var i = 0; i < nodes.length; i++){ var node = nodes[i], index = i; if(nodes[index].x + x >= maxX || nodes[index].y + y >= maxY){ flag = false; break; } if(nodes[index].x + x < 0 || nodes[index].y + y < 0){ flag = false; break; } } return flag; }, move : function(x, y, force){ var me = this, nodes = me.nodes, gc = me.gc, maxX = gc.w, maxY = gc.h; var flag = !!force || me.moveAble(x, y); if(flag){ var len = nodes.length; for(var i = 0; i < nodes.length; i++){ nodes[i].x += x; nodes[i].y += y; } } return flag; }, toRight : function(){ return this.move(1, 0); }, toLeft : function(){ return this.move(-1, 0); }, toUp : function(){ return this.move(0, -1); }, toDown : function(){ return this.move(0, 1); } };上面是形状对象的定义,可以看到形状对象定义了turn、move、toRight、toLeft、toUp、toDown以及detectCross等基本方法。其中detectCross方法是用于检测变型后各个方块的坐标是否还在游戏边界内,并返回最大的超出距离信息,供重置方块坐标。
方块编辑完了后就是要写Game游戏主体对象了。
Game:游戏主体对象
var extend = function(source){ var argLen = arguments.length; for(var i = 1; i < argLen; i++){ var arg = arguments[i]; for(var i in arg){ source[i] = arg[i]; } } }; var Game = function(w, h, frame, score){//游戏世界对象 var me = this; me.w = w; me.h = h; me.holder = new Array(w * h); me.container = me.$c = frame; me.score = score; me.scoreCount = 0; var scoreCount = 0; me.getScoreCount = function(){ return scoreCount; }; me.setScoreCount = function(record){ scoreCount = record; }; var box = me.createBox(); me.container.appendChild(box); me.boxWidth = box.clientWidth + 2;//2为边框宽度 me.boxHeight = box.clientHeight + 2;//2为边框宽度 var bw = me.boxWidth, bh = me.boxHeight; me.container.removeChild(box); extend(me.container.style, {width : w * bw + 'px', height : h * bh + 'px', position : 'relative', display : 'block'});//设置容器高度 me.eventListener = me.createEveltListener(); var level = 1; me.setLevel = function(level2){ if(level2){ me.speed = 1000 / level2; level = level2; } }; me.getLevel = function(){ return level; }; me.setLevel(level); }; Game.prototype = { restore : function(){ var me = this, w = me.w, h = me.h; me.pause(); me.holder = new Array(w * h); me.curShape = false; me.speed = 1000; me.scoreCount = 0; var children = me.container.children; for(var i = 0; i < children.length; i++){ me.container.removeChild(children[i]); } }, createBox : function(){//创建格子元素 var box = doc.createElement('div'); box.className = 'box bg-yellow'; //box.setAttribute('className', 'box bg-yellow'); //box.setAttribute('class', 'box bg-yellow'); return box; }, drawShape : function(shape){//绘制方块的位置 var me = this, bw = me.boxWidth, bh = me.boxHeight; for(var i = 0, nodes = shape.nodes; i < nodes.length; i++){ extend(nodes[i].dom.style, {left : nodes[i].x * bw + 'px', top : nodes[i].y * bh + 'px'}); } }, refreshDisplay : function(){//绘制/刷新已有方块的位置 var me = this, holder = me.holder, bw = me.boxWidth, bh = me.boxHeight; var len = holder.length; var w = me.w, h = me.h; var index = 0; for(var row = 0; row < h; row++){//此处不使用除法运算,由于除法运算精度会导致行数计算错误. for(var col = 0; col < w; col++){ var $dom = holder[index]; if($dom && $dom.nodeType){ extend($dom.style, {left : col * bw + 'px', top : row * bh + 'px'}); } index++; } } }, detectShapeImpact : function(shape){//检测方块是否与世界冲突(碰撞检测) var me = this, holder = me.holder, w = me.w, h = me.h, nodes = shape.nodes; var flag = false; for(var index = 0; index < shape.nodes.length; index++){ var node = shape.nodes[index]; var nx = node.x, ny = node.y; var i = ny * w + nx; if(holder[i] && holder[i].nodeType){//与现有方块冲突 flag = true; break; } } return flag; }, boxesSupport : [ {boxes : [{x:0,y:0},{x:0,y:1},{x:0,y:2},{x:0,y:3}], centerIndex : 1}, {boxes : [{x:0,y:0},{x:0,y:1},{x:1,y:1},{x:1,y:2}], centerIndex : 1}, {boxes : [{x:0,y:0},{x:0,y:1},{x:-1,y:1},{x:-1,y:2}], centerIndex : 1}, {boxes : [{x:0,y:0},{x:0,y:1},{x:0,y:2},{x:1,y:2}], centerIndex : 2}, {boxes : [{x:0,y:0},{x:1,y:0},{x:1,y:1},{x:1,y:2}], centerIndex : 1}, {boxes : [{x:0,y:0},{x:0,y:1},{x:0,y:2},{x:1,y:1}], centerIndex : 2}, {boxes : [{x:0,y:0},{x:1,y:0},{x:0,y:1},{x:1,y:1}], centerIndex : false}, {boxes : [{x:0,y:0}], centerIndex : false} ], randomShape : function(){ var me = this, len = me.boxesSupport.length, x = Math.round(me.w / 2), y = 0, rand = Math.floor(Math.random() * len); var shape = new Shape(x, y, me); shape.init(me.boxesSupport[rand].boxes, me.boxesSupport[rand].centerIndex); shape.putIn(me.container); return shape; }, createEveltListener : function(){ var me = this; return function(event){ var shape = me.curShape; if(!shape){ return; } var mi = false;//MoveInfo移动信息 var isTurn = false; var ev = event || win.event, key = ev.keyCode || ev.which || ev.charCode; switch(key){ case 37 : mi = {x:-1, y:0}; break; case 39 : mi = {x:1, y:0}; break; case 40 : mi = {x:0, y:1}; break; case 38 : //shape.toUp(); //break; case 32 : shape.turn(); isTurn = true; break; default : isTurn = false; mi = false; //console.log('Unknow operate with code ' + event.keyCode); } if(mi){//移动操作 if(shape.moveAble(mi.x, mi.y)){ shape.move(mi.x, mi.y); var flag = me.detectShapeImpact(shape);//检测碰撞信息 if(flag){ shape.move(-mi.x, -mi.y);//还原用于检测碰撞的位置信息. if(mi.y > 0){//有碰撞,且为向下移动 me.mergeShape(shape); return; } } }else if(mi.y > 0){//向下,写不能移动 me.mergeShape(shape); return; } } if(isTurn){//旋转操作(变型操作) var flag = me.detectShapeImpact(shape);//检测碰撞信息 if(flag){ shape.turn(true);//旋转回去 } } me.drawShape(shape); }; }, eventListener : function(event){ }, mergeShape : function(shape){ var me = this, holder = me.holder, w = me.w, h = me.h, nodes = shape.nodes; var flag = false; for(var index = 0; index < shape.nodes.length; index++){ var node = shape.nodes[index]; var nx = node.x, ny = node.y; var i = ny * w + nx; holder[i] = node.dom; } me.clearLine(); me.refreshDisplay(); me.curShape = me.randomShape(); var flag = me.detectShapeImpact(me.curShape);//检测碰撞信息 me.drawShape(me.curShape); if(flag){ me.pause(); alert('游戏结束!'); } }, clearLine : function(){ //清除满行的方块,并移动其上层下来 var me = this, holder = me.holder, w = me.w, h = me.h, len = holder.length; var clearLineCount = 0; for(var i = 0; i < h; i++){ var lineFullFlag = true; for(var j = 0; j < w; j++){ if(!holder[i * w + j]){ lineFullFlag = false; break; } } if(lineFullFlag){//当前行被填满 clearLineCount++; for(var col = 0; col < w; col++){//删除元素 me.container.removeChild(holder[i * w + col]); } var oldHolder = holder, tmpHolder; tmpHolder = holder.slice(0, i * w);//复制前段 tmpHolder = tmpHolder.concat(holder.slice((i+1) * w));//复制后段 tmpHolder = new Array(w).concat(tmpHolder);//填充全段 holder = tmpHolder;//变量交互 me.holder = holder;//维护全局变量 } } //console.log('Clear line (' + clearLineCount + ');'); if(clearLineCount > 0){ me.scoreCount += Math.pow(2, clearLineCount); if(me.score){ me.score.innerHTML = me.scoreCount; } } }, start : function(){ var me = this; if(!me.started){ me.started = true; if(!me.curShape){ me.curShape = me.randomShape(); me.drawShape(me.curShape); } //document.addEventListener('keydown', me.eventListener); document.onkeydown = me.eventListener; var roundFunc = function(){ //下移方块 me.eventListener({keyCode:40}); }; me.runInterval = setInterval(roundFunc, me.speed); } }, pause : function(){ var me = this; me.started = false; win.clearInterval(me.runInterval); document.onkeydown = null; //document.removeEventListener('keydown', me.eventListener); } };
HTML代码以及运行代码
<html> <head> <meta charset="UTF-8" content="text/html;charset=utf-8"> <title>俄罗斯方块</title> <style type="text/css" rel="stylesheet"> .box{width:18px; height:18px; border:1px solid lightgray; display: block; position: absolute;} .bg-red{background-color: red;} .bg-blue{background-color: blue;} .bg-yellow{background-color: yellow;} .bg-gray{background-color: gray;} .frame{border: 1px solid; position: relative; display: block; background-color: lightblue;} .score{border: 1px solid; display: block; background-color: lightgray; width:200px; height:36px; font-size: 18px; line-height: 36px; margin-top:5px;} .wrap{display: block;} .hide{display:none;} .hide2{visibility: hidden;} </style> <script type="text/javascript" src="js/tetris.js"></script> </head> <body> <div class="wrap"> <div class="frame" id="frame"> </div> <div class="score" id="score"></div> </div> <script type="text/javascript"> (function(win, doc){ g = new Game(15, 20, document.getElementById('frame'), document.getElementById('score')); g.start(); })(window, document); </script> </body> </html>
程序源代码下载
程序源代码下载地址:http://download.csdn.net/detail/zyb134506/6928983相关文章推荐
- JAVA游戏编程之三----j2se 手机游戏入门开发--俄罗斯方块_1
- JAVA游戏编程之三----j2me 手机游戏入门开发--俄罗斯方块_2
- Unity3D游戏开发 脚本入门(六)
- Transform组件C#游戏开发快速入门
- Android游戏开发的入门学习(andEngine引擎)
- VC++游戏开发基础系列从入门到精通
- Android 游戏开发笔记一、入门之SurfaceView的使用
- 游戏开发新手入门之DirectX入门
- OUYA游戏开发核心技术剖析OUYA游戏入门示例——StarterKit
- 创建等距世界:游戏开发入门
- Swift游戏开发之俄罗斯方块:No.8 游戏规则
- 游戏开发新手入门之DirectX入门
- 王者荣耀是怎样炼成的(一)《王者荣耀》用什么开发,游戏入门,unity3D介绍
- HTML5游戏开发之俄罗斯方块(续)
- Swift游戏开发之俄罗斯方块:No.10 最后一步!美化你的程序
- JAVA游戏编程之二----j2me MIDlet 手机游戏入门开发--贪吃蛇
- 游戏开发入门图书推荐(转载,附下载地址)
- 初学者的福音:游戏开发新手入门指南
- Unreal SDK 游戏开发从入门到精通
- 网页游戏开发入门教程二(游戏模式+系统)