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

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

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

接下来我们看效果

先放下作者大大的项目地址: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. csp-s模拟测试94

    csp-s模拟测试94 一场简单题,打爆了.$T1$脑抽分解质因数准备分子分母消,想了半天发现$jb$互质直接上天,果断码了高精滚蛋.$T2$无脑手玩大样例,突然灵光一闪想到映射到前$K$大小的区间, ...

  2. jeecg下实现自动默认模糊查询

    也许jeecg的作者深受SAP毒害吧,没考虑到广大使用JEECG的人群为SAP用户,及所开发的项目均为中小项目,无惧大数据模糊查询带来的功能影响. 经网友“&&康&&& ...

  3. class9_Menubar 菜单

    最终的运行效果图(程序见序号5) #!/usr/bin/env python# -*- coding:utf-8 -*-# -------------------------------------- ...

  4. 关于double的输入输出

    double定义的变量输入的时候一定要%lf输入,要是%f输入的话,得到的结果会是0 float输入的时候是%f 但是在输出的时候%lf和%f都可以输出 建议使用double类型时,用%lf输入,%f ...

  5. 剑指offer——23调整数组顺序使奇数位于偶数前面

    题目描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变.   题解: 一种是数 ...

  6. 996弱爆了,我还能12x12, 8116+8!

    Java技术栈 www.javastack.cn 优秀的Java技术公众号 来自长江日报 这些天"马云谈996"的话题火了 昨晚刘强东也在朋友圈发文 "8116+8!&q ...

  7. 面试系列32 集群部署时的分布式session如何实现

    session是啥?浏览器有个cookie,在一段时间内这个cookie都存在,然后每次发请求过来都带上一个特殊的jsessionid cookie,就根据这个东西,在服务端可以维护一个对应的sess ...

  8. vue sChart组件使用页面一片空白问题及示例

    参考:https://www.ctolib.com/mip/lin-xin-vue-schart.html 在网上其他示例里,我试验后发现:渲染到<canvas id="myChart ...

  9. mysql DOS中中文乱码 ERROR 1366 (HY000): Incorrect string value: '\xC4\xEA\xBC\xB6' for column 'xxx' at row 1

    问题:ERROR (HY000): Incorrect string value: 在DOS中插入或查询中文出现乱码 登入mysql,输入命令:show variables like '%char%' ...

  10. 人脸识别-常用的数据库Face Databases From Other Research Groups

    Static/Videos Static Single/Multiple faces Single? Gray/Color Color Resolution Vaious Face pose Fron ...