3D遥控坦克大战

昨天参加了hack day的一个比赛,赛制大致是:24小时,自由组队2~4人,任意发挥。运气比较好,拿了第三名和最佳创意奖。

建议先看看这个demo,bug是有的,chrome下玩玩,测试测试就行,O(∩_∩)O~

  DEMO:http://qianduannotes.sinaapp.com/3dtank/html/index.html

基本效果:

img

关于

懒得去SAE上折腾,没把那另外一半的功能补上,不过我还是介绍下这几个没补上功能吧。

  1. 音效。开始音乐是比较古老的坦克大战开机音乐。

    ① 开始音效 点击播放

    ② 发子弹 点击播放

    ③ 击中坦克 点击播放

    ④ 爆炸 点击播放

  ②和③是自己录制的,呵呵,DIY的东西才好玩。

  2. 登录验证

  img

  采用的是解锁,这个创意应该是非常不错的,当登录的时候,A、B玩家下方会生成一个如上图的canvas解锁块,当然这个解锁卡也会通过socket传送到手机遥控端,手机解锁成功后方可登录。

  img

  3. 坦克360°旋转

  由于键盘控制只能上下左右,所以360°是转不出来的..刚想截一张手机控制游戏的图,总是报错...囧(后台用的是php,socket控制信号传输,刚打开手机端网页的时候php socket报错)。

手机遥控端视图:

img

  这里主要利用的是手机多点触控,touchstart,touchmove,touchend这三个事件。

function canvasAddListener() {
    canvas.addEventListener('touchstart', onTouchStart, false);
    canvas.addEventListener('touchmove', onTouchMove, false);
    canvas.addEventListener('touchend', onTouchEnd, false);
}

  4. 重新开始游戏和打死坦克添加效果等

以上都是没有公开显示出来的效果,下次弄好了再上传吧,嘻嘻。

先说说前台

前台主要采用的是css3和js(这是废话)。

1. css3构建一个3D游戏场地

效果:

img

.box {
    width:500px;
    height:500px;
    position:relative;
    -webkit-transform-style: preserve-3d;
    /*-webkit-transform: rotateY(40deg);*/
    -webkit-transition:all 1s ease-in-out;
}
.inBox {
    width:300px;
    height:300px;
    overflow: hidden;
    text-align: center;
    box-shadow: 0px 0px 2px white;
    background:rgba(255,255,255,.2);
    /*background:#779443;*/
    position:absolute;
    top:100px;
    left:100px;
    color:white;
}
.box-forward {
    -webkit-transform: rotateY(0deg) translateZ(150px);
}
.box-back {
    -webkit-transform: rotateY(180deg) translateZ(150px);
}
.box-left {
    -webkit-transform: rotateY(270deg) translateZ(150px);
}
.box-right {
    -webkit-transform: rotateY(90deg) translateZ(150px);
}
.box-top {
    -webkit-transform:rotateX(90deg) translateZ(150px);
}
.box-bottom {
    -webkit-transform:rotateX(-90deg) translateZ(150px);
}

上面是css部分,比赛过程中,参看了下张鑫旭大哥的文章(之前这块还不是很了解的),文章链接。就不细说了。

下面是HTML部分:

<div class="w">
    <div class="box">
        <div class="inBox box-forward" data-num="upF rightF downF leftF" data-v="1"><div></div></div>
        <div class="inBox box-back" data-num="upF leftF downF rightF" data-v="2"><div></div></div>
        <div class="inBox box-left" data-num="upF forwardF downF backF" data-v="3"><div></div></div>
        <div class="inBox box-right" data-num="upF backF downF forwardF" data-v="4"><div></div></div>
        <div class="inBox box-top" data-num="backF rightF forwardF leftF" data-v="5"><div></div></div>
        <div class="inBox box-bottom" data-num="backF leftF forwardF rightF" data-v="6"><div></div></div>
    </div>
</div>

2. JS这块,写了比较多。

img 3D Tank JavaScript

拆开分析下:

  ① Tank对象

DIY坦克(还行,哈哈哈~):

img

var Tank = function(setting){
    var opts = setting || {};
    this.container = opts.container || forwardF;
    this.dataset   = this.container.getAttribute('data-num').split(" ");
       //....
}

Tank.prototype = {
    init: function(tankId, debug){
        //....

        if(debug){
            this.debug = true;
            this.keySet = debug.keySet;
            window.addEventListener("keydown", function(){
                this_.move_debug();
            }, false);
        }

        window.addEventListener(this.stopN, function(){
            clearInterval(this_.timer);
        }, false);

    },
    paintTank: function(){
       //....
    },
    switchPainter: function(N){
        //....
    },
    direction_debug: function(){
        //....
    },
    move_debug: function(){
        //....
    },
    ani: function(){
        //....
    },
    stopAni: function(){
        var event = document.createEvent('HTMLEvents');
        event.initEvent(this.stopN, true, true);
      event.eventName = this.stopN;
        window.dispatchEvent(event);
    }, 
    detective: function(){
        //....
    },

    detectiveTank: function(){

       //....
    },

    shooter: function(){
       //....
    },
    destroy: function(){
       //....
    }
}

  

  难点在于一些边界的判断,但是好好考虑下也不算什么难点咯~这些代码中应该看到了很多debug之类的变量和函数,因为我写了两种模式,一种是手机端玩,一中是电脑键盘控制(debug模式)。

  ②子弹对象

/**
* @Bullet Tank
* @attrs bullet, timer, gap, x, yadsjsw
*/
var Bullet = function(gA, x, y, tankId){  
           //....
};Bullet.prototype = {
    move: function(){
         //....
    },
    checkHit: function(){
         //....
    },
    hit: function(tTank){
        //....
    },
    destroy: function(){
        clearInterval(this.timer);
        if (typeof tanks[this.tankId] != 'undefined') {
            var tankArr = tanks[this.tankId].bulletBox;
            tankArr.splice(tankArr.indexOf(this),1);
        }
        this.bullet.parentNode && this.bullet.parentNode.removeChild(this.bullet);
    }
}

  和坦克一样,都有一个destroy函数,销毁对象。

  ③ 构建AI对象

function createRandomTank(obj) {
    var obj = obj || {cx: 150, cy: 150, color:"#ECFF0B", bColor:"#ECFF0B", speed: 1, container: backF};
    var rTank = new Tank(obj);
    rTank.init(String(randomTank));
    rTank.shooterTimer = null;
    rTank.shooterTimer = setInterval(function(){
        if(Math.random() > 0.8){
            rTank.shooter();   
        }
    }, 200);

    rTank.moveTimer = null;
    rTank.moveTimer = setInterval(function(){
        if(Math.random() > 0.4){
            var ang = Math.PI * 2 * Math.random();
            rTank.move.call(rTank, ang);
        }
    }, 1200);
    tanks[randomTank] = rTank;
    randomTank++;
}

  机器人是个麻烦的东西,这块虽然不难,销毁他们的时候费了不少力气~主要是那么timer要跟着一起销毁。

  ④ 构建对象说明

tanks[0] = new Tank({cx: 50, cy: 220, color:"red", bColor:"red"});
tanks[0].init('0', {
    keySet:debug_keySet[0]
});

  这里需要说明一下,只要init后面加了第二个参数,就是调试模式,也就是说键盘是可以控制运行的。

  设置了一个全局变量

                //a   w   d  s   j      ←   ↑   →  ↓   p
debug_keySet = [[65, 87, 68, 83, 74], [37, 38, 39, 40, 80]];

3. socket这块

  整个平台信息的交互就是以他为核心,socket这个东西还算比较新,所以学习的时候也没找到太多的资料,只能对着w3c的一些文档边试边做。

var socket;
function ws_init() {
    var host = "ws://192.168.86.1:1111/";
    // var host = "ws://202.114.20.79:1111/";
    try {
        socket = new WebSocket(host);
        logMsg('WebSocket - status '+socket.readyState);
        socket.onopen    = function(msg) { logMsg("Welcome - status "+this.readyState); send('display'); };
        socket.onclose   = function(msg) { logMsg("Disconnected - status "+this.readyState); };
        socket.onmessage = function(msg) { //.... };
    } catch(ex) {
        logMsg(ex);
    }
}
function send(msg) {
    try {
        socket.send(msg + '=');
    } catch(ex) {
        logMsg(ex);
    }
}

  socket在前端部分是非常简单的,就是三个事件onopen, onclose, onmessage来驱动,重点还是后台操作,真心麻烦!

后台部分

后台用的是php,本来打算使用nodeJS,不是十分熟练,24个小时的赛制花太长时间去学习也不现实,所以就用了比较熟悉的php来建立socket连接,还算成功吧。

这个部分以后有时间说。先碎觉~~

最后,别忘了这个DEMO哦, http://qianduannotes.sinaapp.com/3dtank/html/index.html

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务