PK创意闹新春,我正在参加「春节创意投稿大赛」,详情请看:春节创意投稿大赛
前言
款过年了,听说南方过年吃汤圆,北方过年吃饺子,如果南北方人同桌吃饭会怎么样,肯定会有一番争论,这种事情肯定争不出结果 ,开发一款对战游戏,谁有实力听谁的。
效果演示
需求分析
- 对战双方为:水饺,汤圆。
- 使用装水饺和汤圆的碗作为武器
- 使用水饺和汤圆作为子弹
- 要有对战的热闹氛围
- 子弹发射过多时要有堆积效果
- 汤圆和水饺不可以无限堆积,需要有消耗
玩法说明: 在手机屏幕两端分别放置红色碗形状,屏幕中间位置放置对战墙;对战双方通过频繁点击碗来向对战墙发射水饺或者汤圆,发射的水饺和汤圆会在对战墙两边角力,水饺或汤圆较多的一方会将对战墙推向另一方,当对战墙推到对方碗的位置时获得胜利。
代码设计
准备素材
水饺
汤圆
碗
对战平台布局
具体布局如图所示,div容器作为对战平台;两张碗图片位于容器上下两端,作为武器;红色背景5px高的div作为对战墙。
<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>