实现简单的俄罗斯方块小游戏
2018-03-13 10:06
337 查看
[b] 一时兴起所以找了些资料查询了整个游戏的实现过程,里面还有些问题,所以跟大家分享一下代码,希望能给我指点1、2.[/b]实现原理: 其实这个游戏实现的原理非常简单,就是不断的在窗口上画砖块,清砖块。注意这里的清砖块其实就是用背景颜色把某个区域给填充而已,本质还是绘制。 具体的设计思路如下:
通过timer定时执行某个操作来改变活动砖块的坐标,并更新窗口的绘图;
通过键盘事件改变活动砖块的坐标以及形状,并更新窗口的绘图;
每次更新都要检测是否有填满的行,或者是否游戏结束等等;
针对消除的行数更新游戏得分,等级等信息
直接上代码:
1、解决方案结构
2、砖块类
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tetris.Logic
{
/// <summary>
/// 砖块实体类,用来描述一块砖块
/// </summary>
public class Brick
{
private Point[] m_Points;//砖块坐标数组
private int m_X;//砖块中心点在画布上的X坐标
private int m_Y;//砖块中心点在画布上的Y坐标
private Color m_Color;//砖块颜色
private Color m_BgColor;//背景颜色
private int m_BlockSize;//单元格像素
private SolidBrush m_Brush;//画笔
/// <summary>
/// 构造砖块
/// </summary>
/// <param name="sa"> 砖块样式</param>
/// <param name="color">砖块颜色</param>
/// <param name="bgColor">砖块背景</param>
/// <param name="size">砖块单位大小</param>
public Brick(Point[] sa, Color color, Color bgColor, int size)
{
m_Color = color;
m_BgColor = bgColor;
m_BlockSize = size;
m_Points = sa;
m_Brush=new SolidBrush(m_Color);
m_X = 2;
m_Y = 2;
}
/// <summary>
/// 获取砖块样式坐标
/// </summary>
public Point[] Points { get { return m_Points; } }
public int X
{
get { return m_X; }
set { m_X = value; }
}
public int Y
{
get { return m_Y; }
set { m_Y = value; }
}
public Color Color { get { return m_BgColor; } }
/// <summary>
/// 逆时针旋转
/// </summary>
public void ContraRotate()
{
int temp;
for (int i = 0; i < m_Points.Length; i++)
{
temp = m_Points[i].X;
m_Points[i].X = m_Points[i].Y;
m_Points[i].Y = temp;
}
}
/// <summary>
/// 顺时针旋转
/// </summary>
public void DeasilRotate()
{
int temp;
for (int i = 0; i < m_Points.Length; i++)
{
temp = m_Points[i].X;
m_Points[i].X = -m_Points[i].Y;
m_Points[i].Y = temp;
}
}
//把每个点放大为一个矩形区域,然后以砖块颜色填充这个区域。这个函数需要注意的地方是我们在使用Graphics对象的时候要先lock,这样防止同一时间其他地方也在使用这个对象,从而引发异常。
public void Paint(Graphics gp)
{
foreach (var p in m_Points)
{
lock (gp)
{
try
{
gp.FillRectangle(m_Brush, PointToRect(p));
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}
/// <summary>
/// 擦除砖块
/// </summary>
/// <param name="gp"></param>
public void Erase(Graphics gp)
{
using (SolidBrush sb = new SolidBrush(m_BgColor))
{
foreach (Point p in m_Points)
{
lock (gp)
{
try
{
gp.FillRectangle(sb, PointToRect(p));
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}
}
//单点放大成矩形
private Rectangle PointToRect(Point p)
{
Rectangle r=new Rectangle((m_X+p.X)*m_BlockSize+1,(m_Y+p.Y)*m_BlockSize+1,m_BlockSize-2,m_BlockSize-2);
return r;
}
}
}
3、砖块样式信息模板类
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tetris.Logic
{
/// <summary>
/// 砖块样式信息模板
/// </summary>
class BrickTemplate
{
private string m_Code;//砖块样式编码
private Color m_Color;//砖块颜色
/// <summary>
/// 构造砖块信息
/// </summary>
/// <param name="code"></param>
/// <param name="color"></param>
public BrickTemplate(string code, Color color)
{
if (code == null || code.Length != 25 || color == Color.Empty)
{
throw new FormatException("砖块样式信息错误!");
}
m_Code = code;
m_Color = color;
}
public string Code { get { return m_Code; } }
public Color Color { get { return m_Color; } }
}
}
4、砖块列表信息类
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace T
4000
etris.Logic
{
class TemplateArray
{
//砖块模板列表
private ArrayList m_List=new ArrayList();
//砖块模板数
public int Count { get { return m_List.Count; } }
//获取砖块模板
public BrickTemplate this[int index] { get { return (BrickTemplate) m_List[index]; } }
/// <summary>
/// 获取新砖块模板
/// </summary>
/// <param name="code"></param>
/// <param name="color"></param>
public void Add(string code, Color color)
{
m_List.Add(new BrickTemplate(code, color));
}
/// <summary>
/// 清空砖块模板
/// </summary>
public void Clear()
{
m_List.Clear();
}
}
}
5、砖块产生机
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tetris.Logic
{
/// <summary>
/// 砖块生产机
/// </summary>
class BrickFactory
{
private TemplateArray m_BrickArray;//砖块样式模板列表
private Color m_BgColor;//砖块背景色
private int m_RectPix;//单元格大小
public BrickFactory(TemplateArray info, Color bgColor, int rectPix)
{
m_BrickArray = info;
m_RectPix = rectPix;
m_BgColor = bgColor;
}
//随机获得下一个砖块
public Brick CreateBrick()
{
Random rd=new Random();
int index = rd.Next(m_BrickArray.Count);
string code = m_BrickArray[index].Code;
//根据编码设定相应的有效坐标
List<Point> list=new List<Point>();
for (int i = 0; i < code.Length; i++)
{
if (code[i] == '1')
{
Point p=new Point(i%5,i/5);//根据下表计算坐标
p.Offset(-2,-2);//坐标平移,使坐标(0,0)在中心
list.Add(p);
}
}
//生成砖块
Brick brick=new Brick(list.ToArray(),m_BrickArray[index].Color,m_BgColor,m_RectPix);
//随机旋转90度
if(rd.Next(2)==1)
brick.ContraRotate();
return brick;
}
}
}
二、游戏规则的实现
如果你能够成功实现前面所介绍的功能,你已经完成50%了,而且这50%含金量很高,足够你开发一些其他的游戏了,比如简单点的贪吃蛇,或者经典的吃豆子等等小游戏了。不过我们还是要先完成我们俄罗斯方块的游戏再说,不能一知半解的就结束。 我们的砖块虽然已经有了灵魂,但是还是不收束缚的灵魂,所以我们要做的就是控制它,不能让它无限制的移动,该落下的就落下,该停止的就停止。
实现类:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
using System.Windows.Forms.Layout;
namespace Tetris.Logic
{
class GamePalette
{
private readonly Color[] COLORS = new Color[] { Color.White, Color.Tomato, Color.Thistle, Color.Turquoise };//闪烁颜色
private readonly int[] TIME_SPANS = new int[] { 700, 600, 550, 500, 450, 400, 350, 300, 250, 200 };//等级对应速度
private readonly int[] SCORE_SPANS = new int[] { 100, 300, 500, 1000, 1500 };//分值列表
private BrickFactory m_BrickFactory;//砖块产生机
private int m_Width = 15;//画板宽
private int m_Height = 25;//画板高
private Color[,] m_CoorArray;//固定砖块颜色数组
private Color m_BgColor;
private Color m_GridColor;
private int m_Size;//单元格像素
private int m_Level = 0;
private int m_Score = 0;
private bool m_GameOver = false;
private bool m_ShowGrid = false;
private bool m_Pause = false;
private bool m_Ready = false;
private Graphics m_MainPalette;//主画布
private Graphics m_NextPalette;
private Brick m_RunBrick;
private Brick m_NextBrick;//下一个砖块
private System.Timers.Timer m_TimerBrick;//定时器,用来更新砖块
public GamePalette(int width, int height, TemplateArray info, int size, Color bgColor, Graphics gpPalette,
Graphics gpNext, int level, Color gridColor, bool showGrid)
{
m_Width = width;
m_Height = height;
m_CoorArray = new Color[width, height];
m_BgColor = bgColor;
m_MainPalette = gpPalette;
m_NextPalette = gpNext;
m_Size = size;
m_Level = level;
m_GridColor = gridColor;
m_ShowGrid = showGrid;
m_BrickFactory = new BrickFactory(info, bgColor, size);
InitRandomBrick();
}
public bool IsGameOver { get { return m_GameOver; } }
public int Level { get { return m_Level; } }
public int Score { get { return m_Score; } }
public bool MoveDown()
{
if (m_RunBrick == null)
return true;
int xPos = m_RunBrick.X;
int yPos = m_RunBrick.Y + 1;
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
//下一个位置超过画板的高度,不能移动
if (yPos + m_RunBrick.Points[i].Y >= m_Height)
return false;
//下一个位置已经有砖块,不能移动
if (!m_CoorArray[xPos + m_RunBrick.Points[i].X, yPos + m_RunBrick.Points[i].Y].IsEmpty)
return false;
}
m_RunBrick.Erase(m_MainPalette);//清楚当前位置的砖块图形
m_RunBrick.Y++;
m_RunBrick.Paint(m_MainPalette);//重新绘制砖块图形到画布
return true;
}
/// <summary>
/// 快速向下
/// </summary>
public void DropDown()
{
m_TimerBrick.Stop();//暂停定时器
while (MoveDown()) ;//循环调用向下
m_TimerBrick.Start();//开始定时器
}
/// <summary>
/// 左移
/// </summary>
/// <returns></returns>
public bool MoveLeft()
{
if (m_RunBrick == null)
return true;
int xPos = m_RunBrick.X - 1;
int yPos = m_RunBrick.Y;
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
if (yPos + m_RunBrick.Points[i].Y >= m_Height - 1)
return false;
if (xPos + m_RunBrick.Points[i].X <0)
return false;
if (!m_CoorArray[xPos + m_RunBrick.Points[i].X, yPos + m_RunBrick.Points[i].Y].IsEmpty)
return false;
}
m_RunBrick.Erase(m_MainPalette);
m_RunBrick.X--;
m_RunBrick.Paint(m_MainPalette);
return true;
}
/// <summary>
/// 右移
/// </summary>
/// <returns></returns>
public bool MoveRight()
{
if (m_RunBrick == null)
return true;
int xPos = m_RunBrick.X + 1;
int yPos = m_RunBrick.Y;
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
if (yPos + m_RunBrick.Points[i].Y >= m_Height - 1)
return false;
if (xPos + m_RunBrick.Points[i].X >= m_Width)
return false;
if (!m_CoorArray[xPos + m_RunBrick.Points[i].X, yPos + m_RunBrick.Points[i].Y].IsEmpty)
return false;
}
m_RunBrick.Erase(m_MainPalette);
m_RunBrick.X++;
m_RunBrick.Paint(m_MainPalette);
return true;
}
/// <summary>
/// 顺时针旋转
/// </summary>
/// <returns></returns>
public bool DeasilRotate()
{
if (m_RunBrick == null)
return true;
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
int x = m_RunBrick.X - m_RunBrick.Points[i].X;
int y = m_RunBrick.Y + m_RunBrick.Points[i].X;
if (x < 0 || x >= m_Width || y < 0 || y >= m_Height || !m_CoorArray[x, y].IsEmpty)
return false;
}
m_RunBrick.Erase(m_MainPalette);
m_RunBrick.DeasilRotate();
m_RunBrick.Paint(m_MainPalette);
return true;
}
/// <summary>
/// 逆时针旋转
/// </summary>
/// <returns></returns>
public bool ContraRotate()
{
if (m_RunBrick == null)
return true;
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
int x = m_RunBrick.X + m_RunBrick.Points[i].X;
int y = m_RunBrick.Y - m_RunBrick.Points[i].X;
if (x < 0 || x >= m_Width || y < 0 || y >= m_Height || !m_CoorArray[x, y].IsEmpty)
return false;
}
m_RunBrick.Erase(m_MainPalette);
m_RunBrick.DeasilRotate();
m_RunBrick.Paint(m_MainPalette);
return true;
}
public void PaintPalette(Graphics gp)
{
lock (gp)
{
gp.Clear(m_BgColor);
}
if (m_ShowGrid)
PaintGridLine(gp);
PaintBricks(gp);
if (m_RunBrick != null)
m_RunBrick.Paint(gp);
}
/// <summary>
/// 画已有的砖块
/// </summary>
/// <param name="gp"></param>
private void PaintBricks(Graphics gp)
{
lock (gp)
{
for (int row = 0; row < m_Height; row++)
{
for (int column = 0; column < m_Width; column++)
{
try
{
Color c = m_CoorArray[column, row];
if (c.IsEmpty)
c = m_BgColor;
//c = Color.DarkOrange;
using (SolidBrush sb = new SolidBrush(c))
{
gp.FillRectangle(sb, column * m_Size + 1, row * m_Size + 1, m_Size - 2, m_Size - 2);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}
}
/// <summary>
/// 画网格线
/// </summary>
/// <param name="gp"></param>
private void PaintGridLine(Graphics gp)
{
try
{
lock (gp)
{
using (Pen p = new Pen(m_GridColor, 1))
{
//画网格纵线
for (int column = 1; column < m_Width; column++)
{
gp.DrawLine(p, column * m_Size - 1, 0, column * m_Size - 1, m_Size * m_Height);
}
//画网格横线
for (int row = 1; row < m_Height; row++)
{
gp.DrawLine(p, 0, row * m_Size, m_Width * m_Size, m_Size * row);
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
/// <summary>
/// 重画下一个方块
/// </summary>
/// <param name="gp"></param>
public void PaintNext(Graphics gp)
{
lock (gp)
ca01
{
gp.Clear(m_BgColor);
}
if (m_NextBrick != null)
m_NextBrick.Paint(gp);
}
public void Start()
{
m_Ready = true;
m_ShowGrid = true;
m_RunBrick = m_BrickFactory.CreateBrick();
m_RunBrick.X = m_Width / 2;
int y = 0;
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
if (m_RunBrick.Points[i].Y < y)
y = m_RunBrick.Points[i].Y;
}
m_RunBrick.Y = -y;
PaintPalette(m_MainPalette);
Thread.Sleep(20);
m_NextBrick = m_BrickFactory.CreateBrick();
PaintNext(m_NextPalette);
//设定定时器
m_TimerBrick = new System.Timers.Timer(TIME_SPANS[m_Level]);
m_TimerBrick.Elapsed += new System.Timers.ElapsedEventHandler(OnBrickTimedEvent);
m_TimerBrick.AutoReset = true;
m_TimerBrick.Start();
}
public void Close()
{
if (m_TimerBrick != null)
{
m_TimerBrick.Close();
m_TimerBrick.Dispose();
}
m_NextPalette.Dispose();
m_MainPalette.Dispose();
}
public void Pause()
{
m_Pause = true;
m_Ready = false;
}
public void Resume()
{
m_Pause = false;
m_Ready = true;
}
private void OnBrickTimedEvent(object source, ElapsedEventArgs e)
{
if (m_Pause || m_GameOver)
{
if (m_GameOver)
{
PaintGameOver();
return;
}
}
if (m_Ready)
{
if (!MoveDown())
CheckAndOverBrick();
}
}
private void CheckAndOverBrick()
{
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
m_CoorArray[m_RunBrick.X + m_RunBrick.Points[i].X, m_RunBrick.Y + m_RunBrick.Points[i].Y] =
m_RunBrick.Color;
}
CheckAndDelFullRow();//检查并消除横行
//设定活动砖块
m_RunBrick = m_NextBrick;
m_RunBrick.X = m_Width / 2;
int y = 0;
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
if (m_RunBrick.Points[i].Y < y)
y = m_RunBrick.Points[i].Y;
}
m_RunBrick.Y = -y;
//检查游戏结束
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
if (!m_CoorArray[m_RunBrick.X + m_RunBrick.Points[i].X, m_RunBrick.Y + m_RunBrick.Points[i].Y].IsEmpty)
{
m_RunBrick = null;
//闪烁效果
m_TimerBrick.Stop();
for (int row = m_Height - 1; row >= 0; row--)
{
for (int column = 0; column < m_Width; column++)
{
m_CoorArray[column, row] = m_BgColor;
}
PaintBricks(m_MainPalette);
System.Threading.Thread.Sleep(50);
}
//游戏结束
m_GameOver = true;
m_Ready = false;
m_TimerBrick.Start();
return;
}
}
m_RunBrick.Paint(m_MainPalette);
//获取新的砖块
m_NextBrick = m_BrickFactory.CreateBrick();
PaintNext(m_NextPalette);
}
private void CheckAndDelFullRow()
{
int fullRowCount = 0;//满行数
int upRow = m_RunBrick.Y - 2;
int downRow = m_RunBrick.Y + 2;
if (upRow < 0)
upRow = 0;
if (downRow >= m_Height)
downRow = m_Height - 1;
for (int row = upRow; row <= downRow; row++)
{
bool isFull = true;
for (int colmn = 0; colmn < m_Width; colmn++)
{
if (m_CoorArray[colmn, row].IsEmpty)
{
isFull = false;
break;
}
}
if (isFull)
{
fullRowCount++;
m_TimerBrick.Stop();
for (int n = 0; n < COLORS.Length; n++)
{
for (int column = 0; column < m_Width; column++)
{
m_CoorArray[column, row] = COLORS
;
}
PaintBricks(m_MainPalette);
System.Threading.Thread.Sleep(50);
}
for (int rowIndex = row; rowIndex > 0; rowIndex--)
for (int column = 0; column < m_Width; column++)
m_CoorArray[column, rowIndex] = m_CoorArray[column, rowIndex -1];
PaintBricks(m_MainPalette);
m_TimerBrick.Start();
}
}
if (fullRowCount > 0)
{
int currenScore = SCORE_SPANS[fullRowCount - 1];
int temScore = m_Score;//游戏得分
m_Score += currenScore;//追加得分
if (m_Score >= 10000 && m_Score.ToString()[0] != temScore.ToString()[0])
{
m_Level = (m_Level + 1) % TIME_SPANS.Length;
m_TimerBrick.Interval = TIME_SPANS[m_Level];
}
}
}
private void PaintGameOver()
{
StringFormat drawFormat = new StringFormat();
Font font = new Font("Arial BLACK", 30f);
drawFormat.Alignment = StringAlignment.Center;
for (int j = 0; j < COLORS.Length; j++)
{
lock (m_MainPalette)
{
try
{
m_MainPalette.DrawString("GAME OVER", font, new SolidBrush(COLORS[j]), new RectangleF(0, m_Height * m_Size / 2 - 100, m_Width * m_Size, 100), drawFormat);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
Thread.Sleep(100);
}
}
private void InitRandomBrick()
{
if (m_Level > 0)
{
Random r = new Random();
for (int row = 0; row < m_Level; row++)
{
for (int column = 0; column < m_Width * 2 / 3; column++)
{
int rc = r.Next(m_Width);
m_CoorArray[rc, m_Height - row - 1] = COLORS[r.Next(COLORS.Length)];
}
}
}
}
}
}
Form1代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
using Tetris.Logic;
namespace Tetris
{
public partial class Form1 : Form
{
private GamePalette m_Gamepalette;//游戏画板
public Form1()
{
InitializeComponent();
}
private void pbMainPalette_Paint(object sender, PaintEventArgs e)
{
if (m_Gamepalette != null)
m_Gamepalette.PaintPalette(e.Graphics);
}
private void pbNextPalette_Paint(object sender, PaintEventArgs e)
{
if (m_Gamepalette != null)
m_Gamepalette.PaintNext(e.Graphics);
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F2)
{
if (m_Gamepalette != null)
{
m_Gamepalette.Close();
m_Gamepalette = null;
}
TemplateArray array = new TemplateArray();
array.Add("0000001000011100000000000", Color.FromArgb(-128));
array.Add("0000000000111100000000000", Color.FromArgb(-65536));
array.Add("0000000110011000000000000", Color.FromArgb(-16711936));
array.Add("0000000100011100000000000", Color.FromArgb(-4144960));
array.Add("0000000100001100010000000", Color.FromArgb(-16776961));
array.Add("0000000000001110010000000", Color.FromArgb(-65281));
array.Add("0000000000001100011000000", Color.FromArgb(-65281));
//开始游戏
m_Gamepalette = new GamePalette(15, 25, array, 20, Color.Black, pbMainPalette.CreateGraphics(), pbNextPalette.CreateGraphics(), 0, Color.Blue, false);
m_Gamepalette.Start();
}
else
{
if (m_Gamepalette == null || m_Gamepalette.IsGameOver)
return;
if (e.KeyCode == Keys.F3)
{
if (m_Gamepalette.IsGameOver)
m_Gamepalette.Pause();
else
m_Gamepalette.Resume();
}
else if (e.KeyCode == Keys.Left)
{
m_Gamepalette.MoveLeft();
}
else if (e.KeyCode == Keys.Right)
{
m_Gamepalette.MoveRight();
}
else if (e.KeyCode == Keys.Down)
{
m_Gamepalette.MoveDown();
}
else if (e.KeyCode == Keys.Up)
{
m_Gamepalette.ContraRotate();
}
else if (e.KeyCode == Keys.Space)
{
m_Gamepalette.DropDown();
}
}
}
}
}
运行简单效果图:
遇到的问题:就是满行的时候,闪烁效果完后,所有砖块的颜色都变成了背景色,但是实际上的砖块还是存在画布上并没有消失,希望哪位大神指导一下是什么原因造成的。
以上代码均可直接使用运行(为了方便大家查看效果和BUG
);
通过timer定时执行某个操作来改变活动砖块的坐标,并更新窗口的绘图;
通过键盘事件改变活动砖块的坐标以及形状,并更新窗口的绘图;
每次更新都要检测是否有填满的行,或者是否游戏结束等等;
针对消除的行数更新游戏得分,等级等信息
直接上代码:
1、解决方案结构
2、砖块类
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tetris.Logic
{
/// <summary>
/// 砖块实体类,用来描述一块砖块
/// </summary>
public class Brick
{
private Point[] m_Points;//砖块坐标数组
private int m_X;//砖块中心点在画布上的X坐标
private int m_Y;//砖块中心点在画布上的Y坐标
private Color m_Color;//砖块颜色
private Color m_BgColor;//背景颜色
private int m_BlockSize;//单元格像素
private SolidBrush m_Brush;//画笔
/// <summary>
/// 构造砖块
/// </summary>
/// <param name="sa"> 砖块样式</param>
/// <param name="color">砖块颜色</param>
/// <param name="bgColor">砖块背景</param>
/// <param name="size">砖块单位大小</param>
public Brick(Point[] sa, Color color, Color bgColor, int size)
{
m_Color = color;
m_BgColor = bgColor;
m_BlockSize = size;
m_Points = sa;
m_Brush=new SolidBrush(m_Color);
m_X = 2;
m_Y = 2;
}
/// <summary>
/// 获取砖块样式坐标
/// </summary>
public Point[] Points { get { return m_Points; } }
public int X
{
get { return m_X; }
set { m_X = value; }
}
public int Y
{
get { return m_Y; }
set { m_Y = value; }
}
public Color Color { get { return m_BgColor; } }
/// <summary>
/// 逆时针旋转
/// </summary>
public void ContraRotate()
{
int temp;
for (int i = 0; i < m_Points.Length; i++)
{
temp = m_Points[i].X;
m_Points[i].X = m_Points[i].Y;
m_Points[i].Y = temp;
}
}
/// <summary>
/// 顺时针旋转
/// </summary>
public void DeasilRotate()
{
int temp;
for (int i = 0; i < m_Points.Length; i++)
{
temp = m_Points[i].X;
m_Points[i].X = -m_Points[i].Y;
m_Points[i].Y = temp;
}
}
//把每个点放大为一个矩形区域,然后以砖块颜色填充这个区域。这个函数需要注意的地方是我们在使用Graphics对象的时候要先lock,这样防止同一时间其他地方也在使用这个对象,从而引发异常。
public void Paint(Graphics gp)
{
foreach (var p in m_Points)
{
lock (gp)
{
try
{
gp.FillRectangle(m_Brush, PointToRect(p));
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}
/// <summary>
/// 擦除砖块
/// </summary>
/// <param name="gp"></param>
public void Erase(Graphics gp)
{
using (SolidBrush sb = new SolidBrush(m_BgColor))
{
foreach (Point p in m_Points)
{
lock (gp)
{
try
{
gp.FillRectangle(sb, PointToRect(p));
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}
}
//单点放大成矩形
private Rectangle PointToRect(Point p)
{
Rectangle r=new Rectangle((m_X+p.X)*m_BlockSize+1,(m_Y+p.Y)*m_BlockSize+1,m_BlockSize-2,m_BlockSize-2);
return r;
}
}
}
3、砖块样式信息模板类
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tetris.Logic
{
/// <summary>
/// 砖块样式信息模板
/// </summary>
class BrickTemplate
{
private string m_Code;//砖块样式编码
private Color m_Color;//砖块颜色
/// <summary>
/// 构造砖块信息
/// </summary>
/// <param name="code"></param>
/// <param name="color"></param>
public BrickTemplate(string code, Color color)
{
if (code == null || code.Length != 25 || color == Color.Empty)
{
throw new FormatException("砖块样式信息错误!");
}
m_Code = code;
m_Color = color;
}
public string Code { get { return m_Code; } }
public Color Color { get { return m_Color; } }
}
}
4、砖块列表信息类
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace T
4000
etris.Logic
{
class TemplateArray
{
//砖块模板列表
private ArrayList m_List=new ArrayList();
//砖块模板数
public int Count { get { return m_List.Count; } }
//获取砖块模板
public BrickTemplate this[int index] { get { return (BrickTemplate) m_List[index]; } }
/// <summary>
/// 获取新砖块模板
/// </summary>
/// <param name="code"></param>
/// <param name="color"></param>
public void Add(string code, Color color)
{
m_List.Add(new BrickTemplate(code, color));
}
/// <summary>
/// 清空砖块模板
/// </summary>
public void Clear()
{
m_List.Clear();
}
}
}
5、砖块产生机
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tetris.Logic
{
/// <summary>
/// 砖块生产机
/// </summary>
class BrickFactory
{
private TemplateArray m_BrickArray;//砖块样式模板列表
private Color m_BgColor;//砖块背景色
private int m_RectPix;//单元格大小
public BrickFactory(TemplateArray info, Color bgColor, int rectPix)
{
m_BrickArray = info;
m_RectPix = rectPix;
m_BgColor = bgColor;
}
//随机获得下一个砖块
public Brick CreateBrick()
{
Random rd=new Random();
int index = rd.Next(m_BrickArray.Count);
string code = m_BrickArray[index].Code;
//根据编码设定相应的有效坐标
List<Point> list=new List<Point>();
for (int i = 0; i < code.Length; i++)
{
if (code[i] == '1')
{
Point p=new Point(i%5,i/5);//根据下表计算坐标
p.Offset(-2,-2);//坐标平移,使坐标(0,0)在中心
list.Add(p);
}
}
//生成砖块
Brick brick=new Brick(list.ToArray(),m_BrickArray[index].Color,m_BgColor,m_RectPix);
//随机旋转90度
if(rd.Next(2)==1)
brick.ContraRotate();
return brick;
}
}
}
二、游戏规则的实现
如果你能够成功实现前面所介绍的功能,你已经完成50%了,而且这50%含金量很高,足够你开发一些其他的游戏了,比如简单点的贪吃蛇,或者经典的吃豆子等等小游戏了。不过我们还是要先完成我们俄罗斯方块的游戏再说,不能一知半解的就结束。 我们的砖块虽然已经有了灵魂,但是还是不收束缚的灵魂,所以我们要做的就是控制它,不能让它无限制的移动,该落下的就落下,该停止的就停止。
实现类:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
using System.Windows.Forms.Layout;
namespace Tetris.Logic
{
class GamePalette
{
private readonly Color[] COLORS = new Color[] { Color.White, Color.Tomato, Color.Thistle, Color.Turquoise };//闪烁颜色
private readonly int[] TIME_SPANS = new int[] { 700, 600, 550, 500, 450, 400, 350, 300, 250, 200 };//等级对应速度
private readonly int[] SCORE_SPANS = new int[] { 100, 300, 500, 1000, 1500 };//分值列表
private BrickFactory m_BrickFactory;//砖块产生机
private int m_Width = 15;//画板宽
private int m_Height = 25;//画板高
private Color[,] m_CoorArray;//固定砖块颜色数组
private Color m_BgColor;
private Color m_GridColor;
private int m_Size;//单元格像素
private int m_Level = 0;
private int m_Score = 0;
private bool m_GameOver = false;
private bool m_ShowGrid = false;
private bool m_Pause = false;
private bool m_Ready = false;
private Graphics m_MainPalette;//主画布
private Graphics m_NextPalette;
private Brick m_RunBrick;
private Brick m_NextBrick;//下一个砖块
private System.Timers.Timer m_TimerBrick;//定时器,用来更新砖块
public GamePalette(int width, int height, TemplateArray info, int size, Color bgColor, Graphics gpPalette,
Graphics gpNext, int level, Color gridColor, bool showGrid)
{
m_Width = width;
m_Height = height;
m_CoorArray = new Color[width, height];
m_BgColor = bgColor;
m_MainPalette = gpPalette;
m_NextPalette = gpNext;
m_Size = size;
m_Level = level;
m_GridColor = gridColor;
m_ShowGrid = showGrid;
m_BrickFactory = new BrickFactory(info, bgColor, size);
InitRandomBrick();
}
public bool IsGameOver { get { return m_GameOver; } }
public int Level { get { return m_Level; } }
public int Score { get { return m_Score; } }
public bool MoveDown()
{
if (m_RunBrick == null)
return true;
int xPos = m_RunBrick.X;
int yPos = m_RunBrick.Y + 1;
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
//下一个位置超过画板的高度,不能移动
if (yPos + m_RunBrick.Points[i].Y >= m_Height)
return false;
//下一个位置已经有砖块,不能移动
if (!m_CoorArray[xPos + m_RunBrick.Points[i].X, yPos + m_RunBrick.Points[i].Y].IsEmpty)
return false;
}
m_RunBrick.Erase(m_MainPalette);//清楚当前位置的砖块图形
m_RunBrick.Y++;
m_RunBrick.Paint(m_MainPalette);//重新绘制砖块图形到画布
return true;
}
/// <summary>
/// 快速向下
/// </summary>
public void DropDown()
{
m_TimerBrick.Stop();//暂停定时器
while (MoveDown()) ;//循环调用向下
m_TimerBrick.Start();//开始定时器
}
/// <summary>
/// 左移
/// </summary>
/// <returns></returns>
public bool MoveLeft()
{
if (m_RunBrick == null)
return true;
int xPos = m_RunBrick.X - 1;
int yPos = m_RunBrick.Y;
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
if (yPos + m_RunBrick.Points[i].Y >= m_Height - 1)
return false;
if (xPos + m_RunBrick.Points[i].X <0)
return false;
if (!m_CoorArray[xPos + m_RunBrick.Points[i].X, yPos + m_RunBrick.Points[i].Y].IsEmpty)
return false;
}
m_RunBrick.Erase(m_MainPalette);
m_RunBrick.X--;
m_RunBrick.Paint(m_MainPalette);
return true;
}
/// <summary>
/// 右移
/// </summary>
/// <returns></returns>
public bool MoveRight()
{
if (m_RunBrick == null)
return true;
int xPos = m_RunBrick.X + 1;
int yPos = m_RunBrick.Y;
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
if (yPos + m_RunBrick.Points[i].Y >= m_Height - 1)
return false;
if (xPos + m_RunBrick.Points[i].X >= m_Width)
return false;
if (!m_CoorArray[xPos + m_RunBrick.Points[i].X, yPos + m_RunBrick.Points[i].Y].IsEmpty)
return false;
}
m_RunBrick.Erase(m_MainPalette);
m_RunBrick.X++;
m_RunBrick.Paint(m_MainPalette);
return true;
}
/// <summary>
/// 顺时针旋转
/// </summary>
/// <returns></returns>
public bool DeasilRotate()
{
if (m_RunBrick == null)
return true;
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
int x = m_RunBrick.X - m_RunBrick.Points[i].X;
int y = m_RunBrick.Y + m_RunBrick.Points[i].X;
if (x < 0 || x >= m_Width || y < 0 || y >= m_Height || !m_CoorArray[x, y].IsEmpty)
return false;
}
m_RunBrick.Erase(m_MainPalette);
m_RunBrick.DeasilRotate();
m_RunBrick.Paint(m_MainPalette);
return true;
}
/// <summary>
/// 逆时针旋转
/// </summary>
/// <returns></returns>
public bool ContraRotate()
{
if (m_RunBrick == null)
return true;
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
int x = m_RunBrick.X + m_RunBrick.Points[i].X;
int y = m_RunBrick.Y - m_RunBrick.Points[i].X;
if (x < 0 || x >= m_Width || y < 0 || y >= m_Height || !m_CoorArray[x, y].IsEmpty)
return false;
}
m_RunBrick.Erase(m_MainPalette);
m_RunBrick.DeasilRotate();
m_RunBrick.Paint(m_MainPalette);
return true;
}
public void PaintPalette(Graphics gp)
{
lock (gp)
{
gp.Clear(m_BgColor);
}
if (m_ShowGrid)
PaintGridLine(gp);
PaintBricks(gp);
if (m_RunBrick != null)
m_RunBrick.Paint(gp);
}
/// <summary>
/// 画已有的砖块
/// </summary>
/// <param name="gp"></param>
private void PaintBricks(Graphics gp)
{
lock (gp)
{
for (int row = 0; row < m_Height; row++)
{
for (int column = 0; column < m_Width; column++)
{
try
{
Color c = m_CoorArray[column, row];
if (c.IsEmpty)
c = m_BgColor;
//c = Color.DarkOrange;
using (SolidBrush sb = new SolidBrush(c))
{
gp.FillRectangle(sb, column * m_Size + 1, row * m_Size + 1, m_Size - 2, m_Size - 2);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}
}
/// <summary>
/// 画网格线
/// </summary>
/// <param name="gp"></param>
private void PaintGridLine(Graphics gp)
{
try
{
lock (gp)
{
using (Pen p = new Pen(m_GridColor, 1))
{
//画网格纵线
for (int column = 1; column < m_Width; column++)
{
gp.DrawLine(p, column * m_Size - 1, 0, column * m_Size - 1, m_Size * m_Height);
}
//画网格横线
for (int row = 1; row < m_Height; row++)
{
gp.DrawLine(p, 0, row * m_Size, m_Width * m_Size, m_Size * row);
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
/// <summary>
/// 重画下一个方块
/// </summary>
/// <param name="gp"></param>
public void PaintNext(Graphics gp)
{
lock (gp)
ca01
{
gp.Clear(m_BgColor);
}
if (m_NextBrick != null)
m_NextBrick.Paint(gp);
}
public void Start()
{
m_Ready = true;
m_ShowGrid = true;
m_RunBrick = m_BrickFactory.CreateBrick();
m_RunBrick.X = m_Width / 2;
int y = 0;
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
if (m_RunBrick.Points[i].Y < y)
y = m_RunBrick.Points[i].Y;
}
m_RunBrick.Y = -y;
PaintPalette(m_MainPalette);
Thread.Sleep(20);
m_NextBrick = m_BrickFactory.CreateBrick();
PaintNext(m_NextPalette);
//设定定时器
m_TimerBrick = new System.Timers.Timer(TIME_SPANS[m_Level]);
m_TimerBrick.Elapsed += new System.Timers.ElapsedEventHandler(OnBrickTimedEvent);
m_TimerBrick.AutoReset = true;
m_TimerBrick.Start();
}
public void Close()
{
if (m_TimerBrick != null)
{
m_TimerBrick.Close();
m_TimerBrick.Dispose();
}
m_NextPalette.Dispose();
m_MainPalette.Dispose();
}
public void Pause()
{
m_Pause = true;
m_Ready = false;
}
public void Resume()
{
m_Pause = false;
m_Ready = true;
}
private void OnBrickTimedEvent(object source, ElapsedEventArgs e)
{
if (m_Pause || m_GameOver)
{
if (m_GameOver)
{
PaintGameOver();
return;
}
}
if (m_Ready)
{
if (!MoveDown())
CheckAndOverBrick();
}
}
private void CheckAndOverBrick()
{
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
m_CoorArray[m_RunBrick.X + m_RunBrick.Points[i].X, m_RunBrick.Y + m_RunBrick.Points[i].Y] =
m_RunBrick.Color;
}
CheckAndDelFullRow();//检查并消除横行
//设定活动砖块
m_RunBrick = m_NextBrick;
m_RunBrick.X = m_Width / 2;
int y = 0;
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
if (m_RunBrick.Points[i].Y < y)
y = m_RunBrick.Points[i].Y;
}
m_RunBrick.Y = -y;
//检查游戏结束
for (int i = 0; i < m_RunBrick.Points.Length; i++)
{
if (!m_CoorArray[m_RunBrick.X + m_RunBrick.Points[i].X, m_RunBrick.Y + m_RunBrick.Points[i].Y].IsEmpty)
{
m_RunBrick = null;
//闪烁效果
m_TimerBrick.Stop();
for (int row = m_Height - 1; row >= 0; row--)
{
for (int column = 0; column < m_Width; column++)
{
m_CoorArray[column, row] = m_BgColor;
}
PaintBricks(m_MainPalette);
System.Threading.Thread.Sleep(50);
}
//游戏结束
m_GameOver = true;
m_Ready = false;
m_TimerBrick.Start();
return;
}
}
m_RunBrick.Paint(m_MainPalette);
//获取新的砖块
m_NextBrick = m_BrickFactory.CreateBrick();
PaintNext(m_NextPalette);
}
private void CheckAndDelFullRow()
{
int fullRowCount = 0;//满行数
int upRow = m_RunBrick.Y - 2;
int downRow = m_RunBrick.Y + 2;
if (upRow < 0)
upRow = 0;
if (downRow >= m_Height)
downRow = m_Height - 1;
for (int row = upRow; row <= downRow; row++)
{
bool isFull = true;
for (int colmn = 0; colmn < m_Width; colmn++)
{
if (m_CoorArray[colmn, row].IsEmpty)
{
isFull = false;
break;
}
}
if (isFull)
{
fullRowCount++;
m_TimerBrick.Stop();
for (int n = 0; n < COLORS.Length; n++)
{
for (int column = 0; column < m_Width; column++)
{
m_CoorArray[column, row] = COLORS
;
}
PaintBricks(m_MainPalette);
System.Threading.Thread.Sleep(50);
}
for (int rowIndex = row; rowIndex > 0; rowIndex--)
for (int column = 0; column < m_Width; column++)
m_CoorArray[column, rowIndex] = m_CoorArray[column, rowIndex -1];
PaintBricks(m_MainPalette);
m_TimerBrick.Start();
}
}
if (fullRowCount > 0)
{
int currenScore = SCORE_SPANS[fullRowCount - 1];
int temScore = m_Score;//游戏得分
m_Score += currenScore;//追加得分
if (m_Score >= 10000 && m_Score.ToString()[0] != temScore.ToString()[0])
{
m_Level = (m_Level + 1) % TIME_SPANS.Length;
m_TimerBrick.Interval = TIME_SPANS[m_Level];
}
}
}
private void PaintGameOver()
{
StringFormat drawFormat = new StringFormat();
Font font = new Font("Arial BLACK", 30f);
drawFormat.Alignment = StringAlignment.Center;
for (int j = 0; j < COLORS.Length; j++)
{
lock (m_MainPalette)
{
try
{
m_MainPalette.DrawString("GAME OVER", font, new SolidBrush(COLORS[j]), new RectangleF(0, m_Height * m_Size / 2 - 100, m_Width * m_Size, 100), drawFormat);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
Thread.Sleep(100);
}
}
private void InitRandomBrick()
{
if (m_Level > 0)
{
Random r = new Random();
for (int row = 0; row < m_Level; row++)
{
for (int column = 0; column < m_Width * 2 / 3; column++)
{
int rc = r.Next(m_Width);
m_CoorArray[rc, m_Height - row - 1] = COLORS[r.Next(COLORS.Length)];
}
}
}
}
}
}
Form1代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
using Tetris.Logic;
namespace Tetris
{
public partial class Form1 : Form
{
private GamePalette m_Gamepalette;//游戏画板
public Form1()
{
InitializeComponent();
}
private void pbMainPalette_Paint(object sender, PaintEventArgs e)
{
if (m_Gamepalette != null)
m_Gamepalette.PaintPalette(e.Graphics);
}
private void pbNextPalette_Paint(object sender, PaintEventArgs e)
{
if (m_Gamepalette != null)
m_Gamepalette.PaintNext(e.Graphics);
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F2)
{
if (m_Gamepalette != null)
{
m_Gamepalette.Close();
m_Gamepalette = null;
}
TemplateArray array = new TemplateArray();
array.Add("0000001000011100000000000", Color.FromArgb(-128));
array.Add("0000000000111100000000000", Color.FromArgb(-65536));
array.Add("0000000110011000000000000", Color.FromArgb(-16711936));
array.Add("0000000100011100000000000", Color.FromArgb(-4144960));
array.Add("0000000100001100010000000", Color.FromArgb(-16776961));
array.Add("0000000000001110010000000", Color.FromArgb(-65281));
array.Add("0000000000001100011000000", Color.FromArgb(-65281));
//开始游戏
m_Gamepalette = new GamePalette(15, 25, array, 20, Color.Black, pbMainPalette.CreateGraphics(), pbNextPalette.CreateGraphics(), 0, Color.Blue, false);
m_Gamepalette.Start();
}
else
{
if (m_Gamepalette == null || m_Gamepalette.IsGameOver)
return;
if (e.KeyCode == Keys.F3)
{
if (m_Gamepalette.IsGameOver)
m_Gamepalette.Pause();
else
m_Gamepalette.Resume();
}
else if (e.KeyCode == Keys.Left)
{
m_Gamepalette.MoveLeft();
}
else if (e.KeyCode == Keys.Right)
{
m_Gamepalette.MoveRight();
}
else if (e.KeyCode == Keys.Down)
{
m_Gamepalette.MoveDown();
}
else if (e.KeyCode == Keys.Up)
{
m_Gamepalette.ContraRotate();
}
else if (e.KeyCode == Keys.Space)
{
m_Gamepalette.DropDown();
}
}
}
}
}
运行简单效果图:
遇到的问题:就是满行的时候,闪烁效果完后,所有砖块的颜色都变成了背景色,但是实际上的砖块还是存在画布上并没有消失,希望哪位大神指导一下是什么原因造成的。
以上代码均可直接使用运行(为了方便大家查看效果和BUG
);
相关文章推荐
- 使用JavaScript实现简单的小游戏-贪吃蛇
- 一个简单的俄罗斯方块的Win32 API实现
- Java swing实现的俄罗斯方块小游戏源码
- 简单c语言小游戏实现原理
- 一个简单的俄罗斯方块实现
- java俄罗斯方块简单实现,新手适合学习
- canvas标签应用 简单俄罗斯方块游戏的实现
- 简单的小游戏---代码实现三子棋
- 利用Java简单的实现围棋小游戏
- 简单小游戏-剪刀石头布的c语言实现
- C语言实现简单的三子棋小游戏
- Android 小游戏2048 代码简单实现
- JS学习 变量的作用域等 实现简单的玛丽小游戏
- 用JS实现简单的猜数小游戏
- MFC---鼠标单击小游戏简单实现
- python和pygame实现简单俄罗斯方块游戏
- 俄罗斯方块的简单实现
- 石头剪刀布小游戏的没有界面的简单实现。
- C语言程序简单实现贪吃蛇小游戏—不需要graphics.h头文件