尝试用canvas写小游戏
还是习惯直接开门见山,这个游戏是有一个老师抓作弊的学生,老师背身,点学生开始加分,老师会不定时回头,如果老师回头还在点学生在,就会被抓住,游戏game over。
1、写游戏首先是loading条,于是我们就有了以下的一端js代码
function game() { var that = this; this.isStart = false; this.isFlag = true; this.percent = 0; this.init = function () { } this.loading = function (percent) { this.percent = this.percent + percent; $("#progressbarbj").css("width", this.percent + "%"); $(".game-percent").text(this.percent); if (this.percent == 100) { $(".game-loading").animate({ top: "-100%" }, 500, function () { that.isStart = true; that.start(); }); } }; this.start = function () { }; }
以上代码可以看出,对外开放了一个Init的function,在页面里面调用Init,既可以完成游戏的初始化,因为加载是多段的,所以封装了一个Loading方法,用来加载各类图片资源。
2、游戏里面的角色分为,老师和学生(分左右两个学生),于是我们定义了三段配置。
var role_config = { teacher: { first: { path: "img/10001.png" }, front: { imgs: [ { path: "img/10001.png?v=2" }, { path: "img/10002.png?v=2" }, { path: "img/10003.png?v=2" }, { path: "img/10004.png?v=2" }, { path: "img/10005.png?v=2" }, { path: "img/10006.png?v=2" }, { path: "img/10007.png?v=2" }, { path: "img/10008.png?v=2" } ], time: 50, isLoop: true, loopTime: 500 }, trunback: { imgs: [{ path: "img/20001.png" }, { path: "img/20002.png" }, { path: "img/20003.png" }, { path: "img/20004.png"}], time: 50 }, back: { imgs: [ { path: "img/30001.png?v=2" }, { path: "img/30002.png?v=2" }, { path: "img/30003.png?v=2" }, { path: "img/30004.png?v=2" }, { path: "img/30005.png?v=2" } ], time: 50, isLoop: true, loopTime: 400 }, strun: { imgs: [ { path: "img/40001.png?v=2" }, { path: "img/40002.png?v=2" }, { path: "img/40003.png?v=2" }, { path: "img/40004.png?v=2" }, { path: "img/40005.png?v=2" }, { path: "img/40006.png?v=2" }, { path: "img/40007.png?v=2" } ], time: 50 }, trunfront: { imgs: [ { path: "img/50001.png?v=2" }, { path: "img/50002.png?v=2" }, { path: "img/50003.png?v=2" }, { path: "img/50004.png?v=2" }, { path: "img/50005.png?v=2" } ], time: 50 }, catchup: { imgs: [ { path: "img/60001.png?v=2" }, { path: "img/60002.png?v=2" }, { path: "img/60003.png?v=2" }, { path: "img/60004.png?v=2" }, { path: "img/60005.png?v=2" }, { path: "img/60006.png?v=2" }, { path: "img/60007.png?v=2" }, { path: "img/60008.png?v=2" } ], time: 50 } }, studentLeft: { up: { imgs: [{ path: "img/student_10001.png"}], time: 100 }, bow: { imgs: [ { path: "img/student_10002.png" }, { path: "img/student_10005.png" }, { path: "img/student_10006.png" } ], time: 100, isRandom: true } }, studentRight: { up: { imgs: [{ path: "img/student_10003.png"}], time: 100 }, bow: { imgs: [ { path: "img/student_10004.png" }, { path: "img/student_10007.png" }, { path: "img/student_10008.png" } ], time: 100, isRandom: true } } };
front、back、trunback、up等,均为动作,其中的time是图片切换的时长,这样就可以调解动画的速度,loopTime,是单轮循环的时长,因为图片切换到最后一张了以后,可能会有停顿。
3、开始写角色类了。
首先我们定义了一个角色的基类,代码如下:
function role(target) { var that = this; this.target = target; this.actionHandler; this.clearHandler; this.index = 0; this.load = function (array, holder, point, total, callback) { var index = 0; for (var i = 0; i < array.length; i++) { var img = new Image(); img.src = array[i].path; var p = { img: img, x: point.x, y: point.y, w: point.w, h: point.h }; holder.push(p); img.onload = function () { index++; if (index == total - 1) { that.invoke(callback); } }; } }; this.animate = function (points, model, callback) { var loopTime = model.loopTime || 1000; if (this.clearHandler) { clearInterval(this.clearHandler); } if (model.isRandom) { var random = Math.random(); var index = Math.floor(random * points.length); var p = []; p.push(points[index]); this.action(p, model.time, callback); } else { this.action(points, model.time, callback); } if (model.isLoop) { this.clearHandler = setInterval(function () { that.action(points, model.time, callback); }, loopTime); } }; this.action = function (points, time, callback) { var i = 0; if (this.actionHandler) { clearInterval(this.actionHandler); } var point = points[i]; this.draw(point); i++; this.actionHandler = setInterval(function () { if (i == points.length) { clearInterval(that.actionHandler); that.invoke(callback); return; } point = points[i]; that.draw(point); i++; }, time); }; this.draw = function (point) { this.target.clearRect(point.x, point.y, point.w, point.h); this.target.beginPath(); this.target.drawImage(point.img, point.x, point.y, point.w, point.h); this.target.fill(); }; this.invoke = function (callback) { if (typeof callback == "function") { callback(); } }; this.stop = function (callback) { if (this.actionHandler) { clearInterval(this.actionHandler); } if (this.clearHandler) { clearInterval(this.clearHandler); } this.invoke(callback); }; };
我们有个draw方法用来绘制,有个action方法用来连续绘制形成动画,有个animate则是根据逻辑进行动画播放了。
接着我们开始写teacher类和Student类,代码如下:
function teacher(target) { role.call(this, target); var that = this; this.model; this.first = {}; this.imgs = { front: [], trunback: [], back: [], strun: [], trunfront: [], catchup:[] }; this.init = function (point, callback) { this.model = role_config.teacher; this.load(this.model.front.imgs, this.imgs.front, point, this.model.front.imgs.length, callback); }; this.loadtrunback = function (point, callback) { this.load(this.model.trunback.imgs, this.imgs.trunback, point, this.model.trunback.imgs.length, callback); }; this.loadback = function (point, callback) { this.load(this.model.back.imgs, this.imgs.back, point, this.model.back.imgs.length, callback); }; this.loadstrun = function (point, callback) { this.load(this.model.strun.imgs, this.imgs.strun, point, this.model.strun.imgs.length, callback); }; this.loadtrunfront = function (point, callback) { this.load(this.model.trunfront.imgs, this.imgs.trunfront, point, this.model.trunfront.imgs.length, callback); }; this.loadcatchup = function (point, callback) { this.load(this.model.catchup.imgs, this.imgs.catchup, point, this.model.catchup.imgs.length, callback); }; this.front = function (callback) { this.animate(this.imgs.front, this.model.front, callback); }; this.trunback = function (callback) { this.animate(this.imgs.trunback, this.model.trunback, callback); }; this.back = function (callback) { this.animate(this.imgs.back, this.model.back, callback); }; this.strun = function (callback) { this.animate(this.imgs.strun, this.model.strun, callback); }; this.trunfront = function (callback) { this.animate(this.imgs.trunfront, this.model.trunfront, callback); }; this.catchup = function (callback) { this.animate(this.imgs.catchup, this.model.catchup, callback); }; } function studentLeft(target) { role.call(this, target); var that = this; this.model; this.first = {}; this.imgs = { bow: [], up: [] }; this.init = function (point, callback) { this.model = role_config.studentLeft; this.load(this.model.up.imgs, this.imgs.up, point, this.model.bow.imgs.length); this.load(this.model.bow.imgs, this.imgs.bow, point, this.model.bow.imgs.length, callback); }; this.bow = function (callback) { this.animate(this.imgs.bow, this.model.bow, callback); }; this.up = function (callback) { this.animate(this.imgs.up, this.model.up, callback); }; } function studentRight(target) { role.call(this, target); var that = this; this.model; this.imgs = { bow: [], up: [] }; this.init = function (point, callback) { this.model = role_config.studentRight; this.load(this.model.up.imgs, this.imgs.up, point, this.model.bow.imgs.length); this.load(this.model.bow.imgs, this.imgs.bow, point, this.model.bow.imgs.length, callback); }; this.bow = function (callback) { this.animate(this.imgs.bow, this.model.bow, callback); }; this.up = function (callback) { this.animate(this.imgs.up, this.model.up, callback); }; }
分别继承于role,然后有一些自己的动作。
4、然后就是拼接了,根据游戏逻辑来拼接各个动作,于是我们最初的game类,就有了充实,代码如下:
function game() { var that = this; this.teacher; this.studentLeft; this.studentRight; this.isOver = false; this.isStart = false; this.isFlag = true; this.index = 0; this.success = false; this.username = ''; this.percent = 0; this.number = 0; this.overHandler; this.left = {}; this.right = {}; this.time = 0; this.score = 0; this.number = 0; this.status = 0; this.interval = 0; this.sInterval = 0; this.level = [3, 2, 1]; this.difficulty = 0; this.gameTime = 1000; this.ticket = ''; this.totalScore = 0; this.totalRankingCount = 0; this.init = function () { this.left = { scoreContainer: ".game-score-left", name: "left", isPress: false, handler: null, score: 0, step: 5, time: 0 }; this.right = { scoreContainer: ".game-score-right", name: "right", isPress: false, handler: null, score: 0, step: 5, time: 0 }; var teacher_board = document.getElementById("gameteacher"); var studentLeft_board = document.getElementById("gamestudentleft"); var studentRight_board = document.getElementById("gamestudentright"); this.teacher = new teacher(teacher_board.getContext("2d")); this.studentLeft = new studentLeft(studentLeft_board.getContext("2d")); this.studentRight = new studentRight(studentRight_board.getContext("2d")); this.teacher.init({ x: 0, y: 0, w: teacher_board.width, h: teacher_board.height }, function () { that.loading(10); }); this.teacher.loadtrunback({ x: 0, y: 0, w: teacher_board.width, h: teacher_board.height }, function () { that.loading(10); }); this.teacher.loadback({ x: 0, y: 0, w: teacher_board.width, h: teacher_board.height }, function () { that.loading(10); }); this.teacher.loadstrun({ x: 0, y: 0, w: teacher_board.width, h: teacher_board.height }, function () { that.loading(10); }); this.teacher.loadtrunfront({ x: 0, y: 0, w: teacher_board.width, h: teacher_board.height }, function () { that.loading(10); }); this.teacher.loadcatchup({ x: 0, y: 0, w: teacher_board.width, h: teacher_board.height }, function () { that.loading(10); }); this.studentLeft.init({ x: 0, y: 0, w: studentLeft_board.width, h: studentLeft_board.height }, function () { that.loading(20); }); this.studentRight.init({ x: 0, y: 0, w: studentRight_board.width, h: studentRight_board.height }, function () { that.loading(20); }); }; this.loading = function (percent) { this.percent = this.percent + percent; $("#progressbarbj").css("width", this.percent + "%"); $(".game-percent").text(this.percent); if (this.percent == 100) { $(".game-loading").animate({ top: "-100%" }, 500, function () { that.isStart = true; that.start(); that.event(); }); } }; this.start = function () { this.isFlag = false; that.studentLeft.up(); that.studentRight.up(); $(".game-time-text").text((this.gameTime - this.time).toFixed(1)); this.teacher.front(function () { that.teacher.trunback(function () { that.status = status_config.back; that.isFlag = true; that.teacher.back(); }); }); this.listen(); }; this.listen = function () { this.overHandler = setInterval(function () { if (that.isStart && !that.isOver) { that.isCatch(); that.startCount(); if (that.isFlag) { if (that.status == status_config.back) { that.front(that.level[that.difficulty]); } else { that.back(); } } } }, 100); }; this.gameOver = function () { this.teacher.catchup(function () { $(".game-catch-up").show(); }); }; this.timeEnd = function () { $(".game-over").show(); }; this.gameEnd = function () { this.isOver = true; if (this.overHandler) { clearInterval(this.overHandler); } }; this.front = function (intervel) { var r = Math.random(); if (this.time - this.interval >= intervel) { if (r < 0.6) { this.isFlag = false; this.teacher.trunfront(function () { that.interval = that.time; that.status = status_config.front; that.isFlag = true; that.teacher.front(); }); } } else if (this.time - this.sInterval >= 1) { if (r < 0.1) { this.isFlag = false; that.sInterval = that.time; this.teacher.strun(function () { that.isFlag = true; that.teacher.back(); }); } } }; this.back = function () { var r = Math.random(); if (this.time - this.interval >= 1) { if (r < 0.8) { this.isFlag = false; that.status = status_config.back; this.teacher.trunback(function () { that.interval = that.time; that.isFlag = true; that.teacher.back(); }); } } }; this.isCatch = function () { if (!this.isOver) { if (this.status == status_config.front) { if (this.left.isPress || this.right.isPress) { this.catchUp(); return true; } } } return false; }; this.catchUp = function () { this.gameEnd(that.gameOver()); }; this.startCount = function () { this.time = this.time + 0.1; this.difficulty = parseInt(this.time / 10); this.difficulty = this.difficulty >= this.level.length ? this.level.length - 1 : this.difficulty; this.score = this.left.score + this.right.score; $(".game-score-text").text(this.score); if (this.time >= this.gameTime) { this.timeEnd(that.gameEnd()); } }; this.pressDown = function (model) { if (model.handler) { clearInterval(model.handler); } if (this.isStart && !this.isOver) { model.score = model.score + model.step; model.isPress = true; this.scoreAction(model.step, model.scoreContainer); model.handler = setInterval(function () { if (model.isPress) { model.time = model.time + 0.2; if (model.time >= 0.6) { model.step = 10; } if (model.time >= 1.2) { model.step = 50; } if (model.time >= 1.8) { model.step = 100; } model.score = model.score + model.step; that.scoreAction(model.step, model.scoreContainer); } }, 200); } }; this.scoreAction = function (score, container) { var img = $('<img src="img/' + score + '.png" />'); $(container).append(img); img.animate({ "top": "-60" }, 500, function () { setTimeout(function () { img.fadeOut(1000, function () { img.remove(); }); }, 1000); }); }; this.pressUp = function (model) { clearInterval(model.handler); model.isPress = false; model.time = 0; model.step = 5; }; this.event = function () { $(".game-student-left").bind({ "mousedown": function () { that.studentLeft.bow(); that.pressDown(that.left); return false; }, "mouseup": function () { that.studentLeft.up(); that.pressUp(that.left); return false; }, "touchstart": function () { that.studentLeft.bow(); that.pressDown(that.left); return false; }, "touchend": function () { that.studentLeft.up(); that.pressUp(that.left); return false; } }); $(".game-student-right").bind({ "mousedown": function () { that.studentRight.bow(); that.pressDown(that.right); return false; }, "mouseup": function () { that.studentRight.up(); that.pressUp(that.right); return false; }, "touchstart": function () { that.studentRight.bow(); that.pressDown(that.right); return false; }, "touchend": function () { that.studentRight.up(); that.pressUp(that.right); return false; } }); }; }; var status_config = { front: 0, back: 1 }
代码到这里就结束了,是不是很简单,欢迎各位大神指正,小弟刚学js,只因C#博文都没人赞,所以试试js看看。
试玩地址:点这里
本人对代码不做任何知识产权限制,也不保证所有的代码皆为原创。