簡易貪吃蛇


關於貪吃蛇,一直都想做一個,但一直以來奈何總是沒有任何思路,經過這幾天的思索,總算是給弄出來了

主要用到的是 Graphics 

現在來看這張圖,這張圖假定每一個單元格是10*10的,那么在這張圖里,蛇的每一節就是10*10的大小

 

 

可以看出當前蛇的長度是"3"節,假定這一個單元格是10像素的話,

這3節蛇身的坐標分別是  0,0    10,0    20,0

默認情況下蛇應該是向右前進的,那么怎么模擬蛇身在運動呢?

思路是這樣的,首先在蛇頭也就是最右邊位置(坐標20,0) 的前面(坐標30,0)位置再畫一個藍色的單元格

就像這樣

然后擦掉最左邊的蛇尾(坐標0,0)

就變成這樣了

現在蛇身是不是向右前進一步了

其實就是將橫坐標每次+10,而縱坐標不變

然后一直重復這個過程,就變成了蛇一直在向右前進

-----------------------------------------------------------頑皮的分割線--------------------------------------------------------

 

接着是上下左右的問題

首先來模擬向下

向下的話,就是在當前的蛇頭位置,例如當前是30,0 這個位置的下方再畫一個藍色單元格

就像這樣的

此時蛇頭的坐標就應該是(30,10),  其實就是將新加的蛇頭的縱坐標加了10

然后擦掉尾部的10,0坐標

然后一直重復這個過程(也就是橫坐標不變,縱坐標一直加10),就模擬出蛇身向下運動了

 

然后向左的話,就是橫坐標-10 而縱坐標不變

向上的話就是,橫坐標不變,縱坐標-10

 

基本的思路就是這樣,在蛇身運動的過程中不變的增加蛇頭擦除蛇尾

而在更改方向的時候就改變其對應的橫縱坐標.

 

------------------------------------------------------------------------------------俏皮的分割線--------------------------------------------------------------

 

接着說下食物的投放

食物的話,采取隨機投放

但投放的區域是有講究的

例如項目的地圖是一個720*720的方框,蛇身一節是10*10的話,那么食物的大小也應該是10*10

而食物的投放區域也不應該超出地圖,不然蛇根本吃不到怎么搞是吧

我一剛開始想的是創建隨機器,然后隨機在0-720以內進行取值

可是,因為你是10*10的大小,如果隨機出來了的橫坐標是3,縱坐標是5的話

那么這個食物就會出現不在蛇身的運動軌跡上

所以對於食物的投資區域我是( 0-720/食物寬度)*食物寬度,這樣出來的坐標總是能被他的寬度整除

例如如果隨機出來的兩個坐標數字是3和5

那么經過以上處理出來的坐標就應該是30,50,蛇是肯定能完全吃到的

 

--------------------------------------------------沒用的分割線---------------------------------------------------------------------------

 

好了,現在開始制作代碼方面的事

新建一個窗體程序后,我是打算直接用一個圖片框當地圖的,圖片框的大小設置為720*720

而蛇身和食物的大小都設置為10*10

界面搭建

 

= =| 額,不會搭界面,就這樣吧

然后,直接貼全部的代碼

 ===================元素分割線================================

(之前發過代碼,但是因為感覺尚有不足,以及臃腫之處,所以一直編輯來編輯去的修改,這次又改了,所以決定將之前的代碼完全刪掉再重新貼一次)

(有不足之處,請看官們多多包涵)

 1     /// <summary>
 2     /// 蛇身或食物的基本塊(父類)
 3     /// </summary>
 4     public class Cell
 5     {
 6         /// <summary>
 7         /// 塊的頂點X座標
 8         /// </summary>
 9         public int X { get; set; }
10 
11         /// <summary>
12         /// 塊的頂點Y座標
13         /// </summary>
14         public int Y { get; set; }
15 
16         /// <summary>
17         /// 塊的寬度
18         /// </summary>
19         public int Width { get; set; }
20 
21         /// <summary>
22         /// 塊的高度
23         /// </summary>
24         public int Height { get; set; }
25 
26         /// <summary>
27         /// 所在地圖寬度
28         /// </summary>
29         public int MapWidth { get; set; }
30 
31         /// <summary>
32         /// 所在地圖高度
33         /// </summary>
34         public int MapHeight { get; set; }
35 
36         public Cell(int x,int y)
37         {
38             this.X = x;
39             this.Y = y;
40         }
41 
42         public Cell() { }
43 
44         /// <summary>
45         /// 繪制單元格
46         /// </summary>
47         /// <param name="g"></param>
48         /// <param name="c"></param>
49         public virtual void Draw(Graphics g, Color c)
50         {
51             SolidBrush sb = new SolidBrush(c);
52             g.FillRectangle(sb, this.X, this.Y, this.Width, this.Height);
53         }
54     }

 

 1     /// <summary>
 2     /// 食物類
 3     /// </summary>
 4     public class Food:Cell
 5     {
 6         public Food(int x, int y) : base(x, y)
 7         {
 8 
 9         }
10 
11         public Food() { }
12 
13         /// <summary>
14         /// 創建食物
15         /// </summary>
16         /// <returns></returns>
17         public Food GetFood()
18         {
19 
20             Random r = new Random();
21 
22             ///-------------------------------------------------------
23             ///--因為本例中所使用的地圖是正方形,每一個食物都是在地圖的小正方形中
24             ///其生成的坐標點必須要能被整除,所以下面才這樣獲取隨機數
25             int rX = (r.Next(0, this.MapWidth / Width)) * Width;
26             int rY = (r.Next(0, this.MapHeight / Width)) * Width;            
27 
28             //生成新的食物,並給各項賦值
29             Food Newfood = new Food(rX, rY);
30             Newfood.Width = this.Width;
31             Newfood.Height = this.Height;
32             return Newfood;
33         }           
34     }

 

 

  1     /// <summary>
  2     /// 蛇類
  3     /// </summary>
  4     public class Snake: Cell
  5     {
  6         /// <summary>
  7         /// 創建食物對象
  8         /// </summary>
  9         Food food = new Food();
 10       
 11         /// <summary>
 12         /// 創建蛇身集合
 13         /// </summary>
 14         public List<Snake> Snakes = new List<Snake>();
 15 
 16         /// <summary>
 17         /// 創建頭部坐標
 18         /// </summary>
 19         public Point Heade;
 20 
 21         public Snake() { }
 22 
 23         public Snake(int x, int y) : base(x, y)
 24         {
 25         }
 26 
 27         /// <summary>
 28         /// 位置枚舉
 29         /// </summary>
 30         public enum Direction { left, right, up, down }
 31              
 32         /// <summary>
 33         /// 創建枚舉對象
 34         /// </summary>
 35         public Direction de = new Direction();
 36 
 37         /// <summary>
 38         /// 畫出蛇的形狀
 39         /// </summary>
 40         /// <param name="g"></param>
 41         /// <param name="c"></param>
 42         public override void Draw(Graphics g, Color c)
 43         {
 44             //創建單色刷子
 45             SolidBrush sb = new SolidBrush(c);
 46 
 47             foreach (var item in Snakes)
 48             {
 49                 //將蛇身每一節畫出來 
 50                 g.FillRectangle(sb, item.X, item.Y,this.Width,this.Height);
 51             }
 52         }
 53 
 54         /// <summary>
 55         /// 清除蛇尾的那節
 56         /// </summary>
 57         /// <param name="g"></param>
 58         /// <param name="c"></param>
 59         public void Clear(Graphics g, Color c)
 60         {
 61             //將尾部一個給塗掉
 62             Snake s = Snakes[0];
 63             SolidBrush sb = new SolidBrush(c);
 64             g.FillRectangle(sb, s.X, s.Y, Width, Height);
 65         }
 66 
 67         /// <summary>
 68         /// 移動的方法
 69         /// </summary>
 70         /// <param name="g"></param>
 71         /// <param name="c"></param>
 72         /// <param name="ph">當前的朝向</param>
 73         public void Move(Graphics g,Color c,Direction ph)
 74         {
 75             //根據朝向更改對應的坐標
 76             switch (ph)
 77             {
 78                 case Direction.left:
 79                     Heade.X -= Width;
 80                     break;
 81                 case Direction.right:
 82                     Heade.X += Width;
 83                     break;
 84                 case Direction.up:
 85                     Heade.Y -= Height;
 86                     break;
 87                 case Direction.down:
 88                     Heade.Y += Height;
 89                     break;
 90             }
 91 
 92             //將新蛇頭加入到集合中
 93             Snakes.Add(new Snake(Heade.X, Heade.Y));
 94         }
 95 
 96         /// <summary>
 97         /// 判斷當前是否吃到食物
 98         /// </summary>
 99         /// <param name="food"></param>
100         /// <returns></returns>
101         public bool EatFood(Food food)
102         {
103             //拿到食物座標
104             Point foodPos = new Point(food.X, food.Y);
105 
106             //如果當前的蛇頭座標與當前的食物座標重疊
107             if (Heade.X == foodPos.X && Heade.Y == foodPos.Y)
108             {
109                 //已經吃到食物了
110                 return true;
111             }
112             else
113             {
114                 return false;
115             }
116         }
117 
118         /// <summary>
119         /// 判斷 是否蛇頭撞到了自己
120         /// </summary>
121         /// <returns>返回true則為撞到</returns>
122         public bool HitBOdy()
123         {
124             bool falg = false;
125             
126             //判斷蛇頭是否在除蛇頭之外的蛇身內
127             for (int i = 0; i < Snakes.Count-1; i++)
128             {
129                 //如果在
130                 if (Snakes[i].X == Heade.X && Snakes[i].Y == Heade.Y)//Snakes[i].X == snakeHead.X && Snakes[i].Y == snakeHead.Y)
131                 {
132                     //撞到自己
133                     falg = true;
134                     break;
135                 }
136                 else
137                 {
138                     falg = false;
139                 }
140             }
141             return falg;
142         }
143 
144         /// <summary>
145         /// 判斷是否跑出了地圖
146         /// </summary>
147         /// <returns></returns>
148         public bool HitMap()
149         {
150             ///判斷當前蛇頭位置是否在地圖外面
151             if (Heade.X< 0 || Heade.X >= MapWidth || Heade.Y < 0 || Heade.Y >= MapHeight)
152             {
153                 return true;
154             }
155             else
156             {
157                 return false;
158             }
159         }
160     }

 

 

    public partial class 貪吃蛇 : Form
    {

        public 貪吃蛇()
        {
            InitializeComponent();

        }       

        /// <summary>
        /// 初始化蛇
        /// </summary>
        Snake snake = new Snake();

        /// <summary>
        /// 創建食物
        /// </summary>
        Food food = null;

        /// <summary>
        /// 創建對象,用作地圖(也就是大圖片框)
        /// </summary>
        Graphics gp;

        /// <summary>
        /// 窗體初始化
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void 貪吃蛇_Load(object sender, EventArgs e)
        {
            ///創建畫板
            gp = pictureBox1.CreateGraphics();

            //初始化蛇身朝向(默認為向右)
            snake.de = Snake.Direction.right;

            timer1.Interval = hScrollBar1.Value;
            lbSpeed.Text = hScrollBar1.Value.ToString();
            lbSnakeCount.Text = snake.Snakes.Count.ToString();

            //--初始化蛇的各項數值
            snake.Width = 10;
            snake.Height = 10;
            snake.MapHeight = pictureBox1.Height;
            snake.MapWidth = pictureBox1.Width;

            //--增加蛇身
            snake.Snakes.Add(new Snake(0, 0));
            snake.Snakes.Add(new Snake(10, 0));
            snake.Snakes.Add(new Snake(20, 0));
            snake.Snakes.Add(new Snake(30, 0));

            //--初始化食物的各項數值
            food = new SnakeGame.Food(100, 100);
            food.Width = 10;
            food.Height = 10;
            food.MapWidth = pictureBox1.Width;
            food.MapHeight = pictureBox1.Height;            
        }

        private void 貪吃蛇_Paint(object sender, PaintEventArgs e)
        {
            //創建一張位圖,大小就為圖片框的大小
            Bitmap bitmap = new Bitmap(this.pictureBox1.ClientSize.Width, this.pictureBox1.ClientSize.Height);
            Graphics g = Graphics.FromImage(bitmap);

            //畫出初始蛇身
            snake.Draw(g, Color.White);

            //畫出初始食物
            food.Draw(g, Color.Red);

            this.pictureBox1.Image = bitmap;
        }

        /// <summary>
        ///  開始按鈕
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            //點擊開始按鈕后,啟動計時器
            timer1.Enabled = true;
        }

private void timer1_Tick(object sender, EventArgs e) { /// 邏輯為: /// 模擬蛇身在運動 /// 即先讓蛇尾那一格塗成和背景色一樣,然后判斷方向, /// 在相應的方向增加蛇頭位置,之后再將新蛇頭畫出來 //每次刷新的時候都取一次蛇頭位置(給蛇頭賦值) snake.Heade = new Point(snake.Snakes.Last().X, snake.Snakes.Last().Y); //清除蛇尾 snake.Clear(gp, pictureBox1.BackColor); //判斷是否吃到食物,吃到了就不再移除蛇尾,當做是吃了食物增長了一節 if (snake.EatFood(food)) { //已經吃了就再獲取一次食物 food = food.GetFood(); //將重新獲取到食物對象賦值 food.MapHeight = pictureBox1.Height; food.MapWidth = pictureBox1.Width; //畫出重新獲取到的食物 food.Draw(gp, Color.Red); //每增長5節數減10速度 timer1.Interval = Level.levelUp(snake.Snakes.Count, 5, timer1.Interval, 10); lbSpeed.Text = timer1.Interval.ToString(); } else { //移除蛇尾 snake.Snakes.RemoveAt(0); }
//只用直接調用蛇類的行走方法,傳入當前的朝向就可以了 snake.Move(gp, Color.Red, snake.de); //畫出整個蛇身 snake.Draw(gp, Color.White); //判斷邊界 if (snake.HitBOdy()) { timer1.Enabled = false; MessageBox.Show("撞到自己了,游戲結束"); //將開始按鈕禁用 btnStart.Enabled = false; return; } if (snake.HitMap()) { timer1.Enabled = false; MessageBox.Show("撞到牆了,游戲結束"); //將開始按鈕禁用 btnStart.Enabled = false; return; } lbSnakeCount.Text = snake.Snakes.Count.ToString(); } /// <summary> /// 開始按鈕點擊后,判斷WSDA /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnStart_KeyDown(object sender, KeyEventArgs e) { //根據當前的按鍵將朝向對應起來 switch (e.KeyCode) { case Keys.W: snake.de = Snake.Direction.up; break; case Keys.S: snake.de = Snake.Direction.down; break; case Keys.D: snake.de = Snake.Direction.right; break; case Keys.A: snake.de = Snake.Direction.left; break; } } /// <summary> /// 暫停按鈕 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnPause_Click(object sender, EventArgs e) { //將計時器不可用 timer1.Enabled = false; } /// <summary> /// 拉動速度條時改變蛇的速度 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void hScrollBar1_ValueChanged(object sender, EventArgs e) { this.timer1.Interval = hScrollBar1.Value; lbSpeed.Text = hScrollBar1.Value.ToString(); btnStart.Focus(); } /// <summary> /// 重新開始 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnAgain_Click(object sender, EventArgs e) { //重新開始,初始化各項數值 snake.Snakes.Clear(); snake.de = Snake.Direction.right; this.貪吃蛇_Paint(sender, null); this.貪吃蛇_Load(sender, null); btnStart.Focus(); btnStart.Enabled = true; } /// <summary> /// 畫布點擊,只是為了增加改變地圖的背景色而已,沒多大用 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void pictureBox1_Click(object sender, EventArgs e) { if (colorDialog1.ShowDialog() == DialogResult.OK) { this.pictureBox1.BackColor = colorDialog1.Color; } } /// <summary> /// 窗體點擊,只是為了增加改變窗體的背景色而已,沒多大用 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void 貪吃蛇_Click(object sender, EventArgs e) { if (colorDialog1.ShowDialog() == DialogResult.OK) { this.BackColor = colorDialog1.Color; } } /// <summary> /// 背景改變,只是為了增加改變美女圖片框的背景色而已,讓其和窗體背景色保持一致,沒多大用 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void 貪吃蛇_BackColorChanged(object sender, EventArgs e) { pictureBox2.BackColor = this.BackColor; } }

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SnakeGame
{
    /// <summary>
    /// 等級增長類
    /// </summary>
    public static class Level
    {
        /// <summary>
        /// 等級增長,傳入蛇身的長度,每當增長的長度就減少時間間隔(增長蛇的行動速度)
        /// </summary>
        /// <param name="count">蛇身當前長度</param>
        /// <param name="length">當增長到一定的節數</param>
        /// <param name="time">當前計時器間隔時間</param>
        /// <param name="speed">每次減去的速度</param>
        /// <returns></returns>
        public static int levelUp(int count,int length, int time,int speed)
        {
            if (count%length==0&&time>0)
            {
                time -= speed;
            }
            return time;
        }
    }
}

  最后,本示例所用環境如下圖所示


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM