VUE+Canvas实现财神爷接元宝小游戏
之前的canvas小游戏系列欢迎大家戳:
《VUE实现一个Flappy Bird~~~》
《猜单词游戏》
《VUE+Canvas 实现桌面弹球消砖块小游戏》
《VUE+Canvas实现雷霆战机打字类小游戏》
如标题,这个游戏大家也玩过,随处可见,左右方向键控制财神移动,接住从天而降的金元宝等,时间一到,则游戏结束。先来看一下效果:
相比于之前的雷霆战机要打出四处飞的子弹,这次元素的运动轨迹就很单一了,垂直方向的珠宝和水平移动的财神爷,类似于之前的代码,这里就说一下关键步骤点吧:
1、键盘控制水平移动的财神爷
这个很简单,同理于《VUE+Canvas 实现桌面弹球消砖块小游戏》滑块的控制:
drawCaishen() {
let _this = this;
_this.ctx.save();
_this.ctx.drawImage(
_this.caishenImg,
_this.caishen.x,
_this.caishen.y,
120,
120
);
_this.ctx.restore();
},
moveCaishen() {
this.caishen.x += this.caishen.dx;
if (this.caishen.x > this.clientWidth - 120) {
this.caishen.x = this.clientWidth - 120;
} else if (this.caishen.x < 0) {
this.caishen.x = 0;
}
}
2、从天而降的珠宝
这个也很简单,但要注意的是,珠宝的初始x值不能随机取0~clientWidth了,因为这样很容易造成珠宝堆积在一起,影响了游戏的可玩性,所以珠宝最好是分散在不同的轨道上,这里我们把画布宽度分为5条轨道,初始珠宝的时候,我们就把珠宝分散在轨道上,并且y值随机在一定高度造成参差。而后新生成的珠宝都依据轨道分布来生成,避免珠宝挤在一起。
generateTreasure() {
let _this = this;
if (_this.treasureArr.length < MaxNum) {
let random = Math.floor(Math.random() * TreasureNames.length);
let channel = _this.getRandomArbitrary(1, 5);
_this.treasureArr.push({
x: _this.channelWidth * (1 / 2 + (channel - 1)) - 30,
y: 0,
name: TreasureNames[random],
speed: _this.getRandomArbitrary(2, 4)
});
}
},
filterTreasure(item) {
let _this = this;
if (
item.x <= _this.caishen.x + 110 &&
item.x >= _this.caishen.x &&
item.y > _this.caishen.y
) {
// 判断和财神的触碰范围
_this.score += _this.treasureObj[item.name].score;
return false;
}
if (item.y >= _this.clientHeight) {
return false;
}
return true;
},
drawTreasure() {
let _this = this;
_this.treasureArr = _this.treasureArr.filter(_this.filterTreasure);
_this.treasureArr.forEach(item => {
_this.ctx.drawImage(
_this.treasureObj[item.name].class="lazy" data-src,
item.x,
item.y,
60,
60
);
item.y += item.speed;
});
},
getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
这里用filter函数过滤掉应该消失的珠宝,如果用for+splice+i--的方法会造成抖动。
然后给予每个珠宝随机的运动速度,当珠宝进入财神爷的图片范围时则累加相应分数。
3、倒计时圆环
设置倒计时30s,那么在requestAnimationFrame的回调里计算当前时间与上次时间戳毫秒差值是否大于1000,实现秒的计算,然后取另一时间戳累加progress,实现圆环的平滑移动。
drawCountDown() {
// 画进度环
let _this = this;
_this.progress += Date.now() - _this.timeTag2;
_this.timeTag2 = Date.now();
_this.ctx.beginPath();
_this.ctx.moveTo(50, 50);
_this.ctx.arc(
50,
50,
40,
Math.PI * 1.5,
Math.PI * (1.5 + 2 * (_this.progress / (countDownInit * 1000))),
false
);
_this.ctx.closePath();
_this.ctx.fillStyle = "yellow";
_this.ctx.fill();
// 画内填充圆
_this.ctx.beginPath();
_this.ctx.arc(50, 50, 30, 0, Math.PI * 2);
_this.ctx.closePath();
_this.ctx.fillStyle = "#fff";
_this.ctx.fill();
// 填充文字
_this.ctx.font = "bold 16px Microsoft YaHei";
_this.ctx.fillStyle = "#333";
_this.ctx.textAlign = "center";
_this.ctx.textBaseline = "middle";
_this.ctx.moveTo(50, 50);
_this.ctx.fillText(_this.countDown + "s", 50, 50);
}
(function animloop() {
_this.ctx.clearRect(0, 0, _this.clientWidth, _this.clientHeight);
_this.loop();
animationId = window.requestAnimationFrame(animloop);
if (_this.countDown === 0) {
_this.gameOver = true;
window.cancelAnimationFrame(animationId);
}
if (Date.now() - _this.timeTag >= 1000) {
_this.countDown--;
_this.timeTag = Date.now();
}
})();
至此,一个非常简单的财神爷接元宝的小游戏就完成了,当然可以为了增加难度,设置不间断地丢炸弹这一环节,原理同珠宝的运动是一样的。
下面还是附上全部代码,供大家参考学习:
<template>
<div class="caishen">
<canvas id="caishen" width="1200" height="750"></canvas>
<div class="container" v-if="gameOver">
<div class="dialog">
<p class="once-again">恭喜!</p>
<p class="once-again">本回合夺宝:{{ score }}分</p>
</div>
</div>
</div>
</template>
<script>
const TreasureNames = [
"yuanbao",
"tongqian",
"jintiao",
"shuijin_red",
"shuijin_blue",
"fudai"
];
let animationId = null;
let countDownInit = 0;
const MaxNum = 5;
export default {
name: "CaiShen",
data() {
return {
score: 0,
ctx: null,
caishenImg: null,
clientWidth: 0,
clientHeight: 0,
channelWidth: 0,
caishen: {
x: 0,
y: 0,
speed: 8,
dx: 0
},
progress: 0,
countDown: 30,
timeTag: Date.now(),
timeTag2: Date.now(),
treasureArr: [],
gameOver: false,
treasureObj: {
yuanbao: {
score: 5,
class="lazy" data-src: null
},
tongqian: {
score: 2,
class="lazy" data-src: null
},
jintiao: {
score: 10,
class="lazy" data-src: null
},
shuijin_red: {
score: 20,
class="lazy" data-src: null
},
shuijin_blue: {
score: 15,
class="lazy" data-src: null
},
fudai: {
score: 8,
class="lazy" data-src: null
}
}
};
},
mounted() {
let _this = this;
let container = document.getElementById("caishen");
_this.ctx = container.getContext("2d");
_this.clientWidth = container.width;
_this.clientHeight = container.height;
_this.channelWidth = Math.floor(_this.clientWidth / 5);
_this.caishenImg = new Image();
_this.caishenImg.class="lazy" data-src = require("@/assets/img/caishen/财神爷.png");
_this.initTreasures();
countDownInit = _this.countDown;
_this.caishen.x = _this.clientWidth / 2 - 60;
_this.caishen.y = _this.clientHeight - 120;
document.onkeydown = function(e) {
let key = window.event.keyCode;
if (key === 37) {
// 左键
_this.caishen.dx = -_this.caishen.speed;
} else if (key === 39) {
// 右键
_this.caishen.dx = _this.caishen.speed;
}
};
document.onkeyup = function(e) {
_this.caishen.dx = 0;
};
_this.caishenImg.onload = function() {
(function animloop() {
_this.ctx.clearRect(0, 0, _this.clientWidth, _this.clientHeight);
_this.loop();
animationId = window.requestAnimationFrame(animloop);
if (_this.countDown === 0) {
_this.gameOver = true;
window.cancelAnimationFrame(animationId);
}
if (Date.now() - _this.timeTag >= 1000) {
_this.countDown--;
_this.timeTag = Date.now();
}
})();
};
},
methods: {
initTreasures() {
let _this = this;
Object.keys(_this.treasureObj).forEach(key => {
_this.treasureObj[key].class="lazy" data-src = new Image();
_this.treasureObj[
key
].class="lazy" data-src.class="lazy" data-src = require(`@/assets/img/caishen/${key}.png`);
});
for (let i = 0; i < MaxNum; i++) {
let random = Math.floor(Math.random() * TreasureNames.length);
_this.treasureArr.push({
x: _this.channelWidth * (1 / 2 + i) - 30,
y: _this.getRandomArbitrary(0, 20),
name: TreasureNames[random],
speed: _this.getRandomArbitrary(2, 4)
});
}
},
loop() {
let _this = this;
_this.drawCountDown();
_this.drawCaishen();
_this.moveCaishen();
_this.generateTreasure();
_this.drawTreasure();
_this.drawScore();
},
drawCaishen() {
let _this = this;
_this.ctx.save();
_this.ctx.drawImage(
_this.caishenImg,
_this.caishen.x,
_this.caishen.y,
120,
120
);
_this.ctx.restore();
},
moveCaishen() {
this.caishen.x += this.caishen.dx;
if (this.caishen.x > this.clientWidth - 120) {
this.caishen.x = this.clientWidth - 120;
} else if (this.caishen.x < 0) {
this.caishen.x = 0;
}
},
drawScore() {
let _this = this;
_this.ctx.beginPath();
_this.ctx.fillStyle = "#fff";
_this.ctx.textAlign = "center";
_this.ctx.textBaseline = "middle";
_this.ctx.fillText(_this.score + "分", 30, _this.clientHeight - 10);
_this.ctx.closePath();
},
drawCountDown() {
// 画进度环
let _this = this;
_this.progress += Date.now() - _this.timeTag2;
_this.timeTag2 = Date.now();
_this.ctx.beginPath();
_this.ctx.moveTo(50, 50);
_this.ctx.arc(
50,
50,
40,
Math.PI * 1.5,
Math.PI * (1.5 + 2 * (_this.progress / (countDownInit * 1000))),
false
);
_this.ctx.closePath();
_this.ctx.fillStyle = "yellow";
_this.ctx.fill();
// 画内填充圆
_this.ctx.beginPath();
_this.ctx.arc(50, 50, 30, 0, Math.PI * 2);
_this.ctx.closePath();
_this.ctx.fillStyle = "#fff";
_this.ctx.fill();
// 填充文字
_this.ctx.font = "bold 16px Microsoft YaHei";
_this.ctx.fillStyle = "#333";
_this.ctx.textAlign = "center";
_this.ctx.textBaseline = "middle";
_this.ctx.moveTo(50, 50);
_this.ctx.fillText(_this.countDown + "s", 50, 50);
},
filterTreasure(item) {
let _this = this;
if (
item.x <= _this.caishen.x + 110 &&
item.x >= _this.caishen.x &&
item.y > _this.caishen.y
) {
// 判断和财神的触碰范围
_this.score += _this.treasureObj[item.name].score;
return false;
}
if (item.y >= _this.clientHeight) {
return false;
}
return true;
},
drawTreasure() {
let _this = this;
_this.treasureArr = _this.treasureArr.filter(_this.filterTreasure);
_this.treasureArr.forEach(item => {
_this.ctx.drawImage(
_this.treasureObj[item.name].class="lazy" data-src,
item.x,
item.y,
60,
60
);
item.y += item.speed;
});
},
getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
},
generateTreasure() {
let _this = this;
if (_this.treasureArr.length < MaxNum) {
let random = Math.floor(Math.random() * TreasureNames.length);
let channel = _this.getRandomArbitrary(1, 5);
_this.treasureArr.push({
x: _this.channelWidth * (1 / 2 + (channel - 1)) - 30,
y: 0,
name: TreasureNames[random],
speed: _this.getRandomArbitrary(2, 4)
});
}
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
#caishen {
background-color: #b00600;
background-image: url("~assets/img/caishen/brick-wall.png");
}
.container {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.3);
text-align: center;
font-size: 0;
white-space: nowrap;
overflow: auto;
}
.container:after {
content: "";
display: inline-block;
height: 100%;
vertical-align: middle;
}
.dialog {
width: 400px;
height: 300px;
background: rgba(255, 255, 255, 0.5);
box-shadow: 3px 3px 6px 3px rgba(0, 0, 0, 0.3);
display: inline-block;
vertical-align: middle;
text-align: left;
font-size: 28px;
color: #fff;
font-weight: 600;
border-radius: 10px;
white-space: normal;
text-align: center;
.once-again-btn {
background: #1f9a9a;
border: none;
color: #fff;
}
}
</style>
到此这篇关于VUE+Canvas实现财神爷接元宝小游戏的文章就介绍到这了,更多相关vue接元宝游戏内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341