返回

解决配对游戏图片翻转难题:ID匹配与状态控制

javascript

配对游戏图片翻转问题

问题概述

在开发配对卡片游戏时,常见问题是:当两张卡片不匹配时,需要它们翻转回背面。此过程中,可能出现翻转逻辑失效,即无法正确识别卡片是否匹配,或者翻转动作未按预期执行。 此现象,通常会严重影响游戏的正常运行体验。

问题分析

上述代码中,核心逻辑是通过比较两张卡片上的图片src属性来判断是否匹配。 如果图片路径src相同,则卡片被判定为匹配。不匹配时,通过定时器短暂延迟后,会翻转卡片。 但是,存在潜在问题,需要深入剖析:

  1. src 属性匹配的精确性: 依靠src属性进行比较,容易受到诸如不同域名的相对/绝对路径的影响。 例如,./assets/talisman1.png/assets/talisman1.png尽管指向相同图片,字符串匹配会失败,这会被认定为两张不匹配的卡片。
  2. 异步操作带来的状态问题: setTimeout 定时器函数是异步执行的。 在定时器延迟期间,用户可能多次点击其他卡片。 这就可能造成卡片状态与toggledCardsArray数组不同步,导致难以预测的结果。
  3. 翻转状态控制: 代码中仅仅依赖于 .toggled class 来实现卡片的翻转,代码略显单薄,没有其他手段支持对翻转动作的更好控制。

解决方案

以下针对问题,提供多重方案:

解决方案 1: 基于ID匹配卡片

核心原理,在图像对象初始化时添加唯一标识符(id) , 使用id属性,替换比较图片的路径src 属性,降低了匹配的误判概率。

代码示例:

const imagesLinkArray = [
    { id: 1, image: './assets/talisman1.png', newAlt: 'talisman1' },
    { id: 2, image: './assets/talisman2.png', newAlt: 'talisman2' },
    { id: 3, image: './assets/talisman3.png', newAlt: 'talisman3' },
    { id: 4, image: './assets/talisman4.png', newAlt: 'talisman4' },
    { id: 5, image: './assets/talisman5.png', newAlt: 'talisman5' },
    { id: 6, image: './assets/talisman6.png', newAlt: 'talisman6' },
    { id: 1, image: './assets/talisman1.png', newAlt: 'talisman1' },
    { id: 2, image: './assets/talisman2.png', newAlt: 'talisman2' },
    { id: 3, image: './assets/talisman3.png', newAlt: 'talisman3' },
    { id: 4, image: './assets/talisman4.png', newAlt: 'talisman4' },
    { id: 5, image: './assets/talisman5.png', newAlt: 'talisman5' },
    { id: 6, image: './assets/talisman6.png', newAlt: 'talisman6' }
];
  const allImagesSrc = document.querySelectorAll('.card-img');
    allImagesSrc.forEach((el, index) => {
        el.src = imagesLinkArray[index].image;
        el.alt = imagesLinkArray[index].newAlt;
        el.id = imagesLinkArray[index].id;
    });


  if (toggledCardsArray.length === 2) {
            let firstCardId = toggledCardsArray[0].querySelector('.card-img')?.id;
            let secondCardId = toggledCardsArray[1].querySelector('.card-img')?.id;

            // If cards match
            if (firstCardId === secondCardId) {
              ...

}

操作步骤:

  1. 在图像数据imagesLinkArray 中,确保每对匹配卡片拥有相同的 id 属性值,修改卡片的图片元素, 确保 id 也一并更新。
  2. 在卡片比较环节,从 img 元素中取出 id 进行比较, 替换之前的 src 比较。

优势: 精确性高, 不受图像 src 路径影响。 提高了比较效率,避免不必要的问题。

安全建议: 在生产环境,确保id唯一性,防止产生安全隐患。

解决方案 2: 加入翻转状态和延迟锁

此方案核心是在翻转时添加状态变量,确保操作状态的可控性, 引入操作锁,保证异步操作时,避免多次点击导致的竞态条件问题。

代码示例:

let isFlipping = false;
for (let i = 0; i < cards.length; i++) {
    cards[i].addEventListener('click', function () {
           // Lock clicks during flip animation.
        if (this.classList.contains('toggled') || toggledCardsArray.length === 2 || isFlipping) return;


        // Add 'toggled' class and add to toggledCardsArray
        this.classList.add('toggled');
        toggledCardsArray.push(this);

         if (toggledCardsArray.length === 2) {
              isFlipping = true; // Set isFlipping state during operation

            let firstCardId = toggledCardsArray[0].querySelector('.card-img')?.id;
            let secondCardId = toggledCardsArray[1].querySelector('.card-img')?.id;

           // If cards match
            if (firstCardId === secondCardId) {
                winCount++;
                toggledCardsArray = []; // Clear the array for the next pair
                 isFlipping = false;

            } else {
                // If cards do not match, flip them back after a delay
                setTimeout(() => {
                    toggledCardsArray.forEach((card) => {
                        card.classList.remove('toggled');
                    });
                    toggledCardsArray = []; // Reset the array
                      isFlipping = false;

                }, 500);
            }

        ...

操作步骤:

  1. 声明isFlipping变量, 初始化状态false
  2. 在卡片翻转动画执行之前, 设置 isFlipping = true , 在动画执行结束后设置为false, 在事件处理中,判断该变量,禁止在动画执行期间,重复点击操作。
    优势: 可有效避免重复点击和异步问题引发的状态同步错误。 避免用户重复操作,导致游戏体验紊乱。
    安全建议: 确保代码中及时复原操作状态。 防止游戏出现状态死锁。

结论

解决卡片配对游戏中图片翻转问题, 依靠的不仅仅是对匹配的判断, 同时也依赖对于状态的精准把控和合理的代码逻辑。 从 id 匹配、添加状态和锁定机制,能够提高程序的稳定性和用户体验。 希望这些解决方案,能有效帮你处理类似问题。