65.9K
CodeProject 正在变化。 阅读更多。
Home

Pocket PC 版俄罗斯方块

2008年4月25日

CPOL

4分钟阅读

viewsIcon

42407

downloadIcon

658

砖块游戏的 .NET Compact Framework 版本。

Bricks

引言

我展示了两个适用于 Pocket PC 的著名砖块游戏的简单版本。在本文中,我将介绍用于开发游戏的一些技术,并提供源代码。我希望读者喜欢它,因为对我来说这是一种真正的享受。

第一个版本是经典版本,彩色方块从顶部落下。在第二个版本中,方块由气球组成,用户必须在气球上升时将它们正确地放置在屏幕顶部的行中。

我应该说,我主要是受到 Jonas Follesø 在他的文章 Pocket 1945 - C# .NET CF 射击游戏 中的出色工作的激励。该文章于 2004 年上传到 The Code Project,至今仍令人印象深刻(并且将在未来许多年内继续令人印象深刻)。因此,在阅读了“Pocket 1945”文章后,我决定自己编写一个游戏,最终借鉴了 Jonas 在他的游戏中使用的一些概念。

背景

第一个砖块游戏是由 Alexey Pajitnov 于 1985 年在莫斯科的苏联科学院 Dorodnicyn 计算中心工作时编写的(谢谢,维基百科)。该游戏几乎移植到所有已知平台,甚至用人类而不是传统的彩色方块来玩(参见 视频)。

游戏

基本上,“方块!”是一个永无止境的 4 段拼图。共有 7 种类型的块(我在游戏中称之为“I”、“J”、“L”、“O”、“S”、“T”和“Z”),并且每次只有一个块从游戏板的顶部落下。四格骨牌的类型是随机产生的。每次玩家在棋盘上填满一行时,该行将被删除,并且上方的方块会塌陷,从而为更多的块腾出空间。

每次完成一行,玩家将获得 10 分,并乘以游戏等级数。每当玩家完成 10 行时,就会达到一个新的等级。

应用程序设计

该解决方案包含两个项目:Bricks4Pocket 和 Bricks4Pocket.Core。第一个项目主要包含 UI 组件,而第二个项目包含繁重的功能。我认为该项目中的一些类值得在这里提及。

我决定在游戏中使用简化的 MVP(模型-视图-演示器)模式。通过这样做,我可以确保 UI 组件仅被动地响应演示器组件引发的事件,从而加强关注点分离。

代码

该游戏有两个主要实体:形状(继承自 BaseShape 的类)和棋盘(BricksBoard 类)。两者都继承自 BaseBricksArray 抽象类,该类实现砖块(Brick 类)的二维数组。

protected IBrick[,] shapeArray = null;

游戏的核心是 BricksBoard 类。它包含一个 10 x 16 的砖块数组,以及游戏逻辑功能,例如根据箭头按钮移动和旋转一块,测试一块是否仍然可以移动,确定是否有必须删除的完整行,以及确定游戏何时结束。

游戏开始后,立即调用 GetRandomShape,并创建一个随机类型和随机颜色的形状。

private IShape GetRandomShape()
{
    IShape newShape = null;
    Random randomClass = new Random();
    int randomCode = randomClass.Next((int)ShapeCodes.Stick, 
                                      (int)ShapeCodes.Z + 1);
    .
    .
    .

在每次移动尝试之前,BricksBoard 类将调用 TestPieceOnPosition 方法,以确保形状不会离开棋盘并且不会与棋盘中的其他砖块发生碰撞。

public bool TestPieceOnPosition(IShape shape, int x, int y)
{
    for (int row = 0; row < shape.Height; row++)
    {
        for (int column = 0; column < shape.Width; column++)
        {
            //is the position out of range?
            if (column + x < 0)
                return false;

            if (row + y < 0)
                return false;

            if (column + x >= width)
                return false;

            if (row + y >= height)
                return false;

            //will the shape collide in the board?
            if (
                shapeArray[column + x, row + y] != null &&
                shape.ShapeArray[column, row] != null)
            {
                return false;
            }
        }
    }
    return true;
}

如果可以根据移动尝试放置该块,则调用方法 PutPieceOnPosition 以便将砖块从形状转移到棋盘。

如果可能,BaseShape 类的 MoveLeftMoveRight 方法只是将形状的砖块向左或向右移动。

public bool MoveLeft()
{
    bool test = false;
    if (!anchored)
    {
        if (containerBoard == null)
            throw new NullContainerBoardException();

        containerBoard.RemovePieceFromCurrentPosition(this);

        test = containerBoard.TestPieceOnPosition(this, this.X - 1, this.Y);
        if (test)
        {
            containerBoard.RemovePieceFromCurrentPosition(this);
            containerBoard.PutPieceOnPosition(this, this.X - 1, this.Y);
        }
    }
    return test;
}

Rotate90Rotate270 方法分别实现形状的顺时针和逆时针旋转。这是通过在尝试将形状放在棋盘上之前,将砖块从形状的列转置到行来实现的。

public bool Rotate270()
{
    bool test = false;
    if (!anchored)
    {
        if (containerBoard == null)
            throw new NullContainerBoardException();
        
        containerBoard.RemovePieceFromCurrentPosition(this);
        
        IBrick[,] newShapeArray = new IBrick[height, width];
        IBrick[,] oldShapeArray = new IBrick[width, height];
        for (int row = 0; row < height; row++)
        {
            for (int column = 0; column < width; column++)
            {
                newShapeArray[row, width - column - 1] = shapeArray[column, row];
                oldShapeArray[column, row] = shapeArray[column, row];
            }
        }
        .
        .
        .
    }

对于密钥处理功能,我只是从 Jonas Follesø 的文章中复制了 Input 类。此外,我创建了一个封装 Input 类的 InputBuffer 类。 InputBuffer 类能够处理缓冲的击键,并且还允许视图订阅该实用程序类。我通过在 InputBuffer 类和订阅者视图中实现观察者模式来实现此目的

public void AddObserver(IInputObserver observer)
{
    observers.Add(observer);
}

历史

  • 2008-04-24:首次发布。
  • 2008-05-01:代码和文章重命名;输入问题已解决;添加了新游戏“气球!”。
© . All rights reserved.