[游戏开发]2048 游戏实现

0. 前言

整理一下之前做的一个 2048 游戏,使用 C#实现的。后续也整理出了核心代码,是使用 ts 实现的。

原本想做小程序的,但小程序限制太多而且前端三板斧也没学到位,就搁置了小程序版,就也先把 ts 版的核心逻辑代码都一起放上来了。具体项目实现就不讲太多了,主要讲一下核心的实现,实例代码也就用 ts 展示了,毕竟也整理出来了。如果需要 C#的代码可以看项目,会略有差异,但想法都是一样的。

项目链接如下:

链接:https://pan.baidu.com/s/1VfH4CEXEE9C6zM9tV2N0Pg 
提取码:wsad

1. 2048 核心逻辑

2048 的逻辑流程也是很清晰的,主要就是:生成数字,判断能否移动,移动数字。不能再移动也就意味着游戏结束。所以抽像成 3 个函数为 GenNewNum(),GetMoveAble(dir: Dir),Move(dir: Dir)

(1)GenNewNum

生成数字比较简单,找到空位置并尝试生成就可以了,可以直接随机位置,然后尝试生成,这个位置已经有数字了再随机一次。或者把所
有的空位置都找出来,然后再随机,这也是下面使用的。

/**
* 生成新数字
*/
public GenNewNum() {
    
    
    let newNumPlace: Array<number> = new Array<number>()
    for (let i = 0; i < this.sizeI; i++) {
    
    
        for (let j = 0; j < this.sizeJ; j++) {
    
    
            if (this.num[i][j] == 0) {
    
    
                newNumPlace.push(i + j * this.sizeI)
            }
        }
    }
    if (newNumPlace.length > 0) {
    
    
        let index: number = Random.GetRandomIn(newNumPlace)
        let i = index % this.sizeI
        let j = (index - i) / this.sizeI
        this.num[i][j] = Random.GetRandomIn(this.newNumRange)
    }
}

(2) GetMoveAble

判断是否可以移动,有 4 个方向,每个方向都可以独立做处理,但其实每个方向的判断都是类似的。我们可以先考虑第一行情况怎么判断的,只考虑这一行是否可以向左移。那这个判断就可以抽象为 2 个条件,判断及可以如下:

  • 1.是否有 0
  • 2.是否有相同的数字相邻(隔着 0 也可以)
private canMoveOneLine(line: Array<number>): boolean {
    
    
    let last: number = -1
    let can: boolean = false
    for (let i = line.length - 1; i >= 0; i--) {
    
    
        if (last == 0) {
    
    
            can = true //is zero
            break
        } else if (line[i] == last) {
    
    
            can = true //is same
            break
        } else {
    
    
            last = line[i]
        }
    }
    return can
}

那做完这一步的之后,就可以,向左方向的其他的行都可以,通过这个函数来判断。那这个时候其他方向的处理,我们做一下处理就好了。

/**
* 检查能否移动
* @param dir
* @returns moveAble
*/
public GetMoveAble(dir: Dir): boolean {
    
    
    let lineCount = this.getLineCount(dir)
    let canMove: boolean = false
    for (let i = 0; i < lineCount; i++) {
    
    
        let tempLine = this.getNumOneLine(dir, i)
        canMove = this.canMoveOneLine(tempLine)
        if (canMove) {
    
    
            break
        }
    }
    return canMove
}

private getNumOneLine(dir: Dir, lineIndex: number): Array<number> {
    
    
    let i = lineIndex
    let j = lineIndex
    let arr_index: number
    let arr_length: number = this.getOneLineLength(dir)
    let arr: Array<number> = new Array<number>()
    for (let k = 0; k < arr_length; k++) {
    
    
        if (dir == Dir.left || dir == Dir.right) {
    
    
            i = k
        } else {
    
    
            j = k
        }
        if (dir == Dir.down || dir == Dir.right) {
    
    
            arr_index = k
        } else {
    
    
            arr_index = arr_length - 1 - k
        }
        arr[arr_index] = this.num[i][j]
    }
    return arr
}

private getLineCount(dir: Dir) {
    
    
    switch (dir) {
    
    
        case Dir.up:
        case Dir.down:
            return this.sizeJ
        case Dir.left:
        case Dir.right:
            return this.sizeI
    }
    return this.sizeI
}

(3)Move

这里其实也是和上面类似,我们先考虑一行向左移动的处理。首先一行的话,可以按 3 个步骤。

  • (*1) moveZero, 我们先把 0 都移动到一边,把有数字的都凑到一块。
  • (*2) combine, 然后再把相邻的数合并起来。
  • (*3) moveZero, 考虑到数合并之后有空缺,所以最后要再把 0 移动到一边
private MoveOneLine(numOne: Array<number>) {
    
    
    //console.log("MoveOneLine:" + numOne[0], numOne[1], numOne[2], numOne[3])
    this.moveZero(numOne)
    this.combine(numOne)
    this.moveZero(numOne)
    //console.log("MoveOneLine2:" + numOne[0], numOne[1], numOne[2], numOne[3])
}

private moveZero(line: Array<number>) {
    
    
    let count = 0
    for (let i = line.length - 1; i >= 0; i--) {
    
    
        if (line[i] == 0) {
    
    
            count++ //zero count
        } else if (count > 0) {
    
    
            //move to zero
            line[i + count] = line[i]
            line[i] = 0
        }
    }
}

private combine(line: Array<number>) {
    
    
    let last: number = -1
    for (let i = line.length - 1; i >= 0; i--) {
    
    
        if (line[i] != 0 && line[i] == last) {
    
    
            line[i + 1] = last + line[i]
            line[i] = 0
        }
        last = line[i]
    }
}

移动一行做好了之后那么所有方向的也都可以类似处理了,不过和 GetMoveAble 不同的是,还需要把数再赋值回去。不用太担心赋值还有新建数组的开销,每一步才新建一点位置,问题不大,而这样处理,代码维修起来要容易得多。

/**
*  往这个方向移动
* @param dir
*/
public Move(dir: Dir) {
    
    
    let lineCount = this.getLineCount(dir)
    for (let i = 0; i < lineCount; i++) {
    
    
        let tempLine = this.getNumOneLine(dir, i)
        this.MoveOneLine(tempLine)
        this.setNumOneLine(tempLine, dir, i)
    }
    this.GenNewNum()
}

private setNumOneLine(arr: Array<number>, dir: Dir, lineIndex: number) {
    
    
    let i = lineIndex
    let j = lineIndex
    let arr_index: number
    let arr_length: number = arr.length
    for (let k = 0; k < arr_length; k++) {
    
    
        if (dir == Dir.left || dir == Dir.right) {
    
    
            i = k
        } else {
    
    
            j = k
        }
        if (dir == Dir.down || dir == Dir.right) {
    
    
            arr_index = k
        } else {
    
    
            arr_index = arr_length - 1 - k
        }
        this.num[i][j] = arr[arr_index]
    }
}

2. C# form 版

这个是之前在学做游戏的时候做的一个小项目,就不再做更改和讲述了,具体内容就看项目里面了。
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

3. 结束

到这里也就讲完咯,希望能够提供一个参考。

扫描二维码关注公众号,回复: 15719586 查看本文章

猜你喜欢

转载自blog.csdn.net/Blue_carrot_/article/details/129986017