一、项目结构:
-
主窗口(Window)包含一个Frame,Frame加载一个Page(游戏页面)。
包括以下文件:
MainWindow.xaml / MainWindow.xaml.cs
GamePage.xaml / GamePage.xaml.cs
App.xaml / App.xaml.cs (默认)
二、游戏逻辑
- 使用Canvas绘制网格,蛇移动,食物生成,方向控制(键盘),得分显示,游戏结束处理。
游戏循环使用DispatcherTimer。
方向控制:键盘事件。
蛇身体用List<Point>。
食物随机生成。吃到蛇身自动变长、累计得分;
碰撞检测:撞墙或自身。
游戏结束显示消息。
三、数据结构
1、蛇身列表(单链表)
private List<Point> snake = new List<Point>();
2、方向枚举
private enum Direction
{
Up, Down, Left, Right
}
3、键盘控制
方向控制(键盘)
4、游戏整体执行流程
- StartNewGame():
窗口配置、界面、绘制地图、初始化3节蛇、生成首个食物; - GameTimer_Tick():
定时器循环;刷新分数、监听按键、修改方向、判断是否吃到食物,更新列表,碰撞检测; - DrawGame()
绘制网格,绘制食物, 绘制蛇(绿色方块,头部亮绿) - Page_KeyDown
键盘事件
四、程序
1、MainWindow.xaml
<Window x:Class="SnakeGame.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="贪吃蛇" Height="480" Width="640"
WindowStartupLocation="CenterScreen" ResizeMode="NoResize">
<Grid>
<Frame x:Name="MainFrame" NavigationUIVisibility="Hidden" />
</Grid>
</Window>
2、MainWindow.xaml.cs
using System.Windows;
namespace SnakeGame
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// 启动时导航到游戏页面
MainFrame.Navigate(new GamePage());
}
}
}
3、GamePage.xaml
<Page x:Class="SnakeGame.GamePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="GamePage" Loaded="Page_Loaded"
KeyDown="Page_KeyDown" Focusable="True"
Background="White">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- 状态栏 -->
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="5" Background="LightGray">
<TextBlock Text="得分:" FontSize="16" FontWeight="Bold" Margin="5" />
<TextBlock x:Name="ScoreText" Text="0" FontSize="16" FontWeight="Bold" Margin="5,5,20,5" />
<TextBlock Text="方向:" FontSize="16" Margin="5" />
<TextBlock x:Name="DirectionText" Text="→" FontSize="16" Margin="5" />
<Button x:Name="RestartButton" Content="重新开始" Click="RestartButton_Click" Margin="20,0,0,0" Width="80" />
</StackPanel>
<!-- 游戏画布 -->
<Canvas x:Name="GameCanvas" Grid.Row="1" Background="Black" />
</Grid>
</Page>
4、GamePage.xaml.cs
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace SnakeGame
{
public partial class GamePage : Page
{
// 游戏配置
private const int GridSize = 20; // 每个格子大小(像素)
private const int Rows = 20; // 网格行数
private const int Cols = 25; // 网格列数
// 蛇的数据
private List<Point> snake = new List<Point>();
private Point food;
private Direction currentDirection = Direction.Right;
private Direction nextDirection = Direction.Right;
// 游戏状态
private bool isGameOver = false;
private int score = 0;
// 定时器
private DispatcherTimer gameTimer;
// 随机数
private Random rand = new Random();
public GamePage()
{
InitializeComponent();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
// 初始化游戏
StartNewGame();
// 聚焦页面以捕获键盘
this.Focus();
}
private void StartNewGame()
{
// 重置数据
snake.Clear();
// 初始蛇:长度为3,水平放置
snake.Add(new Point(5, 10)); // 头
snake.Add(new Point(4, 10));
snake.Add(new Point(3, 10));
currentDirection = Direction.Right;
nextDirection = Direction.Right;
score = 0;
isGameOver = false;
ScoreText.Text = "0";
DirectionText.Text = "→";
GenerateFood();
// 停止旧定时器
if (gameTimer != null)
{
gameTimer.Stop();
}
// 创建新定时器(间隔200ms)
gameTimer = new DispatcherTimer();
gameTimer.Interval = TimeSpan.FromMilliseconds(300);
gameTimer.Tick += GameTimer_Tick;
gameTimer.Start();
DrawGame();
this.Focus(); // 重新聚焦
}
private void GenerateFood()
{
// 确保食物不在蛇身上
while (true)
{
int x = rand.Next(0, Cols);
int y = rand.Next(0, Rows);
Point p = new Point(x, y);
if (!snake.Contains(p))
{
food = p;
break;
}
}
}
private void GameTimer_Tick(object sender, EventArgs e)
{
if (isGameOver) return;
// 应用有效方向
currentDirection = nextDirection;
// 计算新蛇头位置
Point head = snake[0];
Point newHead = head;
switch (currentDirection)
{
case Direction.Up: newHead = new Point(head.X, head.Y - 1); break;
case Direction.Down: newHead = new Point(head.X, head.Y + 1); break;
case Direction.Left: newHead = new Point(head.X - 1, head.Y); break;
case Direction.Right: newHead = new Point(head.X + 1, head.Y); break;
}
// 判断是否吃到食物
bool ateFood = (newHead.X == food.X && newHead.Y == food.Y);
// 移动蛇:先插入新头
snake.Insert(0, newHead);
if (!ateFood)
{
// 没吃到则移除尾部
snake.RemoveAt(snake.Count - 1);
}
// 碰撞检测
// 1. 撞墙
if (newHead.X < 0 || newHead.X >= Cols || newHead.Y < 0 || newHead.Y >= Rows)
{
GameOver();
return;
}
// 2. 撞自身(新头不能与除头部以外的部分重合,注意如果没吃到食物,尾部已移除,但头部新位置可能与旧身体重合)
// 由于我们插入了新头,检查新头是否与身体其他部分重合(索引从1开始)
for (int i = 1; i < snake.Count; i++)
{
if (snake[i].X == newHead.X && snake[i].Y == newHead.Y)
{
GameOver();
return;
}
}
// 处理吃到食物
if (ateFood)
{
score++;
ScoreText.Text = score.ToString();
GenerateFood(); // 生成新食物
}
// 更新方向显示
UpdateDirectionText();
// 重绘画布
DrawGame();
}
private void GameOver()
{
isGameOver = true;
gameTimer.Stop();
MessageBox.Show($"游戏结束!\n得分:{score}", "贪吃蛇", MessageBoxButton.OK, MessageBoxImage.Information);
// 点击确定后重新开始(可选)
StartNewGame();
}
private void DrawGame()
{
GameCanvas.Children.Clear();
// 绘制网格(可选,灰色细线)
for (int i = 0; i <= Cols; i++)
{
Line line = new Line
{
X1 = i * GridSize,
Y1 = 0,
X2 = i * GridSize,
Y2 = Rows * GridSize,
Stroke = Brushes.DarkGray,
StrokeThickness = 0.5
};
GameCanvas.Children.Add(line);
}
for (int i = 0; i <= Rows; i++)
{
Line line = new Line
{
X1 = 0,
Y1 = i * GridSize,
X2 = Cols * GridSize,
Y2 = i * GridSize,
Stroke = Brushes.DarkGray,
StrokeThickness = 0.5
};
GameCanvas.Children.Add(line);
}
// 绘制食物(红色圆球)
Ellipse foodEllipse = new Ellipse
{
Width = GridSize - 2,
Height = GridSize - 2,
Fill = Brushes.Red
};
Canvas.SetLeft(foodEllipse, food.X * GridSize + 1);
Canvas.SetTop(foodEllipse, food.Y * GridSize + 1);
GameCanvas.Children.Add(foodEllipse);
// 绘制蛇(绿色方块,头部亮绿)
for (int i = 0; i < snake.Count; i++)
{
Rectangle rect = new Rectangle
{
Width = GridSize - 1,
Height = GridSize - 1,
Fill = (i == 0) ? Brushes.LimeGreen : Brushes.Green
};
Canvas.SetLeft(rect, snake[i].X * GridSize + 0.5);
Canvas.SetTop(rect, snake[i].Y * GridSize + 0.5);
GameCanvas.Children.Add(rect);
}
}
private void UpdateDirectionText()
{
switch (currentDirection)
{
case Direction.Up: DirectionText.Text = "↑"; break;
case Direction.Down: DirectionText.Text = "↓"; break;
case Direction.Left: DirectionText.Text = "←"; break;
case Direction.Right: DirectionText.Text = "→"; break;
}
}
// 键盘事件
private void Page_KeyDown(object sender, KeyEventArgs e)
{
if (isGameOver) return;
// 防止反向
switch (e.Key)
{
case Key.W:
case Key.Up:
if (currentDirection != Direction.Down)
nextDirection = Direction.Up;
break;
case Key.S:
case Key.Down:
if (currentDirection != Direction.Up)
nextDirection = Direction.Down;
break;
case Key.A:
case Key.Left:
if (currentDirection != Direction.Right)
nextDirection = Direction.Left;
break;
case Key.D:
case Key.Right:
if (currentDirection != Direction.Left)
nextDirection = Direction.Right;
break;
case Key.R:
StartNewGame();
break;
}
}
// 重新开始按钮
private void RestartButton_Click(object sender, RoutedEventArgs e)
{
StartNewGame();
}
// 方向枚举
private enum Direction
{
Up, Down, Left, Right
}
}
}

运行界面
