软件工程--推箱子游戏
合作者:陈梅、周丹
一、需求分析
推箱子这个游戏是款古老的游戏,目的是在训练玩家的逻辑思维能力。在一个狭小的仓库里面,要求把几个木箱放在指定的位置,稍不小心就会出现箱子出现无法移动或者通道被堵住的情况,所以需要巧妙的利用有限的空间和通道,合理的安排移动的次序和位置,才能顺利的完成任务。
二、分工
周丹:游戏的设计与构思
陈梅:代码的编写
三、游戏功能分析
1、推箱子界面的设计(地图);
2、关卡的设计;
3、创建箱子;
4、创建人物;
5、操作人物的运动;
3、碰撞检测;
四、编程语言
html+css+javascript+jquery 网页游戏
五、游戏涉及的知识点
  1、JQ库的常见方法使用
  2、地图的设计思想  
  3、碰撞检测
  4、坐标系在布局中的实际应用
  5、键盘的事件操作
  6、如何判断箱子阻挡、如何判断是否过关
  7、设计通用的代码模式
六、步骤分析
1、css模块
<style> *{ margin:0; padding:0;} #div1{ position:relative; margin:20px auto;} .pos1{ width:50px; height:50px; background:#666; float:left;} .pos2{ width:50px; height:50px; background:url(images/wall.png) no-repeat; float:left;} .pos3{ width:50px; height:50px; background:red; float:left;} .pos0{ width:50px; height:50px; background:blue; float:left;} .box{ width:50px; height:50px; background:url(images/box.png) no-repeat; position:absolute;} .person{ width:50px; height:50px; background:url(images/person.png) no-repeat; position:absolute;} </style>
2、关卡设计
gk : [ //关卡的数据 { map : [ 1,1,2,2,2,2,1,1, 1,1,2,3,3,2,1,1, 1,2,2,0,3,2,2,1, 1,2,0,0,0,3,2,1, 2,2,0,0,0,0,2,2, 2,0,0,2,0,0,0,2, 2,0,0,0,0,0,0,2, 2,2,2,2,2,2,2,2 ], box : [ {x:4,y:3}, {x:3,y:4}, {x:4,y:5}, {x:5,y:5} ], person : {x:3,y:6} }, { map : [ 1,1,1,1,2,2,2,2,2,2,2,1, 1,1,1,1,2,0,0,2,0,0,2,1, 1,1,1,1,2,0,0,0,0,0,2,1, 2,2,2,2,2,0,0,2,0,0,2,1, 3,3,3,2,2,2,0,2,0,0,2,2, 3,0,0,2,0,0,0,0,2,0,0,2, 3,0,0,0,0,0,0,0,0,0,0,2, 3,0,0,2,0,0,0,0,2,0,0,2, 3,3,3,2,2,2,0,2,0,0,2,2, 2,2,2,2,2,0,0,0,0,0,2,1, 1,1,1,1,2,0,0,2,0,0,2,1, 1,1,1,1,2,2,2,2,2,2,2,1 ], box : [ {x : 5 , y : 6}, {x : 6 , y : 3}, {x : 6 , y : 5}, {x : 6 , y : 7}, {x : 6 , y : 9}, {x : 7 , y : 2}, {x : 8 , y : 2}, {x : 9 , y : 6}, ], person : { x : 5 , y : 9 } } ],
3、创建地图
createMap : function(iNow){ //创建地图 this.oParent.empty(); document.title = '第'+(iNow+1)+'关'; this.nowJson = this.gk[iNow]; this.oParent.css('width',Math.sqrt(this.nowJson.map.length)*50); $.each( this.nowJson.map, $.proxy(function(i,elem){ this.oParent.append('<div class="pos'+ elem +'"></div>'); },this) ); this.createBox(); this.createPerson(); },
4、创建人物
createPerson : function(){ //创建人物 var oPerson = $('<div class="person"></div>'); oPerson.css('left',this.nowJson.person.x*50); oPerson.css('top',this.nowJson.person.y*50); oPerson.data('x',this.nowJson.person.x); oPerson.data('y',this.nowJson.person.y); this.oParent.append( oPerson ); this.bindPerson(oPerson); },
5、创建箱子
createBox : function(){ //创建箱子 $.each(this.nowJson.box,$.proxy(function(i,elem){ var oBox = $('<div class="box"></div>'); oBox.css('left',elem.x*50); oBox.css('top',elem.y*50); this.oParent.append(oBox); },this)); },
6、键盘操作人物移动
bindPerson : function(oPerson){ //操作人物 $(document).keydown($.proxy(function(ev){ switch(ev.which){ case 37: oPerson.css('backgroundPosition','-150px 0'); this.movePerson(oPerson,{x:-1}); break; case 38: oPerson.css('backgroundPosition','0 0'); this.movePerson(oPerson,{y:-1}); break; case 39: oPerson.css('backgroundPosition','-50px 0'); this.movePerson(oPerson,{x:1}); break; case 40: oPerson.css('backgroundPosition','-100px 0'); this.movePerson(oPerson,{y:1}); break; } },this)); },
7、人物移动
movePerson : function(oPerson,opt){ var xValue = opt.x || 0; var yValue = opt.y || 0; if( this.nowJson.map[ (oPerson.data('y') + yValue )*Math.sqrt(this.nowJson.map.length) + (oPerson.data('x') + xValue ) ] != 2 ){ oPerson.data('x',oPerson.data('x') + xValue ); oPerson.data('y',oPerson.data('y') + yValue ); oPerson.css('left' , oPerson.data('x')*50 ); oPerson.css('top' , oPerson.data('y')*50 ); $('.box').each($.proxy(function(i,elem){ if( this.pz( oPerson , $(elem) ) && this.nowJson.map[ (oPerson.data('y') + yValue )*Math.sqrt(this.nowJson.map.length) + (oPerson.data('x') + xValue ) ] != 2 ){ $(elem).css('left' , (oPerson.data('x') + xValue)*50 ); $(elem).css('top' , (oPerson.data('y') + yValue)*50 ); $('.box').each($.proxy(function(j,elem2){ if( this.pz( $(elem) , $(elem2) ) && elem!=elem2 ){ $(elem).css('left' , oPerson.data('x')*50 ); $(elem).css('top' , oPerson.data('y')*50 ); oPerson.data('x',oPerson.data('x') - xValue ); oPerson.data('y',oPerson.data('y') - yValue ); oPerson.css('left' , oPerson.data('x')*50 ); oPerson.css('top' , oPerson.data('y')*50 ); } },this)); } else if( this.pz( oPerson , $(elem) ) ){ oPerson.data('x',oPerson.data('x') - xValue ); oPerson.data('y',oPerson.data('y') - yValue ); oPerson.css('left' , oPerson.data('x')*50 ); oPerson.css('top' , oPerson.data('y')*50 ); } },this)); } this.nextShow(); },
8、碰撞检测
pz : function(obj1,obj2){ //碰撞检测 var L1 = obj1.offset().left; var R1 = obj1.offset().left + obj1.width(); var T1 = obj1.offset().top; var B1 = obj1.offset().top + obj1.height(); var L2 = obj2.offset().left; var R2 = obj2.offset().left + obj2.width(); var T2 = obj2.offset().top; var B2 = obj2.offset().top + obj2.height(); if( R1<=L2 || L1>=R2 || T1>=B2 || B1<=T2 ){ return false; } else{ return true; } }
9、下一关
nextShow : function(){ //下一关 var iNum = 0; $('.box').each($.proxy(function(i,elem){ $('.pos3').each($.proxy(function(j,elem2){ if( this.pz( $(elem) , $(elem2) ) ){ iNum++; } },this)); },this)); if(iNum == this.nowJson.box.length){ this.createMap(1); } },
七、完整代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>无标题文档</title> <style> *{ margin:0; padding:0;} #div1{ position:relative; margin:20px auto;} .pos1{ width:50px; height:50px; background:#666; float:left;} .pos2{ width:50px; height:50px; background:url(images/wall.png) no-repeat; float:left;} .pos3{ width:50px; height:50px; background:red; float:left;} .pos0{ width:50px; height:50px; background:blue; float:left;} .box{ width:50px; height:50px; background:url(images/box.png) no-repeat; position:absolute;} .person{ width:50px; height:50px; background:url(images/person.png) no-repeat; position:absolute;} </style> <script type="text/javascript" src="jquery-1.9.1.min.js"></script> <script> $(function(){ Game.init( $('#div1') ); }); var Game = { gk : [ //关卡的数据 { map : [ 1,1,2,2,2,2,1,1, 1,1,2,3,3,2,1,1, 1,2,2,0,3,2,2,1, 1,2,0,0,0,3,2,1, 2,2,0,0,0,0,2,2, 2,0,0,2,0,0,0,2, 2,0,0,0,0,0,0,2, 2,2,2,2,2,2,2,2 ], box : [ {x:4,y:3}, {x:3,y:4}, {x:4,y:5}, {x:5,y:5} ], person : {x:3,y:6} }, { map : [ 1,1,1,1,2,2,2,2,2,2,2,1, 1,1,1,1,2,0,0,2,0,0,2,1, 1,1,1,1,2,0,0,0,0,0,2,1, 2,2,2,2,2,0,0,2,0,0,2,1, 3,3,3,2,2,2,0,2,0,0,2,2, 3,0,0,2,0,0,0,0,2,0,0,2, 3,0,0,0,0,0,0,0,0,0,0,2, 3,0,0,2,0,0,0,0,2,0,0,2, 3,3,3,2,2,2,0,2,0,0,2,2, 2,2,2,2,2,0,0,0,0,0,2,1, 1,1,1,1,2,0,0,2,0,0,2,1, 1,1,1,1,2,2,2,2,2,2,2,1 ], box : [ {x : 5 , y : 6}, {x : 6 , y : 3}, {x : 6 , y : 5}, {x : 6 , y : 7}, {x : 6 , y : 9}, {x : 7 , y : 2}, {x : 8 , y : 2}, {x : 9 , y : 6}, ], person : { x : 5 , y : 9 } } ], init : function(oParent){ //初始化 this.oParent = oParent; this.createMap(0); }, createMap : function(iNow){ //创建地图 this.oParent.empty(); document.title = '第'+(iNow+1)+'关'; this.nowJson = this.gk[iNow]; this.oParent.css('width',Math.sqrt(this.nowJson.map.length)*50); $.each( this.nowJson.map, $.proxy(function(i,elem){ this.oParent.append('<div class="pos'+ elem +'"></div>'); },this) ); this.createBox(); this.createPerson(); }, createBox : function(){ //创建箱子 $.each(this.nowJson.box,$.proxy(function(i,elem){ var oBox = $('<div class="box"></div>'); oBox.css('left',elem.x*50); oBox.css('top',elem.y*50); this.oParent.append(oBox); },this)); }, createPerson : function(){ //创建人物 var oPerson = $('<div class="person"></div>'); oPerson.css('left',this.nowJson.person.x*50); oPerson.css('top',this.nowJson.person.y*50); oPerson.data('x',this.nowJson.person.x); oPerson.data('y',this.nowJson.person.y); this.oParent.append( oPerson ); this.bindPerson(oPerson); }, bindPerson : function(oPerson){ //操作人物 $(document).keydown($.proxy(function(ev){ switch(ev.which){ case 37: oPerson.css('backgroundPosition','-150px 0'); this.movePerson(oPerson,{x:-1}); break; case 38: oPerson.css('backgroundPosition','0 0'); this.movePerson(oPerson,{y:-1}); break; case 39: oPerson.css('backgroundPosition','-50px 0'); this.movePerson(oPerson,{x:1}); break; case 40: oPerson.css('backgroundPosition','-100px 0'); this.movePerson(oPerson,{y:1}); break; } },this)); }, movePerson : function(oPerson,opt){ var xValue = opt.x || 0; var yValue = opt.y || 0; if( this.nowJson.map[ (oPerson.data('y') + yValue )*Math.sqrt(this.nowJson.map.length) + (oPerson.data('x') + xValue ) ] != 2 ){ oPerson.data('x',oPerson.data('x') + xValue ); oPerson.data('y',oPerson.data('y') + yValue ); oPerson.css('left' , oPerson.data('x')*50 ); oPerson.css('top' , oPerson.data('y')*50 ); $('.box').each($.proxy(function(i,elem){ if( this.pz( oPerson , $(elem) ) && this.nowJson.map[ (oPerson.data('y') + yValue )*Math.sqrt(this.nowJson.map.length) + (oPerson.data('x') + xValue ) ] != 2 ){ $(elem).css('left' , (oPerson.data('x') + xValue)*50 ); $(elem).css('top' , (oPerson.data('y') + yValue)*50 ); $('.box').each($.proxy(function(j,elem2){ if( this.pz( $(elem) , $(elem2) ) && elem!=elem2 ){ $(elem).css('left' , oPerson.data('x')*50 ); $(elem).css('top' , oPerson.data('y')*50 ); oPerson.data('x',oPerson.data('x') - xValue ); oPerson.data('y',oPerson.data('y') - yValue ); oPerson.css('left' , oPerson.data('x')*50 ); oPerson.css('top' , oPerson.data('y')*50 ); } },this)); } else if( this.pz( oPerson , $(elem) ) ){ oPerson.data('x',oPerson.data('x') - xValue ); oPerson.data('y',oPerson.data('y') - yValue ); oPerson.css('left' , oPerson.data('x')*50 ); oPerson.css('top' , oPerson.data('y')*50 ); } },this)); } this.nextShow(); }, nextShow : function(){ //下一关 var iNum = 0; $('.box').each($.proxy(function(i,elem){ $('.pos3').each($.proxy(function(j,elem2){ if( this.pz( $(elem) , $(elem2) ) ){ iNum++; } },this)); },this)); if(iNum == this.nowJson.box.length){ this.createMap(1); } }, pz : function(obj1,obj2){ //碰撞检测 var L1 = obj1.offset().left; var R1 = obj1.offset().left + obj1.width(); var T1 = obj1.offset().top; var B1 = obj1.offset().top + obj1.height(); var L2 = obj2.offset().left; var R2 = obj2.offset().left + obj2.width(); var T2 = obj2.offset().top; var B2 = obj2.offset().top + obj2.height(); if( R1<=L2 || L1>=R2 || T1>=B2 || B1<=T2 ){ return false; } else{ return true; } } }; </script> </head> <body> <div id="div1"></div> </body> </html>
八、游戏结果
1、第一关
  
2、第二关
  
九、游戏总结
1、javascript通过单体的形式来组织代码更加的形象,方便全局的应用。
2、设计游戏重要的不是怎么写,重要的是思路是否清晰、需求分解过后,条理是否分明、代码组织方式是否利于维护、解决问题的方式是否通用稳妥。
3、地图的设计思想,通过坐标的思想。
4、键盘的事件来操作物体的运动。
5、如何判断箱子的阻挡(碰撞检测)、判断过关。
6、代码面向对象的思想。
十、不足之处
1、关卡设计的不够随机,每一关都是同一个位置的箱子。
2、由于时间比较急、只设计了2个关卡,关卡数量比较少。
3、界面没有美工设计,页面比较丑。
                    
                
                
            
        
浙公网安备 33010602011771号