今年大热的新玩法 ,小游戏平台上已经很多这类游戏。这次来拆解下背包玩法怎么实现,难度比上次的植物大战僵尸要大一些。那废话不多说,手把手教你实现一个背包整理,这个功能算是整个游戏的核心亮点。还是老规矩,先看效果。因为传不了视频,详细的可以看我的公众号文章:传送门
玩法理解
如果你还不知道这个是怎么实现,或者对这个玩法没有太深的概念,那最简单的办法就是去玩一下这个游戏,就可以总结一些要点。以下是我个人的一些归纳:
1、5*7的网格背包地图(你也可以根据你的需求定义格子数量)
2、默认3*3已经激活可用的网格
3、武器有很多种,相同类型和等级的武器可以合并升级
4、武器栏默认出现三个,还可以出现激活的格子(控制概率,不是每次都出现)
5、新增激活的格子可以拖拽到地图网格上,必须与已有的网格相邻才能合并成新的大网格。
6、任一组合激活格子可以自由放置,但不能超出网格地图,放置会吸附到最近的地图网格上
7、放置的武器时候可以挤下已经占位的武器
8、武器可以随意摆放,也遵循吸附到网格上
9、武器占据的网格大小可以自由定义,根据武器的样式决定
10、放置组合网格会进入编辑网格模式,此时不能放置武器。
以上是一些比较容易总结出来的点,还有一些细节,需要多多体验才能知道,在实现过程中把握细节,我就不一一罗列。
下图是一个游戏模块总览:
下面的讲解围绕几大类来进行:
脚本类
1、基础网格 BagTile
2、组合激活网格 TilePattern
3、武器 Weapon
最基础的就是这三个类。

图层
1、背包皮肤 BagBackground
2、网格地图 TileContainer
3、主网格 DraggableTile
4、武器图层 WeaponsContainer
5、 新增武器栏 PatternArea
层级关系如下:

BagBackground 没有太多的逻辑,主要功能是,随着网格大小改变背包的外观。
左边是武器编辑模式,右边是地图网格编辑模式。
从地图编辑模式切换的时候背包大小会发生变化,动态改变这个节点的大小就行。背包的装饰元素,利用Widget组件采用相对定位,就可以做到这种效果。先看下实际效果:
背包的装饰元素有很多,需要一个一个的调试,针对几个角(左上、左下、右上、右下)进行相对定位。另外对齐模式 Align Mode要选择图上那种ALWAYS ,否则装饰元素不会跟随背包大小刷新而变化。

背包装饰元素
网格
游戏中的主要操作对象,网格有几种显示状态,在逻辑中根据不同的状态改变外观,用来给玩家反馈。
1、可以放置武器(改变节点颜色,绿色可放置)
2、不可放置武器(改变节点颜色,红色不可放置)
3、可以放置网格(绿虚线框)
4、不可以放置网格(红虚线框)
5、普通模式(咖啡色)
6、编辑模式(白色底+黑线框)
网格还要存放一下当前的行列信息,在编码过程中用得到。
网格地图
1、初始化5*7个网格,网格要有间隙
2、新建一个二维数组存放网格是否激活的信息
3、新建一个二维数组存放网格的武器信息
this.activeGridNodeMap = [
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
];
// 武器初始化,没有类型,没有等级
initWeapons(){
for (let row = 0; row < this.numRows; row++) {
let weapons = [];
for (let col = 0; col < this.numCols; col++) {
weapons.push({type: -1, level: -1})
}
this.weaponsMap.push(weapons)
}
},
具体逻辑就是要围绕上面两个二维数组(实际一个二维数组也够了,就是对象上多几个key),不断的去更新两个二维数组,有任何操作网格、武器、位置的变动都要更新两个二维数组。
基础的网格节点位置是不会变动,可以理解为一个基础锚点,激活的格子锚定底层的网格,武器锚定激活的网格,一共三个层次。
主激活网格
根据activeGridNodeMap信息创建主激活网格,位置信息根据网格大小乘以坐标,可以得到布局。因为激活网格可以移动,为了方便处理,将激活的网格塞进DraggableTile这个节点。DraggableTile的宽高尺寸要根据网格数量动态的调整。根据激活网格信息可以算出DraggableTile边界,同理之前的基础网格地图也有边界。
calculateActiveBound(grid, tileWidth, tileHeight) {
// 省略算边界的逻辑
return {
left: leftBound,
right: rightBound,
top: topBound,
bottom: bottomBound,
width: rightBound - leftBound,
height: topBound - bottomBound
};
}
那么在移动激活网格的时候,限定DraggableTile不超过边界即可。
需要注意的是,移动DraggableTile时,因为武器也是依附在激活网格上。所以武器图层WeaponsContainer也要跟随一起移动。做法就是给他们绑定同样的事件:
setupDraggableTile() {
this.draggableTile.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
this.draggableTile.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
this.draggableTile.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
this.draggableTile.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
this.weaponsContainer.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
this.weaponsContainer.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
this.weaponsContainer.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
this.weaponsContainer.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
},
在事件处理的方法里面,同时改变两个节点的position
onTouchMove(event) {
// 非编辑模式不让拖拽
if(!this.isGridMapEditing){
return
}
const touchLocation = event.getLocation();
const delta = touchLocation.sub(this.startTouchPosition);
const newPosition = this.startTilePosition.add(delta);
// 限制拖拽范围
let position = this.clampPosition(newPosition);
this.draggableTile.position = position
// 编辑武器模式拖动,武器图层一起动
this.weaponsContainer.position = position;
},
另外还需要注意的是,因为移动的是DraggableTile节点,它的子网格坐标没有变化。但是子网格对应基础网格地图坐标是发生变化了,因此要更新activeGridNodeMap这个二维数组。
通过DraggableTile的位置偏移量,除以网格的宽高,可以计算出子网格的行列变化大小,方向决定数字的正负。通过换算更新一下activeGridNodeMap。
以上就是主激活网格的大体逻辑。
添加激活网格
![屏幕快照 2024-09-09 上午9.06.37|472x166]
玩家可以对网格地图进行编辑,在武器栏PatternArea会呈现三个图案,最多会出现一个激活网格图案。网格图案类型很多,可以做一个配置表,如下代码。是不是有点像俄罗斯方块啊,对的, 添加激活网格图案就是跟俄罗斯方块一毛一样 。你要是会写俄罗斯方块,这个就是小菜一碟。通过二维数组,创建对应的图案。
export const Patterns = {
[PatternType.SINGLE]: [
[1]
],
[PatternType.TWO_TILES]: [
[1, 1]
],
[PatternType.THREE_TILES]: [
[1, 1],
[1, 0]
],
[PatternType.T_SHAPE]: [
[1, 1, 1],
[0, 1, 0]
],
[PatternType.FOUR_TILES]: [
[1, 1],
[1, 1]
],
[PatternType.L_SHAPE]: [
[1, 0],
[1, 0],
[1, 1]
],
[PatternType.F_SHAPE]: [
[1, 1],
[0, 1],
],
[PatternType.ONEV_SHAPE]: [
[1],
[1]
],
[PatternType.ONEH_SHAPE]: [
[1, 1, 1]
]
};
然后就是放置图案,逻辑很简单很粗暴。
遍历图案的所有子网格,找到最近的非激活网格,判断条件就是 距离不能超过自己的大小 。如果数量完全找到,说明可以放置,如果找不到则不能放置,同时改变颜色提示玩家该位置可以放置。
需要注意的是,添加激活网格的时候,不能添加武器,二者是互斥的。编辑地图网格的效果如下:
添加武器
添加武器的逻辑跟添加激活网格的逻辑差别不太大。
添加激活网格,落点是在网格地图上,只要满足有空余网格就可以放置。
添加武器,落地是激活网格上,只要激活网格上有满足条件的位置就行。
添加武器的几点细节:
1、相同类型、相同等级的可以合并升级(碰撞检测)
2、不同类型的武器,可以挤下原有位置的武器,被挤下的武器回到待添加区。
3、武器可以放置到任意位置,只要激活网格可以容纳下。
4、武器可以全部放回到待添加区。
基本要处理的就是上面几个要点,首先要给武器定义网格类型
export const WeaponType = {
wallet: {
pattern: PatternType.SINGLE // 钱包
},
longSword: {
pattern: PatternType.ONEV_SHAPE // 长剑
},
darts: {
pattern: PatternType.SINGLE // 飞镖
},
dagger:{
pattern: PatternType.TWO_TILES // 短剑
},
gloves: {
pattern: PatternType.SINGLE // 手套
},
bow: {
pattern: PatternType.ONEV_SHAPE // 弓箭
},
blueBottle: {
pattern: PatternType.ONEV_SHAPE // 蓝
},
shield: {
pattern: PatternType.SINGLE // 盾
},
ax: {
pattern: PatternType.F_SHAPE // 斧头
},
}
类型跟之前定义的激活网格图案是一样的,在最终渲染的时候,只渲染武器皮肤,把网格的皮肤隐藏掉。本质就是包装了武器信息的激活网格。
每次移动武器,添加武器都需要更新武器的二维数组weaponsMap。
添加武器、升级武器具体效果看下面视频:
以上就是背包玩法的整体思路,说实话二维数组会比较难写一点,但只要拆分好模块,一步一步写下来也是可以的,一些通用的逻辑可以让AI来完成,但是要用AI来完整实现这个,还是不太现实。
《花园特工队》的战斗场景就不展开了,其实跟植物大战僵尸是类似,发射武器,杀掉怪物。有兴趣可以看看我之前写的文章。
PS:游戏中用到的素材来源于网络,仅学习使用。如有侵权请告知
欢迎关注我的公众号【入门游戏开发】,获取更多游戏开发知识和游戏源码,手把手教你做游戏。




