这个捕鱼游戏挺有意思的,通过发射子弹,打鱼。打鱼的子弹会消耗金币,但是打鱼如果打到了鱼,就会奖励金币的数量。

我如果写这个的话,应该会画一个 背景海底,然后生成很多鱼的图片,还要有一个大炮,金币。大炮会发射子弹,角度不同发摄子弹的方向不同。

发射子弹就消耗金币,如果打中鱼了就奖励金币,大炮两边的加号和减号就是控制让子弹连续发射的。不过此时发送炸弹的角度不好控制。

接下来我们看效果

先放下作者大大的项目地址:https://github.com/JayHowe/fishingGame

接下来我们分析代码

页面初始化绘制图片

<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
body {
text-align: center;
background-color: #000;
} #c1 {
width: 800px;
height: 600px;
background: url(img/game_bg_2_hd.jpg) no-repeat;
margin: 0 auto;
}
</style>
<script type="text/javascript" src="js/common.js"></script>
<script type="text/javascript" src="js/drawRect.js"></script>
<script type="text/javascript" src="js/sprite.js"></script>
<script type="text/javascript" src="js/fish.js"></script>
<script type="text/javascript" src="js/cannon.js"></script>
<script type="text/javascript" src="js/button.js"></script>
<script type="text/javascript" src="js/bullet.js"></script>
<script type="text/javascript" src="js/coin.js"></script>
<script>
window.onload = function() {
// 绘制背景
let oC = document.getElementById('c1');
let gd = oC.getContext('2d'); let lastFire=0;
let fired=false;
let MAX_FISH=30;
const coinCollector={x:106,y:576};
//游戏总得分
let playerScore=1000; const W = oC.width,
H = oC.height;
// 加载图片
loadImages(_resources, function() {
//炮台
let tower = new Sprite(new DrawRect(_imgs.bottom, 0, 0, 756, 71, ));
tower.x = 400;
tower.y = H - 71 / 2 + 1; //炮
let cannon = new Cannon(1); cannon.x = 443;
cannon.y = 574; //炮弹
let bullets=[]; //鱼
let fishs=[]; // 金币
let coins=[]; //分数数字
let scores=[]; for(let i=0;i<6;i++){
let sprite=new Sprite(new DrawRect(_imgs.number,0,9*24,20,24));
sprite.x=51+23*i;
sprite.y=586;
scores.push(sprite);
}; //鼠标事件
oC.onmousemove = ev => {
let a = ev.offsetX - cannon.x;
let b = ev.offsetY - cannon.y;
let ang = a2d(Math.atan2(b, a)) + 90;
cannon.rotation = ang;
} //加号、减号
let btnMinus = new Button(
new DrawRect(_imgs.bottom, 135, 75, 36, 28),
new DrawRect(_imgs.bottom, 91, 75, 36, 28)
);
btnMinus.x = 371;
btnMinus.y = 566; let btnPlus = new Button(
new DrawRect(_imgs.bottom, 47, 75, 36, 28),
new DrawRect(_imgs.bottom, 3, 75, 36, 28)
);
btnPlus.x = 516;
btnPlus.y = 566; btnMinus.onclick = function() {
if (cannon.type > 1) {
cannon.setType(cannon.type - 1);
} else {
cannon.setType(1);
}
};
btnPlus.onclick = function() {
if (cannon.type < 7) {
cannon.setType(cannon.type + 1);
} else {
cannon.setType(7);
}
}; let aBtn = [btnMinus, btnPlus]; oC.onmousedown = function(ev) {
//检测按钮
aBtn.forEach(btn => {
btn.down(ev.offsetX, ev.offsetY);
}); if(Date.now()-lastFire >= 300) {
lastFire=Date.now();
//炮弹
let bullet=new Bullet(cannon.type, cannon.x,cannon.y,cannon.rotation);
bullets.push(bullet); playerScore-=cannon.type*2; fired=true;
} };
oC.onmouseup = function(ev) {
aBtn.forEach(btn => {
btn.up(ev.offsetX, ev.offsetY);
});
}; function animate() {
requestAnimationFrame(animate); //生成鱼
if(rnd(1,20)==1 && fishs.length<MAX_FISH) {
let fish=new Fish(rnd(1,5));
if(rnd(0,2)==0) {
//左边
fish.x=-100;
fish.rotation=90;
}else {
//右边
fish.x=W+100;
fish.rotation=-90;
}
fish.y=rnd(0,H-100);
fishs.push(fish);
} gd.clearRect(0, 0, oC.width, oC.height); coins=coins.filter(coin=>{
coin.move(coinCollector.x,coinCollector.y);
coin.nextFrame();
coin.draw(gd); if(Math.abs(coin.x-coinCollector.x)<5 && Math.abs(coin.y-coinCollector.y)<5) {
playerScore+=50;
return false;
}else {
return true;
}
});
tower.draw(gd); bullets=bullets.filter(bullet=>{
bullet.move();
bullet.draw(gd);
return !bullet.outRect(-100,-100,W+200,H+200);
});
// console.log(bullets.length); fishs=fishs.filter(fish=>{
fish.move();
fish.draw(gd);
fish.nextFrame();
return !fish.outRect(-100,-100,W+200,H+200);
});
// console.log(fishs.length); cannon.draw(gd);
if(fired) {
ret=cannon.nextFrame();
if(ret) {
fired=false;
}
} btnMinus.draw(gd);
btnPlus.draw(gd); //碰撞
fishs=fishs.filter(fish=>{
let colled=false;
bullets=bullets.filter(bullet=>{
if(!colled && fish.collTest(bullet)){
if(Math.random()<bullet.type*10/(10+(fish.type-1)*20)) {
colled=true;
}
return false;
}else {
return true;
}
}); if(colled) {
fish.isdead=true;
fish.speed=0; setTimeout(function() {
//金币
let a=fish.x-coinCollector.x;
let b=coinCollector.y-fish.y; let i=0;
let timer=setInterval(function(){
let coin=new Coin(1,fish.x,fish.y);
// coin.x+=rnd(-50,50);
// coin.y+=rnd(-50,50);
coins.push(coin);
i++; if(i==Math.pow(2,fish.type)) {
clearInterval(timer);
}
},60); fishs=fishs.filter(item=>item!=fish);
},500); return true;
}else {
return true;
}
}); //分数
let str=playerScore+'';
while(str.length<6) {
str='0'+str;
}
scores.forEach((score,index)=>{
playerScore
score.setDrawRect(new DrawRect(_imgs.number,0,(9-parseInt(str[index]))*24,20,24));
score.draw(gd);
});
}
requestAnimationFrame(animate); });
}
</script>
</head>
<body>
<canvas id="c1" width="800" height="600"></canvas>
</body>
</html>
//bullet
//绘制子弹形状
class Bullet extends Sprite{
constructor(type,x=0,y=0,rotation=0){
const SIZE=[
null,
new DrawRect(_imgs.bullet,86,0,24,26),
new DrawRect(_imgs.bullet,61,0,25,29),
new DrawRect(_imgs.bullet,32,36,29,30),
new DrawRect(_imgs.bullet,30,82,29,33),
new DrawRect(_imgs.bullet,0,82,30,34),
new DrawRect(_imgs.bullet,30,0,31,26),
new DrawRect(_imgs.bullet,0,44,32,38)
]; super(SIZE[type],x,y,rotation); this.type=type;
this.speed=5; this.radius=14;
}
}

绘制按钮

//button
class Button extends Sprite{
constructor(drawRectNormal, drawRectActive, x=0, y=0, rotation=0){
super(drawRectNormal, x, y, rotation); this.drawRectNormal=drawRectNormal;
this.drawRectActive=drawRectActive; this.downAtMe=false;
} down(x, y){
if(this.inRect(x, y)){
this.setDrawRect(this.drawRectActive); this.downAtMe=true;
}else{
this.downAtMe=false;
}
}
up(x, y){
this.setDrawRect(this.drawRectNormal); if(this.inRect(x, y) && this.downAtMe){
//触发onclick
this.onclick && this.onclick();
}
}
}

绘制大炮

//cannon.js
class Cannon extends Sprite {
constructor(type, x = 0, y = 0, rotation = 0) {
if (type > 7 || type < 1) {
throw new Error('unkonw cannon type');
}
const SIZE = [
null,
{ w: 74, h: 74 },
{ w: 74, h: 76 },
{ w: 74, h: 76 },
{ w: 74, h: 83 },
{ w: 74, h: 85 },
{ w: 74, h: 90 },
{ w: 74, h: 94 }
]; //父级
super(
new DrawRect(_imgs[`cannon${type}`], 0, 0, SIZE[type].w, SIZE[type].h),
x, y, rotation
); this.SIZE=SIZE; this.setType(type); this.MAX_FRAME=5;
} setType(type){
this.type = type;
this.setDrawRect(
new DrawRect(_imgs[`cannon${type}`], 0, 0, this.SIZE[type].w, this.SIZE[type].h)
);
}
}

金币

//js\coin.js
class Coin extends Sprite{
constructor(type,x=0,y=0,rotation=0){
const SIZE=[
null,
new DrawRect(_imgs.coin1,0,0,60,60),
new DrawRect(_imgs.coin2,0,0,60,60)
]; super(SIZE[type],x,y,rotation); this.MAX_FRAME=10;
this.speed=10;
} }

初始化加载图片

//common.js
let _imgs=null; const _resources={
fish1: 'img/fish1.png',
fish2: 'img/fish2.png',
fish3: 'img/fish3.png',
fish4: 'img/fish4.png',
fish5: 'img/fish5.png',
cannon1: 'img/cannon1.png',
cannon2: 'img/cannon2.png',
cannon3: 'img/cannon3.png',
cannon4: 'img/cannon4.png',
cannon5: 'img/cannon5.png',
cannon6: 'img/cannon6.png',
cannon7: 'img/cannon7.png',
bottom: 'img/bottom.png',
bullet: 'img/bullet.png',
coin1: 'img/coinAni1.png',
coin2: 'img/coinAni2.png',
number: 'img/number_black.png',
}; function loadImages(json, fn){
let res={};
let complete=0;
let total=0; for(let name in json){
total++; let oImg=new Image(); res[name]=oImg; oImg.onload=function (){
complete++; if(complete==total){
_imgs=res;
fn();
}
}; oImg.onerror=function (){
alert('图片加载失败'+oImg.src);
}; oImg.src=json[name];
}
} function d2a(n){
return n*Math.PI/180;
}
function a2d(n){
return n*180/Math.PI;
}
function rnd(n, m){
return Math.floor(Math.random()*(m-n)+n);
}

画方形的类

//js\drawRect.js
class DrawRect{
constructor(img,sx,sy,sw,sh){
if(!img || !sw || !sh) {
throw new Error('img and sw and sh is required');
}
this.img=img;
this.sx=sx;
this.sy=sy;
this.sw=sw;
this.sh=sh;
}
}

定义的鱼的类型

//js\fish.js
class Fish extends Sprite {
constructor(type, x = 0, y = 0, rotation = 0) {
if (type > 5 || type < 1) {
throw new Error('unkonw fish type');
}
const SIZE = [
null,
{ w: 55, h: 37, r: 12 },
{ w: 78, h: 64, r: 18 },
{ w: 72, h: 56, r: 15 },
{ w: 77, h: 59, r: 15 },
{ w: 107, h: 122, r: 23 }
];
super(new DrawRect(_imgs[`fish${type}`], 0, 0, SIZE[type].w, SIZE[type].h), x, y, rotation); this.type = type;
this.curFrame = 0;
this.MAX_FRAME = 4; this.speed = rnd(1, 4); this.frameRate = 5; this.radius=SIZE[type].r; //死鱼
this.isdead=false;
} draw(gd) {
if(this.isdead) {
this.curFrame+=4;
} if (this.rotation == -90) {
this.scaleY = -1;
} this.rotation -= 90;
super.draw(gd);
this.rotation += 90; if (this.rotation == -90) {
this.scaleY = 1;
} if(this.isdead) {
this.curFrame-=4;
}
}
}

绘制鱼的类型

//js\sprite.js

class Sprite {
//w,h,x,y,rotate
//draw(),碰撞检测()
constructor(drawRect, x = 0, y = 0, rotation = 0) {
if (!(drawRect instanceof DrawRect)) {
throw new Error('img must be DrawRect');
}
this.setDrawRect(drawRect);
this.x = x;
this.y = y;
this.rotation = rotation; this.speed = 0; //动画
this.MAX_FRAME=0;
this.curFrame=0; this.scaleX=1;
this.scaleY=1; this.frameRate=1;
this.frameRateNow=0; // 碰撞检测
this.radius=0;
} setDrawRect(drawRect) {
this.drawRect = drawRect;
this.width = drawRect.sw;
this.height = drawRect.sh;
} nextFrame(){
this.frameRateNow++;
// console.log(this.frameRateNow); if(this.frameRateNow==this.frameRate) {
this.frameRateNow=0; this.curFrame++;
if(this.curFrame>=this.MAX_FRAME) {
this.curFrame=0;
return true;
} return false;
}
} draw(gd) {
gd.save(); gd.translate(this.x, this.y);
gd.rotate(d2a(this.rotation));
gd.scale(this.scaleX,this.scaleY); gd.drawImage(
this.drawRect.img,
this.drawRect.sx, this.drawRect.sy+this.height*this.curFrame, this.width, this.height,
-this.width / 2, -this.height / 2, this.width, this.height,
);
gd.restore();
} inRect(x, y) {
if (
this.x - this.width / 2 <= x && x <= this.x + this.width / 2 &&
this.y - this.height / 2 <= y && y <= this.y + this.height / 2
) {
return true;
} else {
return false;
}
} outRect(x,y,w,h){
if(this.x<x || this.y<y || this.x>x+w || this.y>y+h) {
return true;
}else {
return false;
}
} move(x,y) {
if(arguments.length == 0) {
let x_speed = this.speed * Math.sin(d2a(this.rotation));
let y_speed = this.speed * Math.cos(d2a(this.rotation));
this.x += x_speed;
this.y -= y_speed;
}else {
this.x+=(x-this.x)/20;
this.y+=(y-this.y)/20;
} } collTest(other){
return Math.sqrt(Math.pow(this.x-other.x,2)+Math.pow(this.y-other.y,2))<this.radius+other.radius;
} }

后记:代码我并没有完全看懂

【默默努力】fishingGame的更多相关文章

  1. 2018.5.2(7:20到的办公室开始早课 阮一峰的JS) 所有的默默努力都是为了让自己看起来毫不费力

    continue语句用于立即终止本轮循环,返回循环结构的头部,开始下一轮循环. break语句用于跳出代码块或循环. 标签(label) JavaScript 语言允许,语句的前面有标签(label) ...

  2. 【默默努力】PixelFire

    先放下我玩游戏的效果图: 关于游戏最后的结束部分其实我还没有截图,看着挺好看的,后面的效果 再放作者大大的项目地址:https://github.com/panruiplay/PixelFire 接下 ...

  3. 【默默努力】h5-game-heroVSmonster

    先放下作者大大的项目地址:https://github.com/yangyunhe369/h5-game-heroVSmonster 然后游戏的效果为 截动图的按键与游戏按键应该冲突,我就截几张图片了 ...

  4. 【默默努力】h5-game-blockBreaker

    先放下游戏的效果,我不太会玩游戏 然后放下无私开源的作者大大的地址:https://github.com/yangyunhe369/h5-game-blockBreaker 这个游戏的话,我觉得应该是 ...

  5. 【默默努力】ig-wxz-and-hotdog

    这个是一个非常跟热点的小游戏,思聪吃热狗.这个游戏的话,我感觉思路还挺简单的,天上会掉热狗和障碍物, 思聪在下面张开嘴巴,进行左右移动,接热狗.如果吃到的是热狗就得一分,如果思聪吃到的不是热狗,是障碍 ...

  6. 【默默努力】react-drag-grid

    先放项目地址:https://github.com/Bilif/react-drag-grid 项目运行效果 感谢无私开源的程序员 先看项目入口文件 //index.js import React f ...

  7. 【默默努力】vue-pc-app

    最近在github上面看到了一个团队的项目,真的非常赞.他们进行vue-cli的二次开发,将项目用自己的方式打包. 今天的这个开源项目地址为:https://github.com/tffe-team/ ...

  8. Shader的学习方法总结

    最近网友candycat1992的新书<Unity Shader入门精要>出版了,估计万千的中国unity开发者又要掀起一波学Shader热潮了.我也想把自己这几年学习Shader的一些历 ...

  9. VIM移动

    VIM移动   断断续续的使用VIM也一年了,会的始终都是那么几个命令,效率极低 前几个星期把Windows换成了Linux Mint,基本上也稳定了下来 就今晚,我已经下定决心开始新的VIM之旅,顺 ...

随机推荐

  1. js Date.parse() format.

    date format android chrome linux chrome Mobile safari ios chrome windows safari linux firefox window ...

  2. 使用Docker创建数据容器

    使用Docker创建数据容器 翻译自: Data-only container madness 1.什么是数据容器? 数据容器就是本身只创建一个volume供其他容器共享,创建完后即退出,不执行任何任 ...

  3. linux下df查看空间已经占用%100,但是找不到大文件的解决方法

    有时候在linux下会遇到这种情况:df查看空间已经占用%100,但是找不到大文件,怎么回事呢,经过网上查找资料,得到解决方法: 1.使用lsof查看已删除但未释放的文件 lsof -n | grep ...

  4. CVE-2018-3246 weblogic xxe

    使用P牛2018-2894的容器 http://192.168.245.130:7001/ws_utc/begin.do 导入测试用例 上传时抓取数据包 POST /ws_utc/resources/ ...

  5. Day15:Python 【模块】及__name__:

    什么是模块: 在Python中,随着这代码的撰写,代码越来越长,所以产生了,模块这个概念,模块是什么?模块就是一个.py文件,在撰写代码时,我们把不同的功能的代码封装到一个.py文件里,用得时候导入 ...

  6. SElinux(转)

    转自:http://www.361way.com/rh134-selinux/4653.html RH134小结(四)初识SElinux 2015年8月2日admin发表评论阅读评论   一.SEli ...

  7. python接口自动化(get请求)

    python接口自动化(get请求) get请求的目的:查询资源 一.导包 二.请求的URL 三.请求的参数 四.获取请求的URL 五.获取响应的状态码 六.获取响应的本文信息 #导包 import ...

  8. iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 5000 -j DNAT --to-destination

    启动docker容器时报错: iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 5000 -j DNAT ...

  9. 使用PyCharm创建Django项目及基本配置

    https://segmentfault.com/a/1190000011576316 pycharm是个很不错的python开发工具,大大缩短了python项目的创建时间以及调试时间在使用pytho ...

  10. IDEA与Tomcat的相关配置说明

    1.IDEA会为每个Tomcat部署的项目单独建立一份配置文件 查看控制台的log输出:Using CATAINA_BASE 2.工作空间项目和Tomcat部署的web项目 WEB-INF:内的资源不 ...