南北方人一桌过春节,吃水饺还是汤圆,开发一款水饺汤圆对战游戏,让实力说话!

666 阅读4分钟

PK创意闹新春,我正在参加「春节创意投稿大赛」,详情请看:春节创意投稿大赛

前言

款过年了,听说南方过年吃汤圆,北方过年吃饺子,如果南北方人同桌吃饭会怎么样,肯定会有一番争论,这种事情肯定争不出结果 ,开发一款对战游戏,谁有实力听谁的。

效果演示

在线体验

046928e20390f600052b3086dd765c2b.gif

需求分析

  1. 对战双方为:水饺,汤圆。
  2. 使用装水饺和汤圆的碗作为武器
  3. 使用水饺和汤圆作为子弹
  4. 要有对战的热闹氛围
  5. 子弹发射过多时要有堆积效果
  6. 汤圆和水饺不可以无限堆积,需要有消耗

玩法说明: 在手机屏幕两端分别放置红色碗形状,屏幕中间位置放置对战墙;对战双方通过频繁点击碗来向对战墙发射水饺或者汤圆,发射的水饺和汤圆会在对战墙两边角力,水饺或汤圆较多的一方会将对战墙推向另一方,当对战墙推到对方碗的位置时获得胜利。

代码设计

准备素材

水饺
shuijiao.png

汤圆
tangyuan.png


wan.png

对战平台布局

具体布局如图所示,div容器作为对战平台;两张碗图片位于容器上下两端,作为武器;红色背景5px高的div作为对战墙。

微信截图_20220107172516.png

<div id="div" style="position: relative;border:1px solid red;">
    <img id="shuijiao" src="${rc.contextPath}/static/image/wan.png" style="transform: rotate(180deg);z-index: 9999;">
    <div id="line" style="position: absolute;top:50%;height: 5px;background: red;width: 100%;border-radius: 5px;"></div>
    <img id="tangyuan" src="${rc.contextPath}/static/image/wan.png" style="bottom:0;">
</div>

游戏初始化

主要初始化游戏布局宽高、准备子弹集合、设置游戏参数,为睡觉和汤圆武器添加点击事件监听,代码如下。

//水饺子弹集合
var sArr = [];
//汤圆子弹集合
var tArr = [];
//子弹发射过程旋转角度
var deg = 1440;
//动画持续时间
var timer = 500;
//对战墙移动距离
var up = 0;
//出使化游戏平台宽高
var gameHeight = window.screen.height - 200;
var gameWidth = window.screen.width-100;
//子弹每层最大数量
var num = parseInt(gameWidth / 20);
var div = document.getElementById('div');
var line = document.getElementById('line');
line.style.top = gameHeight/2 + "px";
div.style.height = gameHeight + 'px';
div.style.width = gameWidth + 'px';
div.style.margin = '50px auto';
var shuijiao = document.getElementById('shuijiao');
var tangyuan = document.getElementById('tangyuan');
//给水饺武器添加点击事件监听
shuijiao.addEventListener("touchstart",function(e){
    sendShuijiao();
});
//给汤圆武器添加点击事件监听
tangyuan.addEventListener("touchstart",function(e){
    sendTangyuan();
});

子弹发射效果设计

子弹从各自武器中心位置向对战墙移动并旋转指定的角度(初始化时deg参数),当子弹到达对战墙时加入指定的子弹集合中。

具体逻辑为:在布局div容器中添加img元素,设置其宽高及相对容器的位置,使其处于武器中心;然后通过Jquery的animate函数使img元素以动画效果运动至对战墙中心位置,动画结束后将img加入子弹集合中备用。

function sendShuijiao(){
    var img = document.createElement('img');
    let id = 't' + new Date().getTime();
    img.id=id;
    img.src = '${rc.contextPath}/static/image/shuijiao.png';
    img.style.position = 'absolute';
    img.style.width = '20px';
    img.style.height = '20px';
    img.style.top = '20px';
    img.style.left = gameWidth/2 -10 +'px';
    img.style.transform = 'rotate(180deg)';
    div.append(img);
    $('#' + id).animate(
        {top:new Number(line.style.top.replace('px','')) - 10 - 10 * up + 'px'}, //180 指旋转度数
        {
            step: function(now,fix){
                $(this).css('-webkit-transform','rotate('+now*deg/timer+'deg)');
                $(this).css('-ms-transform','rotate('+now*deg/timer+'deg)');
                $(this).css('-moz-transform','rotate('+now*deg/timer+'deg)');
                $(this).css('-o-transform','rotate('+now*deg/timer+'deg)');
                $(this).css('-transform','rotate('+now*deg/timer+'deg)');
            },
            durantion:timer
        },'linear')
    setTimeout(function(){
        sArr.push(img)
    },timer)
}


function sendTangyuan(){
    var img = document.createElement('img');
    let id = 't' + new Date().getTime();
    img.id=id;
    img.src = '${rc.contextPath}/static/image/tangyuan.png';
    img.style.position = 'absolute';
    img.style.width = '20px';
    img.style.height = '20px';
    img.style.bottom = '20px';
    img.style.left = gameWidth/2 -10 + 'px';
    div.append(img);
    $('#' + id).animate(
        {bottom:gameHeight - new Number(line.style.top.replace('px','')) - 20 + 10 * up + 'px'}, //180 指旋转度数
        {
            step: function(now,fix){
                $(this).css('-webkit-transform','rotate('+now*deg/timer+'deg)');
                $(this).css('-ms-transform','rotate('+now*deg/timer+'deg)');
                $(this).css('-moz-transform','rotate('+now*deg/timer+'deg)');
                $(this).css('-o-transform','rotate('+now*deg/timer+'deg)');
                $(this).css('-transform','rotate('+now*deg/timer+'deg)');
            },
            durantion:timer
        },'linear')
    setTimeout(function(){
        tArr.push(img)
    },timer)

}

对战氛围设计

对战氛围主要体现在,对战双方发射的子弹要聚集在对战墙两侧不停的移动,表示对战双方都在努力的推动对战墙向另外一方移动,已达到对战的胜利。

具体实现逻辑为:在渲染子弹设置其css样式left属性时添加随机数,子弹的渲染频率50毫秒一次,最终会呈现子弹在其原来位置左右晃动,仿佛是在努力的使劲推对战墙,代码如下。

function calcLeft(i){
    var index = i % num;
    if(index % 2 === 0){
        return gameWidth/2 - parseInt(index / 2) * 20 + Math.random() * 5
    }else{
        return gameWidth/2 + parseInt(index / 2) * 20 + Math.random() * 5
    }
}

对战过程渲染设计

子弹渲染频率通过定时任务setInterval函数设置,以60帧为例则时间间隔为17毫秒;

为体现子弹堆积效果,在设置子弹的top、bottom属性时,减去相应的层高(parseInt(i / num) * 10)。

为使子弹不会出现无限堆积,水饺子弹和汤圆子弹做一对一抵消,同时双方各保留10个子弹,保证对战的效果持续。

通过判断对战墙位置于双方武器位置的对比,判断对战墙到达一方武器时,另一方获胜。

var task = setInterval(function(){
    for (var i = 0; i < tArr.length; i++) {
        $(tArr[i]).css({bottom:gameHeight - new Number(line.style.top.replace('px','')) - parseInt(i / num) * 10 - 16 + 'px',left:calcLeft(i) + 'px'});
    }
    for (var i = 0; i < sArr.length; i++) {
        $(sArr[i]).css({
            top: new Number(line.style.top.replace('px','')) - parseInt(i / num) * 10 - 16 + 'px',
            left: calcLeft(i) + 'px'
        });
    }
    var sl = sArr.length;
    var tl = tArr.length;
    up =( tl - sl)/10;
    var tempArr = tArr;
    if(up > 0){
        tempArr = sArr;
    }
    var len = 0;
    if(tempArr.length > 10 ){
        len = tempArr.length-10;
    }
    for (var i = 0; i < len; i++) {
        $(sArr.pop()).remove();
        $(tArr.pop()).remove();
    }
    if(new Number(line.style.top.replace('px','')) <= 40){
        alert("汤圆胜利了")
        clearInterval(task)
    }else if(new Number(line.style.top.replace('px','')) >= gameHeight - 50){
        alert("水饺胜利了")
        clearInterval(task)
    }
    $(line).css({top:new Number(line.style.top.replace('px','')) - up + 'px'});
},17);

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
    <script type="text/javascript" src="${rc.contextPath}/static/js/jquery.min.js"></script>
    <style type="text/css">
        *{
            margin:0;
            padding:0;
        }
        img{
            width:50px;
            position: absolute;
            left:calc(50% - 25px);
        }
    </style>
</head>
<body style="overflow: hidden;">
<a href="javascript:void(0);" onclick="reStart()" style="position: absolute;top:10px;right:10px;">重置</a>
<div id="div" style="position: relative;border:1px solid red;">
    <img id="shuijiao" src="${rc.contextPath}/static/image/wan.png" style="transform: rotate(180deg);z-index: 9999;">
    <div id="line" style="position: absolute;top:50%;height: 5px;background: red;width: 100%;border-radius: 5px;"></div>
    <img id="tangyuan" src="${rc.contextPath}/static/image/wan.png" style="bottom:0;">
</div>
<script>
    //水饺子弹集合
    var sArr = [];
    //汤圆子弹集合
    var tArr = [];
    //子弹旋转角度
    var deg = 1440;
    //动画只需时间
    var timer = 500;
    //对战墙移动距离
    var up = 0;
    //出使化游戏平台宽高
    var gameHeight = window.screen.height - 200;
    var gameWidth = window.screen.width-100;
    var num = parseInt(gameWidth / 20);
    var div = document.getElementById('div');
    var line = document.getElementById('line');
    var shuijiao = document.getElementById('shuijiao');
    var tangyuan = document.getElementById('tangyuan');
    div.style.height = gameHeight + 'px';
    div.style.width = gameWidth + 'px';
    div.style.margin = '50px auto';
    line.style.top = gameHeight/2 + "px";
    shuijiao.addEventListener("touchstart",function(e){
        sendShuijiao();
    });
    tangyuan.addEventListener("touchstart",function(e){
        sendTangyuan();
    });
    function sendShuijiao(){
        var img = document.createElement('img');
        let id = 't' + new Date().getTime();
        img.id=id;
        img.src = '${rc.contextPath}/static/image/shuijiao.png';
        img.style.position = 'absolute';
        img.style.width = '20px';
        img.style.height = '20px';
        img.style.top = '20px';
        img.style.left = gameWidth/2 -10 +'px';
        img.style.transform = 'rotate(180deg)';
        div.append(img);
        $('#' + id).animate(
            {top:new Number(line.style.top.replace('px','')) - 10 - 10 * up + 'px'}, //180 指旋转度数
            {
                step: function(now,fix){
                    $(this).css('-webkit-transform','rotate('+now*deg/timer+'deg)');
                    $(this).css('-ms-transform','rotate('+now*deg/timer+'deg)');
                    $(this).css('-moz-transform','rotate('+now*deg/timer+'deg)');
                    $(this).css('-o-transform','rotate('+now*deg/timer+'deg)');
                    $(this).css('-transform','rotate('+now*deg/timer+'deg)');
                },
                durantion:timer
            },'linear')
        setTimeout(function(){
            sArr.push(img)
        },timer)
    }


    function sendTangyuan(){
        var img = document.createElement('img');
        let id = 't' + new Date().getTime();
        img.id=id;
        img.src = '${rc.contextPath}/static/image/tangyuan.png';
        img.style.position = 'absolute';
        img.style.width = '20px';
        img.style.height = '20px';
        img.style.bottom = '20px';
        img.style.left = gameWidth/2 -10 + 'px';
        div.append(img);
        $('#' + id).animate(
            {bottom:gameHeight - new Number(line.style.top.replace('px','')) - 20 + 10 * up + 'px'}, //180 指旋转度数
            {
                step: function(now,fix){
                    $(this).css('-webkit-transform','rotate('+now*deg/timer+'deg)');
                    $(this).css('-ms-transform','rotate('+now*deg/timer+'deg)');
                    $(this).css('-moz-transform','rotate('+now*deg/timer+'deg)');
                    $(this).css('-o-transform','rotate('+now*deg/timer+'deg)');
                    $(this).css('-transform','rotate('+now*deg/timer+'deg)');
                },
                durantion:timer
            },'linear')
        setTimeout(function(){
            tArr.push(img)
        },timer)

    }

    var task = setInterval(function(){
        for (var i = 0; i < tArr.length; i++) {
            $(tArr[i]).css({bottom:gameHeight - new Number(line.style.top.replace('px','')) - parseInt(i / num) * 10 - 16 + 'px',left:calcLeft(i) + 'px'});
        }
        for (var i = 0; i < sArr.length; i++) {
            $(sArr[i]).css({
                top: new Number(line.style.top.replace('px','')) - parseInt(i / num) * 10 - 16 + 'px',
                left: calcLeft(i) + 'px'
            });
        }
        var sl = sArr.length;
        var tl = tArr.length;
        up =( tl - sl)/10;
        var tempArr = tArr;
        if(up > 0){
            tempArr = sArr;
        }
        var len = 0;
        if(tempArr.length > 10 ){
            len = tempArr.length-10;
        }
        for (var i = 0; i < len; i++) {
            $(sArr.pop()).remove();
            $(tArr.pop()).remove();
        }
        if(new Number(line.style.top.replace('px','')) <= 40){
            alert("汤圆胜利了")
            clearInterval(task)
        }else if(new Number(line.style.top.replace('px','')) >= gameHeight - 50){
            alert("水饺胜利了")
            clearInterval(task)
        }
        $(line).css({top:new Number(line.style.top.replace('px','')) - up + 'px'});
    },16);

    function calcLeft(i){
        var index = i % num;
        if(index % 2 === 0){
            return gameWidth/2 - parseInt(index / 2) * 20 + Math.random() * 5
        }else{
            return gameWidth/2 + parseInt(index / 2) * 20 + Math.random() * 5
        }
    }

    function reStart(){
        window.location.reload();
    }
</script>

</body>
</html>