我知道这个游戏命名得很烂,你喜欢的话,叫“亲贤人远小人”也行,黑点就是玩家,红点就是“贤臣”,绿点就是“小人”,反正是个游戏吧!游戏的剧本怎么写那是汉语言文学的问题~
自上次《【JavaScript】贪吃蛇》(点击打开链接)时隔两个月终于有时间再写写游戏了~
JavaScript真的是一门碉堡的语言,简单的一个js就可以写出一个游戏!没有c++与java写很久都没有把游戏写出来,而且里面的线程实在是烦死了~
据说这个游戏非常符合所谓OOP面向对象设计思想……其实这些软件工程的思想说多了也是放屁,关键是你懂得怎么一步一步,不看任何资料把程序写出来,
估计是因为面向对象设计思想,好记,再大型的程序也能写出来,所以也就应用广泛……
一、基本目标
是这样的一个游戏:
1、首先在一个400x400px的区域内,有三个点,一个黑、一个绿、一个红,你控制的黑点,目标是不让绿点碰到,并在被绿点碰到之前尽可能地碰到红点,Gameover之后显示所得积分,并且自动刷新页面,重新开始
2、你可以用左上角的上下左右来控制黑点,也可以用键盘的WASD或者←↑↓→去控制黑点的移动,当然,如果黑点碰到墙也会Gameover的,至于红点与绿点的移动当然是AI来控制,而且这红点与绿点如果一旦移出黑框会自动跳回进来,AI会做好控制。
3、黑点碰到红点之后就不停地加分,我认为这个游戏如果挂到手机也就是个Flappy Bird的游戏,问题,本猿猴不像高富帅那样有自己的云服务器~也就能这样教教后来者罢了
二、基本思想
纯粹的html+css+javascript无须任何库,兼容主流浏览器,
1、用html+css布置好游戏场景
2、写好javascript的键盘响应事件,此事件与黑点diva类分离,通过调用diva中的各种move方法,改变黑点的位置,黑点在移动过程中注意判断是否遇到红点、绿点与墙
3、写好红点类divb与绿点类的divc方法,开两个计时器,不停地调用他们的自移动方法,让这两点随机移动,绿点与红点在移动过程中也要注意判断是否遇到黑点,如果已经移动到墙角,注意要重新产生随机数,让其不出墙,不然一枝红杏出墙来,你就等着笑话吧
三、制作过程
1、用html+css布置好游戏场景,注意所有使用到position:absolute;,也就一个黑色框、黑点、红点、绿点,还有一句游戏说明与控制方向的表格,注意黑点、红点、绿点是要设定超过12px的宽与高,不然会变形,怎么用css布局图层在《【CSS】关于div的对齐与网页布局》(点击打开链接)已经说过了~这里不再大篇幅讲解,代码如下,我习惯在<script></script>之间完成脚本,所以没有用<script type="text/javascript" src="xx.js" ></script>,这鬼东西还不能写成<script type="text/javascript" src="xx.js" />,一个小游戏不是大工程就不要这样引用了,看得不爽:
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
</head>
<body>
黑点捉红点游戏,积分:<span id="count">0</span>,不要被绿点碰到!
<table border="1">
<tr>
<td></td>
<td><input type="button" value="上" onClick="move(this)" /></td>
<td></td>
</tr>
<tr>
<td><input type="button" value="左" onClick="move(this)" /></td>
<td></td>
<td><input type="button" value="右" onClick="move(this)" /></td>
</tr>
<tr>
<td></td>
<td><input type="button" value="下" onClick="move(this)" /></td>
<td></td>
</tr>
</table>
<div style="position:absolute; left:100px; top:100px; width:400px; height:400px; border:1px solid #000000;" ></div>
<div id="diva" style="position:absolute; left:101px; top:120px; background-color:#000000; width:20px; height:20px;"></div>
<div id="divb" style="position:absolute; left:200px; top:200px; background-color:#900000; width:20px; height:20px;"></div>
<div id="divc" style="position:absolute; left:200px; top:200px; background-color:#009000; width:20px; height:20px;"></div>
</body>
</html>
function move(obj){
switch(obj.value){
case "上":
diva.move_up();
break;
case "左":
diva.move_left();
break;
case "右":
diva.move_right();
break;
case "下":
diva.move_down();
break;
}
}
这里move的参数是obj,也就会把整个按钮传递过来,然后再用obj.value来取这个按钮的值,如果是上,则调用黑点类diva中的move_up()方法,如此类推
3、键盘响应函数也这个函数也是这样的道理,与《【JavaScript】贪吃蛇》(点击打开链接)中的那个完全一模一样
document.onkeydown = function(event) {
var code;
if (window.event) {
code = window.event.keyCode;
} else {
code = event.keyCode;
}
switch(code){
case 38:
diva.move_up();
break;
case 37:
diva.move_left();
break;
case 39:
diva.move_right();
break;
case 40:
diva.move_down();
break;
case 87:
diva.move_up();
break;
case 65:
diva.move_left();
break;
case 68:
diva.move_right();
break;
case 83:
diva.move_down();
break;
}
}
相应的键值则调用相应diva中的移动方法,
var code;
if (window.event) {
code = window.event.keyCode;
} else {
code = event.keyCode;
}
这句纯粹为了兼容主流浏览器的键盘响应,至少在ie与谷歌是没问题了,你的浏览器或者拉到手机就非主流那再想想办法吧……本文不是讨论兼容性的
4、好,上面说了这么多黑点类diva,那么这个类到底是如何呢?
function Diva(x,y){
this.x=x;
this.y=y;
this.move_up=function(){
this.y--;
document.getElementById("diva").style.top=this.y+"px";
this.ifredcatch();
this.ifgreencatch();
this.ifcross();
}
this.move_left=function(){
this.x--;
document.getElementById("diva").style.left=this.x+"px";
this.ifredcatch();
this.ifgreencatch();
this.ifcross();
}
this.move_right=function(){
this.x++;
document.getElementById("diva").style.left=this.x+"px";
this.ifredcatch();
this.ifgreencatch();
this.ifcross();
}
this.move_down=function(){
this.y++;
document.getElementById("diva").style.top=this.y+"px";
this.ifredcatch();
this.ifgreencatch();
this.ifcross();
}
this.ifredcatch=function(){
if((this.x>divb.x-20)&&(this.x<divb.x+20)&&(this.y>divb.y-20)&&(this.y<divb.y+20)){
count++;
document.getElementById("count").innerHTML = count.toString();
}
}
this.ifgreencatch=function(){
if((this.x>divc.x-20)&&(this.x<divc.x+20)&&(this.y>divc.y-20)&&(this.y<divc.y+20)){
alert("Game Over," + "积分:" + count);
history.go(0);
clearTimeout(timer1);
clearTimeout(timer2);
}
}
this.ifcross=function(){
if(!((this.x>100)&&(this.x<480)&&(this.y>100)&&(this.y<480))){
alert("Game Over," + "积分:" + count);
history.go(0);
clearTimeout(timer1);
clearTimeout(timer2);
}
}
}
首先在开头有构造方法,我们一会儿自然要在开头声明一个这样的东西:
var diva=new Diva(101,120);
表示黑点一开始在101,120这个位置,这里也与html中的布局代码,黑点的最初位置left:101px; top:120px;相对应:
<div id="diva" style="position:absolute; left:101px; top:120px; background-color:#000000; width:20px; height:20px;"></div>
然后这个类的各个移动方法很简单,
首先是这个类的x,y要自减,使得在脚本中黑点类diva的位置变化,然后控制上面css使得在外观上这个黑点发生变化,例如:
this.move_up=function(){
this.y--;
document.getElementById("diva").style.top=this.y+"px";
this.ifredcatch();
this.ifgreencatch();
this.ifcross();
}
向上移就是把y的坐标减1嘛,如果更新一下html+css中的黑点的位置
之后黑点自己每移动一次都要判断是否碰到红点、绿点、墙
(1)碰到红点的方法:
this.ifredcatch=function(){
if((this.x>divb.x-20)&&(this.x<divb.x+20)&&(this.y>divb.y-20)&&(this.y<divb.y+20)){
count++;
document.getElementById("count").innerHTML = count.toString();
}
}
如果黑点的坐标不超过红点的坐标的20px,那么黑点也就碰到红点,这个不用给大家画图了吧,高中甚至是初三的两点距离问题而已~
然后积分变量count+1,更新一下上面前台的count行内文本
当然,要在头部声明一个
var count = 0;
Javascript没有public与privated的概念,要声明类中成员请用this,反正任何东西都是public,黑点可以如上所示地调用红点里面的x,
(2)碰到绿点方法:
同理(1)碰到红点的方法,可得:
this.ifgreencatch=function(){
if((this.x>divc.x-20)&&(this.x<divc.x+20)&&(this.y>divc.y-20)&&(this.y<divc.y+20)){
alert("Game Over," + "积分:" + count);
<span style="white-space:pre"> </span>history.go(0);
clearTimeout(timer1);
clearTimeout(timer2);
}
}
当然,切记碰到绿点是结束游戏的动作,history.go(0);是刷新本页,清理红点与绿点计时器,不要红点与绿点再继续移动了,当然不清也没关系,毕竟已经刷新了嘛,还是清清把,当然,要在头部声明两个计时器,一个是红点每1毫秒动一次,绿点每1毫秒动一次,看gif就知道这个间隔并不快,而且游戏还挺好玩~
timer1 = setInterval("divb.move()", 1);
timer2 = setInterval("divc.move()", 1);
(3)是否碰墙
哎呀,这个太简单了,就是判断黑点的坐标是不是等于在墙里面,当然,这里要判断是否在100~480之类,因为div的坐标在其左上角,如果不是再结束游戏:
this.ifcross=function(){
if(!((this.x>100)&&(this.x<480)&&(this.y>100)&&(this.y<480))){
alert("Game Over," + "积分:" + count);
history.go(0);
clearTimeout(timer1);
clearTimeout(timer2);
}
}
5、红点类divb
function Divb(x,y){
this.x=x;
this.y=y;
this.move=function(){
var random=Math.floor(Math.random() * 5);
switch(random){
case 1:
if(!(this.y<100)){
this.y=this.y-5;
document.getElementById("divb").style.top=this.y+"px";
diva.ifredcatch();
break;
}
case 2:
if(!(this.x<100)){
this.x=this.x-5;
document.getElementById("divb").style.left=this.x+"px";
diva.ifredcatch();
break;
}
case 3:
if(!(this.x>480)){
this.x=this.x+5;
document.getElementById("divb").style.left=this.x+"px";
diva.ifredcatch();
break;
}
case 4:
if(!(this.y>480)){
this.y=this.y+5;
document.getElementById("divb").style.top=this.y+"px";
diva.ifredcatch();
break;
}
}
}
}
由于有构造方法要在头部声明:
var divb=new Divb(200,200);
里面的移动方法,首先用
var random=Math.floor(Math.random() * 5);
产生一个1~4的随机数
之后,做任意方向的移动,而且移动的幅度要大一点,否则黑点太容易碰到你就不好~
当然如果可以预见到这样的移动会出墙的话,则不移动了。
这里配合上面定义的计时器,那么红点就会不停地移动,
在红点移动的过程中要调用黑点类的ifredcatch()判断红点是否碰到黑点,做相应的处理。
6、绿点类divc
同理,红点类divb可得,当然,绿点的移动幅度要比红点更大,这样才更好地碰到黑色,让玩家更快输掉:
function Divc(x,y){
this.x=x;
this.y=y;
this.move=function(){
var random=Math.floor(Math.random() * 5);
switch(random){
case 1:
if(!(this.y<100)){
this.y=this.y-10;
document.getElementById("divc").style.top=this.y+"px";
diva.ifgreencatch();
break;
}
case 2:
if(!(this.x<100)){
this.x=this.x-10;
document.getElementById("divc").style.left=this.x+"px";
diva.ifgreencatch();
break;
}
case 3:
if(!(this.x>480)){
this.x=this.x+10;
document.getElementById("divc").style.left=this.x+"px";
diva.ifgreencatch();
break;
}
case 4:
if(!(this.y>480)){
this.y=this.y+10;
document.getElementById("divc").style.top=this.y+"px";
diva.ifgreencatch();
break;
}
}
}
}
当然,不要忘记在头部加入红点与绿点的声明哦,因为有构造方法!
var divb=new Divb(200,200);
var divc=new Divc(200,200);
综上所述,整个程序就写完了,全代码如下,复制到一个记事本,保存为*.html就可以玩了,注意保存的编码不要用ANSI,用Unicode最好用utf-8哦!原因在《【HTML】明明加了<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />却还是乱码的可能原因》(点击打开链接)已经说过
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
</head>
<body>
黑点捉红点游戏,积分:<span id="count">0</span>,不要被绿点碰到!
<table border="1">
<tr>
<td></td>
<td><input type="button" value="上" onClick="move(this)" /></td>
<td></td>
</tr>
<tr>
<td><input type="button" value="左" onClick="move(this)" /></td>
<td></td>
<td><input type="button" value="右" onClick="move(this)" /></td>
</tr>
<tr>
<td></td>
<td><input type="button" value="下" onClick="move(this)" /></td>
<td></td>
</tr>
</table>
<div style="position:absolute; left:100px; top:100px; width:400px; height:400px; border:1px solid #000000;" ></div>
<div id="diva" style="position:absolute; left:101px; top:120px; background-color:#000000; width:20px; height:20px;"></div>
<div id="divb" style="position:absolute; left:200px; top:200px; background-color:#900000; width:20px; height:20px;"></div>
<div id="divc" style="position:absolute; left:200px; top:200px; background-color:#009000; width:20px; height:20px;"></div>
</body>
</html>
<script>
var diva=new Diva(101,120);
var divb=new Divb(200,200);
var divc=new Divc(200,200);
var count = 0;
timer1 = setInterval("divb.move()", 1);
timer2 = setInterval("divc.move()", 1);
function Diva(x,y){
this.x=x;
this.y=y;
this.move_up=function(){
this.y--;
document.getElementById("diva").style.top=this.y+"px";
this.ifredcatch();
this.ifgreencatch();
this.ifcross();
}
this.move_left=function(){
this.x--;
document.getElementById("diva").style.left=this.x+"px";
this.ifredcatch();
this.ifgreencatch();
this.ifcross();
}
this.move_right=function(){
this.x++;
document.getElementById("diva").style.left=this.x+"px";
this.ifredcatch();
this.ifgreencatch();
this.ifcross();
}
this.move_down=function(){
this.y++;
document.getElementById("diva").style.top=this.y+"px";
this.ifredcatch();
this.ifgreencatch();
this.ifcross();
}
this.ifredcatch=function(){
if((this.x>divb.x-20)&&(this.x<divb.x+20)&&(this.y>divb.y-20)&&(this.y<divb.y+20)){
count++;
document.getElementById("count").innerHTML = count.toString();
}
}
this.ifgreencatch=function(){
if((this.x>divc.x-20)&&(this.x<divc.x+20)&&(this.y>divc.y-20)&&(this.y<divc.y+20)){
alert("Game Over," + "积分:" + count);
history.go(0);
clearTimeout(timer1);
clearTimeout(timer2);
}
}
this.ifcross=function(){
if(!((this.x>100)&&(this.x<480)&&(this.y>100)&&(this.y<480))){
alert("Game Over," + "积分:" + count);
history.go(0);
clearTimeout(timer1);
clearTimeout(timer2);
}
}
}
function Divb(x,y){
this.x=x;
this.y=y;
this.move=function(){
var random=Math.floor(Math.random() * 5);
switch(random){
case 1:
if(!(this.y<100)){
this.y=this.y-5;
document.getElementById("divb").style.top=this.y+"px";
diva.ifredcatch();
break;
}
case 2:
if(!(this.x<100)){
this.x=this.x-5;
document.getElementById("divb").style.left=this.x+"px";
diva.ifredcatch();
break;
}
case 3:
if(!(this.x>480)){
this.x=this.x+5;
document.getElementById("divb").style.left=this.x+"px";
diva.ifredcatch();
break;
}
case 4:
if(!(this.y>480)){
this.y=this.y+5;
document.getElementById("divb").style.top=this.y+"px";
diva.ifredcatch();
break;
}
}
}
}
function Divc(x,y){
this.x=x;
this.y=y;
this.move=function(){
var random=Math.floor(Math.random() * 5);
switch(random){
case 1:
if(!(this.y<100)){
this.y=this.y-10;
document.getElementById("divc").style.top=this.y+"px";
diva.ifgreencatch();
break;
}
case 2:
if(!(this.x<100)){
this.x=this.x-10;
document.getElementById("divc").style.left=this.x+"px";
diva.ifgreencatch();
break;
}
case 3:
if(!(this.x>480)){
this.x=this.x+10;
document.getElementById("divc").style.left=this.x+"px";
diva.ifgreencatch();
break;
}
case 4:
if(!(this.y>480)){
this.y=this.y+10;
document.getElementById("divc").style.top=this.y+"px";
diva.ifgreencatch();
break;
}
}
}
}
function move(obj){
switch(obj.value){
case "上":
diva.move_up();
break;
case "左":
diva.move_left();
break;
case "右":
diva.move_right();
break;
case "下":
diva.move_down();
break;
}
}
document.onkeydown = function(event) {
var code;
if (window.event) {
code = window.event.keyCode;
} else {
code = event.keyCode;
}
switch(code){
case 38:
diva.move_up();
break;
case 37:
diva.move_left();
break;
case 39:
diva.move_right();
break;
case 40:
diva.move_down();
break;
case 87:
diva.move_up();
break;
case 65:
diva.move_left();
break;
case 68:
diva.move_right();
break;
case 83:
diva.move_down();
break;
}
}
</script>
这个游戏当然还有改进的地方,就是红点与绿点中的移动方法move()中的随机数产生问题,怎么以不同概率产生随机数是要思考的。不如收到玩家黑点的移动动作,我们的红点与这个移动动作相同的移动,概率要大一点,为了更多地远离黑点,绿点则与这个移动动作相反的移动,概率大一点,为了更多地接近黑点,反之亦然。