最近一直忙着11月份軟考的復習,一直沒有練習C#。覺得對於一個新手,長時間不練肯定不行,所以覺得敲敲代碼練練手。剛好電腦磁盤里有以前Down的C#開源游戲,於是挑中了貪吃蛇,來練練手。
貪吃蛇游戲界面如下:
為了實現貪吃蛇,一共寫了四個類,分別是:
1)Bean:食物類,就是玩家要吃的那個食物,里面實現了兩個方法:食物的顯示與食物的消失(因為食物被吃了以后應該消失)
2)Block:蛇塊類,就是組成蛇的單元,玩家吃到食物,蛇的蛇塊增加一節。
3)Snake:蛇類,游戲中的主角,玩家通過上下左右四個鍵來控制蛇的運動。
4)Map:地圖類,用於畫出游戲主界面,通過定時器來控制,每個單元時間重新畫一幅地圖,用於動態顯示
以下是代碼的實現
1 //食物類 2 public class Bean 3 { 4 //用於畫食物的頂端坐標 5 private Point _origin; 6 7 public Point Origin 8 { 9 get { return _origin; } 10 set { _origin = value; } 11 } 12 13 //顯示食物 14 public void ShowBean(Graphics g) 15 { 16 //定義紅色的畫筆 17 SolidBrush brush = new SolidBrush(Color.Red); 18 //畫實心矩形表示食物 19 g.FillRectangle(brush,Origin.X,Origin.Y,15,15); 20 } 21 22 public void UnShowBean(Graphics g) 23 { 24 //定義系統背景顏色的畫筆 25 SolidBrush brush = new SolidBrush(Color.Silver); 26 //畫實心矩形顏色為系統背景顏色,相當於食物被吃掉了 27 g.FillRectangle(brush,Origin.X,Origin.Y,15,15); 28 } 29 }
//蛇身體的每一單元,簡稱塊 public class Block { //是否為蛇頭 private bool _isHead; public bool IsHead { get { return _isHead; } set { _isHead = value; } } //蛇塊的編號 private int _blockNumber; public int BlockNumber { get { return _blockNumber; } set { _blockNumber = value; } } //蛇塊的左上角位置 private Point _origin; public Point Origin { get { return _origin; } set { _origin = value; } } //根據指定位置畫蛇塊 public void ShowBlock(Graphics g) { Bitmap bitMap; if (IsHead) { //蛇頭 bitMap = new Bitmap(Image.FromFile("Blue.gif")); } else { bitMap = new Bitmap(Image.FromFile("Yellow.gif")); } g.DrawImage(bitMap,Origin.X,Origin.Y,15,15); } //消除蛇塊 public void UnShowBlock(Graphics g) { SolidBrush brush = new SolidBrush(Color.Silver); g.FillRectangle(brush,Origin.X,Origin.Y,15,15); } }
1 public class Snake 2 { 3 //用於存放蛇的集合 4 private List<Block> blocksList; 5 //0-上,1-右,2-下,3-左 6 private int direction = 1; 7 //蛇頭的編號,蛇的長度 8 private int headNumber; 9 //蛇頭左上角坐標 10 private Point headPoint; 11 private Point mapLeft; 12 //游戲開始時,初始的蛇 13 public Snake(Point map, int count) 14 { 15 mapLeft = map; 16 Block blockSnake; 17 //定義蛇的起始位置(蛇尾) 18 Point p = new Point(map.X + 15, map.Y + 15); 19 blocksList = new List<Block>(); 20 //循環畫蛇塊用於填充蛇集合 21 for (int i = 0; i < count; i++) 22 { 23 //x坐標加15 24 p.X += 15; 25 //實例化蛇塊 26 blockSnake = new Block(); 27 //定義蛇塊的左上角位置 28 blockSnake.Origin = p; 29 //定義蛇塊的編號1,2,3... 30 blockSnake.BlockNumber = i + 1; 31 if (i == count - 1) 32 { 33 //蛇頭 34 //給蛇頭位置賦值 35 headPoint = blockSnake.Origin; 36 blockSnake.IsHead = true; 37 } 38 blocksList.Add(blockSnake); 39 40 } 41 //蛇的長度賦值 42 headNumber = count; 43 } 44 //蛇頭坐標的只讀屬性 45 public Point HeadPoint 46 { 47 get { return headPoint; } 48 } 49 //蛇的運動方向的屬性 50 public int Direction 51 { 52 get { return direction; } 53 set { direction = value; } 54 } 55 /// <summary> 56 /// 蛇的轉換方向 57 /// </summary> 58 /// <param name="pDirection">想要改變的方向</param> 59 public void TurnDirection(int pDirection) 60 { 61 switch (direction) 62 { 63 //原來向上運動 64 case 0: 65 //希望向左運動 66 if (pDirection == 3) 67 direction = 3; 68 //希望向右運動 69 else if (pDirection == 1) 70 direction = 1; 71 break; 72 //原來向右運動 73 case 1: 74 //下 75 if (pDirection == 2) 76 direction = 2; 77 //上 78 else if (pDirection == 0) 79 direction = 0; 80 break; 81 case 2: 82 if (pDirection == 1) 83 direction = 1; 84 else if (pDirection == 3) 85 direction = 3; 86 break; 87 case 3: 88 if (pDirection == 0) 89 direction = 0; 90 else if (pDirection == 2) 91 direction = 2; 92 break; 93 94 95 } 96 } 97 98 public Point getHeadPoint //只讀蛇頭位置屬性 99 { 100 get { return headPoint; } 101 } 102 //蛇吃到食物后變長,蛇頭+1 103 public void SnakeGrowth() 104 { 105 //找到蛇頭的坐標 106 Point head = blocksList[blocksList.Count - 1].Origin; 107 int x = head.X; 108 int y = head.Y; 109 //判斷蛇的運動方向,改變蛇頭的位置 110 switch (direction) 111 { 112 case 0: 113 //向上運動 114 y -= 15; 115 break; 116 case 1: 117 x += 15; 118 break; 119 case 2: 120 y += 15; 121 break; 122 case 3: 123 x -= 15; 124 break; 125 } 126 //把原先蛇頭的塊變為普通塊 127 blocksList[blocksList.Count - 1].IsHead = false; 128 //實例化新蛇頭 129 Block headNew = new Block(); 130 headNew.IsHead = true; 131 headNew.BlockNumber = blocksList.Count + 1; 132 headNew.Origin = new Point(x,y); 133 blocksList.Add(headNew); 134 headNumber++; 135 headPoint = headNew.Origin; 136 } 137 138 //蛇向前運動(沒有吃到食物的情況),蛇尾移除,蛇頭移位+1 139 public void Go(Graphics g) 140 { 141 Block snakeTail = blocksList[0]; 142 //消除蛇尾塊 143 snakeTail.UnShowBlock(g); 144 //集合中移除設為塊 145 blocksList.RemoveAt(0); 146 foreach (var item in blocksList) 147 { 148 item.BlockNumber--; 149 } 150 //由於SnakeGrowth中僅僅使蛇頭+1,但是headNumber++了。但是此值並沒有改變,所以先-- 151 headNumber--; 152 SnakeGrowth(); 153 } 154 155 //畫出蛇 156 public void ShowSnake(Graphics g) 157 { 158 foreach (var item in blocksList) 159 { 160 item.ShowBlock(g); 161 } 162 } 163 //隱藏蛇 164 public void UnShowSnake(Graphics g) 165 { 166 foreach (var item in blocksList) 167 { 168 item.UnShowBlock(g); 169 } 170 } 171 //重置蛇 172 public void Reset(Point map, int count) 173 { 174 Block blockSnake; 175 //定義蛇的起始位置(蛇尾) 176 Point p = new Point(mapLeft.X + 15, mapLeft.Y + 15); 177 blocksList = new List<Block>(); 178 //循環畫蛇塊用於填充蛇集合 179 for (int i = 0; i < count; i++) 180 { 181 //x坐標加15 182 p.X += 15; 183 //實例化蛇塊 184 blockSnake = new Block(); 185 //定義蛇塊的左上角位置 186 blockSnake.Origin = p; 187 //定義蛇塊的編號1,2,3... 188 blockSnake.BlockNumber = i + 1; 189 if (i == count - 1) 190 { 191 //蛇頭 192 //給蛇頭位置賦值 193 headPoint = blockSnake.Origin; 194 blockSnake.IsHead = true; 195 } 196 blocksList.Add(blockSnake); 197 198 } 199 //蛇的長度賦值 200 headNumber = count; 201 direction = 1; 202 } 203 //是否碰到自己 204 public bool IsTouchMyself() 205 { 206 bool isTouched = false; 207 for (int i = 0; i < blocksList.Count-1; i++) 208 { 209 if (headPoint == blocksList[i].Origin) 210 { 211 isTouched = true; 212 break; 213 } 214 } 215 return isTouched; 216 } 217 }
1 public class Map 2 { 3 private Point mapLeft; 4 private static int unit = 15; 5 //定義地圖長,為28個蛇塊的長度 6 private readonly int length = 30 * unit; 7 //定義地圖寬,為20個蛇塊的寬度 8 private readonly int width = 25 * unit; 9 //定義分數 10 public int score = 0; 11 //定義蛇 12 private readonly Snake snake; 13 public bool victory = false; 14 public Snake Snake 15 { 16 get { return snake; } 17 } 18 19 Bean food; 20 public Map(Point start) 21 { 22 //把地圖左上角的點的值賦值給全局變量 23 mapLeft = start; 24 //實例化蛇 25 snake = new Snake(start,5); 26 //實例化食物 27 food = new Bean(); 28 food.Origin = new Point(start.X+30,start.Y+30); 29 } 30 //顯示新食物 31 public void ShowNewFood(Graphics g) 32 { 33 //消除原先食物 34 food.UnShowBean(g); 35 //產生隨機位置的食物 36 food = FoodRandom(); 37 //顯示食物 38 food.ShowBean(g); 39 } 40 //隨機產生一個新食物 41 private Bean FoodRandom() 42 { 43 Random d=new Random (); 44 int x = d.Next(0, length / unit); 45 int y = d.Next(0, width / unit); 46 Point origin = new Point(mapLeft.X+x*15,mapLeft.Y+y*15); 47 Bean food = new Bean(); 48 food.Origin = origin; 49 return food; 50 } 51 //畫地圖 52 public void ShowMap(Graphics g) 53 { 54 //創建一支紅筆 55 Pen pen = new Pen(Color.Blue); 56 //畫出地圖的框 57 g.DrawRectangle(pen,mapLeft.X,mapLeft.Y,length,width); 58 //顯示食物 59 food.ShowBean(g); 60 if (CheckBean()) 61 { 62 //吃到了食物 63 //顯示新食物 64 ShowNewFood(g); 65 //蛇變長 66 snake.SnakeGrowth(); 67 //分數增加 68 score += 10; 69 //if (score >= 100) 70 //{ 71 // victory = true; 72 //} 73 //顯示蛇 74 snake.ShowSnake(g); 75 } 76 else 77 { 78 snake.Go(g); 79 snake.ShowSnake(g); 80 } 81 } 82 //判斷是否吃到了食物 83 public bool CheckBean() 84 { 85 86 return food.Origin.Equals(snake.HeadPoint); 87 } 88 89 //檢查蛇是否撞牆 90 public bool CheckSnake() 91 { 92 return !(snake.getHeadPoint.X > mapLeft.X-5 && snake.getHeadPoint.X < (mapLeft.X + length-5) && snake.getHeadPoint.Y > mapLeft.Y-5 && snake.getHeadPoint.Y < (mapLeft.Y + width -5)); 93 } 94 95 //重新開始 96 public void Reset(Graphics g) 97 { 98 snake.UnShowSnake(g); 99 snake.Reset(mapLeft,5); 100 } 101 }
1 這是窗體的代碼 2 public frmMain() 3 { 4 InitializeComponent(); 5 //定義地圖 6 map = new Map(new Point(50,50)); 7 BackColor = Color.Silver; 8 } 9 private readonly Map map; 10 private int gradeNum = 100; 11 private int pink; 12 private void frmMain_Load(object sender, EventArgs e) 13 { 14 SetStyle( 15 ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true); 16 UpdateStyles(); 17 } 18 19 private void timer1_Tick(object sender, EventArgs e) 20 { 21 //this.BackgroundImage = Image.FromFile(pink+".jpg"); 22 開始ToolStripMenuItem.Text = "重新開始"; 23 lblScore.Text = map.score.ToString(); 24 if (map.score >= 500) 25 { 26 timer1.Enabled = false; 27 MessageBox.Show("恭喜,成功!!!"); 28 } 29 Bitmap bmp = new Bitmap(Width, Height); 30 //Image face = Image.FromFile("1.jpg"); 31 BackgroundImage = bmp; 32 Graphics g = Graphics.FromImage(bmp); 33 map.ShowMap(g); 34 if (map.CheckSnake()||map.Snake.IsTouchMyself()) 35 { 36 timer1.Enabled = false; 37 MessageBox.Show("很遺憾,失敗了!!!"); 38 } 39 40 } 41 //鍵盤響應事件 42 private void frmMain_KeyDown(object sender, KeyEventArgs e) 43 { 44 int k, d = 0; 45 k = e.KeyValue; 46 if (k == 37) //左 47 d = 3; 48 else if (k == 40) //下 49 d = 2; 50 else if (k == 38) //上 51 d = 0; 52 else if (k == 39) //右 53 d = 1; 54 map.Snake.TurnDirection(d); 55 } 56 57 private void 開始ToolStripMenuItem_Click(object sender, EventArgs e) 58 { 59 if (lblGrade.Text == "") 60 { 61 MessageBox.Show("請先選擇級別"); 62 return; 63 } 64 if (開始ToolStripMenuItem.Text == "開始") 65 { 66 開始ToolStripMenuItem.Text = "重新開始"; 67 timer1.Enabled = true; 68 } 69 else if (開始ToolStripMenuItem.Text == "重新開始") 70 { 71 開始ToolStripMenuItem.Text = "開始"; 72 繼續ToolStripMenuItem.Text = "暫停"; 73 timer1.Enabled = true; 74 map.Reset(CreateGraphics()); 75 map.score = 0; 76 } 77 78 } 79 80 private void 繼續ToolStripMenuItem_Click(object sender, EventArgs e) 81 { 82 if (繼續ToolStripMenuItem.Text == "繼續") 83 { 84 繼續ToolStripMenuItem.Text = "暫停"; 85 timer1.Enabled = true; 86 } 87 else if(繼續ToolStripMenuItem.Text == "暫停") 88 { 89 繼續ToolStripMenuItem.Text = "繼續"; 90 timer1.Enabled = false; 91 } 92 } 93 94 private void 低級ToolStripMenuItem_Click(object sender, EventArgs e) 95 { 96 gradeNum = 200; 97 lblGrade.Text = "菜鳥"; 98 timer1.Interval = gradeNum; 99 } 100 101 private void 中級ToolStripMenuItem_Click(object sender, EventArgs e) 102 { 103 gradeNum = 150; 104 lblGrade.Text = "平民"; 105 timer1.Interval = gradeNum; 106 } 107 108 private void 高級ToolStripMenuItem_Click(object sender, EventArgs e) 109 { 110 gradeNum = 100; 111 lblGrade.Text = "高手"; 112 timer1.Interval = gradeNum; 113 } 114 115 private void 大神ToolStripMenuItem_Click(object sender, EventArgs e) 116 { 117 gradeNum = 50; 118 lblGrade.Text = "大神"; 119 timer1.Interval = gradeNum; 120 } 121 122 private void 膚色1ToolStripMenuItem_Click(object sender, EventArgs e) 123 { 124 this.BackgroundImage = Image.FromFile("1.jpg"); 125 pink = 1; 126 } 127 128 private void 膚色2ToolStripMenuItem_Click(object sender, EventArgs e) 129 { 130 this.BackgroundImage = Image.FromFile("2.jpg"); 131 pink = 2; 132 } 133 }
編程過程借鑒了網路大神的幫忙。
還有一個問題,希望看到的大神可以幫忙解決:原本希望把選擇級別菜單下的幾個級別都注冊成一個點擊事件,再通過switch判斷點擊的級別的text屬性,再作出相應操作,但是不知道在代碼中怎么得到被點擊的級別的對象,所以方法實現不了。
也就是說,我想實現的效果是把菜單中的幾個按鈕的點擊事件注冊成一個事件,但是在寫代碼的過程中,不知道怎么才能得到被點擊按鈕(Button clickedBtn=sender as Button 不對,但也不知道還有什么其他的方法)的對象,也就無法確定點擊的是哪個按鈕。
膚色的單元還沒有實現,有待優化。
游戲方法:點擊菜單欄中的選擇級別,選擇一個級別,按F2開始游戲,游戲過程中可以按F4暫停,繼續游戲,每吃到一個食物加10分,達到500分成功。