【C#】贪吃蛇小游戏
程序员文章站
2022-07-02 17:52:19
...
自学了几天C#,尝试写了一个贪吃蛇小游戏,比较详细地做了注释,实现:
(1)贪吃蛇基本功能
(2)长按加速
(3)计分板
XAML代码及界面
<Window x:Class="贪吃蛇.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:贪吃蛇"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="400" ResizeMode="NoResize" KeyDown="Window_KeyDown" KeyUp="Window_KeyUp" >
<Canvas x:Name="map" Margin="0" Background="White">
<Label x:Name="showScore" Canvas.Left="340" Canvas.Top="10" Height="27" Width="44" Background="#FFC7C7C7"/>
</Canvas>
</Window>
C#代码
using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
namespace 贪吃蛇
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
DispatcherTimer timer = new DispatcherTimer();//适用于UI界面的计时器
DispatcherTimer keytimer = new DispatcherTimer();//用于判定键盘长按
//界面300*400
const int SIDE = 10;//每个格子的边长
int row;//行数
int column;//列数
int score;//得分
Random rNumber = new Random();//随机数
Border food = new Border();//食物
Snake snake = new Snake();//生成一条蛇
public MainWindow()//初始化即主程序
{
InitializeComponent();//初始化窗体内容
//行列数生成
row = 300 / SIDE;
column = 400 / SIDE;
score = 0;//分数初始为0
//食物初始化
food.Width = food.Height = SIDE;//食物大小
food.CornerRadius = new CornerRadius(90);//通过边缘角度设置食物形状:圆形
food.Background = new RadialGradientBrush(Colors.Black, Colors.Black);//食物颜色:黑色
//生成第一颗食物
Canvas.SetLeft(food, rNumber.Next(0, column - 4) * SIDE);
Canvas.SetTop(food, rNumber.Next(0, row - 4) * SIDE);
map.Children.Add(food);
Canvas.SetZIndex(food,1);//设置食物所在层数
//计时器设置
timer.Interval = TimeSpan.FromMilliseconds(200);//设置计时器事件间隔,即界面刷新速度,直接设置
timer.Start();//计时器启动
timer.Tick += timer_Tick;
//蛇位置初始化
Canvas.SetLeft(snake[0], 2 * SIDE);Canvas.SetTop(snake[0], 0); map.Children.Add(snake[0]);
Canvas.SetZIndex(snake[0], 2);//设置蛇头所在层数
Canvas.SetLeft(snake[1], 1 * SIDE); Canvas.SetTop(snake[1], 0); map.Children.Add(snake[1]);
Canvas.SetLeft(snake[2], 0); Canvas.SetTop(snake[2], 0); map.Children.Add(snake[2]);//记得“map.Children.Add”把元素加入窗口
//得分板初始化
showScore.Content = score;
}
//新食物生成
public void NewFoodGenerate()
{
//删除已存在食物
map.Children.Remove(food);
//随机生成食物位置
Canvas.SetLeft(food, rNumber.Next(0, column - 4) * SIDE);
Canvas.SetTop(food, rNumber.Next(0, row - 4) * SIDE);
//将食物加入Canvas
map.Children.Add(food);
//本应加入程序防止食物生成在体内,但性能可能有限制
}
//计时器每一个计时周期内的时间处理程序
void timer_Tick(object sender, EventArgs e)
{
//蛇身自动移动
for (int n = snake.Count - 1; n >= 1; n--)
{
//吃食物增长长度
if ((Canvas.GetLeft(snake[0]) == Canvas.GetLeft(food)) && (Canvas.GetTop(snake[0]) == Canvas.GetTop(food)))
{
snake.Add();
map.Children.Add(snake[snake.Count - 1]);
NewFoodGenerate();
n = snake.Count - 1;
score++;
showScore.Content = score;//更改得分
}
Canvas.SetLeft(snake[n], Canvas.GetLeft(snake[n - 1]));
Canvas.SetTop(snake[n], Canvas.GetTop(snake[n - 1]));
}
//蛇头移动
switch (snake[0].Tag.ToString())
{
case "W":
Canvas.SetTop(snake[0], Canvas.GetTop(snake[0])-SIDE);
break;//待拓展出错处理程序
case "A":
Canvas.SetLeft(snake[0], Canvas.GetLeft(snake[0]) - SIDE);
break;
case "S":
Canvas.SetTop(snake[0], Canvas.GetTop(snake[0]) + SIDE);
break;
case "D":
Canvas.SetLeft(snake[0], Canvas.GetLeft(snake[0]) + SIDE);
break;
}
//最后再检测是否失败
if (Fail())
{
MessageBox.Show($"最终得分:{score}");
timer.Stop();
Close();
}
}
//失败鉴定程序
public bool Fail()
{
//碰壁
if(Canvas.GetLeft(snake[0])<0|| Canvas.GetLeft(snake[0])> (column - 4) * SIDE
|| Canvas.GetTop(snake[0])<0|| Canvas.GetTop(snake[0])> (row - 4) * SIDE)
{
return true;
}
//碰到自己
for(int n = 1; n < snake.Count; n++)
{
if(Canvas.GetLeft(snake[0])== Canvas.GetLeft(snake[n])&& Canvas.GetTop(snake[0])== Canvas.GetTop(snake[n]))
{
return true;
}
}
return false;
}
//获取键盘值,输入则得值
private void Window_KeyDown(object sender, KeyEventArgs e)
{
string key = e.Key.ToString();//一个事件间隔内只能输入一个键
if (key == "W" && snake[0].Tag.ToString() != "S")
{
snake[0].Tag = "W";
}
if (key == "A" && snake[0].Tag.ToString() != "D")
{
snake[0].Tag = "A";
}
if (key == "S" && snake[0].Tag.ToString() != "W")
{
snake[0].Tag = "S";
}
if (key == "D" && snake[0].Tag.ToString() != "A")
{
snake[0].Tag = "D";
}
keytimer.Interval = TimeSpan.FromMilliseconds(400);//400ms后开始加速
keytimer.IsEnabled=true;//计时器启动
keytimer.Tick += keytimer_Tick;//事件关联到事件处理程序上
}
//加速事件处理程序
void keytimer_Tick(object sender, EventArgs e)
{
timer.Interval = TimeSpan.FromMilliseconds(100);//加速
}
//结束加速
private void Window_KeyUp(object sender, KeyEventArgs e)
{
timer.Interval = TimeSpan.FromMilliseconds(200);//还原速度
keytimer.IsEnabled=false;
}
}
public class Snake : CollectionBase//继承链表类
{
const int SIDE = 10;//每个格子的边长
//生成蛇节点
public Border CreateSnakeNode()
{
Border snakeNode = new Border();
snakeNode.Width = snakeNode.Height = SIDE;
snakeNode.Background = new RadialGradientBrush(Colors.Red, Colors.Red);
return snakeNode;
}
//重载Add,贪吃蛇游戏只需自动增加相同的节点即可
public void Add()
{
List.Add(CreateSnakeNode());
}
//初始化的蛇有三个节点
public Snake()
{
Add();
Add();
Add();
this[0].Background = new RadialGradientBrush(Colors.White,Colors.Red);//标记蛇头
this[0].Tag = "D";//初始蛇向右走
}
//重载[]
public Border this[int nodeIndex]
{
get { return (Border)List[nodeIndex]; }
}
}
}
改进:可以做些界面联动,AI蛇
参考:
Hui110110 C#中的贪吃蛇
http://blog.csdn.net/hui110110/article/details/52431657
Yang-Fei [C#].NET中几种Timer的使用
https://www.cnblogs.com/yang-fei/p/6169089.html
绿意无痕 C# WPF Border控件总结
http://blog.csdn.net/lvyiwuhen/article/details/53212450
yongh701【C#】按钮长按
上一篇: Spring Boot 实现单点登录的第三种方案!
下一篇: 05-06 管理员管理页面搭建