背景
想做一个可以全部人一起打boss的玩法 但是又局限在了这样的回合制 于是打算这样子实现
- 建立websocket链接 将当前玩家伤害数据上报
- 将伤害信息分发给所有在攻略boss的玩家
- 将伤害实时弹出显示
参考文章
使用技术
- reconnecting-websocket (websocket)
- lottie-web (渲染AE动画特效)
- lottie素材库
- animejs (动画特效)
具体实现
定义消息结构
根据上述需求 分析得到 我们需要发送伤害信息给所有人 仅此一个简单消息 因此我们设计如下
javascript
// 系统消息
const msg1 = {type: 'system', subType: 'connect-success', bossInfo: "boss相关信息"}
// 伤害信息
const msg2 = {type: 'battle', subType: 'hurt', bossInfo: "boss相关信息", userInfo: "当前用户信息"}
// 心跳包
const msg3 = {type: "system", subType: "heart-beat"}
使用type
区分业务大类 subType
细分业务 后续拓展可以增加subType
即可 针对新类型也可以做默认处理(之前开发即时聊天有了解过一些)
定义boss数据
boss数据可以用副本的形式实现 将所有boss汇总到一个副本里面 使用bossid做区分 额外增加单日讨伐次数 以及 最大回合数限制
javascript
[`Z_BOSS_WAR--${ALL_DM_PREFIX.BOSS_WAR(2)}`]: {
rememberProgress: true,
missionId: 1,
mapId: commonMapId,
backgroundImage: `${DIGIWORLD_PATH}/fileIsland/龙眼湖.jpg`,
bgImg: `${FULL_RES_PATH}/bossWar/bg-default.png`,
maxTimes: 3, // 单日最大次数
maxRound: 10, // 最大回合数
unlockLimit: {
userLevel: 10,
completeMission: null
},
recommend: {},
name: '高贵南瓜兽',
description: '',
// 敌人列表
enemyList: [
{
lineName: 'Z_BOSS_WAR', //路线
id: ALL_DM_PREFIX.BOSS_WAR(2), // 当前dm id
level: 30,
presetAttr: {
...$A.L1(30),
de: 200,
spd: 200
}
}
],
// 掉落物
spoilsList: [
{
id: 'OTHER_1',
getItem: getItem(100, [100, 200])
}
]
}
切入点
在原有的战斗逻辑基础上 额外增加一个对战类型 (之前预留的) 判断是这个类型就初始化socket即可
javascript
computed: {
isBossMatch () {
return this.battleType === "boss"
}
},
mounted () {
if (this.isBossMatch) {
// 初始化socket
this.initSocket()
}
}
消息处理
- 系统消息 目前系统消息只有
连接成功
和心跳
所以只需处理伤害逻辑 - 伤害逻辑需要判断是不是我自己造成的伤害 需要排除
- 将收到的伤害信息 实时修改内存中的boss血量数据 并显示到界面上
javascript
let idx = this.tamersDamageList.push({ ...damage })
this.$nextTick(() => {
anime({
targets: this.$refs.damageBox[idx - 1],
translateY: [{ value: `-${getRandomNum(20,40)}px` }],
translateX: [{ value: `${getRandomNum(-20,40)}px` }],
opacity: [{ value: 1 }, { value: 0, delay: 1000 }],
duration: 1000
})
})
将伤害显示到界面上 通过随机一个x y偏移值 达到伤害跳出的效果
伤害统计
这个就是交给后台去实现了 貌似是存到了redis
里面 咱也不懂
讨伐结束
boss的血量比较多 然后伤害却很低 为了限制给单个玩家刷数据 额外设定了对战回合数上限 10回合之后boss就会逃跑了
其他
开发完之后 感觉背景挺单调的 突然想到了之前的lottle
下面简单贴出用法 用来实现背景的动画
javascript
<template>
<div style="lottie-box">
<div id="domId" style="width: 100vw;height: 100vh">
</div>
</div>
</template>
<script>
import Lottie from 'lottie-web'
const JSON_DATA = require("@/static/other/lottie/wsj.json")
export default {
mounted() {
this.init()
},
methods: {
init() {
const animation = Lottie.loadAnimation({
container: document.getElementById('domId'), // 绑定dom节点
renderer: 'canvas', // 渲染方式:svg、canvas
loop: true, // 是否循环播放,默认:false
autoplay: true, // 是否自动播放, 默认true
animationData: JSON_DATA,// AE动画使用bodymovie导出为json数据
rendererSettings: {
preserveAspectRatio: 'none', // 不保留长宽比
progressiveLoad: false, // 禁用渐进加载
hideOnTransparent: true // 在透明背景时隐藏动画
}
})
animation.addEventListener('DOMLoaded', function() {
console.log(`帧数:${animation.totalFrames}`);
});
// animation.playSegments([[0,5]], true)
// animation.play(); // 播放,从当前帧开始播放
// animation.stop(); // 停止,并回到第0帧
// animation.pause(); // 暂停,并保持当前帧
// animation.goToAndStop(value, isFrame); // 跳到某个时刻/帧并停止isFrame(默认false)指示value表示帧还是时间(毫秒)
// animation.goToAndPlay(value, isFrame); // 跳到某个时刻/帧并进行播放
// animation.goToAndStop(30, true); // 跳转到第30帧并停止
// animation.goToAndPlay(300); // 跳转到第300毫秒并播放
// animation.goToAndPlay(0); // 从头播放动画
// animation.playSegments(arr, forceFlag); // arr可以包含两个数字或者两个数字组成的数组,forceFlag表示是否立即强制播放该片段
// animation.playSegments([10,20], false); // 播放完之前的片段,播放10-20帧
// animation.playSegments([[0,5],[10,18]], true); // 直接播放0-5帧和10-18帧
// animation.setSpeed(speed); // 设置播放速度,speed为1表示正常速度
// animation.setDirection(direction); // 设置播放方向,1表示正向播放,-1表示反向播放
// animation.destroy(); // 删除该动画,移除相应的元素标签等。
}
}
}
</script>
<style lang="less">
.lottie-box {
overflow: hidden;
width: 100%;
height: 100%;
}
</style>
遇到的一些问题
- 由于伤害是经过了防御值计算后发送给所有用户的 因此 来自其他玩家的伤害数据不需要参与防御值的计算 又得单独处理
- boss的初始化问题 目前还是先比较蠢手动创建本地数据 再通过接口创建
- 奖励问题 目前还处于测试阶段 因此就没什么奖励了 后续打算做boss券去换道具
结束
感谢阅读 你又摸鱼了10分钟