關於貪吃蛇,一直都想做一個,但一直以來奈何總是沒有任何思路,經過這幾天的思索,總算是給弄出來了 主要用到的是 Graphics 現在來看這張圖,這張圖假定每一個單元格是10*10的,那麼在這張圖裡,蛇的每一節就是10*10的大小 可以看出當前蛇的長度是"3"節,假定這一個單元格是10像素的話, 這3 ...
關於貪吃蛇,一直都想做一個,但一直以來奈何總是沒有任何思路,經過這幾天的思索,總算是給弄出來了
主要用到的是 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() { } 37 38 public Cell(int x,int y) 39 { 40 this.X = x; 41 this.Y = y; 42 } 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 } 55 }View Code
1 /// <summary> 2 /// 食物類 3 /// </summary> 4 public class Food:Cell 5 { 6 7 public Food() { } 8 9 public Food(int x, int y) : base(x, y) 10 { 11 12 } 13 14 /// <summary> 15 /// 創建食物 16 /// </summary> 17 /// <returns></returns> 18 public Food GetFood() 19 { 20 21 Random r = new Random(); 22 23 ///------------------------------------------------------- 24 ///--因為本例中所使用的地圖是正方形,每一個食物都是在地圖的小正方形中 25 ///其生成的坐標點必須要能被整除,所以下麵才這樣獲取隨機數 26 int rX = (r.Next(0, this.MapWidth / Width)) * Width; 27 int rY = (r.Next(0, this.MapHeight / Width)) * Width; 28 29 30 Food Newfood = new Food(rX, rY); 31 Newfood.Width = this.Width; 32 Newfood.Height = this.Height; 33 return Newfood; 34 35 } 36 }View Code
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 15 16 17 /// <summary> 18 /// 創建蛇身集合 19 /// </summary> 20 public List<Snake> Snakes = new List<Snake>(); 21 22 /// <summary> 23 /// 創建頭部坐標 24 /// </summary> 25 public Point Heade; 26 27 public Snake() { } 28 29 30 31 public Snake(int x, int y) 32 { 33 this.X = x; 34 this.Y = y; 35 36 } 37 38 39 /// <summary> 40 /// 畫形 41 /// </summary> 42 /// <param name="g"></param> 43 /// <param name="c"></param> 44 public override void Draw(Graphics g, Color c) 45 { 46 SolidBrush sb = new SolidBrush(c); 47 48 foreach (var item in Snakes) 49 { 50 //將蛇身每一節畫出來 51 g.FillRectangle(sb, item.X, item.Y,this.Width,this.Height); 52 } 53 } 54 55 56 57 /// <summary> 58 /// 清除 59 /// </summary> 60 /// <param name="g"></param> 61 /// <param name="c"></param> 62 public void Clear(Graphics g, Color c) 63 { 64 //將尾部一個給塗掉 65 Snake s = Snakes[0]; 66 SolidBrush sb = new SolidBrush(c); 67 g.FillRectangle(sb, s.X, s.Y, Width, Height); 68 } 69 70 71 72 73 /// <summary> 74 /// 蛇向右走 75 /// </summary> 76 /// <param name="g"></param> 77 /// <param name="c"></param> 78 public void MoveRight(Graphics g, Color c) 79 { 80 //改變橫坐標 81 Heade.X += Width; 82 Snakes.Add(new Snake(Heade.X, Heade.Y)); 83 } 84 85 /// <summary> 86 /// 向下 87 /// </summary> 88 /// <param name="g"></param> 89 /// <param name="c"></param> 90 public void MoveDown(Graphics g, Color c) 91 { 92 //改變縱坐標 93 Heade.Y += Height; 94 Snakes.Add(new Snake(Heade.X, Heade.Y)); 95 } 96 97 /// <summary> 98 /// 向左 99 /// </summary> 100 /// <param name="g"></param> 101 /// <param name="c"></param> 102 public void MoveLeft(Graphics g, Color c) 103 { 104 //改變橫坐標 105 Heade.X -= Width; 106 Snakes.Add(new Snake(Heade.X, Heade.Y)); 107 } 108 109 /// <summary> 110 /// 向上 111 /// </summary> 112 /// <param name="g"></param> 113 /// <param name="c"></param> 114 public void MoveUp(Graphics g, Color c) 115 { 116 //改變縱坐標 117 Heade.Y -= Height; 118 Snakes.Add(new Snake(Heade.X, Heade.Y)); 119 } 120 121 /// <summary> 122 /// 判斷當前是否吃到食物 123 /// </summary> 124 /// <param name="food"></param> 125 /// <returns></returns> 126 public bool EatFood(Food food) 127 { 128 129 //拿到蛇頭所在 130 Snake end = Snakes.Last(); 131 132 //拿到蛇頭位置 133 Point snakeHead = new Point(end.X, end.Y); 134 135 //拿到食物座標 136 Point foodPos = new Point(food.X, food.Y); 137 138 //如果當前的蛇頭座標與當前的食物座標重疊 139 if (snakeHead.X == foodPos.X && snakeHead.Y == foodPos.Y) 140 { 141 //已經吃到食物了 142 return true; 143 } 144 else 145 { 146 return false; 147 } 148 149 } 150 151 152 /// <summary> 153 /// 判斷 是否蛇頭撞到了自己 154 /// </summary> 155 /// <returns>返回true則未撞到</returns> 156 public bool HitBOdy() 157 { 158 //蛇頭現在的位置是否在除蛇頭之外蛇身內 159 160 //沒有撞到 161 bool falg = false; 162 163 //拿到蛇頭所在 164 Snake end = Snakes.Last(); 165 166 //拿到蛇頭位置 167 Point snakeHead = new Point(end.X, end.Y); 168 169 //判斷蛇頭是否在除蛇頭之外的蛇身內 170 for (int i = 0; i < Snakes.Count-1; i++) 171 { 172 //如果在 173 if (Snakes[i].X == snakeHead.X && Snakes[i].Y == snakeHead.Y) 174 { 175 //撞到自己 176 falg = true; 177 break; 178 } 179 else 180 { 181 falg = false; 182 } 183 } 184 return falg; 185 } 186 187 /// <summary> 188 /// 判斷是否跑出了地圖 189 /// </summary> 190 /// <returns></returns> 191 public bool HitMap() 192 { 193 ///判斷當前蛇頭位置是否在地圖外面 194 if (Heade.X < 0 || Heade.X > this.MapWidth || Heade.Y < 0 || Heade.Y > this.MapHeight) 195 { 196 return true; 197 } 198 else 199 { 200 return false; 201 } 202 } 203 }View Code
1 /// <summary> 2 /// 等級增長類 3 /// </summary> 4 public static class Level 5 { 6 /// <summary> 7 /// 等級增長,傳入蛇身的長度,每當增長的長度就減少時間間隔(增長蛇的行動速度) 8 /// </summary> 9 /// <param name="count">蛇身當前長度</param> 10 /// <param name="length">當增長到一定的節數</param> 11 /// <param name="time">當前計時器間隔時間</param> 12 /// <param name="speed">每次減去的速度</param> 13 /// <returns></returns> 14 public static int levelUp(int count,int length, int time,int speed) 15 { 16 if (count%length==0&&time>0) 17 { 18 time -= speed; 19 } 20 return time; 21 } 22 }View Code
public partial class 貪吃蛇 : Form { public 貪吃蛇() { InitializeComponent(); } /// <summary> /// 蛇身朝向 /// </summary> private enum Direction { left, right, up, down } /// <summary> /// 初始化朝向為右 /// </summary> private Direction de = Direction.right; /// <summary> /// 初始化蛇 /// </summary> Snake snake = new Snake(); /// <summary> /// 創建食物 /// </summary> Food food = null; 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 button1_Click(object sender, EventArgs e) { timer1.Enabled = true; } /// <summary> /// 創建對象 /// </summary> Graphics gp; private void timer1_Tick(object sender, EventArgs e) { ///邏輯為: /// 模擬蛇身在運動 /// 即先讓蛇尾那一格塗成和背景色一樣,然後判斷方向, /// 在相應的方向增加蛇頭位置,之後再將新蛇頭畫出來 ///創建畫板 gp = pictureBox1.CreateGraphics(); //取到當前的蛇頭座標以備後面的移動時提取 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); } //判斷行進方向 switch (de) { case Direction.left: snake.MoveLeft(gp, Color.Red); break; case Direction.right: snake.MoveRight(gp, Color.Red); break; case Direction.up: snake.MoveUp(gp, Color.Red); break; case Direction.down: snake.MoveDown(gp, Color.Red); break; default: break; } //畫出整個蛇身 snake.Draw(gp, Color.White); //判斷邊界 if (snake.HitBOdy()) { timer1.Stop(); MessageBox.Show("撞到自己了,游戲結束"); //將開始按鈕禁用 btnStart.Enabled = false; return; } if (snake.HitMap()) { timer1.Stop(); MessageBox.Show("撞到牆了,游戲結束"); //將開始按鈕禁用 btnStart.Enabled = false; return; } lbSnakeCount.Text = snake.Snakes.Count.ToString(); } /// <summary> /// 窗體初始化 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void 貪吃蛇_Load(object sender, EventArgs e) { 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; } /// <summary> /// 判斷當前的按鍵 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnStart_KeyDown(object sender, KeyEventArgs e) { //根據當前的按鍵將朝向對應起來 switch (e.KeyCode) { case Keys.W: de = Direction.up; break; case Keys.S: de = Direction.down; break; case Keys.D: de = Direction.right; break; case Keys.A: de = 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 button3_Click(object sender, EventArgs e) { //重新開始,初始化各