練手WPF(四)——貪吃蛇小遊戲的簡易實現(下)

8、生成新的單節蛇身
咱們這裏先說說遊戲小原理好了,遊戲運行後,經過計時器事件不斷生成新的單節蛇身類SnakeNode,添加到List中的0位置,原來的蛇頭變成了第二節。該節新蛇頭的座標經過蛇頭前進方向Direct進行判斷,即若是原蛇頭往左運行,則新蛇頭在原蛇頭位置的左一格生成,其餘方向以此類推。最後將該節添加到SnakeNodes列表中,並將相應圖形添加到遊戲區。node

private void GenNewSnakeNode()
{
    SnakeNode snakeNode = null;
    switch (Direct)
    {
        case Direction.UP:
            snakeNode = new SnakeNode(new Point(SnakeNodes[SnakeHead]._pos.X,
                SnakeNodes[SnakeHead]._pos.Y - 1));
            break;

        case Direction.DOWN:
            snakeNode = new SnakeNode(new Point(SnakeNodes[SnakeHead]._pos.X,
                SnakeNodes[SnakeHead]._pos.Y + 1));
            break;

        case Direction.LEFT:
            snakeNode = new SnakeNode(new Point(SnakeNodes[SnakeHead]._pos.X - 1,
                SnakeNodes[SnakeHead]._pos.Y));
            break;

        case Direction.RIGHT:
            snakeNode = new SnakeNode(new Point(SnakeNodes[SnakeHead]._pos.X + 1,
                SnakeNodes[SnakeHead]._pos.Y));
            break;
    }

    if (snakeNode != null)
    {
        SnakeNodes.Insert(0, snakeNode);
        myCanvas.Children.Add(SnakeNodes[0]._rect);
    }
}

 

9、隨機水果位置
注意該新位置不得與蛇身任何一節重疊,若是重疊則另外生成新位置,直到成功爲止。dom

private Point SetFruitToRandomPos()
{
    bool flag = true;
    Point pos = new Point();
    while (flag)
    {
        flag = false;
        pos = new Point(rnd.Next(0, CellWidth - 1), rnd.Next(0, CellHeight - 1));
        foreach (var node in SnakeNodes)
        {
            if (pos.X == node._pos.X && pos.Y == node._pos.Y)
            {
                flag = true;
                break;
            }
        }
    }

    return pos;
}

 

10、碰撞檢測(蛇頭與水果)
由於只有蛇頭會最早觸碰到水果,因此只需判斷蛇頭座標與水果座標是否相同便可。若是撞到水果,則將水果隨機生成新位置;若是沒有碰到,則刪除蛇尾一節(由於以後會經過計時器會生成新一節蛇頭,這樣就會保存蛇身長度,感受位置前進了一格)。ui

private void CheckCollision()
{
    if (SnakeNodes[SnakeHead]._pos.X == fruit._pos.X && SnakeNodes[SnakeHead]._pos.Y == fruit._pos.Y)
    {
        fruit.SetPostion(SetFruitToRandomPos());
    }
    else
    {
        if (myCanvas.Children.Contains(SnakeNodes[SnakeNodes.Count - 1]._rect))
            myCanvas.Children.Remove(SnakeNodes[SnakeNodes.Count - 1]._rect);

        SnakeNodes.RemoveAt(SnakeNodes.Count - 1);
    }
}

 

11、判斷是否遊戲結束
很簡單,看看蛇頭是否觸碰遊戲區四邊或蛇身的任何一節,若是是則返回true。
注意蛇身循環是從1開始的,若是從0開始你知道會出現什麼狀況。spa

        private bool IsGameOver()
        {
            if (SnakeNodes[SnakeHead]._pos.X == -1 || SnakeNodes[SnakeHead]._pos.X == CellWidth
                || SnakeNodes[SnakeHead]._pos.Y == -1 || SnakeNodes[SnakeHead]._pos.Y == CellHeight)
            {
                return true;
            }

            foreach (var node in SnakeNodes)
            {
                if (node == SnakeNodes[SnakeHead])
                    continue;

                if (node._pos.X == SnakeNodes[SnakeHead]._pos.X && node._pos.Y == SnakeNodes[SnakeHead]._pos.Y)
                {
                    return true;
                }
            }

            return false;
        }

 

十3、刪除遊戲區的全部蛇身節
這個會在從新開始遊戲時使用到:code

private void RemoveSnakeNodeAll()
{
    for (int i = 0; i < SnakeNodes.Count; i++)
    {
        if (myCanvas.Children.Contains(SnakeNodes[i]._rect))
        {
            myCanvas.Children.Remove(SnakeNodes[i]._rect);
        }
    }
}

 

順便再來一發刪除遊戲區的水果:blog

private void RemoveFruit()
{
    if (fruit == null) 
    {
        return;
    }

    if (myCanvas.Children.Contains(fruit._ellipse))
    {
        myCanvas.Children.Remove(fruit._ellipse);
    }
}

 

十4、按鍵操做
經過方向鍵,設置Direct值便可。遊戲

private void MyCanvas_PreviewKeyDown(object sender, KeyEventArgs e)
{
    switch (e.Key)
    {
        case Key.Left:
            if (Direct != Direction.RIGHT)
            {
                Direct = Direction.LEFT;
            }
            break;

        case Key.Right:
            if (Direct != Direction.LEFT)
            {
                Direct = Direction.RIGHT;
            }
            break;

        case Key.Up:
            if (Direct != Direction.DOWN)
            {
                Direct = Direction.UP;
            }
            break;

        case Key.Down:
            if (Direct != Direction.UP)
            {
                Direct = Direction.DOWN;
            }
            break;

        case Key.Escape:
            Application.Current.Shutdown();
            break;

        case Key.Space:
            if (CurrGameState == GameState.NONE)
                return;

            if (CurrGameState == GameState.PAUSE)
            {
                CurrGameState = GameState.GAMEING;
                timer.Start();
                MenuControl_Pause.Header = "暫停";
            }
            else if (CurrGameState == GameState.GAMEING)
            {
                CurrGameState = GameState.PAUSE;
                timer.Stop();
                MenuControl_Pause.Header = "繼續";
            }
            break;
    }
}

 

十4、定義開始遊戲方法
先刪除界面中的蛇身和水果(保留底部暗格),隨機位置生成水果和三節蛇身。事件

private void StartGame()
{
    RemoveSnakeNodeAll();
    RemoveFruit();

    int startX = rnd.Next(5, CellWidth - 6);
    int startY = rnd.Next(5, CellHeight - 6);
    Direct = Direction.RIGHT;

    fruit = new Fruit(SetFruitToRandomPos(), myCanvas);

    SnakeNodes = new List<SnakeNode>();
    SnakeNodes.Add(new SnakeNode(new Point(startX, startY)));
    GenNewSnakeNode();
    GenNewSnakeNode();
}

 

十5、開始遊戲菜單ip

調用StartGame()方法,並設定計時器開始。it

private void MenuFile_NewGame_Click(object sender, RoutedEventArgs e)
{
    StartGame();
    timer.Start();
    CurrGameState = GameState.GAMEING;
    MenuControl_Pause.Header = "暫停";
}

 

十6、暫停與繼續
直接上代碼,沒有要說的了。

private void MenuControl_Pause_Click(object sender, RoutedEventArgs e)
{
    if (CurrGameState == GameState.GAMEING)
    {
        CurrGameState = GameState.PAUSE;
        timer.Stop();
        MenuControl_Pause.Header = "繼續";
    }
    else if (CurrGameState == GameState.PAUSE)
    {
        CurrGameState = GameState.GAMEING;
        timer.Start();
        MenuControl_Pause.Header = "暫停";
    }
}

 

十7、退出遊戲

private void MenuFile_Exit_Click(object sender, RoutedEventArgs e)
{
    Application.Current.Shutdown();
}

 

十8、其餘菜單功能
本身實現好了

private void MenuHelp_About_Click(object sender, RoutedEventArgs e)
{
}

相關文章
相關標籤/搜索