背景
前面我们已经实现了叠加“剑诀”,并完成了一次伤害(这个伤害后面玩法优化的时候还要另外优化),接下来就是补全我们“出鞘”打击时的特效及触发三连。
目标
完成剑式出招后,在固定位置飞出n把仙剑,飞剑先上升到一定高度,之后下落到地上的剑诀上,如果不能形成同属性三连,则直接造成伤害。如果形成同属性三连,进行横向纵向斜对的打击,特效目前和落剑的相同。
开发过程
1、算法实现
本节主要的难点是三连,这个算法,我们就借助GPT来完成
我先把我的初步需求告诉gpt
在[[0, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 2, 2, 0, 0], [0, 0, 1, 0, 0], [0, 0, 2, 0, 0], [1, 0, 1, 1, 0], [2, 2, 1, 2, 0], [0, 1, 2, 2, 1], [0, 1, 2, 2, 2], [2, 1, 0, 1, 1]]这个二维的数组中,我想做成一个js的行数,输入是二维数组,输出是字母在第几行中连续出现且数量超过3个,字母在第几列中连续出现且数量超过3个
期初gpt给出的还是可以的,横向和纵向都能用,但是后面加斜角算法的时候,gpt被我限定在了原先横向和纵向的里面,调教了半天一直调教不出来,也导致了本次开发过程陷入了僵局。
后来,将问题重新梳理,并将问题拆解为了2个小问题,最终实现了效果。
原先的问题是,在数字矩阵中,算出横向纵向以及斜角方向,给攻击直线的2个点。
将问题拆解为2个问题:
1、整理横向纵向以及斜角的数据
2、对字符数据进行,连续字符检测
第一个问题问gpt
0 0 0 0 0
0 1 0 0 0
0 2 2 0 0
0 0 1 0 0
0 0 2 0 0
1 0 1 1 0
2 2 1 2 0
0 1 2 2 1
0 1 2 2 2
2 1 0 1 1一个js函数输入是上面的 二维数组,输出为个参数
row_strs_arr = ['00000','01000','02200'...] // 行中的元素拼接
col_strs_arr = ['0000012002','0120002111'...] // 列中的元素拼接
diagonal_1_strs_arr = ['2','01','010','2121'...] // 一个斜角方向依次元素拼接
diagonal_2_strs_arr = ['1','12','021','1220'...] // 另一个斜角方向依次元素拼接
在几次沟通了后,gpt4明白了需求
function processMatrix(matrix) {
const rowCount = matrix.length;
const colCount = matrix[0].length;
const rowStrsArr = matrix.map(row => row.join(''));
const colStrsArr = [];
for (let col = 0; col < colCount; col++) {
let colStr = '';
for (let row = 0; row < rowCount; row++) {
colStr += matrix[row][col];
}
colStrsArr.push(colStr);
}
const diagonal1StrsArr = [];
const diagonal2StrsArr = [];
for (let k = -(rowCount - 1); k <= colCount - 1; k++) {
let diagonal1Str = '';
let diagonal2Str = '';
for (let i = 0; i < rowCount; i++) {
if (i + k >= 0 && i + k < colCount) {
diagonal1Str += matrix[i][i + k];
}
if (colCount - 1 - (i + k) >= 0 && colCount - 1 - (i + k) < colCount) {
diagonal2Str += matrix[i][colCount - 1 - (i + k)];
}
}
if (diagonal1Str) {
diagonal1StrsArr.unshift(diagonal1Str);
}
if (diagonal2Str) {
diagonal2StrsArr.unshift(diagonal2Str);
}
}
return {
row_strs_arr: rowStrsArr,
col_strs_arr: colStrsArr,
diagonal_1_strs_arr: diagonal1StrsArr,
diagonal_2_strs_arr: diagonal2StrsArr,
};
}
第一个问题解决后,紧接着处理第二个问题
{
"row_strs_arr": ["00000", "00000", "01000", "02200", "00100", "00100", "00220", "00010", "21120", "21120"],
"col_strs_arr": ["0000000022", "0012000011", "0002112011", "0000002122", "0000000000"],
"diagonal_1_strs_arr": ["0", "00", "000", "0000", "00000", "01200", "02100", "00120", "00210", "00020", "0012", "011", "21", "2"],
"diagonal_2_strs_arr": ["0", "00", "000", "0010", "00020", "00200", "00100", "00100", "00202", "02012", "0111", "021", "02", "0"]
}有了上个函数如上的输出后,我现在通过上面的数据再放到一个函数中,
这个js函数 输入为上面的数据和搜索字符2个参数, 输出的结构如下:
[{
name: "row_strs_arr",
items: [{
count: 3,
index: 1
},
{
count: 4,
index: 5
},
]
},
{
name: "col_strs_arr",
items: [{
count: 3,
index: 1
},
{
count: 4,
index: 5
},
]
},
{
name: "diagonal_1_strs_arr",
items: [{
count: 3,
index: 1
},
{
count: 4,
index: 5
},
]
},
{
name: "diagonal_2_strs_arr",
items: [{
count: 3,
index: 1
},
{
count: 4,
index: 5
},
]
}
]
name : 所在 前一个参数的key
items-count: 搜索字符连续出现的次数
items-index: 在 前一个参数的key对应的值的index值
接着就得到gpt给的另一个方法(当然中间也调整过)
function searchOccurrences(data, searchChar) {
const result = [];
for (const key in data) {
if (data.hasOwnProperty(key)) {
const items = [];
const arr = data[key];
for (let i = 0; i < arr.length; i++) {
const str = arr[i];
let maxCount = 0;
let count = 0;
for (let index = 0; index < str.length; index++) {
if (str[index] === searchChar) {
count++;
maxCount = Math.max(maxCount, count);
} else {
count = 0;
}
}
if (maxCount >= 3) {
items.push({ count: maxCount, index: i });
}
}
if (items.length > 0) {
result.push({ name: key, items: items });
}
}
}
return result;
}
将两个函数拼接在一起完美解决了
看效果
2、打击效果的实现
有了gpt给出的算法,接下来我们来实现打击的效果和规则
如果是普通三连,飞剑在所在一行上造成一次伤害
如果是四连,则飞剑在飞过后,再进行一次往返伤害
如果是五连及以上,(目前先简单增加一次往返,后期可以考虑,所处剑诀增加一个buff,其他三连经过都会增加,其他剑诀的一次往返)
/** 飞出飞剑 */
static upSwordItems(entity: Entity, callback: Function) {
const comp = entity.getComponent(BattleChuQiaoComp);
if (comp) {
let radius = 120;
let sword_index = 0;
const swordsNode: Node = ooxh.game.swordsNode
swordsNode.scale = v3(0.2, 0.2, 0.2)
swordsNode.position = v3(0, -400, 0)
swordsNode.eulerAngles = v3(0, 0, 0)
for (var i = 0; i < comp.wuxingAllArr.length; i++) {
let wuxing = comp.wuxingAllArr[i]
if (wuxing > 0) {
sword_index++
let _jiao = ((360 * sword_index) / comp.wuxingHasArr.length)
let _x = Math.sin(_jiao * Math.PI / 180) * radius + 0
let _y = Math.cos(_jiao * Math.PI / 180) * radius + 0
let _z = 0
let at_i = Math.floor(i / COLS)
let at_j = i % COLS
//
let sword = SwordItem.createItem()
sword.wuxing = wuxing
sword.gridKey = at_i + '_' + at_j
sword.node.setPosition(v3(_x, _y, _z))
sword.node.getChildByName('body').setRotationFromEuler(v3(90, 0, 0))
swordsNode.addChild(sword.node);
}
}
tween(swordsNode)
.to(1, {
eulerAngles: v3(0, 0, 180), // 有一个旋转
scale: v3(1, 1, 1) // 放大为原始大小
}, { easing: easing.sineOut })
.to(0, {
eulerAngles: v3(180, 0, 180), // 翻转,使剑头朝上
}, { easing: easing.sineOut })
.to(1, {
position: v3(swordsNode.position.x, swordsNode.position.y, swordsNode.position.z + 1000), // 向上飞
}, { easing: easing.sineOut })
.to(0, {
eulerAngles: v3(0, 0, 0), // 去除翻转和旋转,为落剑做准备
}, { easing: easing.sineOut })
.call(() => {
BattleChuQiaoSystem.downSwordItems(entity, callback)
}).start();
}
}
/** 落剑 */
static downSwordItems(entity: Entity, callback: Function) {
const comp = entity.getComponent(BattleChuQiaoComp);
if (comp) {
const gridComp = entity.getComponent(BattleGridComp);
const swordsNode: Node = ooxh.game.swordsNode
swordsNode.children.forEach((_node, _index) => {
let sword = _node.getComponent(SwordItem)
let pos = gridComp.posMap.get(sword.gridKey)
if (pos) {
_node.setPosition(v3(pos.x, pos.y, _node.position.z))
}
tween(swordsNode)
.to(0.5, {
position: v3(0, 0, 0),
}, { easing: easing.sineOut })
.call(() => {
SwordItem.removeItem(_node.getComponent(SwordItem))
if (_index == swordsNode.children.length - 1) {
this.attackUnits()
BattleChuQiaoSystem.sanLianSword(entity, callback)
// callback && callback()
}
}).start();
})
}
}
下面是三连的打击特效实现
/** 三连特效 */
static sanLianSword(entity: Entity, callback: Function) {
const comp = entity.getComponent(BattleChuQiaoComp);
if (comp) {
let wuxingOneArr = Array.from(new Set(comp.wuxingHasArr));
wuxingOneArr.forEach((wuxing) => {
// let ttt = [[0, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 2, 2, 0, 0], [0, 0, 1, 0, 0], [0, 0, 2, 0, 0], [1, 0, 1, 1, 0], [2, 2, 1, 2, 0], [0, 1, 2, 2, 1], [0, 1, 2, 2, 2], [2, 1, 0, 1, 1]]
// let resLian = GridUtil.findBooms(ttt, wuxing.toString())
let resLian = GridUtil.findBooms(comp.gridData, wuxing.toString())
console.log(resLian)
if (resLian.length > 0) {
resLian.forEach((_lian) => {
if (_lian.name == 'row_strs_arr') {
console.log('横向爆炸')
const pos_arr = [[300, -600], [300, -500], [300, -400], [300, -300], [300, -200], [300, -100], [300, 0], [300, 100], [300, 200], [300, 300]]
_lian.items.forEach((_lian_item) => {
let _start_pos = pos_arr[_lian_item.index]
let _end_pos = [-_start_pos[0], _start_pos[1]]
comp.wuxingLianAttackArr.push({ name: 'row', start_pos_x: _start_pos[0], start_pos_y: _start_pos[1], end_pos_x: _end_pos[0], end_pos_y: _end_pos[1], wuxing: wuxing })
})
}
if (_lian.name == 'col_strs_arr') {
console.log('纵向爆炸')
const pos_arr = [[200, -700], [100, -700], [0, -700], [-100, -700], [-200, -700]]
_lian.items.forEach((_lian_item) => {
let _start_pos = pos_arr[_lian_item.index]
let _end_pos = [_start_pos[0], 400]
comp.wuxingLianAttackArr.push({ name: 'col', start_pos_x: _start_pos[0], start_pos_y: _start_pos[1], end_pos_x: _end_pos[0], end_pos_y: _end_pos[1], wuxing: wuxing })
})
}
if (_lian.name == 'diagonal_1_strs_arr') {
console.log('斜角1爆炸')
const pos_start_arr = [[300, -1100], [300, -1000], [300, -900], [300, -800], [300, -700], [300, -600], [300, -500], [300, -400], [300, -300], [300, -200], [300, -100], [300, 0], [300, 100], [300, 200], [300, 300]]
const pos_end_arr = [[-300, -500], [-300, -400], [-300, -300], [-300, -200], [-300, -100], [-300, 0], [-300, 100], [-300, 200], [-300, 300], [-300, 400], [-300, 500], [-300, 600], [-300, 700], [-300, 800], [-300, 900]]
_lian.items.forEach((_lian_item) => {
let _start_pos = pos_start_arr[_lian_item.index]
let _end_pos = pos_end_arr[_lian_item.index]
comp.wuxingLianAttackArr.push({ name: 'diagonal_1', start_pos_x: _start_pos[0], start_pos_y: _start_pos[1], end_pos_x: _end_pos[0], end_pos_y: _end_pos[1], wuxing: wuxing })
})
}
if (_lian.name == 'diagonal_2_strs_arr') {
console.log('斜角2爆炸')
const pos_start_arr = [[-300, -1100], [-300, -1000], [-300, -900], [-300, -800], [-300, -700], [-300, -600], [-300, -500], [-300, -400], [-300, -300], [-300, -200], [-300, -100], [-300, 0], [-300, 100], [-300, 200], [-300, 300]]
const pos_end_arr = [[300, -500], [300, -400], [300, -300], [300, -200], [300, -100], [300, 0], [300, 100], [300, 200], [300, 300], [300, 400], [300, 500], [300, 600], [300, 700], [300, 800], [300, 900]]
_lian.items.forEach((_lian_item) => {
let _start_pos = pos_start_arr[_lian_item.index]
let _end_pos = pos_end_arr[_lian_item.index]
comp.wuxingLianAttackArr.push({ name: 'diagonal_2', start_pos_x: _start_pos[0], start_pos_y: _start_pos[1], end_pos_x: _end_pos[0], end_pos_y: _end_pos[1], wuxing: wuxing })
})
}
})
}
});
this.doLian(comp, callback)
}
}
之后就是根据算好的两点进行飞剑动作
static swordFlyAtTwoPoint(comp: BattleChuQiaoComp, callback: Function) {
if (comp.wuxingLianAttackArr.length > 0) {
let lian: ILian = comp.wuxingLianAttackArr.pop()
let sword = SwordItem.createItem()
sword.wuxing = lian.wuxing
sword.node.setPosition(v3(lian.start_pos_x, lian.start_pos_y, 0))
// 根据当前的 类型 旋转不同的角度
if (lian.name == 'row') {
sword.node.eulerAngles = v3(0, 90, 0)
}
if (lian.name == 'col') {
sword.node.eulerAngles = v3(0, 180, 0)
}
if (lian.name == 'diagonal_1') {
sword.node.eulerAngles = v3(0, 135, 0)
}
if (lian.name == 'diagonal_2') {
sword.node.eulerAngles = v3(0, 225, 0)
}
ooxh.game.swordsNode.addChild(sword.node);
tween(sword.node)
.to(0.5, {
position: v3(lian.end_pos_x, lian.end_pos_y, 0),
}, { easing: easing.sineOut })
.call(() => {
SwordItem.removeItem(sword)
if (comp.wuxingLianAttackArr.length > 0) {
this.swordFlyAtTwoPoint(comp, callback)
} else {
console.log('sword.node 全部完成 move')
callback && callback()
}
}).start();
}
}
调整好角度后看看打击效果吧
测试效果
打击效果基本达到了,还有一些细节需要调整。
下一个开发计划
游戏胜利及失败