前言
这两天在独立开发群里有做web的小伙伴想搞下小游戏开发,我突然想到,之前在语雀写了一个入门小游戏的文档知识库,本来想做成章节视频来分享,然而我发现B站号出了点问题,所以准备在掘金上先写一篇文。
我最近愈发觉得那种动辄几千上万字的技术文章,且里面掺杂了各种代码块的技术分享,在现在的一个大环境下不是那么适合了,其一是容易陷进去细节里面,我觉得代码细节查阅文档或者去问AI都是可以的,其二就是大而全的技术文章很难完全吃透,甚至可能陷入知识盲点的循环,然后又去解决盲点,最终无始无终。
所以我看了下我语雀的那些文档,瞬间就想尝试下用不求甚解
+ 高性价比
的学习法,来写这篇文章,希望用尽量少贴甚至不贴代码块的方式,来搞一篇技术分享来给那些对小游戏感兴趣的小伙伴。如果能让小伙伴付出最少得成本入门,我需要尽量减少模棱两可得描述,尽量再每一段给出一个无歧义的结论(个人拙见,不对请指正哈);更需要尽量减少字数,可以快速阅读完。
引擎选择
结论:cocos creator
和 layaAir
二选一。
原因:
- 用TS开发,省去了语言学习的成本。
- 国人开发的,中文文档支持更好,开发习惯更适合国人。
- 学习新技能能更快速获得正向反馈,学习成果越好,效率越高;等熟悉入门了再换别的引擎不迟。
引擎开发游戏的整体思路
- 引擎一般自带一个IDE,类似于一个拖拽的低代码编辑器,大部分操作都在IDE里面靠鼠标拖拽完成。
- 游戏逻辑需要写脚本,脚本挂载在固定的节点元素上,所有的逻辑都是操作节点(类似jquery)。
- 抛弃已经熟悉的现代化web框架 MVVM 那种疯狂操作数据的方式,引擎的核心是脚本操作节点。
- 动画需要美术制作图集,引擎一键生成动效。
引擎开发游戏极简学习内容
场景
- 场景就是界面,当然也就有 创建场景、销毁场景、场景切换等动作,一般游戏界面很少,小游戏就那么几个,不像web动辄几十几百
资源
- 大部分是美术资源,预制体资源
节点
- 主要是指精灵,类似于web的div,万能容器,什么都能用它,游戏界面上所有的元素其实都是节点。
UI节点
- 主要是引擎官方高度封装的各种常规UI组件,比如输入框,列表什么的,不细说。
脚本
- 操作界面元素的TS脚本,官方封装了生命周期,不同的生命周期做不同的操作。
物理
- 主要是刚体和碰撞体。一般不会用纯数学逻辑检测物体碰撞,而是用引擎封装的刚体和碰撞体检测。
网络
- 引擎官方一般提供HTTP和 WebSocket两种,就按照平时封装 axios、fetch、websocket的方法整体封装一遍即可。
- 联网小游戏需要额外封装 websocket,主要就是 广播全服、广播房间和广播某个具体用户之类的方法。
事件
- 场景、节点、UI节点的回调事件,主要是什么点击、触屏、滑动、失焦聚焦之类的。
编译发布
- 引擎非常成熟,配置1分钟,点一下按钮即可编译成各个平台的应用,主流就是移动双端(安卓、ios)和小程序双端(微信、抖音)。
部分游戏逻辑极简说明
操作玩家移动
- 给玩家节点挂载纹理,也就是玩家的美术资源,比如玩家是一只坤
- 给玩家节点挂载脚本,脚本监听键盘或者触屏事件,比如按A向左移动,那么就监听到按A,则将玩家节点的x坐标减少一定数值,监听的间隔帧数越小,则手感越好,同样的,占用内存越多,也越消耗性能。
handleMouseMove(e: any | null = null) {
if (!this._started) return;
if (!this.enabled) return;
const touchPos = KeyBoardManager.getTouchPos();
const mouseX = touchPos?.x, mouseY = touchPos?.y;
if (mouseX === this.mouseX && mouseY === this.mouseY) {
return;
}
if ((this.mouseX === 0 && this.mouseY === 0) || (mouseX === 0 && mouseY === 0)) {
return;
}
let angle = Math.atan2(mouseY - this.mouseY, mouseX - this.mouseX);
let playerX = Math.round(this.player.x + this.speed * Math.cos(angle));
let playerY = Math.round(this.player.y + this.speed * Math.sin(angle));
this.player.pos(playerX, playerY);
this.mouseX = mouseX;
this.mouseY = mouseY;
}
碰撞检测
- 给玩家、地形、怪物等挂载刚体和碰撞体组件。
- 玩家遇到障碍物、玩家遇到怪物等情况,一般需要进行碰撞检测,在碰撞检测的回调里写相关逻辑,比如玩家掉血,释放被动技能等。
释放技能
- 当玩家下达释放技能指令,创建技能动画节点,并挂在刚体碰撞体。
- 进行检测,技能命中怪物,则执行怪物脚本的方法,比如掉血、被击中动效等。
怪物AI
- 怪物身上挂载了怪物脚本,定时检测指定半径内是否有玩家出现。
- 如出现,则以一定速率向玩家移动,普通三角函数计算即可。
- 如果玩家移动,则获取玩家新坐标,变换方向继续向玩家移动。
moveToPlayer(): void {
const enabled = GameRT.instance.getEnabled();
if (!enabled) {
return
}
const pos = GameRT.instance.getPLayerPos();
const direction = {
x: pos.x - this.owner.x,
y: pos.y - this.owner.y
}
// 计算向量的长度
const length = Math.sqrt(direction.x * direction.x + direction.y * direction.y);
// 归一化方向向量
direction.x /= length;
direction.y /= length;
this.owner.pos(this.owner.x + direction.x * this.speed, this.owner.y + direction.y * this.speed)
}
怪物生成
- 固定位置生成,将怪物列表及生成时间存储在list里面,按照死亡间隔定时生成怪物。
- 随机生成怪物,通过游戏设定在玩家指定半径外随机生成。
小游戏整体开发流程
写在最后
有正反馈,更有获得感的学习才是我们所追求的,为了学技能而学技能属于自我感动,共勉!