程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> C#貪吃蛇源碼

C#貪吃蛇源碼

編輯:關於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; //因為Point是Struct,是值類型,所以要賦值過來

}

 

/// <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 = "N";

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;

}

}

}

}

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved