没事的情况下,写了个C#的贪吃蛇,虽然有点丑,不过基本功能总算完成了,以后再慢慢改进吧。
首先是开始前的界面:
运行中的画面,蓝色代表蛇身,红色代表食物(确实有点丑)
菜单栏中增添了一些功能,在Open中有游戏开始于退出的功能,在Setting中有难度选择与背景音乐播放的功能,另外贪吃蛇还支持暂停与继续功能对应的键盘P(即按P键可以实现暂停功能,再按一次,可实现恢复),空格键有加速功能~
应该没特别大的BUG,还在完善中,有机会我再一步一步完善下,尽量使得其美观。
注意:
1、关于解决屏幕更新闪烁的问题,我新建了个NewPanel类,然后设置了其中的一些参数(网上找到的解决方法)
2、关于蛇的移动,我主要是改变移动方向来实现键盘的操作,也就是说蛇始终在保持自动移动。
部分代码(代码比较丑,请见谅):
NewPanel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace SnackGame
{
public class NewPanel:Panel
{
public NewPanel()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.UserPaint, true);
}
}
}
Node(相当于坐标):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SnackGame
{
public class Node
{
private int x;
private int y;
public int X { get { ;return x; } }
public int Y { get { ; return y; } }
public Node(int x, int y)
{
this.x = x;
this.y = y;
}
}
}
Snack类(游戏的核心部分):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
namespace SnackGame
{
public class Snack
{
public delegate void UpdateResult();
static int[] movex = { 0, 20, 0, -20 }, movey = { -20, 0, 20, 0 };
public LinkedList<Node> s;
public LinkedList<Node> food;
private List<Node> r;
private int Height;
private int Width;
private bool[,] issnack;
private bool[,] isfood;
private int sleeptime=0;
private Random ran;
public int LastDirect;
private bool iswork;
public UpdateResult Update;
public UpdateResult Wait;
public Thread SnackStart;
public bool Iswork { get { return iswork; } set { iswork = value; } }
public int SleepTime
{
get { return sleeptime; }
set
{
if (value > 0)
sleeptime = value;
}
}
public Snack(int Height, int Width)
{
s = new LinkedList<Node>();
food = new LinkedList<Node>();
r = new List<Node>();
ran = new Random();
this.Height = Height;
this.Width = Width;
InitSnack();
}
public Snack()
{
s = new LinkedList<Node>();
food = new LinkedList<Node>();
r = new List<Node>();
}
private void InitSnack()
{
s.AddFirst(new Node((ran.Next() % (Width / 20-1) + 1) * 20, (ran.Next() % (Height / 20-1) + 1) * 20));
issnack = new bool[Height, Width];
isfood = new bool[Height, Width];
LastDirect = ran.Next() % 4;
while (IsLost(s.First().X + movex[LastDirect], s.First().Y + movey[LastDirect]))
LastDirect = ran.Next() % 4;
iswork = false;
GetFoodPosition();
}
private void GetFoodPosition()
{
for (int i = 0; i < Height - 1; i += 20)
for (int j = 0; j < Width - 1; j += 20)
{
if (issnack[i, j])
continue;
r.Add(new Node(i, j));
}
while (true)
{
Node ita = r[ran.Next() % r.Count];
if (!issnack[ita.Y, ita.X])
{
isfood[ita.Y, ita.X] = true;
food.AddFirst(ita);
break;
}
}
}
public void Add(Node p)
{
s.AddFirst(p);
issnack[p.Y, p.X] = true;
}
public void Del()
{
issnack[s.Last().Y, s.Last().X] = false;
s.RemoveLast();
}
public void Move(int pos)
{
lock (this)
{
int x = s.First().X;
int y = s.First().Y;
Console.WriteLine("xxx " + x + " " + y);
if (IsLost(x + movex[pos], y + movey[pos]))
{
MessageBox.Show("You are Lost!");
iswork = false;
return;
}
Add(new Node(x + movex[pos], y + movey[pos]));
if (isfood[y + movey[pos], x + movex[pos]])
{
isfood[y + movey[pos], x + movex[pos]] = false;
food.Clear();
GetFoodPosition();
}
else
Del();
Update();
}
}
public void Work()
{
while (iswork)
{
Wait();
Move(LastDirect);
Thread.Sleep(200);
}
}
public bool IsLost(int x, int y)
{
if (x < 0 || y < 0 || y >= Width-20 || x >= Height-20 || issnack[y, x])
return true;
return false;
}
}
}
界面部分:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Media;
namespace SnackGame
{
public partial class MainWindow : Form
{
const int pos = 20;
Snack snack;
Random ran;
Thread ThreadSnackGame;
private System.Windows.Forms.Timer tm=new System.Windows.Forms.Timer();
public AutoResetEvent autoEvent=new AutoResetEvent(false);
private SoundPlayer soud;
private bool isStop;
private bool musicOn;
public MainWindow()
{
InitializeComponent();
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
panel_Map.Size = new Size(Height, Width);
snack = new Snack();
panel_Map.Invalidate();
ThreadSnackGame = new Thread(new ThreadStart(snack.Work));
ProgressBar.CheckForIllegalCrossThreadCalls = false;
tm.Interval = 1;
tm.Tick += new EventHandler(tm_Tick);
ran = new Random();
ChangePanelSize(401, 401);
musicOn = false;
soud = new SoundPlayer("BackgroundMusic.wav");
}
private void Wait()
{
autoEvent.WaitOne();
}
private void tm_Tick(object sender, EventArgs e)
{
autoEvent.Set();
}
private void ChangePanelSize(int Height, int Width)
{
panel_Map.Size = new Size(Height, Width);
}
private void InitSnackGame()
{
snack = new Snack(panel_Map.Height, panel_Map.Width);
ThreadSnackGame.Abort();
ThreadSnackGame = new Thread(new ThreadStart(snack.Work));
snack.Update = new Snack.UpdateResult(UpdatePanel);
snack.Wait = new Snack.UpdateResult(Wait);
ThreadSnackGame.IsBackground = true;
isStop = false;
}
private void UpdatePanel()
{
panel_Map.Invalidate();
}
private void SpeedUp()
{
snack.SleepTime /= 4;
Thread.Sleep(200);
snack.SleepTime *= 4;
}
private void Stop()
{
isStop = true;
tm.Stop();
}
private void Continue()
{
isStop = false;
tm.Start();
}
private void startToolStripMenuItem1_Click(object sender, EventArgs e)
{
tm.Start();
InitSnackGame();
snack.Iswork = true;
ThreadSnackGame.Start();
}
private void panel_Map_Paint(object sender, PaintEventArgs e)
{
int num = 0;
Graphics g = e.Graphics;
Pen pen = new Pen(Color.Black, 1);
g.DrawLine(pen, 0, 0, 500, 0);
Point[] point_st = new Point[panel_Map.Width / 20 + panel_Map.Height / 20 + 5];
Point[] point_ed = new Point[panel_Map.Height / 20 + panel_Map.Width / 20 + 5];
for (int i = 0; i < panel_Map.Width; i += pos)
{
point_st[num] = new Point(i, 0);
point_ed[num++] = new Point(i, panel_Map.Height);
}
for (int i = 0; i < panel_Map.Height; i += pos)
{
point_st[num] = new Point(0, i);
point_ed[num++] = new Point(panel_Map.Width, i);
}
for (int i = 0; i < point_st.Length; i++)
g.DrawLine(pen, point_st[i], point_ed[i]);
foreach (Node node in snack.s)
g.FillEllipse(Brushes.Blue, node.X, node.Y, 20, 20);
foreach (Node node in snack.food)
g.FillEllipse(Brushes.Red, node.X, node.Y, 20, 20);
}
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
int nowdirect = -1;
if (e.KeyCode == Keys.Space)
{
SpeedUp();
return;
}
if (e.KeyCode == Keys.P)
{
if (isStop)
{
Continue();
return;
}
Stop();
return;
}
switch (e.KeyCode)
{
case Keys.Up: nowdirect = 0; break;
case Keys.Right: nowdirect = 1; break;
case Keys.Down: nowdirect = 2; break;
case Keys.Left: nowdirect = 3; break;
default: return;
}
if (snack.LastDirect != nowdirect && Math.Abs(nowdirect - snack.LastDirect) != 2)
{
panel_Map.Invalidate();
snack.LastDirect = nowdirect;
}
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Close();
}
private void easyToolStripMenuItem_Click(object sender, EventArgs e)
{
ChangePanelSize(401, 401);
midToolStripMenuItem.Checked = false;
hardToolStripMenuItem.Checked = false;
}
private void midToolStripMenuItem_Click(object sender, EventArgs e)
{
ChangePanelSize(501, 501);
easyToolStripMenuItem.Checked = false;
hardToolStripMenuItem.Checked = false;
}
private void hardToolStripMenuItem_Click(object sender, EventArgs e)
{
ChangePanelSize(601, 601);
easyToolStripMenuItem.Checked = false;
midToolStripMenuItem.Checked = false;
}
private void MusicToolStripMenuItem_Click(object sender, EventArgs e)
{
if (musicOn == false)
{
musicOn = true;
soud.PlayLooping();
return;
}
soud.Stop();
musicOn = false;
}
}
}