一直想用canvas写一个魂斗罗游戏,但是发现自己水平和学习能力差太多,收集好素材之后发现一个棍子英雄的小游戏挺火,就产生了写本文这个游戏的想法。直接上demo,建议在chrome下:点我。
简单说下写这个小游戏遇到的2个坑爹问题:
1.之前下的素材music.mp3这个文件,其实是3段音频。最后几秒是魂斗罗结束时的音乐,我想在结束的时候直接从调用最后几秒的音频。查了一下audio,很容易找到了audio的currentTime这个属性可以设置音频的播放位置。但是我本地测试的过程中,这个属性是一直不起作用的。后来百度(百度根本不好用),谷歌了半天才在stackoverflow上找到了一段描述,大概的意思就是需要一个sever环境,然后把测试环境放到了服务器环境就可以正常使用currentTime这个属性设置音频的播放起始位置了,一个大坑。
2.第二个大坑平时写transition的时候习惯的将transition-property写为all。比如transition:all 0s ease 0s;。在这个游戏中,当鼠标松开的时候棍子应该是以100% 100%的位置为远点进行旋转的。由于我写的是all,所以transfor-origin也产生了一个从默认的50% 50%到100% 100%的一个过渡过程,反应在页面上就是棍子开始明显转动的远点不是100% 100%的位置,但是动画完成到100%的时候原点是正确的。
剩下的就是逻辑实现,本来是想做到自适应和重置游戏的,最后多少都有点不足,重置游戏直接用刷新页面的暴力方式来解决了。页面有很多bug,不行就刷一次,哈哈。
直接上代码:
html部分:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Child'sPlay</title> <link href="style/style.css" rel="stylesheet" type="text/css"/> </head> <body> <div class="main" id="main"> <div id="score">当前得分<i class="current">0</i>分;最高得分<i class="top">0</i>分</div> <div class="bg"> <div class="move" id="move"> <div class="game-box" id="game_box"> <div class="contra" id="contra"></div> <div class="item" style="left: 0;"> <div class="land"><div class="stick" id="stick"></div> </div> </div> </div> </div> </div> </div> <div class="loading"> <progress id="progress" value="0" max="8"></progress> <h3>游戏方法:按住鼠标让棍子变长,使其放下的时候正好落入下一块陆地时放开鼠标。</h3> </div> <div class="begin"> <h3><i>点</i><i>击</i><i>键</i><i>盘</i><i>J</i><i>开</i><i>始</i><i>游</i><i>戏</i></h3> </div> <audio id="audio"> <source src="audio/start.mp3" type="audio/mpeg"/> </audio> <audio id="audio2"> <source src="audio/music.mp3" type="audio/mpeg"/> </audio> <audio id="audio3"> <source src="audio/Game_Over.mp3" type="audio/mpeg"/> </audio> <script src="js/jquery-1.11.1.min.js"></script> <script src="js/contra.js"></script> <script src="js/source.js"></script> <script src="js/record.js"></script> </body> </html>
css代码:
*{ margin: 0; padding: 0;} body{ overflow: hidden;} .loading{ position: absolute; left: 0; top: 0; right: 0; bottom: 0; z-index: 2; background: #000;} #progress{ 100%; height: 200px;border: 1px solid #0064B4;background-color:#e6e6e6;} progress::-webkit-progress-bar { background: #e6e6e6; } progress::-webkit-progress-value { background: #0064B4; } .loading h3{ text-align: center; color: #fff;} .begin{ position: absolute; 100%; background: url("img/begin.jpg") 0 0 no-repeat; z-index: 3;} .begin h3{ display: none; position: absolute; left: 0; top: 20px; 100%; text-align: center; color: #fff;} .begin h3 i{ -webkit-animation: slide_letters 1.8s linear 1s infinite; -moz-animation: slide_letters 1.8s linear 1s infinite; -ms-animation: slide_letters 1.8s linear 1s infinite; animation: slide_letters 1.8s linear 1s infinite} .main{ position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: hidden;} #score{ position: absolute; left: 0; top: 50px; 100%; text-align: center; color: #fff;} #score i{ color: red;} .main .bg{ background: url("img/bg.png") 0 0 repeat-x;} .land{ position: relative; float: left; background: url("img/land.png") 0 0 no-repeat; background-size: 100% 100%;} .move{ position: relative; left: 0;} .game-box{ position: fixed; bottom: 5%; 99999px;} .game-box .item{ position: absolute; top: 0;} #audio{ display: none; position: fixed; right: 20px; top: 20px;} .contra{ background: url("img/contra.png"); 97px; height: 109px; position: absolute; left: 0; bottom: 65%; z-index: 9;} .stick{ position: absolute; right: 0; bottom: 65%; 2px; background: gold; z-index: 99;} .rolling{ transition: transform 1s ease-in 0s;transform-origin:center bottom; -webkit-transform-origin:center bottom; -webkit-transform: rotate(90deg); transform: rotate(90deg); } .run{ 87px; height: 110px; background: url("img/run.png") 0px center no-repeat; overflow: hidden;-webkit-animation:run 1s steps(8) infinite;animation:run 1s steps(8) infinite;} @-webkit-keyframes run{ 100%{background-position: -696px 0px;} } @keyframes run{ 100%{background-position: -696px 0px;} } .begin h3 i:nth-child(1) { -webkit-animation-delay: 0.2s; -moz-animation-delay: 0.2s; -ms-animation-delay: 0.2s; animation-delay: 0.2s; } .begin h3 i:nth-child(2) { -webkit-animation-delay: 0.4s; -moz-animation-delay: 0.4s; -ms-animation-delay: 0.4s; animation-delay: 0.4s; } .begin h3 i:nth-child(3) { -webkit-animation-delay: 0.6s; -moz-animation-delay: 0.6s; -ms-animation-delay: 0.6s; animation-delay: 0.6s; } .begin h3 i:nth-child(4) { -webkit-animation-delay: 0.8s; -moz-animation-delay: 0.8s; -ms-animation-delay: 0.8s; animation-delay: 0.8s; } .begin h3 i:nth-child(5) { -webkit-animation-delay: 1s; -moz-animation-delay: 1s; -ms-animation-delay: 1s; animation-delay: 1s; } .begin h3 i:nth-child(6) { -webkit-animation-delay: 1.2s; -moz-animation-delay: 1.2s; -ms-animation-delay: 1.2s; animation-delay: 1.2s; } .begin h3 i:nth-child(7) { -webkit-animation-delay: 1.4s; -moz-animation-delay: 1.4s; -ms-animation-delay: 1.4s; animation-delay: 1.4s; } .begin h3 i:nth-child(8) { -webkit-animation-delay: 1.6s; -moz-animation-delay: 1.6s; -ms-animation-delay: 1.6s; animation-delay: 1.6s; } .begin h3 i:nth-child(9) { -webkit-animation-delay: 1.8s; -moz-animation-delay: 1.8s; -ms-animation-delay: 1.8s; animation-delay: 1.8s; } @-webkit-keyframes slide_letters { 0%,50% { color: #fff; } 25% { color: red;} } @-moz-keyframes slide_letters { 0%,50% { color: #fff; } 25% { color: red;} } @-ms-keyframes slide_letters { 0%,50% { color: #fff; } 25% { color: red;} } @keyframes slide_letters { 0%,50% { color: #fff; } 25% { color: red;} }
进度条加载source.js
var gameSource = { "bg":"style/img/bg.png", "contra":"style/img/contra.png", "land":"style/img/land.png", "run":"style/img/run.png", "begin":"style/img/begin.jpg" }; var loadNum = 0; function loading(){ for(i in gameSource){ var obj = new Image(); obj.src = gameSource[i]; //console.log(gameSource[i]) obj.onload = function(){ console.log(this.src+" completed"); //gameSource.splice(i,1); loadNum++; $("#progress").attr({"value":loadNum}); }; } } loading() var matchState = [1,1,1]; function checkState(){ var audioReadyState = [$("#audio")[0].readyState,$("#audio2")[0].readyState,$("#audio3")[0].readyState]; if(loadNum==8){ $("#progress").attr({"value":loadNum}); clearInterval(checkStateTimer); contraHero.begin(); $("#audio")[0].play(); } for(var i=0; i<audioReadyState.length;i++){ if(audioReadyState[i]==4 && matchState[i]){ matchState[i] = 0; console.log("test",i,audioReadyState,matchState) loadNum++; console.log(audioReadyState); } } } var checkStateTimer = setInterval(checkState,1000); //var a = []; //alert(~a); // -1 //alert(+a); // 0 //alert(++a); // 1 //alert(!a); // false
localStorage保存得分记录
if(localStorage.TopScore){ $("#score .top").html(localStorage.TopScore); } function writeTopScore(score){ if(!localStorage.TopScore || score > localStorage.TopScore){ localStorage.TopScore = score; $("#score .top").html(localStorage.TopScore); } }
contraHero主体逻辑代码
var contraHero = { canPlay : false, isOn:false,//标记按住鼠标 dom : { contra : $("#contra"), stick : $("#stick"), main : $("#main"), bg : $(".bg"), move : $("#move"), gameBox : $("#game_box"), audio : $("#audio"), audio2 : $("#audio2"), item : $("#game_box .item") }, data : { landWidth : 0, //适配后一个陆地的宽度 landHeight :0 , //适配后一个陆地的高度 stickHeight:0, //棍子长度 stickLong : "", //棍子边长定时器 currentItem : 0, //魂斗罗当前所在的item索引 itemPosition : [[0,1]] //每片陆地的left值和对应的陆地数 }, init : function(){ this.initEvent(); this.initUnit(); this.makeNextLand(); this.mainTitle(); }, //getLastPositionLeft : function(){ // var n = contraHero.data.itemPosition.length-1; // return contraHero.data.itemPosition[n][0] + contraHero.data.itemPosition[n][1]*contraHero.data.landWidth; //}, makeNextLand : function(){ var left = parseInt(Math.random()*500) + contraHero.data.itemPosition[contraHero.data.itemPosition.length-1][1] * contraHero.data.landWidth + contraHero.data.itemPosition[contraHero.data.itemPosition.length-1][0]; console.log(contraHero.data.itemPosition[contraHero.data.itemPosition.length-1][1] * contraHero.data.landWidth,contraHero.data.itemPosition[contraHero.data.itemPosition.length-1][0],left,"left") var landNo = parseInt(Math.random()*3)+1; contraHero.data.itemPosition.push([left,landNo]); var str = ''; for(var i=0;i<contraHero.data.itemPosition[contraHero.data.itemPosition.length-1][1];i++){ str +='<div class="land" style="'+contraHero.data.landWidth+'px;height:'+contraHero.data.landHeight+'px;">'+'</div>'; }; str ='<div class="item" style="left:'+contraHero.data.itemPosition[contraHero.data.itemPosition.length-1][0]+'px;height:'+contraHero.data.landHeight+'px;">'+str+'</div>'; contraHero.dom.gameBox.append(str); }, increase : function(){ if(contraHero.isOn){ contraHero.data.stickLong = setInterval(function(){ contraHero.data.stickHeight +=3; $("#stick").css({"height":contraHero.data.stickHeight+"px"}) },20) } }, initEvent : function(){ $(window).mousedown(function(){ contraHero.isOn = true; contraHero.data.stickHeight = 0; contraHero.increase() }); $(window).mouseup(function(){ contraHero.isOn = false; contraHero.data.stickHeight = $("#stick").height(); clearInterval(contraHero.data.stickLong); contraHero.putDownStick(); }); $(window).resize(function(){ contraHero.initEvent(); contraHero.initUnit(); }); }, initUnit : function(){ var screenHeight = contraHero.dom.main.height(); var ratio = screenHeight/489; var bgSizeWidth = parseInt(1284*ratio); contraHero.data.landWidth = parseInt(34*ratio); contraHero.data.landHeight = parseInt(122*ratio); var gameBoxHeight = contraHero.dom.gameBox.height(); contraHero.dom.bg.css({"background-size":bgSizeWidth+"px "+screenHeight+"px","height":screenHeight+"px"}); $(".land").css({"width":contraHero.data.landWidth+"px","height":contraHero.data.landHeight+"px"}); contraHero.dom.gameBox.css({"height":contraHero.data.landHeight+"px"}); $(".begin").css({"background-size":"100% "+screenHeight+"px","height":screenHeight+"px","left":"100%"}); contraHero.begin(); }, check : function(){ var min = contraHero.data.itemPosition[contraHero.data.currentItem+1][0] - contraHero.data.itemPosition[contraHero.data.currentItem][0] - contraHero.data.itemPosition[contraHero.data.currentItem][1] * contraHero.data.landWidth; var max = min + contraHero.data.itemPosition[contraHero.data.currentItem+1][1] * contraHero.data.landWidth; console.log(contraHero.data.currentItem,min,max,contraHero.data.stickHeight) if(contraHero.data.stickHeight >=min && contraHero.data.stickHeight <= max){ return true; }else{ return false; } }, begin: function () { $(".begin").css({"background-size":"100% "+screen.height+"px","height":screen.height+"px","left":"100%"}); $(".begin").animate({"left":"0"},6400,function(){ $(".loading").remove(); $(".begin h3").show(); $(document).on("keyup",function(e){ e = e || window.e; if(e.keyCode==74){ console.log("testet") $(".begin").remove(); contraHero.init(); console.log("j") $(document).off("keyup"); } }) }); }, moveScreen : function(n){ var distance = -contraHero.data.itemPosition[n][0] contraHero.dom.move.animate({"left":distance+"px"}); contraHero.dom.bg.animate({backgroundPositionX:distance}); }, mainTitle:function(){ contraHero.dom.audio2[0].play(); contraHero.loopMainTitleTimer = setInterval(this.checkMainTitle,1000); }, checkMainTitle:function(){ var curTime = contraHero.dom.audio2[0].currentTime; contraHero.dom.audio2[0].play(); if(curTime-64>=0){ contraHero.dom.audio2[0].load(); }; }, overMusic :function(){ //contraHero.dom.audio2[0].pause(); //contraHero.dom.audio2[0].currentTime = 140; //contraHero.dom.audio2[0].play(); $("#audio3")[0].play(); }, putDownStick : function(){ $("#stick").addClass("rolling"); setTimeout(this.run,1000); }, contraFixPosition : function(){ contraHero.dom.contra.animate({"left":contraHero.data.itemPosition[contraHero.data.currentItem][0] + contraHero.data.itemPosition[contraHero.data.currentItem][1] * contraHero.data.landWidth -70 +"px"}); console.log(contraHero.data.itemPosition[contraHero.data.currentItem][0],contraHero.data.itemPosition[contraHero.data.currentItem][1],"fix") }, run : function(){ contraHero.dom.contra.addClass("run"); console.log(parseInt(contraHero.dom.contra.css("left")),contraHero.data.stickHeight,"wtf") contraHero.dom.contra.animate({"left":parseInt(contraHero.dom.contra.css("left"))+contraHero.data.stickHeight+"px"},5*contraHero.data.stickHeight,function(){ if(contraHero.check()){ contraHero.data.currentItem++; $("#score .current").html(contraHero.data.currentItem); writeTopScore(contraHero.data.currentItem); contraHero.contraFixPosition(); contraHero.moveScreen(contraHero.data.currentItem); $("#stick").remove(); contraHero.dom.contra.removeClass("run"); contraHero.makeNextLand(); contraHero.dom.gameBox.find(".item").eq(contraHero.data.currentItem).find(".land:last").html('<div class="stick" id="stick"></div>'); }else{ //contraHero.dom.contra.removeClass("run"); // //contraHero.reset(); //contraHero.init(); contraHero.killMusic(); contraHero.overMusic(); $("#stick").css({"transform":"rotate(180deg)"}); $("#contra").animate({"top":"1000px"},7000,function(){ location.reload(); }); } }) }, killMusic:function(){ $("#audio")[0].pause(); $("#audio2")[0].pause(); $("#audio3")[0].pause(); clearInterval(contraHero.loopMainTitleTimer); }, reset : function(){ contraHero.dom.contra.siblings(".item:gt(0)").remove(); $("#stick").removeClass("rolling").removeAttr("style"); contraHero.dom.contra.removeAttr("style"); contraHero.dom.bg.css({"background-position-x":"0"}); contraHero.dom.move.removeAttr("style"); contraHero.data.stickHeight = 0; contraHero.data.itemPosition = [[0,1]]; } }
最后附上下载包,渣代码,见笑。ContraHero