广告位联系
返回顶部
分享到

基于JavaScript编写一个翻卡游戏

JavaScript 来源:互联网 作者:佚名 发布时间:2023-02-16 22:23:07 人浏览
摘要

前言 首先将这个游戏需求拆分成三个部分: 翻卡动画 生成随机分布数组 点击事件 翻卡动画 假如我们的盒子模型不是个二维的平面,而是有个三维的体积,让它可以有正反两面,那我

前言

首先将这个游戏需求拆分成三个部分:

  • 翻卡动画
  • 生成随机分布数组
  • 点击事件

翻卡动画

假如我们的盒子模型不是个二维的平面,而是有个三维的体积,让它可以有正反两面,那我们在做的时候是不是只要将它真实的翻个面就可以了。让我们来想想将它变成三维的方法。 之后发现了这个属性:

1

transform: translateZ(1px);

使用了它,就可以把盒子内部的元素与盒子的底部撑出个高度。

1

2

3

4

<!-- html -->

<div class="card">

    <div class="top">我是正面哦~</div>

</div>

只用给叫做“top”的子盒子一个“距离父亲的距离”,再将叫做“card”的父盒子预先翻转180度rotateY(180deg),等到点击的时候给它翻回来transform: rotateY(0)就可以了。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

.card{

  ...

  height: 100%;

  width: 100%;

  position: relative;

  transform-style: preserve-3d;

  transform: rotateY(180deg);

  transition: all 600ms;

  background: pink;

  &.select {

    transform: rotateY(0);

  }

  .top{

    ...

    height: 100%;

    width: 100%;

    position: absolute;

    top: 0;

    left: 0;

    box-sizing: border-box;

    background: white;

    border: 2px solid #b6a6dc;

    transform: translateZ(1px);

  }

}

生成随机分布数组

我们先来说下在理想环境中,每个元素都能匀均出现(次数相等)的情况。再来说下不能均匀出现的情况下,怎样最大限度的均匀。

均匀元素下的随机算法

此算法脑内模型由西塔(θ)先生友情提供

假设我们一共需要20个元素,有5个不同类型的格子,正好每个格子出现4次。我们就有了一个待分配元素的集合W:

1

2

3

4

5

6

7

8

const total = 20

const icons = ['a', 'b', 'c', 'd', 'e']

// => 得到集合W

const W = ['a', 'a', 'a', 'a',

           'b', 'b', 'b', 'b',

           'c', 'c', 'c', 'c',

           'd', 'd', 'd', 'd',

           'e', 'e', 'e', 'e']

混淆集合

有个指针p从下标0开始,在长度为20的数组格子里面负责填图案,填图案的规律是从集合w中随机取一个元素,取完后删除该元素,p移动到下一个格子里,迭代至完成。

1

2

3

4

5

6

7

8

9

10

11

12

function createRandomList(W: string[], total: number) {

    const list: any[] = []

    function it(time: number): any {

        if (time === 0) return list

        // 随机每次集合元素下标

        const randomNum = Math.floor(Math.random() * (W.length))

        list.push(W[randomNum]) // 新数组中加入随机到的元素

        W.splice(randomNum, 1) // 删除集合中的元素

        return it(--time)

    }

    return it(total)

}

我们再让这个方法灵活一点,使它的返回结果能够随便指定格式:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

// fn非必传项,默认返回原数据

function createRandomList(W: string[], total: number, fn: (<T>(icon: string, index?: number) => T) = icon => icon) {

    const list: any[] = []

    // 迭代器

    function it(time: number): any {

        if (time === 0) return list

        // 随机每次集合元素下标

        const randomNum = Math.floor(Math.random() * (W.length))

        list.push(fn(W[randomNum], total-time)) // 将元素和下标传入fn中,将fn的计算结果加入到新数组中

        W.splice(randomNum, 1) // 删除集合中的元素

        return it(--time)

    }

    return it(total)

}

不均匀元素下的随机算法

1

const W = []

不均匀元素,其实就是集合W里的元素分布规则改变了,混淆算法仍然不受影响。之后,让我们来思考下怎么定义一个“不均匀中的最大程度均匀”的集合。 将集合W分为两个部分: 最大可均匀分布部分 + 随机部分

最大可均匀分布的部分,它代表着icons中的每个元素都能出现相同的最多偶数次。可以这样得到它:

  • icons个数x2,得到完整一次配对需要多少格子
  • 总格子数 / 一次完整格子数,得到可以完整配对的最大次数n
  • 循环n次,每次循环往W里添加icons x 2

1

2

3

4

// 得到最大重复次数

const times = Math.floor(total / (icons.length * 2))

for (let index = 0; index < times; index++)

    W.push(...icons, ...icons)

剩下的是需要随机分布的部分,它代表着,某几个元素可以在这里出现2次,剩下的则不会出现。

  • 总格子数 % icons个数x2, 得到剩下未分配的格子
  • 未分配格子 / 2, 就是需要随机从icons中取出的元素个数n,这个n一定小于icons的个数
  • 从icons中随机取n个数,可以采用每取一个数,将该数从原集合删除,重复n次的方法
  • 将得到的n个数x2,往W里添加

第(3)条是不是听起来很耳熟,好像前面做过,没错就是前面写的createRandomList函数,W集合变成了icons,total变成了需要的个数n。

1

2

3

4

5

// 剩下未分配的格子个数

const lastCount = total % (icons.length * 2)

// 从icons中随机获取n个数

const lastList = createRandomList(icons, lastCount / 2)

W.push(...lastList, ...lastList)

合在一起就是就是创建W的方法:

1

2

3

4

5

6

7

8

9

10

function createW(icons: string[], total: number) {

    const times = Math.floor(total / (icons.length * 2))

    const lastCount = total % (icons.length * 2)

    const W = []

    for (let index = 0; index < times; index++)

        W.push(...icons, ...icons)

    const lastList = createRandomList(icons, lastCount / 2)

    W.push(...lastList, ...lastList)

    return W

}

生成最终数组

完整的生成随机数组代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

function createW(icons: string[], total: number) {

    const times = Math.floor(total / (icons.length * 2))

    const lastCount = total % (icons.length * 2)

    const W = []

    for (let index = 0; index < times; index++)

        W.push(...icons, ...icons)

    const lastList = createRandomList(icons, lastCount / 2)

    W.push(...lastList, ...lastList)

    return W

}

function createRandomList(W: string[], total: number, fn: (<T>(icon: string, index?: number) => T) = icon => icon) {

    const list: any[] = []

    function it(time: number): any {

        if (time === 0) return list

        const randomNum = Math.floor(Math.random() * (W.length))

        list.push(fn(W[randomNum], total-time))

        W.splice(randomNum, 1)

        return it(--time)

    }

    return it(total)

}

 

// ['a', 'b', 'c', "d"] => ['c', 'd'...x15...'b', 'c', 'a']

createRandomList(createW(icons, total), total)

点击事件

乱序的随机数组有了,点一点还不简单吗! 先让生成的数组属性更丰富一些,来帮助我们展示内容。

1

2

3

4

5

6

type CardItem = { icon: string; isDel: boolean; isSelect: boolean, index: number }

 

let list: CardItem[] = []

 

// isSelect属性判断是否翻转,isDel属性判断是否已经消除,icon属性标注元素属性,index用来快速找到点击元素位于数组中的位置

list = createRandomList(createW(icons, total), total, (icon: string, index) => ({ icon, isDel: false, isSelect: false, index }))

这下可以用生成的数组去展示了。接下来我们写个点击事件,接收参数是点击的数组元素:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

// isLock用来锁定动画完成前不能进行别的操作

function handlerTap(card: CardItem) {

    if (isLock) return

    list[card.index].isSelect = true

    const selectors = list.filter(item => item.isSelect && !item.isDel)

    // 假如选择元素<2,直接返回,不走之后流程

    if (selectors.length <= 1) return

    isLock = true

    const [item1, item2] = selectors

    // 翻转动画完成后进行操作

    setTimeout(() => {

        // 如果选择的元素相同,则消除属性等于true

        if (item1.icon === item2.icon) {

            list[item1.index].isDel = true

            list[item2.index].isDel = true

        }

        //将所有卡牌翻转过背面

        list = list.map(item => ({...item, isSelect: false}))

        isLock = false

        // 判断是否所有卡牌都已经翻转完成

        if (list.every(item => item.isDel)) console.log( "your win!")

    }, 800)

}

完整代码

100行整)。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

<script lang="ts">

    type CardItem = { icon: string; isDel: boolean; isSelect: boolean, index: number }

    const icons = ['a', 'b', 'c', "d"]

    const total = 20

    let list: CardItem[] = []

    let isLock = false

 

    function handlerTap(card: CardItem) {

        if (isLock) return

        list[card.index].isSelect = true

        const selectors = list.filter(item => item.isSelect && !item.isDel)

        if (selectors.length <= 1) return

        isLock = true

        const [item1, item2] = selectors

        setTimeout(() => {

            if (item1.icon === item2.icon) {

                list[item1.index].isDel = true

                list[item2.index].isDel = true

            }

            list = list.map(item => ({...item, isSelect: false}))

            isLock = false

            if (list.every(item => item.isDel)) console.log( "your win!")

        }, 800)

    }

    function createW(icons: string[], total: number) {

        const times = Math.floor(total / (icons.length * 2))

        const lastCount = total % (icons.length * 2)

        const W = []

        for (let index = 0; index < times; index++)

            W.push(...icons, ...icons)

        const lastList = createRandomList(icons, lastCount / 2)

        W.push(...lastList, ...lastList)

        return W

    }

    function createRandomList(W: string[], total: number, fn: (<T>(icon: string, index?: number) => T) = icon => icon) {

        const list: any[] = []

        function it(time: number): any {

            if (time === 0) return list

            const randomNum = Math.floor(Math.random() * (W.length))

            list.push(fn(W[randomNum], total-time))

            W.splice(randomNum, 1)

            return it(--time)

        }

        return it(total)

    }

 

    list = createRandomList(createW(icons, total),

            total,

            (icon: string, index) => ({ icon, isDel: false, isSelect: false, index }))

</script>

<div class="game-box">

    {#each list as item}

        <div class="grid">

            {#if !item.isDel}

                <div class="card {item.isSelect && 'select'}" on:click="{() => handlerTap(item)}">

                    <div class="top">{item.icon}</div>

                </div>

            {/if}

        </div>

    {/each}

</div>

<style lang="less">

    .game-box{

      margin: 10px auto 0;

      width: 90vw;

      height: 80vh;

      display: grid;

      grid-template-columns: repeat(4, calc(100% / 4 - 3px));

      grid-template-rows: repeat(5, calc(100% / 5 - 3px));

      grid-row-gap:3px;

      grid-column-gap: 3px;

      .card{

        height: 100%;

        width: 100%;

        box-sizing: border-box;

        position: relative;

        transform-style: preserve-3d;

        transform: rotateY(180deg);

        transition: all 600ms;

        background: pink;

        &.select {

          transform: rotateY(0);

        }

        .top{

          height: 100%;

          width: 100%;

          position: absolute;

          top: 0;

          left: 0;

          box-sizing: border-box;

          display: flex;

          justify-content: center;

          align-items: center;

          background: white;

          border: 2px solid #b6a6dc;

          transform: translateZ(1px);

        }

      }

    }

</style>


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://juejin.cn/post/7200184156176580667
相关文章
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计