1. 程式人生 > >Vue+Websocket實現多人在線王者飛機(一)

Vue+Websocket實現多人在線王者飛機(一)

Vue requestAnimationFra 飛機大戰 WebSocket

看了Vue官方教程(貌似和自己寫的框架差別不大,聽前前端同事一直吹Vue,於是學習了一下,和自己寫的框架好像也沒強哪裏去嘛,就是要傲嬌哈哈),等有空也整理自己的框架,開源好了),想找個項目練練手(沒找到好的),就寫個飛機大戰吧。
飛機大戰總共三個頁面:登錄、匹配、遊戲頁面,三個頁面的功能:
1、 登錄:玩家填寫用戶名,socket連接
2、匹配:等待其他玩家準備
3、遊戲:戰鬥頁面
上述流程仿照了王者榮耀,所以就叫王者飛機(當然和王者榮耀相差十萬八千裏)最終效果:
技術分享圖片
git地址:https://gitee.com/djxfire/PlaneWar.git
根據上面的設計,Vue套餐基本就都用上了。
首先,先編寫遊戲頁面(讓飛機動起來):

我們定義一下玩家類player.js:

export default class Player {
  constructor (name, x, y, enermy = false) {
    this.name = name // 玩家名稱
    this.position = {} // 位置
    this.position.x = x
    this.position.y = y
    this.speed = 1
    this.direct = 0 //  方向0:朝上,1朝左,2朝下,3朝右
    this.attack = 1 // 攻擊力
    this.flood = 5 // 血量
    this.sock = null // socket連接
    this.state = 0 // 0:停止,1:移動
    this.bullets = [] // 發射的子彈
    this.enermys = [] // 敵人
    if (!enermy) {
      this.init()
    }
  }
  init () {
    // TODO: WebSocket連接
  }
  move (direct) {
    this.direct = direct
    switch (direct) {
      case 0:
        if (this.position.y > 0) {
          this.position.y -= this.speed
        }
        break
      case 1:
        if (this.position.x > 0) {
          this.position.x -= this.speed
        }
        break
      case 2:
        if (this.position.y < window.innerHeight - 55) {
          this.position.y += this.speed
        }
        break
      case 3:
        if (this.position.x < window.innerWidth - 55) {
          this.position.x += this.speed
        }
        break
    }
  }
}

玩家類的具體信息參照註釋。
接下來編寫Game.vue組件

<template>
  <div class = "scene">
    <div v-for="(monster,index) of monsters" :key="index" class="monster" :style="{left:monster.position.x + ‘px‘,top:monster.position.y + ‘px‘}">
      <img src="../assets/monster.png"
           :class="{‘turn-left‘:monster.direct == 1,‘turn-right‘:monster.direct == 3,‘turn-up‘:monster.direct == 0,‘turn-down‘:monster.direct == 2}"/>
      <p>{{ monster.name }}</p>
    </div>
    <span v-for="(monster,index) of monsters" :key="index">
      <div class="bullet" v-for="(bullet,index2) of monster.bullets"
           :key = "index2"
           :style="{left:bullet.position.x + ‘px‘,top:bullet.position.y + ‘px‘}">
      </div>
    </span>
    <div class = "monster"
         :style="{left:own.position.x + ‘px‘,top:own.position.y + ‘px‘}">
      <img src="../assets/monster.png"
           :class="{‘turn-left‘:own.direct == 1,‘turn-right‘:own.direct == 3,‘turn-up‘:own.direct == 0,‘turn-down‘:own.direct == 2}"/>
      <p>{{ own.name }}</p>
    </div>
    <div class="bullet" v-for="(bullet,index) of own.bullets"
         :key = "index"
         :style="{left:bullet.position.x + ‘px‘,top:bullet.position.y + ‘px‘}">

    </div>
    <div class = "play-control">
      <div>
        <div @touchstart="turn(0)" @touchend="turnEnd()" class="up"></div>
      </div>
      <div style="text-align: initial;">
        <div @touchstart="turn(1)" @touchend="turnEnd()" class="left"></div>
        <div @touchstart="turn(3)" @touchend="turnEnd()" class="right"></div>
        <div class="clear"></div>
      </div>
      <div>
        <div @touchstart="turn(2)" @touchend="turnEnd()" class = "down"></div>
      </div>
    </div>
    <div class = "shoot-control" @touchstart="shoot()">
    </div>
  </div>
</template>

<script>
import Player from ‘../player‘
export default {
  name: ‘game‘,
  data () {
    return {
      own: new Player(‘test‘, Math.round(Math.random() * window.innerWidth), Math.round(Math.random() * window.innerHeight)),
      monsters: []
    }
  },
  methods: {
    turn (direct) {
      this.own.move(direct)
    },
    turnEnd () {
    },
    shoot () {
    }
  }
}
</script>

<style scoped>
.scene{
  position: relative;
  overflow: hidden;
  width:100%;
  height:calc(100vh);
}
.bomb{
  animation:bombframe 3s;
  -webkit-animation: bombframe 3s;
}
.turn-right{
  -webkit-transform: rotate(270deg);
  -moz-transform: rotate(270deg);
  -ms-transform: rotate(270deg);
  -o-transform:rotate(270deg) ;
  transform: rotate(270deg);
}
.turn-left{
  -webkit-transform: rotate(90deg);
  -moz-transform: rotate(90deg);
  -ms-transform: rotate(90deg);
  -o-transform: rotate(90deg);
  transform: rotate(90deg);
}
.turn-up{
  -webkit-transform: rotate(180deg);
  -moz-transform: rotate(180deg);
  -ms-transform: rotate(180deg);
  -o-transform: rotate(180deg);
  transform: rotate(180deg);
}
.turn-down{
  -webkit-transform: rotate(0deg);
  -moz-transform: rotate(0deg);
  -ms-transform: rotate(0deg);
  -o-transform: rotate(0deg);
  transform: rotate(0deg);
}
.monster{
  position: absolute;
  display: inline-block;
  text-align: center;
}
.bullet{
  position: absolute;
  display: inline-block;
  width: 4px;
  height:4px;
  background: #000;
  -webkit-border-radius: 4px;
  -moz-border-radius: 4px;
  border-radius: 4px;
}
.play-control{
  position: fixed;
  text-align:center;
  bottom:0;
  left:0;
  background: #E9E9E9;
  width:100px;
  height:100px;
  opacity: 0.5;
  z-index: 999;
  -webkit-border-radius: 100px;
  -moz-border-radius: 100px;
  border-radius: 200px;
}
.clear{
  clear: both;
}
.play-control .left{
  display: inline-block;
  width:0;
  height:0;
  float: left;
  border-top: 25px solid transparent;
  border-right: 25px solid #A8A8A8;
  border-bottom: 25px solid transparent;
  vertical-align: middle;
}
.play-control .right{
  display: inline-block;
  width:0;
  height:0;
  float: right;
  vertical-align: middle;
  border-top: 25px solid transparent;
  border-left: 25px solid #A8A8A8;
  border-bottom: 25px solid transparent;
}
.play-control .up{
  display: inline-block;
  vertical-align: top;
  width:0;
  height:0;
  border-right: 25px solid transparent;
  border-bottom: 25px solid #A8A8A8;
  border-left: 25px solid transparent;
}
.play-control .down{
  display: inline-block;
  vertical-align: bottom;
  width:0;
  height:0;
  border-right: 25px solid transparent;
  border-top: 25px solid #A8A8A8;
  border-left: 25px solid transparent;
}
.play-control .left:active{
  border-right: 25px solid #A88888;
}
.play-control .right:active{
  border-left: 25px solid #A88888;
}
.play-control .up:active{
  border-bottom: 25px solid #A88888;
}
.play-control .down:active{
  border-top: 25px solid #A88888;
}
.shoot-control{
  position: fixed;
  text-align:center;
  bottom:0;
  right:0;
  background: #E9E9E9;
  width:100px;
  height:100px;
  opacity: 0.5;
  z-index: 999;
  -webkit-border-radius: 100px;
  -moz-border-radius: 100px;
  border-radius: 200px;
  background-size: 100%;
  background: url(../assets/BButton.png) no-repeat;
}
</style>

現在點擊方向盤飛機就動起來了,然而需要不停的點擊按鍵,顯然不合理,所以我們使用requestAnimationFrame()方法實現動畫,在Game組件中添加代碼:

 mounted () {
    let that = this
    function _callback () {
      for (let monster of that.own.enermys) {
        monster.onframe()
        for (let bullet of monster.bullets) {
          bullet.onframe()
        }
      }
      that.own.onframe()
      for (let bullet of that.own.bullets) {
        bullet.onframe()
      }
      requestAnimationFrame(_callback)
    }
    _callback()
  }

添加player的onframe代碼:

onframe () {
    if (this.state === 1) {
      this.move(this.direct)
      this.send({ opt: ‘upposition‘, name: this.name, x: this.position.x, y: this.position.y, direct: this.direct })
    }
  }

當方向盤touchstart時own.state置為1,當touchend時置為0,修改方法:

turn (direct) {
      this.$store.dispatch(‘move‘, direct)
    },
    turnEnd () {
      this.$store.dispatch(‘turnEnd‘)
    },

至此,當按下方向鍵時,飛機開始運動,當放開方向鍵時,飛機停止運動。
下一篇將實現NodeJs實現WebSocket遊戲服務端的邏輯

Vue+Websocket實現多人在線王者飛機(一)