zoukankan      html  css  js  c++  java
  • javascript 推箱子游戏介绍及问题

    最近没什么事情,我的一个亲戚在学校学习PHP,课程中老师让他们编写一个javascript版本的推箱子小游戏,他没什么头绪,就来问我,我当时很闲,就随口答应他包在我身上。结果真正写的时候还是花了点时间,最后写出来的成品也有各种问题,在这里希望大家能一起探讨学习!(大神们请屏蔽鄙人的粗糙简单)

    首先看一下最终的效果图,,不好意思,只是做了个简化版本,图中黄色的块是我们控制来推动的盒子,粉红色的块是被推的盒子,红色的块表示最终要被推到的位置,黑色的快表示墙,盒子不能穿过墙,游戏的方向控制使用wasd四个按键,分别表示上下左右,玩起来没有任何意思,这里简述一下盒子推动的思路:


    首先,推箱子最少需要两个箱子,一个箱子a用键盘来控制移动,另外一个箱子则是被推动的箱子b,用键盘来控制箱子a移动很简单,使用键盘事件onkeydown,用keyCode来选择你要移动箱子所使用的键,一般常用 wasd这四个键,被推的箱子b如果要实现被推着走,需要两步来完成,第一步是判断两个箱子相碰撞,因为只有当两个箱子配到一起的时候才可能发生推着走的事情,当确定两个箱子碰到以后,我们再次按下前后左右键,我们需要把箱子a移动的距离与之对应的加到箱子b的身上,但这里要判断箱子a移动的方向,如果箱子a是从左边往右边移动20px,那么箱子b也应该向右边移动20px,如果是从箱子a是从上边往下边移动20px,那么箱子b也应该往下方移动20px,以此类推,从右边往左边推和从下边往上边推也是一样的,这样就有推箱子的效果。(大神有其他方法也请告诉小弟,多谢)

    第二个问题就是游戏地图的创建,其实我使用的是很常见的一种创建方式,我自己给他取了个名字,叫做数组地图,主要是因为他很形象模拟地图,数组写成什么样子,最后生成的地图就是什么样子,比如

    [
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,4,4,4,4,4,4,4,4,4,4,4,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,1,1,1,1,6,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,1,1,1,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,4,4,4,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,4,4,4,4,4,4,4,4,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,4,1,1,1,1,1,2,4,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,4,1,1,4,1,1,4,4,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,4,4,4,4,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,4,4,4,4,1,4,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
    ]

    这个数组看起来像是一个矩形,实际上就是一个数组,只是我们人为的把他排列成一个矩形的样子,之所以这样做就是为了依照我们排列的样子来创建地图,首先我们看到这个数组里有很多1,还有3,4,2,他们分别表示不同的类型,比如说1表示可以行走的地方,4表示不能行走的地方,3表示箱子a初始化时候的位置,2表示箱子最终要被推到的地方,6表示被推的箱子b初始化的位置,我们在css表里面写好对应的样式,比如 .pos1{20px;height:20px;background:#666;float:left}, .pos2{20px;height:20px;background:red;float:left}, .pos3{20px;height:20px;background:yellow;float:left},.pos4{20px;height:20px;background:black;float:left},.pos6{20px;height:20px;background:pink;float:left},写好这些样式以后,开始用js循环整个数组,再循环的过程中不断创建div,而每一个div都要给他们一个class,至于这个class是什么则根据数组样式来赋值,比如数组里的第一个值是1,那么对应的样式就是.pos1,如果数组里的样式是2,那么对应的class就是.pos2,以此类推,每创建一个div就把它插入到父盒子里面,因为每一个小div都有浮动属性,他们自然会一个一个排开,最终就会平凑成数组展现出来的样式。这种地图创建方好处就是非常容易修改地图样式,并且可以很直观的修改地图,只要修改数组就等于修改了地图,简单的游戏中常用这种方式。

    式。下面是代码:

    this.gameBox = document.getElementById('game_box'); //父盒子
    for(var i=0;i<this.map.length;i++){
    this.litBox = document.createElement('div');
    this.litBox.className = "pos"+this.map[i];
    this.gameBox.appendChild(this.litBox);
    }


    第三个问题就是推箱子时候,当箱子遇到class为.pos4的div时候,也就是遇到墙的时候,不能把箱子推过去,这个问题肯定有更好的解决方法,因为我在这个例子中所有的障碍都是用div完成,所以我们看到的那一列或者一黑色竖障碍都是div,我原本希望使用上面说的碰撞检测的方法来处理,但是发现这个方法检测多个连续的元素时候不是很好用,不过也有可能是我用不对,我最终的使用的方法比较愚蠢,将所有的class为.pos4的div全部选中,用循环得到每个div的上下左右值,并把他们放到一个数组里,然后在我们每次按下按键的时候都检测一次当前箱子的位置。并把这个位置拿到数组里面的去循环比较,看看是否有一样的值,如果有一个一样的,那么表示箱子碰到了障碍,只是这种做法比较耗损性能,毕竟在不停的循环检测,所以不是很推荐,不过我暂时也没想出其他方法(求大神赐教!!!)

    第四个问题就是,这个例子只是演示了推一个子箱子,实际的游戏中为了增加难度会有很多被推的箱子,至于如何实现,相信大家也有自己的想法了。

    /*******以下是整个案例代码(因本人技术有限,大神就当成反面教材吧) 请大家多多指教***************/

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html lang="en">
    <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <title>Document</title>
    <style>
    *{margin:0px;padding:0px;}
    #game_box{500px;height:500px;margin:150px auto;position:relative;}
    .pos1{20px;height:20px;background: #666;float: left;}
    .pos2{20px;height:20px;background: red;float: left;}
    .pos3{20px;height:20px;background:#666;float: left;}
    .pos4{20px;height:20px;background:black;float: left;}
    .pos6{20px;height:20px;background: #666;float: left;}
    .begin{20px;height:20px;background:yellow;position: absolute;top:0;left:0px;z-index:1000;}
    .target{20px;height:20px;background:pink;position: absolute;top:0;left:0px;z-index:1000;}
    </style>
    </head>
    <body>
    <div id="game_box"></div>
    </body>

    <script>
    var game = {
    gameBox:0, //外层的大盒子
    litBox:0, //里面的每一个小格子
    beginLeft:0, //运动的盒子最开始距离左边的距离
    beginRight:0, //运动的盒子最开始距离上边的距离
    targetLeft:0, //将要推动的盒子最开始距离左边的距离
    targetTop:0, //将要推动的盒子最开始距离上边的距离
    mObj:0, //运动的盒子元素
    mBox:0, //被推动的盒子
    prevleft:0, //记录运动的盒子上一步距离左边的位置
    prevtop:0, //记录运动的盒子上一步距离上边的位置
    pushprevleft:0,
    pushprevtop:0,
    iftouch:false, //记录是否碰撞到目标盒子
    prev:0, //保存之前按下的方向值
    direct:0,
    last:0,
    //地图数组
    map:[
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,4,4,4,4,4,4,4,4,4,4,4,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,1,1,1,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,4,4,4,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,4,4,4,4,4,4,4,4,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,4,1,1,1,1,1,2,4,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,4,1,1,4,1,1,4,4,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,4,4,4,4,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,1,6,1,4,1,4,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,4,4,4,4,4,4,1,1,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,1,1,1,1,1,4,1,1,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,1,1,1,1,1,4,1,1,1,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,1,1,1,1,1,4,4,4,4,1,
    1,1,1,1,4,1,1,1,4,1,4,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,4,4,4,4,4,1,4,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,4,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
    ],
    //创建地图
    createMap:function(){
    this.gameBox = document.getElementById('game_box');
    for(var i=0;i<this.map.length;i++){
    this.litBox = document.createElement('div');
    this.litBox.className = "pos"+this.map[i];
    this.gameBox.appendChild(this.litBox);
    if(this.map[i]==3){
    this.beginLeft = this.litBox.offsetLeft;
    this.beginRight = this.litBox.offsetTop;
    }
    if(this.map[i]==6){
    this.targetLeft = this.litBox.offsetLeft;
    this.targetTop = this.litBox.offsetTop;
    }
    }
    },
    //获取class方法
    getClass:function(classname,parent){
    var par = parent||document;
    var pele = par.getElementsByTagName('*');
    var arr=[];
    for(var i=0;i<pele.length;i++){
    var pclass = pele[i].className.split(' ');
    for(var j=0;j<pclass.length;j++){
    if(pclass[j] == classname){
    arr.push(pele[i]);
    break;
    }
    }
    }
    if(arr.length == 1){
    return arr[0];
    }else{
    return arr;
    }
    },

    //创建将要移动的盒子
    createObj:function(){
    this.mObj = document.createElement('div');
    this.mObj.className = "begin";
    this.gameBox.appendChild(this.mObj);
    this.mObj.style.left = this.beginLeft+'px';
    this.mObj.style.top = this.beginRight+'px';
    },
    //创建将要被推的盒子
    createMbox:function(){
    this.mBox = document.createElement('div');
    this.mBox.className = "target";
    this.gameBox.appendChild(this.mBox);
    this.mBox.style.left = this.targetLeft+'px';
    this.mBox.style.top = this.targetTop+'px';
    },
    //控制盒子移动
    move:function(obj){
    _this = this;
    document.onkeydown=function(ev){
    _this.prevleft = obj.offsetLeft;
    _this.prevtop = obj.offsetTop;
    _this.pushprevleft = _this.mBox.offsetLeft;
    _this.pushprevtop = _this.mBox.offsetTop;
    var oev = ev || event;
    var key = oev.keyCode;
    if(key == 87){
    obj.style.top=obj.offsetTop-20+'px';
    }
    else if(key == 68){
    obj.style.left=obj.offsetLeft+20+'px';
    }
    else if(key == 83){
    obj.style.top=obj.offsetTop+20+'px';
    }
    else if(key == 65){
    obj.style.left=obj.offsetLeft-20+'px';
    }
    _this.rang(_this.mObj,1);
    _this.current(_this.mObj,1);
    _this.pushbox(_this.iftouch?key:0);
    }
    },
    rang:function(obj,no2){
    if(no2==1){
    if(obj.offsetLeft<=0){
    obj.style.left=0+'px';
    }
    if(obj.offsetLeft>=this.gameBox.offsetWidth-this.mObj.offsetWidth){
    obj.style.left=this.gameBox.offsetWidth-this.mObj.offsetWidth+'px';
    }
    if(obj.offsetTop<=0){
    obj.style.top=0+'px';
    }
    if(obj.offsetTop>=this.gameBox.offsetHeight-this.mObj.offsetHeight){
    obj.style.top = this.gameBox.offsetHeight-this.mObj.offsetHeight+'px';
    }
    }
    if(no2==2){
    if(obj.offsetLeft<0){
    obj.style.left=0+'px';
    this.mObj.style.top = this.prevtop+'px';
    this.mObj.style.left = this.prevleft+'px';
    }
    if(obj.offsetLeft>this.gameBox.offsetWidth-this.mObj.offsetWidth){
    obj.style.left=this.gameBox.offsetWidth-this.mObj.offsetWidth+'px';
    this.mObj.style.top = this.prevtop+'px';
    this.mObj.style.left = this.prevleft+'px';
    }
    if(obj.offsetTop<0){
    obj.style.top=0+'px';
    this.mObj.style.top = this.prevtop+'px';
    this.mObj.style.left = this.prevleft+'px';
    }
    if(obj.offsetTop>this.gameBox.offsetHeight-this.mObj.offsetHeight){
    obj.style.top = this.gameBox.offsetHeight-this.mObj.offsetHeight+'px';
    this.mObj.style.top = this.prevtop+'px';
    this.mObj.style.left = this.prevleft+'px';
    }
    }

    },
    //不能触碰的区域
    forbidden:function(){
    var totalBox = this.getClass('pos4');
    var arr=[];
    for(var i=0;i<totalBox.length;i++){
    var json = {};
    json.left = totalBox[i].offsetLeft;
    json.top = totalBox[i].offsetTop;
    json.right = totalBox[i].offsetLeft+20;
    json.bottom = totalBox[i].offsetTop+20;
    arr.push(json);
    }
    return arr;
    },

    //盒子当前的位置判断
    current:function(obj,no){
    var notouch = this.forbidden();
    var curleft = obj.offsetLeft;
    var curright = obj.offsetLeft+20;
    var curtop = obj.offsetTop;
    var curbottom= obj.offsetTop+20;
    for(var i =0;i<notouch.length;i++){
    if(curleft == notouch[i].left && curtop == notouch[i].top){ //如果碰到黑色的墙就退回到前一步的位置
    if(no==1){
    obj.style.top = this.prevtop+'px';
    obj.style.left = this.prevleft+'px';
    }
    if(no==2){
    obj.style.top = _this.pushprevtop+'px';
    obj.style.left = _this.pushprevleft+'px';
    this.mObj.style.top = this.prevtop+'px';
    this.mObj.style.left = this.prevleft+'px';
    }
    }
    }
    },

    //检测碰撞
    crash:function(){
    if(this.mObj.offsetLeft==this.mBox.offsetLeft-20&&this.mObj.offsetTop==this.mBox.offsetTop){
    //left
    this.iftouch = true;
    this.direct = 'left';
    this.last=2;
    }
    else if(this.mObj.offsetLeft==this.mBox.offsetLeft+20&&this.mObj.offsetTop==this.mBox.offsetTop){
    //right
    this.iftouch = true;
    this.direct = 'right';
    this.last=2;
    }
    else if(this.mObj.offsetTop==this.mBox.offsetTop-20&&this.mObj.offsetLeft==this.mBox.offsetLeft){
    //top
    this.iftouch = true;
    this.direct = 'top';
    this.last=2;
    }
    else if(this.mObj.offsetTop==this.mBox.offsetTop+20&&this.mObj.offsetLeft==this.mBox.offsetLeft){
    //bottom
    this.iftouch = true;
    this.direct = 'bottom';
    this.last=2;
    }
    },

    //推盒子方法
    pushbox:function(key){
    this.crash();
    //判断回传的keycode是多少来检测用户往哪个方向推箱子
    if(key){
    if(this.direct == 'left'){ //检测到从左边往右边推箱子
    if(key!=68){
    this.iftouch = false;
    this.crash();
    }else{
    this.mBox.style.left=this.mBox.offsetLeft+20+'px';
    this.current(this.mBox,2);
    this.rang(this.mBox,2);
    }
    }
    if(this.direct == 'top'){ //检测到从上往下推箱子
    if(key!=83){
    this.iftouch = false;
    this.crash();
    }else{
    this.mBox.style.top=this.mBox.offsetTop+20+'px';
    this.current(this.mBox,2);
    this.rang(this.mBox,2);
    }
    }
    if(this.direct == 'bottom'){ //检测到从下往上推箱子
    if(key!=87){
    this.iftouch = false;
    this.crash();
    }else{
    this.mBox.style.top=this.mBox.offsetTop-20+'px';
    this.current(this.mBox,2);
    this.rang(this.mBox,2);
    }
    }
    if(this.direct == 'right'){ //检测到从右往左推箱子
    if(key!=65){
    this.iftouch = false;
    this.crash();
    }else{
    this.mBox.style.left=this.mBox.offsetLeft-20+'px';
    this.current(this.mBox,2);
    this.rang(this.mBox,2);
    }
    }
    }
    },

    //初始化对象
    init:function(){
    this.createMap();
    this.createObj();
    this.createMbox();
    this.move(this.mObj);
    this.forbidden();
    }

    }
    game.init();

    </script>


    </html>

  • 相关阅读:
    50个c/c++源代码网站
    VC 编译参数介绍
    基于Winsock API的VC网络编程实战
    notable
    The Beauty of Eventlet
    Transparent HTTP proxy
    用Python写一个本地Sogou代理服务器程序
    普林斯顿大学的计算机学课的作业
    HTML.py a Python module to easily generate HTML tables and lists
    Transparent HTTP proxy in python
  • 原文地址:https://www.cnblogs.com/shibaxiong/p/4588082.html
Copyright © 2011-2022 走看看