跳转至

贪食蛇

原文: http://zetcode.com/gui/csharpqyoto/nibbles/

在 Qyoto C# 编程教程的这一部分中,我们将创建贪食蛇游戏克隆。

Nibbles是较旧的经典视频游戏。 它最初是在 70 年代后期创建的。 后来它被带到 PC 上。 在这个游戏中,玩家控制蛇。 目的是尽可能多地吃苹果。 蛇每次吃一个苹果,它的身体就会长大。 蛇必须避开墙壁和自己的身体。

开发

蛇的每个关节的大小为 10px。 蛇由光标键控制。 最初,蛇具有三个关节。 游戏立即开始。 游戏结束后,我们在窗口中心显示"Game Over"消息。

board.cs

using System;
using QtCore;
using QtGui;

public class Board : QFrame 
{
    const int WIDTH = 300;
    const int HEIGHT = 300;
    const int DOT_SIZE = 10;
    const int ALL_DOTS = 900;
    const int RAND_POS = 30;
    const int DELAY = 140;

    int[] x = new int[ALL_DOTS];
    int[] y = new int[ALL_DOTS];

    int dots;
    int apple_x;
    int apple_y;

    bool left = false;
    bool right = true;

    bool up = false;
    bool down = false;
    bool inGame = true;

    QBasicTimer timer;
    QImage ball;
    QImage apple;
    QImage head;

    public Board() 
    {        
        StyleSheet = "QWidget { background-color: black }";

        FocusPolicy = Qt.FocusPolicy.StrongFocus;

        ball = new QImage("dot.png");
        apple = new QImage("apple.png");
        head = new QImage("head.png");

        InitGame();
    }

    void InitGame() 
    {
        dots = 3;

        for (int z = 0; z < dots; z++) {
            x[z] = 50 - z*10;
            y[z] = 50;
        }

        LocateApple();

        timer = new QBasicTimer();
        timer.Start(DELAY, this);
    }

    protected override void OnPaintEvent(QPaintEvent e)
    {
        QPainter painter = new QPainter();
        painter.Begin(this);

        if (inGame) 
        {
            DrawObjects(painter);
        } else {
            GameOver(painter);
        }

        painter.End();
    }

    void DrawObjects(QPainter painter) 
    {
        painter.DrawImage(apple_x, apple_y, apple);

        for (int z = 0; z < dots; z++) 
        {
            if (z == 0)
                painter.DrawImage(x[z], y[z], head);
            else painter.DrawImage(x[z], y[z], ball);
        }
    }

    void GameOver(QPainter painter) 
    {
        String msg = "Game Over";
        QFont small = new QFont("Helvetica", 12,
            (int) QFont.Weight.Bold);
        QFontMetrics metr = new QFontMetrics(small);

        int textWidth = metr.Width(msg);
        int h = Height;
        int w = Width;

        painter.SetPen(GlobalColor.white);
        painter.Font = small;
        painter.Translate(new QPoint(w/2, h/2));
        painter.DrawText(-textWidth/2, 0, msg);
    }

    void CheckApple() 
    {
        if ((x[0] == apple_x) && (y[0] == apple_y)) 
        {
            dots++;
            LocateApple();
        }
    }

    void Move() 
    {
        for (int z = dots; z > 0; z--) 
        {
            x[z] = x[(z - 1)];
            y[z] = y[(z - 1)];
        }

        if (left) 
        {
            x[0] -= DOT_SIZE;
        }

        if (right) 
        {
            x[0] += DOT_SIZE;
        }

        if (up)
        {
            y[0] -= DOT_SIZE;
        }

        if (down) 
        {
            y[0] += DOT_SIZE;
        }
    }

    void CheckCollision() 
    {
        for (int z = dots; z > 0; z--) 
        {
            if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) 
            {
                inGame = false;
            }
        }

        if (y[0] > HEIGHT) 
        {
            inGame = false;
        }

        if (y[0] < 0) 
        {
            inGame = false;
        }

        if (x[0] > WIDTH) 
        {
            inGame = false;
        }

        if (x[0] < 0) 
        {
            inGame = false;
        }
    }

    void LocateApple() 
    {
        Random rand = new Random();

        int r = (int) (rand.Next(RAND_POS));
        apple_x = ((r * DOT_SIZE));
        r = (int) (rand.Next(RAND_POS));
        apple_y = ((r * DOT_SIZE));
    }

    protected override void OnTimerEvent(QTimerEvent arg1) 
    {
        if (inGame) 
        {
            CheckApple();
            CheckCollision();
            Move();
        } else 
        {
            timer.Stop();
        }

        Repaint();
    }

    protected override void OnKeyPressEvent(QKeyEvent e)
    {        
        int key = e.Key();

        if (key == (int) Qt.Key.Key_Left && !right) 
        {
            left = true;
            up = false;
            down = false;
        }

        if ((key == (int) Qt.Key.Key_Right) && !left) 
        {
            right = true;
            up = false;
            down = false;            
        }

        if ((key == (int) Qt.Key.Key_Up) && !down) 
        {
            up = true;
            right = false;
            left = false;            
        }

        if ((key == (int) Qt.Key.Key_Down) && !up) 
        {
            down = true;
            right = false;
            left = false;       
        }
    }
}

首先,我们将定义一些在游戏中使用的全局变量。

WIDTHHEIGHT常数确定电路板的大小。 DOT_SIZE是苹果的大小和蛇的点。 ALL_DOTS常数定义了板上可能的最大点数。 RAND_POS常数用于计算苹果的随机位置。 DELAY常数确定游戏的速度。

int[] x = new int[ALL_DOTS];
int[] y = new int[ALL_DOTS];

这两个数组存储蛇的所有可能关节的 x,y 坐标。

InitGame()方法初始化变量,加载图像并启动超时功能。

if (inGame) 
{
    DrawObjects(painter);
} else {
    GameOver(painter);
}

PaintEvent()方法内部,我们检查inGame变量。 如果为真,则绘制对象。 苹果和蛇的关节。 否则,我们显示"Game Over"文本。

void DrawObjects(QPainter painter) 
{
    painter.DrawImage(apple_x, apple_y, apple);

    for (int z = 0; z < dots; z++) 
    {
        if (z == 0)
            painter.DrawImage(x[z], y[z], head);
        else painter.DrawImage(x[z], y[z], ball);
    }
}

DrawObjects()方法绘制苹果和蛇的关节。 蛇的第一个关节是其头部,用红色圆圈表示。

void CheckApple() 
{
    if ((x[0] == apple_x) && (y[0] == apple_y)) 
    {
        dots++;
        LocateApple();
    }
}

CheckApple()方法检查蛇是否击中了苹果对象。 如果是这样,我们添加另一个蛇形关节并调用LocateApple()方法,该方法将随机放置一个新的Apple对象。

Move()方法中,我们有游戏的关键算法。 要了解它,请看一下蛇是如何运动的。 您控制蛇的头。 您可以使用光标键更改其方向。 其余关节在链上向上移动一个位置。 第二关节移动到第一个关节的位置,第三关节移动到第二个关节的位置,依此类推。

for (int z = dots; z > 0; z--)
{
    x[z] = x[(z - 1)];
    y[z] = y[(z - 1)];
}

该代码将关节向上移动。

if (left) 
{
    x[0] -= DOT_SIZE;
}

将头向左移动。

CheckCollision()方法中,我们确定蛇是否击中了自己或撞墙之一。

for (int z = dots; z > 0; z--) 
{
    if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) 
    {
        inGame = false;
    }
}

如果蛇用头撞到关节之一,我们就结束游戏。

if (y[0] > HEIGHT) 
{
    inGame = false;
}

如果蛇击中了棋盘的底部,我们就结束了游戏。

LocateApple()方法在板上随机放置一个苹果。

Random rand = new Random();

int r = (int) (rand.Next(RAND_POS));

我们得到一个从 0 到RAND_POS-1的随机数。

apple_x = ((r * DOT_SIZE));
...
apple_y = ((r * DOT_SIZE));

这些行设置了 apple 对象的 x,y 坐标。

if (inGame) 
{
    CheckApple();
    CheckCollision();
    Move();
} else 
{
    timer.Stop();
}

每 140 毫秒,将调用TimerEvent()方法。 如果我们参与了游戏,我们将调用三种构建游戏逻辑的方法。 否则,我们将停止计时器。

在 Board 类的KeyPressEvent()方法中,我们确定按下的键。

if (key == (int) Qt.Key.Key_Left && !right) 
{
    left = true;
    up = false;
    down = false;
}

如果单击左光标键,则将left变量设置为true。 在Move()方法中使用此变量来更改蛇对象的坐标。 还要注意,当蛇向右行驶时,我们不能立即向左转。

nibbles.cs

using System;
using QtCore;
using QtGui;

/**
 * ZetCode Qyoto C# tutorial
 *
 * In this program, we create
 * a Nibbles game clone.
 *
 * @author Jan Bodnar
 * website zetcode.com
 * last modified October 2012
 */

public class QyotoApp : QMainWindow 
{    
    public QyotoApp() 
    {
        WindowTitle = "Nibbles";

        CentralWidget= new Board();

        Resize(310, 310);
        Move(300, 300);
        Show();
    }

    [STAThread]
    public static int Main(String[] args) 
    {
        new QApplication(args);
        new QyotoApp();

        return QApplication.Exec();
    }
}

在这个类中,我们设置了贪食蛇游戏。

Nibbles

图:贪食蛇

这是用 Qyo​​to 库和 C# 编程语言编程的贪食蛇电脑游戏。



回到顶部