用C#实现贪吃蛇游戏

先说了,如果不会GDI+,类的定义这些基础东西的,请先搜索相关贴子学习。

 

这次教大家用C#做一个简单的贪吃蛇游戏。

先介绍用到的技术:

GDI+

定义类

枚举

 

因为C#是一门面向对象的语言,我们不用把所有代码都写在一个窗体上,应该有一个分层思想(界面还界面,数据处理还数据处理),如果还要网上对战的话,搞个三层吧(界面层,数据处理或逻辑层,Socket层),这样不仅方便维护,还为后面添加新Idea会容易一点。

 

废话少讲,现在开始

 

在编写游戏代码之前,先想一想,贪食蛇里有什么呢?

蛇,这是必要的。

食物,这也是必要的。

 

蛇应该是怎样的一条蛇?

不断向一个方向移动,那就是有方向了。方向可以用整数代表,不过为了方便以后编写代码,

还是定义一个枚举好一点。还要会移动。还要有一个位置。还要有一个速度

蛇有一个长度,也要记录下它各个身体的位置,因为蛇吃食物会不断加长,所以他的身体数量(即长度)是会变化的,所以最好是定义一个泛型List<Point>来记录身体上的所有位置。

蛇怎样才能移动,原理是这样,后面一个蛇身替代头部的位置,然后头部向前走一步。

 

食物呢?我们不搞传统的贪吃蛇,食物可以是多个的,也应该有不同类型,吃到不同类型的食物,蛇会作出相应的变化。也就是说,食物也有类型,所以定义一个enum为食物类型

 

为了搞搞新意思,加上一个蛇的速度,蛇吃到快的食物,会加快速度,吃到慢的食物,会减慢速度。所以定义了速度

枚举一般写在一个Common.cs

 

总的来说,定义类应该是这样:

蛇:方向(枚举),状态(枚举),移动(方法),位置(头部位置),长度,各身体的位置,速度

食物:位置,类型

 

不说太多,我将会在代码里写详尽的注释。大家应该看得明白。

代码如下:

 

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows.Forms;

using System.Drawing;

 

namespace snake

{

 

    public enum SnakeDirection

    {

        Up, Down, Left, Right

    }

 

    public enum SnakeState

    {

        Normal = 50, Fast = 20, Low = 100

    }

 

    public enum FoodType

    {

        Normal, Fast, Slow, Long, Short, LENGTH

    }

}

 

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Drawing;

 

namespace snake

{

    public class Snake

    {

        public Snake(Point headLocation, int snakeLength, SnakeDirection direction)

        {

            bodiesLocation = new List<Point>();

            bodiesLocation.Add(headLocation);

            for (int i = 0; i < snakeLength; i++)

            {

                bodiesLocation.Add(new Point(-1, -1));

            }

            this.direction = direction;

            this.speed = (int)SnakeState.Normal;

        }

 

        /// <summary>向前移动一格</summary>

        public void Run()

        {

            //从蛇尾开始,前面身体的位置替代后面的身体位置

            for (int i = bodiesLocation.Count - 1; i >= 1; i--)

            {

                bodiesLocation[i] = bodiesLocation[i - 1];

            }

 

            //头部向前走一步

            Point head = bodiesLocation[0];

            switch (direction)

            {

                case SnakeDirection.Up:

                    head = new Point(head.X, head.Y - 1);

                    break;

                case SnakeDirection.Down:

                    head = new Point(head.X, head.Y + 1);

                    break;

                case SnakeDirection.Left:

                    head = new Point(head.X - 1, head.Y);

                    break;

                case SnakeDirection.Right:

                    head = new Point(head.X + 1, head.Y);

                    break;

            }

            bodiesLocation[0] = head; //因为PointStruct,是值类型,所以要赋值过来

        }

 

        /// <summary>移动方向</summary>

        public SnakeDirection Direction

        {

            get { return direction; }

            set { direction = value; }

        }

        /// <summary>状态</summary>

        public SnakeState State

        {

            get { return state; }

            set

            {

                state = value;

                speed = (int)state; //改变蛇的速度

            }

        }

        /// <summary>头部位置</summary>

        public Point HeadLocation

        {

            get { return bodiesLocation[0]; }

        }

        /// <summary>蛇身体</summary>

        public List<Point> BodiesLocation

        {

            get { return bodiesLocation; }

        }

 

        /// <summary>蛇的速度</summary>

        public int Speed

        {

            get { return speed; }

            set { speed = value; }

        }

 

        /// <summary>移动方向</summary>

        private SnakeDirection direction;

        /// <summary>状态</summary>

        private SnakeState state;

        /// <summary>蛇身体的位置</summary>

        private List<Point> bodiesLocation;

        /// <summary>蛇的速度</summary>

        private int speed = (int)SnakeState.Normal;

 

    }

}

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows.Forms;

using System.Drawing;

 

namespace snake

{

    public class Food

    {

        public Food(Point location,FoodType type)

        {

            this.location = location;

            this.type = type;

        }

 

        public Food() { }

 

        /// <summary>食物类型</summary>

        public FoodType Type

        {

            get { return type; }

            set { type = value; }

        }

 

        /// <summary>位置</summary>

        public Point Location

        {

            get { return location; }

            set { location = value; }

        }

 

        /// <summary>食物类型</summary>

        private FoodType type;

        /// <summary>位置</summary>

        private Point location;

    }

}

 

为了方便维护,定义了一个类GameManager,专门处理游戏的逻辑,运算之类的。

首先这个类相当于一个游戏,所以蛇,食物是必不可少的,

游戏一定要有一个画面的,所以要定义一个Draw()方法把画面画出来

要随机产生食物,所以要定义一个produceFood()方法

蛇要吃到食物,所以要定义一个SnakeEatFood方法

还要判断游戏是否结束,所以要定义一个IsGameOver方法

因为GameManager相当于逻辑层,snake不对外开放,所以定义了两个属性,只返回snake的两个属性,这样方便把数据显示在界面上。因为蛇是在GameManager里的,不会在窗体出现,所以定义了一个方法SnakeRun(),调用snake.Run()OK了。

还要定义很多常量参数,详细的请看代码

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Drawing;

using System.Windows.Forms;

 

namespace snake

{

    public class GameManager

    {

        /// <summary>

        /// 产生食物

        /// </summary>

        public void ProduceFood()

        {

            Random rnd = new Random();

            //随机产生一个食物

 

 

            Point newFoodLoaction; //新产生的食物位置

            bool flag = false; //是否应该重新产生食物

            do

            {

                newFoodLoaction = new Point(rnd.Next(FIELD_WIDTH), rnd.Next(FIELD_HEIGHT));

                //如果食物在蛇的身上,则重新产生食物位置

                foreach (Point snakeP in snake.BodiesLocation)

                {

                    if (newFoodLoaction == snakeP)

                    {

                        flag = true;

                        break;

                    }

                }

            } while (flag);

 

            Food f = new Food(newFoodLoaction, (FoodType)rnd.Next((int)FoodType.LENGTH));

            foods.Add(f);

        }

 

        /// <summary>

        /// 蛇吃食物

        /// </summary>

        public void SnakeEatFood()

        {

            foreach (Food food in foods)

            {

                //如果蛇的头部碰到食物

                if (snake.HeadLocation == food.Location)

                {

                    foods.Remove(food); //删去食物

 

                    //蛇吃食物的效果

                    Random rnd = new Random();

                    int r;

                    switch (food.Type)

                    {

                        case FoodType.Fast:

                            //状态为快

                            snake.State = SnakeState.Fast;

                            break;

                        case FoodType.Slow:

                            //状态为慢

                            snake.State = SnakeState.Low;

                            break;

                        case FoodType.Normal:

                            //速度变回正常

                            snake.State = SnakeState.Normal;

                            break;

                        case FoodType.Short:

                            //随机缩短

 

                            if (snake.BodiesLocation.Count > 1) r = 0;

                            else if (snake.BodiesLocation.Count < 5) r = 1;

                            else r = rnd.Next(5);

 

                            for (int i = 0; i < r; i++)

                            {

                                snake.BodiesLocation.RemoveAt(snake.BodiesLocation.Count - 1);

                            }

                            break;

                        case FoodType.Long:

                            //随机加长

                            r = rnd.Next(5);

                            for (int i = 0; i < r; i++)

                            {

                                snake.BodiesLocation.Add(new Point(-1, -1));

                            }

                            break;

                        //可以在这里添加更多吃下食物后蛇的变化

                    }

 

                    break; //--break foreach

                }

            }

        }

 

        /// <summary>

        /// 是否游戏完结了

        /// </summary>

        /// <returns>是否游戏完结</returns>

        public bool IsGameOver()

        {

            Point head = snake.HeadLocation; //蛇头

 

            //如果蛇头碰到身体,游戏结束

            for (int i = 1; i < snake.BodiesLocation.Count; i++)

            {

                if (head == snake.BodiesLocation[i]) return true;

            }

 

            //如果蛇头碰到墙壁,游戏结束

            if (head.X < 0 || head.X > FIELD_WIDTH - 1 || head.Y < 0 || head.Y > FIELD_HEIGHT - 1) return true;

 

            return false; //都不是,游戏还没有结束

        }

 

        /// <summary>

        /// 蛇向前行走

        /// </summary>

        public void SnakeRun()

        {

            snake.Run();

        }

 

        /// <summary>

        /// 画出游戏界面

        /// </summary>

        /// <param name="g"></param>

        public void Draw(Graphics g)

        {

            //画蛇

            Color snakeColor = Color.Black;

            foreach (Point p in snake.BodiesLocation)

            {

                Rectangle r = new Rectangle(new Point(p.X * GRID_WIDTH, p.Y * GRID_WIDTH), new Size(GRID_WIDTH, GRID_WIDTH));

                g.FillRectangle(new SolidBrush(snakeColor), r);

            }

 

            //画食物

            foreach (Food f in foods)

            {

                Font font = new Font(new FontFamily("宋体"), 12); //字体

                Rectangle layout = new Rectangle(new Point(f.Location.X * GRID_WIDTH, f.Location.Y * GRID_WIDTH), new Size(GRID_WIDTH, GRID_WIDTH)); //字体所在的矩形

                Color fontColor = Color.Black; //字体颜色

                Color backColor = Color.Yellow; //字体背景色

                string s = null;

 

                switch (f.Type)

                {

                    case FoodType.Fast:

                        s = "";

                        break;

                    case FoodType.Long:

                        s = "";

                        break;

                    case FoodType.Slow:

                        s = "";

                        break;

                    case FoodType.Normal:

                        s = "";

                        break;

                    case FoodType.Short:

                        s = "";

                        break;

                    default:

                        Console.WriteLine("出错了");

                        break;

                }

                g.FillEllipse(new SolidBrush(backColor), layout);

                g.DrawString(s, font, new SolidBrush(Color.Black), layout);

            }

        }

 

        /// <summary>

        /// 蛇的方向

        /// </summary>

        public SnakeDirection SnakeDirection

        {

            get { return snake.Direction; }

            set { snake.Direction = value; }

        }

 

        /// <summary>

        /// 蛇的速度

        /// </summary>

        public int SnakeSpeed

        {

            get { return snake.Speed ; }

        }

 

        /// <summary></summary>

        private Snake snake = new Snake(SNAKE_INIT_LOCATION, SNAKE_INIT_LENGTH, SNAKE_INIT_DIRECTION);

        /// <summary>食物</summary>

        private List<Food> foods = new List<Food>();

 

        /// <summary>格子宽度</summary>

        public const int GRID_WIDTH = 20;

        /// <summary>游戏场地宽度</summary>

        public const int FIELD_WIDTH = 30;

        /// <summary>游戏场地高度</summary>

        public const int FIELD_HEIGHT = 30;

        /// <summary>蛇的初始化长度</summary>

        public const int SNAKE_INIT_LENGTH = 5;

        /// <summary>蛇开始运动的方向</summary>

        public const SnakeDirection SNAKE_INIT_DIRECTION = SnakeDirection.Right;

        /// <summary>蛇开始的位置</summary>

        public static Point SNAKE_INIT_LOCATION = new Point(0, 0);

    }

}

 

因为所有逻辑方面的东西都放在GameManager里,所以,就算界面改N次都好,都可以轻松实现

窗体方面,主要定义两个Timer,一个用于产生食物,一个用于控制蛇的运动。

还有别忘了每次更新数据后,要调用控件的Invalidate()方法更新画面哦。不然什么都不会动。

最后,菜单什么的,随便你怎样加。

 

这个是窗体的代码:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

 

namespace snake

{

    public partial class GameForm : Form

    {

        private GameManager manager = new GameManager(); //控制游戏所有流程的对象

 

        public GameForm()

        {

            InitializeComponent();

        }

 

        private void GameForm_Load(object sender, EventArgs e)

        {

            tmrGame.Interval = (int)SnakeState.Normal;

            tmrGame.Start();

            tmrFood.Start();

 

            this.Height = GameManager.FIELD_HEIGHT * GameManager.GRID_WIDTH;

            this.Width = GameManager.FIELD_WIDTH * GameManager.GRID_WIDTH;

        }

 

        private void tmrGame_Tick(object sender, EventArgs e)

        {

            if (manager.IsGameOver())

            {

                tmrGame.Stop();

                tmrFood.Stop();

            }

            manager.SnakeRun();

            manager.SnakeEatFood();

            tmrGame.Interval = manager.SnakeSpeed; //改变速度

            this.Invalidate();

        }

 

        private void GameForm_Paint(object sender, PaintEventArgs e)

        {

            manager.Draw(e.Graphics);

        }

 

        private void tmrFood_Tick(object sender, EventArgs e)

        {

            manager.ProduceFood();

            this.Invalidate();

        }

 

        private void GameForm_KeyDown(object sender, KeyEventArgs e)

        {

            if (e.KeyCode == Keys.Up && manager.SnakeDirection != SnakeDirection.Down)

            {

                manager.SnakeDirection = SnakeDirection.Up;

            }

            else if (e.KeyCode == Keys.Down && manager.SnakeDirection != SnakeDirection.Up)

            {

                manager.SnakeDirection = SnakeDirection.Down;

            }

            else if (e.KeyCode == Keys.Left && manager.SnakeDirection != SnakeDirection.Right)

            {

                manager.SnakeDirection = SnakeDirection.Left;

            }

            else if (e.KeyCode == Keys.Right && manager.SnakeDirection != SnakeDirection.Left)

            {

                manager.SnakeDirection = SnakeDirection.Right;

            }

        }

    }

}

 

写了一个上午,有点累,下午还要帮我老爸买年票。先写到这里。

还是那句话,希望这段代码能帮上你们。

 

  • 16
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值