[Game Development] 2048 Game Realization

0. Preface

Let me sort out a 2048 game I made before, which was implemented using C#. The core code was also sorted out later, which was implemented using ts.

I originally wanted to make a small program, but the small program has too many restrictions and the front-end three tricks have not been learned, so I put the small program version on hold, and first put the core logic code of the ts version together. I won’t talk too much about the specific project implementation. I will mainly talk about the core implementation, and the example code will be displayed in ts. After all, it has also been sorted out. If you need C# code, you can look at the project, there will be slight differences, but the idea is the same.

The project link is as follows:

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

1. 2048 core logic

The logical flow of 2048 is also very clear, mainly: generate numbers, determine whether to move, and move numbers. No more movement means game over. So it is abstracted into 3 functions as GenNewNum(), GetMoveAble(dir: Dir), Move(dir: Dir)

(1)GenNewNum

It is relatively simple to generate a number. Just find an empty position and try to generate it. You can directly randomize the position and then try to generate it. There is already a number in this position and then randomize it again. Or
find out all the empty positions, and then randomize, which is also used below.

/**
* 生成新数字
*/
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

There are 4 directions to judge whether it can move, and each direction can be processed independently, but in fact the judgment of each direction is similar. We can first consider how to judge the situation in the first row, and only consider whether this row can be moved to the left. Then this judgment can be abstracted into two conditions, and the judgment can be as follows:

  • 1. Whether there are 0
  • 2. Whether there are the same numbers next to each other (it can also be separated by 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
}

After finishing this step, you can, and other lines in the left direction can be judged by this function. At this time, for the processing in other directions, we can do it for a while.

/**
* 检查能否移动
* @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

This is actually similar to the above. Let's first consider the processing of moving a line to the left. For the first line, you can follow 3 steps.

  • (*1) moveZero, we first move all the 0s to one side, and put all the ones with numbers together.
  • (*2) combine, and then combine adjacent numbers.
  • (*3) moveZero, considering that there is a vacancy after the number is merged, so finally move 0 to the side
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]
    }
}

After moving one line is done, all directions can be handled similarly, but the difference from GetMoveAble is that the number needs to be assigned back. Don't worry too much about assignment and the overhead of creating a new array. It's not a big problem to only create a little position every step, and it's much easier to maintain the code in this way.

/**
*  往这个方向移动
* @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 version

This is a small project I did when I was learning how to make games, so I won’t make any changes and narration, the specific content depends on the project.
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description

3. end

That's all for now, I hope it can provide a reference.

Guess you like

Origin blog.csdn.net/Blue_carrot_/article/details/129986017