“灵剑出鞘”小游戏开发日志(8)---- “剑出鞘”的特效及三连触发

背景

前面我们已经实现了叠加“剑诀”,并完成了一次伤害(这个伤害后面玩法优化的时候还要另外优化),接下来就是补全我们“出鞘”打击时的特效及触发三连。

目标

完成剑式出招后,在固定位置飞出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();
        }
    }

调整好角度后看看打击效果吧

测试效果 

打击效果基本达到了,还有一些细节需要调整。 

 下一个开发计划

 游戏胜利及失败 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值