好友
阅读权限10
听众
最后登录1970-1-1
|
本帖最后由 Molingo 于 2023-12-3 17:06 编辑
先简单说一下实现思路
1.画板
游戏画面是有10*20的小方格组成,用代码在窗体中创建10*20个label控件,将长宽设为一样大小且统一设置黑色背景;
每个图形都是由四个小方格组成,通过改变画板上label的背景颜色,即可在绘制出图形;
2.图形的移动和变型
游戏中有7中图形,每个图形都是有4个小方格组成,可以左右移动或变形;在每个图形中选择其中一个方块,作为整个图形中心点,并记录这个图形点在整个画布中的(X,Y)坐标;通过中心点的坐标,绘制不同的图形;在移动图形时,只需要改变中心点坐标;例:左右移动时,只需要对中心的X轴-1或+1即可;移动之后,先记录上一次坐标位置,并通过坐标擦除掉图形(将label背景色设为黑色);并根据移动后的坐标进行绘制方块,图形有四种变化,每一次做变型时,根据中心点坐标先擦除在绘制;
3.图形的绘制和移动旋转判断
label是用10*20的数组进行存放,其数组索引即为小方格的坐标; 创建一个10*24的二维数组(需要在画布上方外腾出位置,绘制下落的图形),记录画布中每个方格坐标中是否存在方格,当图形在做移动和旋转时,可通过图形坐标点检查周围是否存在方块,来判定是否可以旋转或移动或下落;在窗体中增加一个定时器,让图形的中心点Y轴-1,来达到自动下落的功能;如图中,根据中心点绘制出红色图形;根据中线点判断灰色部分是否存在方块,来判定图形能否做旋转;
4.满行消除与游戏结束
当图形无法下落时,通过用来记录方块是否存在的数组,判断画布每一行是否满行;满行后,擦除当行,让上面每一行都往下覆盖,实现消除;当方块无法下落且Y轴坐标高于画布最高的点时,游戏结束;
5.游戏截图
画布类:
[C#] 纯文本查看 复制代码 public class Canvas
{
private Label[,] canvals = new Label[20, 10];
public Label[,] canvals2 = new Label[4, 4];
public enum MoveDirection
{
left,
right,
Variant
}
private readonly Control Control;
private readonly Control Control2;
private Random Random;
public Score score;
private Blocks Blocks { get; set; }
private Blocks NextBlocks { get; set; }
/// <summary>
/// 记录是否存在方块
/// </summary>
public bool[][] isExitBlock = new bool[24][];
/// <summary>
/// 记录方块的颜色
/// </summary>
public Color[][] blockColor = new Color[24][];
/// <summary>
/// 初始化画板
/// </summary>
/// <param name="c"></param>
/// <param name="c2"></param>
public Canvas(Control c, Control c2)
{
Control = c; Control2 = c2;
DrawCanvas();
InitializeGame();
}
/// <summary>
/// 初始化游戏
/// </summary>
public void InitializeGame()
{
for (int i = 0; i < isExitBlock.Length; i++)
{
isExitBlock[i] = new bool[10];
blockColor[i] = new Color[10];
}
Random = new Random((int)DateTime.Now.Ticks);
Blocks = CreateNextBlock();
score = new Score();
}
/// <summary>
/// 创建下一个方块
/// </summary>
public Blocks CreateNextBlock()
{
NextBlocks = CreateBlock();
NextBlocks.BlockType = Random.Next(1, 5);
NextBlocks.BlockPoint = new Point(4, 21);
NextBlocks.NextBlockType = NextBlocks.BlockType;
return NextBlocks;
}
/// <summary>
/// 随机创建方块
/// </summary>
/// <returns></returns>
private Blocks CreateBlock()
{
int type = Random.Next(1, 8);
switch (type)
{
case 1:
return new Block1(this);
case 2:
return new Block2(this);
case 3:
return new Block3(this);
case 4:
return new Block4(this);
case 5:
return new Block5(this);
case 6:
return new Block6(this);
case 7:
return new Block7(this);
default:
return new Block1(this);
}
}
/// <summary>
/// 构建画板
/// </summary>
private void DrawCanvas()
{
for (int i = 0; i < canvals.GetLength(0); i++)
{
for (int j = 0; j < canvals.GetLength(1); j++)
{
Label label = canvals[i, j] = new Label();
label.Width = 30;
label.Height = 30;
label.BorderStyle = BorderStyle.FixedSingle;
label.BackColor = Color.Black;
label.Location = new Point(j * 30, Control.Height - 30 - i * 30);
Control.Controls.Add(label);
}
}
for (int i = 0; i < canvals2.GetLength(0); i++)
{
for (int j = 0; j < canvals2.GetLength(1); j++)
{
Label label = canvals2[i, j] = new Label();
label.Width = 30;
label.Height = 30;
label.BorderStyle = BorderStyle.FixedSingle;
label.BackColor = Color.White;
label.Location = new Point(j * 30, i * 30);
Control2.Controls.Add(label);
}
}
}
/// <summary>
/// 默认方块下落
/// </summary>
/// <param name="block"></param>
public bool Freefall()
{
if (Blocks.IsFall())
{
Blocks.PreBlockPoint = Blocks.BlockPoint;
Blocks.PreBlockType = Blocks.BlockType;
Blocks.EraseBlock();
Blocks.SetPointY(Blocks.BlockPoint.Y - 1);
Blocks.Drawblock();
ShowBlock();
if (Blocks.BlockPoint.Y == 20)
{
EraseNextBlock();
CreateNextBlock().DrawNextblock();
}
return true;
}
else
{
if (Blocks.BlockPoint.Y == 21)
{
score.GameOverTime = DateTime.Now;
score.SaveGamelog();
return false;
}
FullLineDispel();
Blocks = NextBlocks;
return true;
}
}
/// <summary>
/// 方块显示
/// </summary>
/// <param name="blocks"></param>
public void ShowBlock()
{
for (int i = 0; i < canvals.GetLength(0); i++)
{
for (int j = 0; j < canvals.GetLength(1); j++)
{
if (isExitBlock[i][j])
canvals[i, j].BackColor = blockColor[i][j];
else
canvals[i, j].BackColor = Color.Black;
}
}
}
/// <summary>
/// 方块的移动或变形
/// </summary>
/// <param name="move"></param>
public void MoveOrVariantBlock(MoveDirection move)
{
switch (move)
{
case MoveDirection.left:
if (Blocks.LeftMove())
{
Blocks.PreBlockType = Blocks.BlockType;
Blocks.PreBlockPoint = Blocks.BlockPoint;
Blocks.EraseBlock();
Blocks.SetPointX(Blocks.BlockPoint.X - 1);
Blocks.Drawblock();
ShowBlock();
}
break;
case MoveDirection.right:
if (Blocks.rightMove())
{
Blocks.PreBlockType = Blocks.BlockType;
Blocks.PreBlockPoint = Blocks.BlockPoint;
Blocks.EraseBlock();
Blocks.SetPointX(Blocks.BlockPoint.X + 1);
Blocks.Drawblock();
ShowBlock();
}
break;
case MoveDirection.Variant:
if (Blocks.IsVariant())
{
Blocks.PreBlockType = Blocks.BlockType;
Blocks.PreBlockPoint = Blocks.BlockPoint;
Blocks.EraseBlock();
Blocks.BlockType += 1;
Blocks.Drawblock();
ShowBlock();
}
break;
default:
break;
}
}
/// <summary>
/// 满行消除
/// </summary>
public void FullLineDispel()
{
int row = -1;
for (int i = 0; i <= canvals.GetLength(0); i++)
{
int count = 0;
for (int j = 0; j < canvals.GetLength(1); j++)
{
if (isExitBlock[i][j]) count++;
}
if (count == canvals.GetLength(1))
{
if (row == i)
{
score.GameScore += 2;
}
else
{
score.GameScore += 1;
}
score.Rows+=1;
row = i;
if (score.GameScore % 20 == 0)
{
score.Leave = score.GameScore / 20;
score.FallingVelocity -= score.Leave * 50;
}
for (int k = i; k < 23; k++)
{
isExitBlock[k] = isExitBlock[k + 1];
blockColor[k] = blockColor[k + 1];
}
isExitBlock[23] = new bool[10];
blockColor[23] = new Color[10];
i--;
}
}
}
/// <summary>
/// 擦除显示下一方块的画板
/// </summary>
public void EraseNextBlock()
{
foreach (var item in canvals2)
{
item.BackColor = Color.White;
}
}
}
方块抽象类:
[C#] 纯文本查看 复制代码 public abstract class Blocks
{
public Blocks(Canvas canvas)
{
c = canvas;
}
protected Canvas c;
protected static Random random = new Random((int)DateTime.Now.Ticks);
protected static Color[] Colors = new Color[] {
Color.Magenta,
Color.DeepPink,
Color.Red,
Color.Orange,
Color.DarkTurquoise,
Color.Coral,
Color.Yellow,
Color.MediumSpringGreen,
Color.Cyan,
Color.DodgerBlue,
Color.Aqua,
Color.MediumVioletRed,
Color.DarkMagenta,
Color.OrangeRed,
Color.Aquamarine,
Color.SeaGreen,
Color.PaleVioletRed,
Color.PaleGreen,
Color.PaleTurquoise,
Color.SpringGreen,
Color.Tomato,
Color.Turquoise,
Color.Violet,
Color.LightCoral,
Color.YellowGreen,
Color.Plum,
Color.MediumTurquoise,
Color.DarkCyan,
Color.GreenYellow
};
/// <summary>
/// 设置当前方块的X坐标
/// </summary>
/// <param name="x"></param>
public void SetPointX(int x) => BlockPoint = new Point(x, BlockPoint.Y);
/// <summary>
/// 设置当前方块的y坐标
/// </summary>
/// <param name="y"></param>
public void SetPointY(int y) => BlockPoint = new Point(BlockPoint.X, y);
private int _blockType;
/// <summary>
/// 当前方块的类型
/// </summary>
public int BlockType
{
get => _blockType;
set => _blockType = value > 4 ? 1 : value;
}
/// <summary>
/// 当前方块中心点的位置
/// </summary>
public Point BlockPoint { get; set; }
/// <summary>
/// 当前方块的颜色
/// </summary>
public Color CurrentBlockColor { get; protected set; } = Colors[random.Next(0, Colors.Length)];
/// <summary>
/// 方块上一次的中心点位置
/// </summary>
public Point PreBlockPoint { get; set; }
/// <summary>
/// 上一次方块的类型
/// </summary>
public int PreBlockType { get; set; }
/// <summary>
/// 下一个方块类型
/// </summary>
public int NextBlockType { get; set; }
/// <summary>
/// 是否可以继续下落
/// </summary>
/// <returns></returns>
public abstract bool IsFall();
/// <summary>
/// 是否可以左移
/// </summary>
/// <returns></returns>
public abstract bool LeftMove();
/// <summary>
/// 是否可以右移
/// </summary>
/// <returns></returns>
public abstract bool rightMove();
/// <summary>
/// 是否可以变形
/// </summary>
/// <returns></returns>
public abstract bool IsVariant();
/// <summary>
/// 擦除方块
/// </summary>
public abstract void EraseBlock();
/// <summary>
/// 绘制方块
/// </summary>
public abstract void Drawblock();
/// <summary>
/// 绘制下一个方块
/// </summary>
public abstract void DrawNextblock();
}
其中一个方块的实现类:
[C#] 纯文本查看 复制代码 class Block3 : Blocks
{
public Block3(Canvas c) : base(c)
{
}
public override void Drawblock()
{
switch (BlockType)
{
case 1:
c.isExitBlock[BlockPoint.Y][BlockPoint.X] = c.isExitBlock[BlockPoint.Y][BlockPoint.X + 1] = c.isExitBlock[BlockPoint.Y + 1][BlockPoint.X] = c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X + 1] = true;
c.blockColor[BlockPoint.Y][BlockPoint.X] = c.blockColor[BlockPoint.Y][BlockPoint.X + 1] = c.blockColor[BlockPoint.Y + 1][BlockPoint.X] = c.blockColor[BlockPoint.Y - 1][BlockPoint.X + 1] = CurrentBlockColor;break;
case 2:
c.isExitBlock[BlockPoint.Y][BlockPoint.X] = c.isExitBlock[BlockPoint.Y][BlockPoint.X + 1] = c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X] = c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X - 1] = true;
c.blockColor[BlockPoint.Y][BlockPoint.X] = c.blockColor[BlockPoint.Y][BlockPoint.X + 1] = c.blockColor[BlockPoint.Y - 1][BlockPoint.X] = c.blockColor[BlockPoint.Y - 1][BlockPoint.X - 1] = CurrentBlockColor; break;
case 3:
c.isExitBlock[BlockPoint.Y][BlockPoint.X] = c.isExitBlock[BlockPoint.Y][BlockPoint.X - 1] = c.isExitBlock[BlockPoint.Y + 1][BlockPoint.X - 1] = c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X] = true;
c.blockColor[BlockPoint.Y][BlockPoint.X] = c.blockColor[BlockPoint.Y][BlockPoint.X - 1] = c.blockColor[BlockPoint.Y + 1][BlockPoint.X - 1] = c.blockColor[BlockPoint.Y - 1][BlockPoint.X] = CurrentBlockColor; break;
case 4:
c.isExitBlock[BlockPoint.Y][BlockPoint.X] = c.isExitBlock[BlockPoint.Y][BlockPoint.X - 1] = c.isExitBlock[BlockPoint.Y + 1][BlockPoint.X] = c.isExitBlock[BlockPoint.Y + 1][BlockPoint.X + 1] = true;
c.blockColor[BlockPoint.Y][BlockPoint.X] = c.blockColor[BlockPoint.Y][BlockPoint.X - 1] = c.blockColor[BlockPoint.Y + 1][BlockPoint.X] = c.blockColor[BlockPoint.Y + 1][BlockPoint.X + 1] = CurrentBlockColor; break;
}
}
public override void DrawNextblock()
{
switch (NextBlockType)
{
case 1:
c.canvals2[0, 1].BackColor = CurrentBlockColor;
c.canvals2[1, 1].BackColor = CurrentBlockColor;
c.canvals2[1, 2].BackColor = CurrentBlockColor;
c.canvals2[2, 2].BackColor = CurrentBlockColor;
break;
case 2:
c.canvals2[1, 1].BackColor = CurrentBlockColor;
c.canvals2[1, 2].BackColor = CurrentBlockColor;
c.canvals2[2, 0].BackColor = CurrentBlockColor;
c.canvals2[2, 1].BackColor = CurrentBlockColor;
break;
case 3:
c.canvals2[0, 0].BackColor = CurrentBlockColor;
c.canvals2[1, 0].BackColor = CurrentBlockColor;
c.canvals2[1, 1].BackColor = CurrentBlockColor;
c.canvals2[2, 1].BackColor = CurrentBlockColor;
break;
case 4:
c.canvals2[0, 1].BackColor = CurrentBlockColor;
c.canvals2[0, 2].BackColor = CurrentBlockColor;
c.canvals2[1, 0].BackColor = CurrentBlockColor;
c.canvals2[1, 1].BackColor = CurrentBlockColor;
break;
}
}
public override void EraseBlock()
{
switch (PreBlockType)
{
case 1:
c.isExitBlock[PreBlockPoint.Y][PreBlockPoint.X] = c.isExitBlock[PreBlockPoint.Y][PreBlockPoint.X + 1] = c.isExitBlock[PreBlockPoint.Y + 1][PreBlockPoint.X] = c.isExitBlock[PreBlockPoint.Y - 1][PreBlockPoint.X + 1] = false; break;
case 2:
c.isExitBlock[PreBlockPoint.Y][PreBlockPoint.X] = c.isExitBlock[PreBlockPoint.Y][PreBlockPoint.X + 1] = c.isExitBlock[PreBlockPoint.Y - 1][PreBlockPoint.X] = c.isExitBlock[PreBlockPoint.Y - 1][PreBlockPoint.X - 1] = false; break;
case 3:
c.isExitBlock[PreBlockPoint.Y][PreBlockPoint.X] = c.isExitBlock[PreBlockPoint.Y][PreBlockPoint.X - 1] = c.isExitBlock[PreBlockPoint.Y + 1][PreBlockPoint.X - 1] = c.isExitBlock[PreBlockPoint.Y - 1][PreBlockPoint.X] = false; break;
case 4:
c.isExitBlock[PreBlockPoint.Y][PreBlockPoint.X] = c.isExitBlock[PreBlockPoint.Y][PreBlockPoint.X - 1] = c.isExitBlock[PreBlockPoint.Y + 1][PreBlockPoint.X] = c.isExitBlock[PreBlockPoint.Y + 1][PreBlockPoint.X + 1] = false; break;
}
}
public override bool IsFall()
{
bool b = false;
switch (BlockType)
{
case 1:
if (BlockPoint.Y > 1 && !c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X] && !c.isExitBlock[BlockPoint.Y - 2][BlockPoint.X + 1]) b = true;
break;
case 2:
if (BlockPoint.Y > 1 && !c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X + 1] && !c.isExitBlock[BlockPoint.Y - 2][BlockPoint.X] && !c.isExitBlock[BlockPoint.Y - 2][BlockPoint.X - 1]) b = true;
break;
case 3:
if (BlockPoint.Y > 1 && !c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X - 1] && !c.isExitBlock[BlockPoint.Y - 2][BlockPoint.X]) b = true;
break;
case 4:
if (BlockPoint.Y > 0 && !c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X - 1] && !c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X] && !c.isExitBlock[BlockPoint.Y][BlockPoint.X + 1]) b = true;
break;
}
return b;
}
public override bool IsVariant()
{
bool b = false;
switch (BlockType)
{
case 1:
if (BlockPoint.X > 0 && BlockPoint.X < 9 && !c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X] && !c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X - 1] && !c.isExitBlock[BlockPoint.Y + 1][BlockPoint.X + 1]) b = true;
break;
case 2:
if (BlockPoint.X > 0 && BlockPoint.X < 9 && !c.isExitBlock[BlockPoint.Y][BlockPoint.X - 1] && !c.isExitBlock[BlockPoint.Y + 1][BlockPoint.X - 1] && !c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X + 1]) b = true;
break;
case 3:
if (BlockPoint.X > 0 && BlockPoint.X < 9 && !c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X - 1] && !c.isExitBlock[BlockPoint.Y + 1][BlockPoint.X] && !c.isExitBlock[BlockPoint.Y + 1][BlockPoint.X + 1]) b = true;
break;
case 4:
if (BlockPoint.X > 0 && BlockPoint.X < 9 && BlockPoint.Y > 0 && !c.isExitBlock[BlockPoint.Y + 1][BlockPoint.X - 1] && !c.isExitBlock[BlockPoint.Y ][BlockPoint.X + 1] && !c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X + 1]) b = true;
break;
}
return b;
}
public override bool LeftMove()
{
bool b = false;
switch (BlockType)
{
case 1:
if (BlockPoint.X > 0 && !c.isExitBlock[BlockPoint.Y][BlockPoint.X - 1] && !c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X] && !c.isExitBlock[BlockPoint.Y + 1][BlockPoint.X - 1]) b = true;
break;
case 2:
if (BlockPoint.X > 1 && !c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X - 2] && !c.isExitBlock[BlockPoint.Y][BlockPoint.X - 1]) b = true;
break;
case 3:
if (BlockPoint.X > 1 && !c.isExitBlock[BlockPoint.Y][BlockPoint.X - 2] && !c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X - 1] && !c.isExitBlock[BlockPoint.Y + 1][BlockPoint.X - 2]) b = true;
break;
case 4:
if (BlockPoint.X > 1 && !c.isExitBlock[BlockPoint.Y][BlockPoint.X - 2] && !c.isExitBlock[BlockPoint.Y + 1][BlockPoint.X - 1]) b = true;
break;
}
return b;
}
public override bool rightMove()
{
bool b = false;
switch (BlockType)
{
case 1:
if (BlockPoint.X < 8 && !c.isExitBlock[BlockPoint.Y + 1][BlockPoint.X + 1] && !c.isExitBlock[BlockPoint.Y][BlockPoint.X + 2] && !c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X + 2]) b = true;
break;
case 2:
if (BlockPoint.X < 8 && !c.isExitBlock[BlockPoint.Y][BlockPoint.X + 2] && !c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X + 1]) b = true;
break;
case 3:
if (BlockPoint.X < 9 && !c.isExitBlock[BlockPoint.Y][BlockPoint.X + 1] && !c.isExitBlock[BlockPoint.Y - 1][BlockPoint.X + 1] && !c.isExitBlock[BlockPoint.Y + 1][BlockPoint.X]) b = true;
break;
case 4:
if (BlockPoint.X < 8 && !c.isExitBlock[BlockPoint.Y][BlockPoint.X + 1] && !c.isExitBlock[BlockPoint.Y + 1][BlockPoint.X + 2]) b = true;
break;
}
return b;
}
}
完整源代码:
MyTetris.zip
(304.47 KB, 下载次数: 16)
俄罗斯方块--对应代码的图形参考.zip
(10.02 KB, 下载次数: 11)
|
-
-
免费评分
-
查看全部评分
|