热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Weex开发小游戏是件很high的事儿

前言作为一个移动端初学者、爱好者,能使用前端技术开发原生游戏一直是一件渴望而不可及的事情,暂且不说游戏逻辑的复杂度,算法的健壮性ÿ
前言

作为一个移动端初学者、爱好者,能使用前端技术开发原生游戏一直是一件渴望而不可及的事情,暂且不说游戏逻辑的复杂度,算法的健壮性,单单是场景、画布、布局就让我们无处下手。

几年前曾经参与 Appcan 技术的技术孵化和推广,尝试使用 Hybrid 技术写过一个小游戏,《Hybrid混合实现app小游戏》,由于此游戏结构场景比较简单,所以未使用大型的游戏引擎,Cocos2d-x游戏引擎,所有逻辑全部手工。同样也是可「三端同构」,但本质上还是一个 H5小游戏,只是在真机上,执行环境是一个 UIWebview,所以,H5可以做的,他都可以做,H5不能做到,他未必不能做,如摄像头、陀螺仪等。但缺点也很致命,执行效率完全受限于原生控件 UIWebview,要知道对于一个游戏来讲,流畅度是第一要义。

总的来讲,使用 Hybrid 技术开发游戏的方案虽然可行,但是,效果并不是我想要的。

自从 ReactNative 开源以来,一直想着要使用 ReactNative 开发游戏。个人原因,一直未付诸实践。直到上周有网友问我,「Weex是否能拿来做游戏开发」,试试就知道,那就先拿 Weex 开刀,来挑战下 game app 同构的能力,给还没上车的朋友带波节奏。

准备工作

如果你还未入门,没关系,就当看个热闹了,知道 Weex 能不能快速开发游戏就可以了。

如果你想先入门,以下几篇文章你可以当作是导读。

  • 《Native、Web App、Hybrid、React Native 和 Weex》
  • 《Weex 快速创建工程 Hello World》
  • 《网易严选 App 感受 Weex 开发》
扫雷游戏 Demo

官方提供的 WeexPlayground 中也提供了一个游戏 demo 扫雷,如下图

此 demo 是为了实践以下三件事:

  1. 界面指示器
  2. 动态数据绑定
  3. 更复杂的事件

总体表现还是不错的。更多细节,可详读《Weex版扫雷游戏开发》

我的小游戏

别人的东西再炫酷也始终是别人的,不自己动手码一个说话都不硬气!

没有实践就没有发言权,此处献上源码的 Github 链接:https://github.com/zwwill/just-do-8,欢迎「Star」「Fork」,支持瞎搞 ψ(`∇´)ψ

先来感受下最终的效果

界面

体验

IOS已上线 https://itunes.apple.com/cn/a...

也可以直接使用 Weex Playground 扫码体验 Weex Playground下载地址

近期将发布到应用市场,届时还望大家多多支持。

规则

规则很简单,会玩「俄罗斯方块」和「2048」就一定会玩这款小游戏

一期功能

由于要快速产出,界面随便就别太在意了,另外很多功能还没有开发,如,全球排名、分享、游戏设置等,这些都放在后面慢慢迭代吧(如果有第二版的话( ̄. ̄))

源码分析

接下来是一大波源码分析,不感冒?那就直接跳过。
由于篇幅有限,此处只做简要介绍,详细请见工程源码,地址请爬楼

项目结构

只有三个文件(一个场景两个组件)。我来逐一讲解下每个文件的职能。

index.vue

【index.vue】是一个场景文件,用于根据状态切换场景,以及监听处理所有的手势

【模版 | 简码】

我们监听了 Weex 的一堆事件来「合成」我们需要的【切换】【左右滑动】【下降】等主要游戏操作。如@swipe@click@panstart@panend@horizontalpan,同时给组件注册@screenLock@screenUnlock@over@win等事件,用于游戏场景切换。

  • @swipe:swipe的属性direction提供在屏幕上滑动时触发的方向,本项目用到updown,官方给的说法是『direction的值可能为upleftbottomright』但实际上我得到的却是down而不是bottom,具体请客还在和Weex的开发团队进行沟通,确认后会更新上来。另外要注意的是@swipe@click@panstart@panend@horizontalpan这些事件同时使用时会出现冲突问题,Android 平台下问题比较多,具体大家在做的时候需要做好兼容
  • @click:常规的click事件
  • @panstart、@panend、@horizontalpan:用于计算左右滑动距离,每滑动40个显示像素就向组件发起滑块左右滑动的指令

具体事件的使用姿势,大家可以详读官方文档

每一个事件方法的功能实现和视觉此处就略去了。

stoneMap.vue

【stoneMap.vue】就像是「大内总管」,一切闲杂喽啰的事都归他管。主要管理的数字块的布局、状态、游戏分值等

【简码】


  • this.stones:用于管理所有实例进来的数字块,将他们投影到界面上
  • this.map:是一个6*9的逻辑网,标记 this.stones 中的的数字块的逻辑位置

此处主要介绍下事件的控制分发和逻辑网的计算,讲解在注释中

【action() | 简码】

/*** 事件的控制分发* */
action(_action) {if (!!this.actionLock) return;switch (_action) {case 'click':case 'up':// click 和 up 触发上方三个活动数字块的互相切换this.actionChange();break;case 'left':case 'right':// left 和 right 触发上方三个活动数字块的的整体平移this.actionSliderMove(_action);break;case 'down':case 'bottom':// down 触发上方三个活动数字块进场// bottom 起到兼容的作用this.actionDown();break;default:break;}
}

【mapCalculator() | 全码】

/*** 计算map* */
mapCalculator: (function () {var updateStone &#61; function (_stones, _id, _s) {/** * 此方法控制得分规则* 横竖对角线&#43;1分* 十字、X型&#43;2分* 8字型、9宫格分别&#43;3分、&#43;4分&#xff0c;当然&#xff0c;不可能存在这两种情况* */if (_stones[_id]) {_s !&#61; 0 && _s <8 && (_stones[_id][&#39;score&#39;] &#61;&#61; 0 ? _stones[_id][&#39;score&#39;] &#61; _s : _stones[_id][&#39;score&#39;]&#43;&#43;);} else {_stones[_id] &#61; {id: _id,score: _s}}};return function (_map) {let hasChange &#61; false,activeStones &#61; {},height &#61; _map.length - 1,width &#61; _map[0].length - 1,_tp_id, _s;// 全逻辑网遍历for (let y &#61; height; y >&#61; 0; y--) {for (let x &#61; 0; x <&#61; width; x&#43;&#43;) {_tp_id &#61; _map[y][x] || "";// 排除四角if (!_tp_id || (x &#61;&#61; 0 || x &#61;&#61; width) && (y &#61;&#61; 0 || y &#61;&#61; height)) continue; _s &#61; parseInt(this.$refs[_tp_id][0].num);let _p1, _p2;if (x &#61;&#61; 0 || x &#61;&#61; width || y &#61;&#61; 0 || y &#61;&#61; height) {// 侧边&#xff0c;将其单独提炼出来是为了减少计算量三分之一的计算量if (x &#61;&#61; 0 || x &#61;&#61; width) {// 竖排if (!_map[y - 1][x] || !_map[y &#43; 1][x]) continue;_p1 &#61; this.$refs[_map[y - 1][x]][0];_p2 &#61; this.$refs[_map[y &#43; 1][x]][0];} else if (y &#61;&#61; 0 || y &#61;&#61; height) {// 横排if (!_map[y][x - 1] || !_map[y][x &#43; 1]) continue;_p1 &#61; this.$refs[_map[y][x - 1]][0];_p2 &#61; this.$refs[_map[y][x &#43; 1]][0];}if (_p1 && _p2 && _p1.num &#61;&#61; _s && _p2.num &#61;&#61; _s) {hasChange &#61; true;updateStone(activeStones, _tp_id, &#43;&#43;_s);updateStone(activeStones, _p1.id, 0);updateStone(activeStones, _p2.id, 0);}} else {// 中间可形成九宫格区域const _map_matrix &#61; [[[0, 1], [0, -1]],[[-1, 1], [1, -1]],[[-1, 0], [1, 0]],[[-1, -1], [1, 1]]];for (let _i &#61; 0, _mm; _i <_map_matrix.length; _i&#43;&#43;) {_mm &#61; _map_matrix[_i];if (!_map[y &#43; _mm[0][0]][x &#43; _mm[0][1]] || !_map[y &#43; _mm[1][0]][x &#43; _mm[1][1]]) continue;_p1 &#61; this.$refs[_map[y &#43; _mm[0][0]][x &#43; _mm[0][1]]][0];_p2 &#61; this.$refs[_map[y &#43; _mm[1][0]][x &#43; _mm[1][1]]][0];if (_p1 && _p2 && _p1.num &#61;&#61; _s && _p2.num &#61;&#61; _s) {hasChange &#61; true;updateStone(activeStones, _tp_id, _s &#43; 1);updateStone(activeStones, _p1.id, 0);updateStone(activeStones, _p2.id, 0);}}}}}// 存在更新块if (hasChange) {setTimeout(() &#61;> {for (let s in activeStones) {this.sChange(s, undefined, activeStones[s].score);}// 数字块整理setTimeout(() &#61;> {this.stonesTrim();}, 100)}, 400)} else {let _errorStone &#61; "";for (let _i &#61; 0; _i this.highScore) {storage.setItem(&#39;H-SCORE&#39;, this.totalScore)}} else {this.$emit(&#39;screenUnlock&#39;);setTimeout(() &#61;> {this.pushStones();}, 100);}}}
})()

【stonesTrim | 全码】

/*** 整理数字块&#xff0c;堆积下降* */
stonesTrim() {let hasChange &#61; false,height &#61; this.map.length - 1,width &#61; this.map[0].length - 1,_tp_id, _step &#61; 0;for (let x &#61; 0; x <&#61; width; x&#43;&#43;) {_step &#61; 0;for (let y &#61; height; y >&#61; 0; y--) {_tp_id &#61; this.map[y][x] || "";if (!_tp_id) {_step&#43;&#43;;continue;} else if (_step > 0) {hasChange &#61; true;this.sChange(_tp_id, {y: _step});this.map[y &#43; _step][x] &#61; _tp_id;this.map[y][x] &#61; "";}}}setTimeout(() &#61;> {this.mapUpdate();}, hasChange ? 200 : 0);
}

stone.vue

【stone.vue】就像被「大内总管」管理着的「小太监」&#xff08;数字块&#xff09;&#xff0c;「小太监」的一举一动都是被「总管」支配的&#xff0c;包括其长相&#xff08;颜色&#xff09;、品级&#xff08;数字&#xff09;以及生死&#xff08;生命周期&#xff09;&#xff0c;但状态的改变都是由自己执行&#xff0c;直接自己整容&#xff0c;自己升级&#xff0c;还要。。自杀。底层人民好无奈 ╮(╯_╰)╭

【简码】

好了&#xff0c;辣么乐色的代码我都不好意思再唠叨了。换个话题&#xff0c;来讲讲这个小游戏从无到有中间的一些方案的变更吧。

各种尝试

由于对 Weex 的过高期望&#xff0c;导致很多最初的方案都被「阉割」或者「整容」。

动画

想让元素动起来&#xff0c;传统前端一般有两种方式

1、CSS 动画
2、JS 动画
在 Weex 上由多了一个
3、animation 内建模块&#xff0c;可执行原生动画

由于 css3 的 transition 在 Weex 的 0.16.0&#43; 版本才能使用&#xff0c;官方提供的 demo 框架引用的 SDK 版本低于此版本&#xff0c;方案1&#xff0c;无效&#xff01;

Weex 上的视觉是通过解析 VDom&#xff0c;在调用原生控件渲染成的&#xff0c;完全没有 DOM &#xff0c;所以 JS 动画的方案&#xff0c;无效&#xff01;

看了只剩下 Weex 的 animation 内建动画模块了。

虽然不太喜欢&#xff0c;用起来也很别扭&#xff0c;但是没办法&#xff0c;有总比没有强。知促常乐吧。

来看一下 animation 的使用姿势

animation.transition(this.$refs.test, {styles: {color: &#39;#000&#39;,transform: &#39;translate(100px, 100px) sacle(1.3)&#39;,backgroundColor: &#39;#CCC&#39;},duration: 800, // mstimingFunction: &#39;ease&#39;,needLayout:false,delay: 0 // ms}, function () {// animation finished.})

想实现一个多态循环的动画&#xff0c;还要写一个方法&#xff0c;想想就难受

音乐

没有声音还能算是游戏吗&#xff1f;&#xff01;

嗯 ~ ~ ~ 好像可以算

无所谓啦~ 开心最重要 ︿(&#xffe3;︶&#xffe3;)︿

尴尬的是 Weex 官方压根就没给咱们提供这样的 API&#xff0c;好在有三方的插件可用&#xff0c;Nat&#xff0c; 刚好可以用上。

Weex 提倡使用网络资源&#xff0c;所有我把音频文件上传到了 CDN 上&#xff0c;为了能快一点。。

当然不可能一路顺风&#xff01;

我们来看看 Nat Audio 模块的使用方式

Nat.audio.play(&#39;http://cdn.instapp.io/nat/samples/audio.mp3&#39;)

然而 Nat.audio 只提供了 play() | pause() | stop() 三个 API。

为什么没有 replay() 重放&#xff1f;我想用的就是重放。这都不是事儿&#xff0c;使用 play() 硬着头皮上吧&#xff01;

由于 Nat.audio 不支持 Web 端&#xff0c;每次修改都是真机调试&#xff0c;那个速度&#xff0c;唉~~~我终于理解原生小伙伴们的痛苦了。。

这也不是事儿&#xff0c;最气愤的就是&#xff0c;Nat.audio.play() 每次播放相同的音频竟然不是走的缓存&#xff01;难道缓存机制还要自己做&#xff1f;&#xff01;&#xff1f;&#xff01;ヽ(&#xff40;⌒´)&#xff89; 我的天&#xff01;

最后还是乖乖的用背地文件吧。还要写平台路径适配。。

没想到音频的槽点这么多&#xff01;还要我没用 Weex 做网易云音乐。

手势指令

前文也有讲过&#xff0c;小游戏用到了&#64;swipe&#64;click&#64;panstart&#64;panend&#64;horizontalpan这么多事件监听。官方也有友情提醒「horizontalpan 手势在 Android 下会与 click 事件冲突」&#xff0c;但实际上 ios 平台上也会有冲突。

具体的我就不再描述了。此处只想说明&#xff0c;Weex 在手势指令上虽然可以满足游戏的基础指令要求&#xff0c;但细节上还是不太理想。

总结

总的来讲&#xff0c;Weex 算是满足了我做小游戏的要求。如果想做大游戏&#xff0c;就不建议使用 Weex 了&#xff0c;Weex 确实做不了&#xff0c;但者也不是 Weex 诞生的意义。

好了&#xff0c;此次尝试就到这吧。为了不让思路断掉&#xff0c;我又通宵了&#xff0c;罪过&#xff0c;罪过 ~ ~ ~&#xff0c;希望此文对感兴趣的小伙伴有所帮助。

mark: 03:05



推荐阅读
  • 本文介绍了如何使用vue-awesome-swiper组件,包括在main.js中引入和使用swiper和swiperSlide组件,以及设置options和ref属性。同时还介绍了如何在模板中使用swiper和swiperSlide组件,并展示了如何通过循环渲染swipes数组中的数据,并使用picUrl属性显示图片。最后还介绍了如何添加分页器。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • 20211101CleverTap参与度和分析工具功能平台学习/实践
    1.应用场景主要用于学习CleverTap的使用,该平台主要用于客户保留与参与平台.为客户提供价值.这里接触到的原因,是目前公司用到该平台的服务~2.学习操作 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • uniapp开发H5解决跨域问题的两种代理方法
    本文介绍了uniapp开发H5解决跨域问题的两种代理方法,分别是在manifest.json文件和vue.config.js文件中设置代理。通过设置代理根域名和配置路径别名,可以实现H5页面的跨域访问。同时还介绍了如何开启内网穿透,让外网的人可以访问到本地调试的H5页面。 ... [详细]
  • 本文介绍了如何使用elementui分页组件进行分页功能的改写,只需一行代码即可调用。通过封装分页组件,避免在每个页面都写跳转请求的重复代码。详细的代码示例和使用方法在正文中给出。 ... [详细]
  • 如何用JNI技术调用Java接口以及提高Java性能的详解
    本文介绍了如何使用JNI技术调用Java接口,并详细解析了如何通过JNI技术提高Java的性能。同时还讨论了JNI调用Java的private方法、Java开发中使用JNI技术的情况以及使用Java的JNI技术调用C++时的运行效率问题。文章还介绍了JNIEnv类型的使用方法,包括创建Java对象、调用Java对象的方法、获取Java对象的属性等操作。 ... [详细]
  • 先看看ElementUI里关于el-table的template数据结构:<template><el-table:datatableData><e ... [详细]
  • 随着前端技术的发展,越来越多的开发者开始使用react、vue等web框架,但很少有人深入理解这些框架的源码。然而,这些框架底层都是由原生的javascript构建而成。对于初学前端的人来说,可能会认为javascript很容易上手,但实际上只是因为它被高度封装了。与能够使用封装类的人相比,能够理解框架原理的人则处于另一个层面。本文将深入剖析jquery源码,探寻框架底层的原理,帮助读者更好地理解web框架的运行机制。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 网址:https:vue.docschina.orgv2guideforms.html表单input绑定基础用法可以通过使用v-model指令,在 ... [详细]
author-avatar
fly-fox
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有