使用 Vue 开发一个简略版的飞机大战小游戏
如题,假设你为了向更多访问你博客的人展示你的技术,你决定小试身手开发一个飞机大战小游戏。
功能: 开始游戏前用户名必填,玩家可以发射子弹,敌军与行星随机出现,鼠标可操控玩家移动,敌军可发射子弹
一、实现思路
如题所述:
玩家可操控玩家飞机可发射子弹,敌军与行星随机生成;
这意味着我们需要一个单独的玩家飞机dom,以及敌军、行星与子弹 用 vue 循环生成的3个dom。
敌军与行星生成后的dom的位置由数据里的 x 与 y 值决定。
按下空格时产生的子弹由当时按下空格键的时候的飞机的位置来决定。
敌军随机发射的子弹由当时发射子弹的敌军的位置来决定。
游戏开始时用户名必填,那么我们只需要在 Vue 实例里为该 input 绑定一个数据,再为开始游戏按钮绑定点击事件。随后计算用户名的长度只要大于3,就调用游戏开始函数或初始化函数。
玩家鼠标操控移动飞机移动只需要为其父节点绑定鼠标移动事件,然后更改 player 里的 x 与 y 的数据 (x与y的值不能小于0,x与y的值不能大于父节点的宽高) 并且赋予 玩家飞机即可。
击毁敌军只需要拿 子弹与敌军 的 x,y 计算对比即可。
二、所需知识点
1. Vue 事件绑定
2. Vue 监听事件
3. Vue 计算属性
4. Vue Style操作
三、实现步骤
第一步:创建 HTML 与 CSS 文件
HTML
- <!DOCTYPE html>
- <html>
- ?? ?<head>
- ?? ??? ?<meta charset="utf-8">
- ?? ??? ?<title>Vue 飞机大战</title>
- ?? ??? ?<link rel="stylesheet" href="css/style.css" >
- ?? ?</head>
- ?? ?<body>
- ?? ??? ?<main>
- ?? ??? ??? ?-
- ?? ??? ??? ?<div class="game-plane"?
- ?? ??? ??? ??? ?@mousemove="touchmove"
- ?? ??? ??? ??? ?:style="{backgroundPosition:'0px '+ positionY +'px'}" ref='plane'>
- ?? ??? ??? ??? ?
- ?? ??? ??? ??? ?<div id="hit">
- ?? ??? ??? ??? ??? ?<h2>击毁:{{ hitCount }}</h2>
- ?? ??? ??? ??? ??? ?<h2>与敌机相撞:{{ boom }}</h2>
- ?? ??? ??? ??? ??? ?<h2>被击中次数:{{ HitTimes }}</h2>
- ?? ??? ??? ??? ??? ?<h2>用户名:{{ username }}</h2>
- ?? ??? ??? ??? ?</div>
- ?? ??? ??? ??? ?
- ?? ??? ??? ??? ?<!-- 玩家 -->
- ?? ??? ??? ??? ?<img src="image/player.png" alt="player" id="p" :style="{top:p.y + 'px',left:p.x+'px'}">
- ?? ??? ??? ??? ?
- ?? ??? ??? ??? ?<!-- 星球 -->
- ?? ??? ??? ??? ?<img v-for="(item,index) of plane.arr" :style="{top:item.y + 'px',left:item.x+'px'}" src="image/plane.png" alt="plane">
- ?? ??? ??? ??? ?
- ?? ??? ??? ??? ?<!-- 敌军 -->
- ?? ??? ??? ??? ?<img v-for="(item,index) of e.arr" :style="{top:item.y + 'px',left:item.x+'px'}" src="image/e.png" class="e" alt="e">
- ?? ??? ??? ??? ?
- ?? ??? ??? ??? ?<!-- 子弹 -->
- ?? ??? ??? ??? ?<img v-for="(item,index) of bullets.arr" class="b"
- ?? ??? ??? ??? ? :style="{top:item.y + 'px',left:item.x+'px'}"?
- ?? ??? ??? ??? ? :src="item.tag == 'p' ? 'image/p_b.png' : 'image/e_b.png' "?
- ?? ??? ??? ??? ? alt="p_b">
- ?? ??? ??? ??? ?
- ?? ??? ??? ?</div>
- ?? ??? ?
- ?? ??? ??? ?<!-- 开始面板 -->
- ?? ??? ??? ?<div class="alert" ref="alert">
- ?? ??? ??? ??? ?<div class="content">
- ?? ??? ??? ??? ??? ?<div class="left">
- ?? ??? ??? ??? ??? ??? ?<h1>Vue 飞机大战</h1>
- ?? ??? ??? ??? ??? ??? ?<p>作者:柴不是柴</p>
- ?? ??? ??? ??? ??? ??? ?<img :src="faceChange" class="face">
- ?? ??? ??? ??? ??? ?</div>
- ?? ??? ??? ??? ??? ?<div class="right">
- ?? ??? ??? ??? ??? ??? ?<input type="text" v-model="username" placeholder="请输入你的名字">
- ?? ??? ??? ??? ??? ??? ?<input type="submit" @click="startBtnClick" ?value="开始游戏">
- ?? ??? ??? ??? ??? ?</div>
- ?? ??? ??? ??? ?</div>
- ?? ??? ??? ?</div>
- ?? ??? ?</main>
- ?? ??? ?
- ?? ??? ?<script src="js/vue.js"></script>
- ?? ??? ?<script src="js/data.js"></script>
- ?? ??? ?<script src="js/app.js"></script>
- ?? ?</body>
- </html>
CSS
- * {
- ?? ?padding: 0;
- ?? ?margin: 0;
- }
-
- main {
- ?? ?display: flex;
- ?? ?justify-content: center;
- ?? ?align-items: center;
- ?? ?width: 100%;
- ?? ?height: 100vh;
- ?? ?background-color: #282828;
- }
-
- main .game-plane {
- ?? ?position: relative;
- ?? ?width: 1200px;
- ?? ?max-width: 1200px;
- ?? ?height: 900px;
- ?? ?background-image: url(../image/background.png);
- ?? ?background-size: 100% auto;
- ?? ?box-shadow: 0 2px 30px rgba(255,255,255,0.5);
- ?? ?overflow: hidden;
- }
-
- main .game-plane img { position: absolute; }
-
- .alert {
- ?? ?position: absolute;
- ?? ?top: calc(50% - 100px);
- ?? ?left: 0;
- ?? ?width: 100%;
- ?? ?height: 200px;
- ?? ?background: #FFF;
- ? ? box-shadow: 0 0 0 999em rgba(0, 0, 0, 0.5);
- }
-
- .alert .content {
- ?? ?display: grid;
- ?? ?grid-template-columns: 4fr 6fr;
- ?? ?grid-template-rows: 100%;
- ?? ?gap: 20px;
- ?? ?margin: 0 auto;
- ?? ?max-width: 1200px;
- ?? ?width: 100%;
- ?? ?height: 100%;
- }
-
- .alert .content .left {
- ?? ?display: flex;
- ?? ?flex-direction: column;
- ?? ?align-items: center;
- ?? ?justify-content: center;
- }
-
- .alert .content .left * { margin: 5px 0; }
-
- .alert .content .right {
- ?? ?display: flex;
- ?? ?flex-direction: column;
- ?? ?align-items: center;
- ?? ?justify-content: center;
- }
-
- .alert .content .right input {
- ?? ?width: 100%;
- ?? ?display: block;
- ?? ?box-sizing: border-box;
- ?? ?padding: 10px;
- }
-
- .e { transform: rotate(180deg); }
-
- .b { width: 30px; }#hit {
- ?? ?position: absolute;
- ?? ?top: 20px;
- ?? ?left: 20px;
- ?? ?color: #FFF;
- }
第二步:创建一个全局 data 文件
- window.el = document.querySelector(".game-plane");
- window.data = {
- ?? ?p : {// 玩家 Player
- ?? ??? ?w : document.querySelector("#p").offsetWidth,
- ?? ??? ?h : document.querySelector("#p").offsetHeight,
- ?? ??? ?x : el.offsetWidth / 2 - document.querySelector("#p").offsetWidth / 2,
- ?? ??? ?y : el.offsetHeight - document.querySelector("#p").offsetHeight
- ?? ?},
- ?? ?
- ?? ?e : {// 敌机 enemy plane
- ?? ??? ?arr : [],
- ?? ??? ?speed : 6,
- ?? ?},
- ?? ?
- ?? ?plane : { arr : [] },// 星球?? ?
- ?? ?bullets : { arr : [] },// 子弹
- ?? ?hitCount : 0,// 击中总数
- ?? ?boom : 0,// 碰撞次数
- ?? ?HitTimes : 0,// 被击中次数
- ?? ?start : false,// 游戏是否开始
- ?? ?positionY : 0,// 背景 Y 值
- ?? ?timers : [],// 定时器
- ?? ?face : "ordinary",// 表情
- ?? ?username : "" // 玩家名
- }
第三步:创建Vue 实例
- var Game = new Vue({
- ?? ?el : "main",
- ?? ?data,
- ?? ?
- ?? ?methods:{
- ?? ??? ?startBtnClick() {
- ?? ??? ??? ?if ( this.username.length <= 2 ) return alert("用户名不可少于3位字符哦!");
- ?? ??? ??? ?this.init();
- ?? ??? ?},
- ?? ??? ?
- ?? ??? ?init() {// 初始化
- ?? ??? ??? ?let _this = this;
- ?? ??? ??? ?this.start = true;
- ?? ??? ??? ?this.$refs.alert.style.display = "none";
- ?? ??? ??? ?
- ?? ??? ??? ?this.createE();
- ?? ??? ??? ?this.createPlane();
- ?? ??? ??? ?this.timers.push( setInterval( this.bgMove,20 ) )
- ?? ??? ??? ?this.timers.push( setInterval(function() { _this.move('bullets') }, 20 ) )
- ?? ??? ?},
- ?? ??? ?
- ?? ??? ?bgMove () { // 背景移动 顺带判断玩家是否装上敌军
- ?? ??? ??? ?this.positionY += 5;?
- ?? ??? ??? ?if ( this.hit_check(this.p) ) this.boom++;
- ?? ??? ?},
- ?? ??? ?
- ?? ??? ?touchmove(){// 飞机移动
- ?? ??? ??? ?let touch,x,y;
- ?? ??? ??? ?if ( !this.start ) return;
- ?? ??? ??? ?
- ?? ??? ??? ?if(event.touches) touch = event.touches[0];
- ?? ??? ??? ?else touch = event;
- ?? ??? ??? ?
- ?? ??? ??? ?x = touch.clientX - this.$refs.plane.offsetLeft - this.p.w / 2;
- ?? ??? ??? ?y = touch.clientY - this.$refs.plane.offsetTop - this.p.h / 2;
- ?? ??? ??? ?
- ?? ??? ??? ?y = y < 0 ? 0 : y > (this.$refs.plane.offsetHeight - this.p.h) ? this.$refs.plane.offsetHeight - this.p.h : y;
- ?? ??? ??? ?x = x < 0 ? 0 : x > (this.$refs.plane.offsetWidth - this.p.w) ? this.$refs.plane.offsetWidth - this.p.w : x;
- ?? ??? ??? ?
- ?? ??? ??? ?this.p.x = x;
- ?? ??? ??? ?this.p.y = y;
- ?? ??? ?},
- ?? ??? ?
- ?? ??? ?createE() { // 创建敌军
- ?? ??? ??? ?let _this = this,x;
- ?? ??? ??? ?
- ?? ??? ??? ?this.timers.push( setInterval( function() {
- ?? ??? ??? ??? ?x = Math.ceil( Math.random() * ( _this.$refs.plane.offsetWidth - 80 ) );
- ?? ??? ??? ??? ?_this.build('e',{ x: x, y: 5 }) ?? ?
- ?? ??? ??? ?}, 1000 ));
- ?? ??? ??? ?
- ?? ??? ??? ?this.timers.push( setInterval( function() { _this.move('e') }, 20 ));
- ?? ??? ?},
- ?? ??? ?
- ?? ??? ?createPlane() {// 创建行星
- ?? ??? ??? ?let _this = this,x;
- ?? ??? ??? ?
- ?? ??? ??? ?this.timers.push( setInterval( function() {
- ?? ??? ??? ??? ?x = Math.ceil( Math.random() * ( _this.$refs.plane.offsetWidth - 80 ) );
- ?? ??? ??? ??? ?_this.build('plane',{ x: x, y: 5 })?
- ?? ??? ??? ?}, 2000 ));
- ?? ??? ??? ?
- ?? ??? ??? ?this.timers.push( setInterval( function() { _this.move('plane') }, 20 ));
- ?? ??? ?},
- ?? ??? ?
- ?? ??? ?createButter(table,e) {// 创建子弹
- ?? ??? ??? ?if ( !this.start ) return;
- ?? ??? ??? ?
- ?? ??? ??? ?let bullter = {
- ?? ??? ??? ??? ?x:(e.x + (e.w ? e.w : 30) / 2),
- ?? ??? ??? ??? ?y:e.y - (e.h ? e.h : -30),
- ?? ??? ??? ??? ?speed : table == "p" ? -6 : 10,
- ?? ??? ??? ??? ?tag : table
- ?? ??? ??? ?};
- ?? ??? ??? ?
- ?? ??? ??? ?this.build('bullets',bullter);
- ?? ??? ?},
- ?? ??? ?
- ?? ??? ?build(table,data) {// 公共创建
- ?? ??? ??? ?let _this = this;
- ?? ??? ??? ?this[table].arr.push( data );
- ?? ??? ?},
- ?? ??? ?
- ?? ??? ?move(table) {// 公共移动
- ?? ??? ??? ?for( let i = 0; i < this[table].arr.length; i ++ ){
- ?? ??? ??? ??? ?let e = this[table].arr[i],
- ?? ??? ??? ??? ??? ?math = Math.random() * 100,
- ?? ??? ??? ??? ??? ?speed = this[table].speed ? this[table].speed : 5;
- ?? ??? ??? ??? ?
- ?? ??? ??? ??? ?if ( table == 'bullets' ) speed = e.speed;
- ?? ??? ??? ??? ?
- ?? ??? ??? ??? ?e.y += speed;
- ?? ??? ??? ? ?
- ?? ??? ??? ??? ?if ( table !== 'bullets' ) {// 如果不是子弹dom的移动
- ?? ??? ??? ??? ??? ?if( e.y > this.$refs.plane.offsetHeight - 55 ) this[table].arr.splice(i,1);
- ?? ??? ??? ??? ??? ?
- ?? ??? ??? ??? ??? ?if ( table == 'e' && math < 1 ) { this.createButter('e',e); }
- ?? ??? ??? ??? ?} else {
- ?? ??? ??? ??? ??? ?if ( e.tag == 'p' ) {
- ?? ??? ??? ??? ??? ??? ?if ( this.hit_check(e) ) this[table].arr.splice(i,1);
- ?? ??? ??? ??? ??? ??? ?else if ( e.y < 0 ) this[table].arr.splice(i,1);
- ?? ??? ??? ??? ??? ?} else {
- ?? ??? ??? ??? ??? ??? ?if ( this.hit(e,this.p) ) {
- ?? ??? ??? ??? ??? ??? ??? ?this[table].arr.splice(i,1);
- ?? ??? ??? ??? ??? ??? ??? ?this.HitTimes++;
- ?? ??? ??? ??? ??? ??? ?}
- ?? ??? ??? ??? ??? ??? ?else if ( e.y > this.$refs.plane.offsetHeight - 30 ) this[table].arr.splice(i,1);
- ?? ??? ??? ??? ??? ?}
- ?? ??? ??? ??? ?}
- ?? ??? ??? ?}
- ?? ??? ?},
- ?? ??? ?
- ?? ??? ?hit_check(b) {// 是否击毁敌军
- ?? ??? ??? ?for( let i = 0; i < this.e.arr.length; i ++ ){
- ?? ??? ??? ??? ?if( this.hit(b,this.e.arr[i]) ){?
- ?? ??? ??? ??? ??? ?this.e.arr.splice(i,1);
- ?? ??? ??? ??? ??? ?this.hitCount++;
- ?? ??? ??? ??? ??? ?return true;
- ?? ??? ??? ??? ?}
- ?? ??? ??? ?}
- ?? ??? ?},
- ?? ??? ?
- ?? ??? ?hit(b,e) {// 碰撞
- ?? ??? ??? ?let d = this.judgeHit( b.x, b.y, e.x, e.y );
- ?? ??? ??? ?if( d < 35 ) return true;
- ?? ??? ?},
- ?? ??? ?
- ?? ??? ?judgeHit(x1, y1, x2, y2) {// 计算两个点的距离差
- ?? ??? ??? ?let a = x1 - x2,
- ?? ??? ??? ??? ?b = y1 - y2,
- ?? ??? ??? ??? ?result = Math.sqrt( Math.pow( a, 2) + Math.pow( b, 2 ) );
- ?? ??? ??? ?return Math.round( result );
- ?? ??? ?},
- ?? ??? ?
- ?? ??? ?pause() {// 暂停
- ?? ??? ??? ?this.start = false;
- ?? ??? ??? ?this.timers.forEach(element => { clearInterval(element); })
- ?? ??? ?}
- ?? ?},
- ?? ?
- ?? ?watch: {
- ?? ??? ?username () {// 监听玩家输入事件
- ?? ??? ??? ?if ( this.username.length > 2 ) this.face = "shy";
- ?? ??? ??? ?else this.face = "ordinary";
- ?? ??? ?}
- ?? ?},
-
- ?? ?mounted(){
- ?? ??? ?let _this = this;
- ?? ??? ?document.onkeyup = function(e) {
- ?? ??? ??? ?( e.keyCode == 32 ) && _this.createButter("p",_this.p);
- ?? ??? ??? ?// ( e.keyCode == 80 ) && _this.pause();
- ?? ??? ?}
- ?? ?},
- ?? ?
- ?? ?computed:{ faceChange() { return "image/"+this.face + ".png"; } }
- });
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持w3xue。