欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

练手WPF(四)——贪吃蛇小游戏的简易实现(下)

程序员文章站 2022-10-08 18:29:58
八、生成新的单节蛇身我们这里先说说游戏小原理好了,游戏运行后,通过计时器事件不断生成新的单节蛇身类SnakeNode,添加到List中的0位置,原来的蛇头变成了第二节。该节新蛇头的坐标通过蛇头前进方向Direct进行判断,即如果原蛇头往左运行,则新蛇头在原蛇头位置的左一格生成,其他方向以此类推。最后 ......

八、生成新的单节蛇身
我们这里先说说游戏小原理好了,游戏运行后,通过计时器事件不断生成新的单节蛇身类snakenode,添加到list中的0位置,原来的蛇头变成了第二节。该节新蛇头的坐标通过蛇头前进方向direct进行判断,即如果原蛇头往左运行,则新蛇头在原蛇头位置的左一格生成,其他方向以此类推。最后将该节添加到snakenodes列表中,并将相应图形添加到游戏区。

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);
    }
}

 

九、随机水果位置
注意该新位置不得与蛇身任何一节重叠,如果重叠则另外生成新位置,直到成功为止。

private point setfruittorandompos()
{
    bool flag = true;
    point pos = new point();
    while (flag)
    {
        flag = false;
        pos = new point(rnd.next(0, cellwidth), rnd.next(0, cellheight));
        foreach (var node in snakenodes)
        {
            if (pos.x == node._pos.x && pos.y == node._pos.y)
            {
                flag = true;
                break;
            }
        }
    }

    return pos;
}

 

十、碰撞检测(蛇头与水果)
因为只有蛇头会最先触碰到水果,所以只需判断蛇头坐标与水果坐标是否相同即可。如果撞到水果,则将水果随机生成新位置;如果没有碰到,则删除蛇尾一节(因为之后会通过计时器会生成新一节蛇头,这样就会保存蛇身长度,感觉位置前进了一格)。

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);
    }
}

 

十一、判断是否游戏结束
很简单,看看蛇头是否触碰游戏区四边或蛇身的任何一节,如果是则返回true。
注意蛇身循环是从1开始的,如果从0开始你知道会出现什么情况。

        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;
        }

 

十三、删除游戏区的所有蛇身节
这个会在重新开始游戏时使用到:

private void removesnakenodeall()
{
    for (int i = 0; i < snakenodes.count; i++)
    {
        if (mycanvas.children.contains(snakenodes[i]._rect))
        {
            mycanvas.children.remove(snakenodes[i]._rect);
        }
    }
}

 

顺便再来一发删除游戏区的水果:

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

    if (mycanvas.children.contains(fruit._ellipse))
    {
        mycanvas.children.remove(fruit._ellipse);
    }
}

 

十四、按键操作
通过方向键,设置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;
    }
}

 

十四、定义开始游戏方法
先删除界面中的蛇身和水果(保留底部暗格),随机位置生成水果和三节蛇身。

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();
}

 

十五、开始游戏菜单

调用startgame()方法,并设定计时器开始。

private void menufile_newgame_click(object sender, routedeventargs e)
{
    startgame();
    timer.start();
    currgamestate = gamestate.gameing;
    menucontrol_pause.header = "暂停";
}

 

十六、暂停与继续
直接上代码,没有要说的了。

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 = "暂停";
    }
}

 

十七、退出游戏

private void menufile_exit_click(object sender, routedeventargs e)
{
    application.current.shutdown();
}

 

十八、其他菜单功能
自己实现好了

private void menuhelp_about_click(object sender, routedeventargs e)
{
}

练手WPF(四)——贪吃蛇小游戏的简易实现(下)

练手WPF(四)——贪吃蛇小游戏的简易实现(下)