频道栏目
读书频道 > 软件开发 > C# > 深入体验C#项目开发
1.6.2 游戏控制、处理方法
2013-03-27 13:22:38     我来说两句
收藏   我要投稿

本文所属图书 > 深入体验C#项目开发

C#是当今使用最为频繁的编程语言之一,一直在开发领域占据重要的地位。本书通过10个综合实例的实现过程,详细讲解了C#在实践项目中的综合运用过程。这些项目从作者的学生时代写起,到项目经理结束,一直贯穿于作...  立即去当当网订购

2005年7月12日,清晨,为什么要设置等级

当今社会人人平等,但是我们玩的游戏却设置了很多等级和关口,这是为什么呢?我现在想通了,作为一款游戏,目的是娱乐用户,激发用户玩的热情。因此我设置了分数功能,这样就可以激发用户永远去追逐高分。

过去的两天我完成了时间处理程序的编码工作,从实现过程可以看出,整个游戏项目的控制和处理是通过事件处理程序控制的。每个事件处理程序只是调用处理方法名称,而各实现具体功能的处理方法须在文件you.cs中定义。

1. 初始设置

这里的初始设置是指定义游戏的控制类youxiControl,并设置玩游戏过程中的返回行数、参数、等级、积分和方块的操作等参数,具体代码如下。

public class youxiControl : Control
{
        private const int rowCount = 21;     // 行数
        private const int colCount = 11;     // 列数
        private int brickWidth = 16;      // 小块宽度
        private int brickHeight = 16;      // 小块高度
        private ImageList imageList;      // 方块素材
        private Bitmap backBitmap;       // 背景图片
        private List<List<List<Point>>> brickTemplets = new List<List<List<Point>>>();
   // 方块模板[模板序号,朝向]
        private byte[,] points = new byte[colCount, rowCount];// 点阵
        private byte brickIndex = 0;      // 模板序号
        private byte facingIndex = 0;     // 当前变化号
        private Point brickPoint = new Point();   // 方块的位置
        private byte afterBrickIndex = 0;    // 下一个模板序号
        private byte afterFacingIndex = 0;    // 下一个变化号
        private System.Windows.Forms.Timer timer;  // 控制下落的时间器
        private int lines;        // 消行数
        private Random random = new Random();   // 随机数
        private int level = 0;       // 当前速度
        private int score = 0;       // 成绩
        /// 下落速度,数值表示每次下落的时间差,以毫秒为单位
        private int[] speeds = new int[] { 700, 500, 400, 300, 320, 320, 100, 80, 70, 60, 50 };
        /// 每次消除行所增加的积分
        private int[] scoress = new int[] { 0001, 0100, 0300, 0500, 1000, 3200 };
        private bool playing = false;    // 玩家是否正在游戏
        private youxiNext youxiNext;     // 下一个方块的显示控件
        private youxiScore youxiScore;    // 积分显示控件
        private int stepIndex = -1;     // 当前回放的步数
        private bool reviewing = false;    // 是否正在回放中
        private Thread threadReview = null;   // 回放使用的线程
        private int reviewSpeed = 1;     // 回放的速度,数值表示倍数
        private List<StepInfo> StepInfos = new List<StepInfo>();
   // 记录玩家每一步的操作
        private int lastRecordTime = 0;     // 最后记录的时间
        private bool recordMode = false;     // 是否采用记录模式
        private ProgressBar progressBar;     // 回放进度条
        private bool extended = false;     // 扩展方块
        /// 消除的行数
        public int Lines { get { return lines; } }
        /// 当前的积分
        public int Score { get { return score; } }
        /// 当前的关数
        public int Level { get { return level; } }
        /// 方块的操作
        public enum BrickOperates
        {
            boMoveLeft = 0,        // 左移
            boMoveRight = 1,         // 右移
            boMoveDown = 2,         // 下移
            boMoveBottom = 3,        // 直下
            boTurnLeft = 4,        // 左旋
            boTurnRight = 5,        // 右旋
        }
}

2. 重新开始处理

单击窗体内的【重新开始】按钮后将会执行buttonReplay_Click事件,该事件调用方法Replay(),设置重新开始游戏。具体代码如下。

public void Replay(bool ARecordMode, bool AExtended)
{
    if (threadReview != null)
    {
        threadReview.Abort();
        threadReview = null;
    }
    if (AExtended != extended)
        NewTemplets(AExtended);
    reviewing = false;
    playing = true;
    recordMode = ARecordMode;
    Clear();
    StepInfos.Clear();
    afterBrickIndex = (byte)random.Next(brickTemplets.Count);
    afterFacingIndex = (byte)random.Next(brickTemplets[afterBrickIndex].Count);
    if (youxiNext != null) youxiNext.Update(this);
    if (recordMode && !reviewing)
    {
        StepInfos.Add(new StepInfo(0, 0, afterBrickIndex, afterFacingIndex));
        lastRecordTime = Environment.TickCount;
    }
    level = 0;
    score = 0;
    lines = 0;
    stepIndex = -1;
    if (progressBar != null) progressBar.Value = 0;
    NextBrick();
    timer.Interval = speeds[level];
    timer.Enabled = true;
    if (youxiScore != null) youxiScore.Update(this);
    if (CanFocus) Focus();
}

3. 回放处理

单击窗体内的Review按钮后将会执行buttonReview_Click事件,该事件调用方法Review(),设置查看游戏的历史回放进程。具体代码如下。

public void Review()
{
    if (threadReview != null)
    {
        threadReview.Abort();
        threadReview = null;
    }
    timer.Enabled = false;
    reviewing = true;
    playing = true;
    Clear();
    level = 0;
    score = 0;
    lines = 0;
    stepIndex = 0;
    NextStep();
    NextStep();
    if (progressBar != null)
        progressBar.Maximum = StepInfos.Count;
    threadReview = new Thread(new ThreadStart(DoReview));
    threadReview.Start();
}

在执行上述方法时,先通过DoReview()方法设置回放速度。DoReview()方法的具体实现代码如下。

private void DoReview()
{
    while (reviewing)
    {
        if (stepIndex < 0 || stepIndex >= StepInfos.Count)
        {
            reviewing = false;
            break;
        }
        Thread.Sleep((int)((double)StepInfos[stepIndex].timeTick / reviewSpeed));
        if (!reviewing) break;
       
        Invoke(new EventHandler(DoInvoke));
    }
    Invoke(new EventHandler(DoInvoke));
    threadReview = null;
}

4. 载入处理

载入处理的范围比较广,包括游戏载入和统计数据载入,在此将着重介绍统计数据的载入。具体过程如下。

(1) 通过方法LoadFromFile()从指定文件中载入玩家的操作记录,变量AFileName是载入的源文件。方法LoadFromFile()的具体代码如下。

public void LoadFromFile(string AFileName)
{
    if (!File.Exists(AFileName)) return;
    FileStream vFileStream = new FileStream(AFileName,
        FileMode.Open, FileAccess.Read);
    LoadFromStream(vFileStream);
    vFileStream.Close();
}

(2) 通过方法LoadFromStream()从指定流中载入玩家的操作记录,变量AStream是载入的流。方法LoadFromStream()的具体代码如下。

/// 从流中载入玩家的操作记录
/// </summary>
public void LoadFromStream(Stream AStream)
{
    if (AStream == null) return;
    byte[] vBuffer = new byte[3];
    if (AStream.Read(vBuffer, 0, vBuffer.Length) != 3) return;
    if (vBuffer[0] != 116 || vBuffer[1] != 114 || vBuffer[2] != 102) return;
    if (colCount != (byte)AStream.ReadByte()) return;
    if (rowCount != (byte)AStream.ReadByte()) return;
    if (threadReview != null)
    {
        threadReview.Abort();       // 如果正在回放
        threadReview = null;
    }
    timer.Enabled = false;
    playing = false;
    reviewing = false;
    brickTemplets.Clear();
    if (progressBar != null) progressBar.Value = 0;
    int vTempletsCount = AStream.ReadByte();
    for (int i = 0; i < vTempletsCount; i++)
    {
        List<List<Point>> templets = new List<List<Point>>();
        int vPointsLength = AStream.ReadByte();
        for (int j = 0; j < vPointsLength; j++)
        {
            List<Point> bricks = new List<Point>();
            int vPointCount = AStream.ReadByte();
            for (int k = 0; k < vPointCount; k++)
            {
                int vData = AStream.ReadByte();
                if (vData < 0) break;
                bricks.Add(new Point(vData & 3, vData >> 4 & 3));
            }
            templets.Add(bricks);
        }
        brickTemplets.Add(templets);
    }
    StepInfos.Clear();
    vBuffer = new byte[sizeof(int)];
    if (AStream.Read(vBuffer, 0, vBuffer.Length) != vBuffer.Length) return;
    int vStepCount = BitConverter.ToInt32(vBuffer, 0);
    for (int i = 0; i < vStepCount; i++)
    {
        StepInfo vStepInfo = new StepInfo();
        vStepInfo.param1 = (byte)AStream.ReadByte();
        vBuffer = new byte[sizeof(ushort)];
        if (AStream.Read(vBuffer, 0, vBuffer.Length) != vBuffer.Length) return;
        vStepInfo.timeTick = (ushort)BitConverter.ToInt16(vBuffer, 0);
        int vData = AStream.ReadByte();
        vStepInfo.command = (byte)(vData & 3);
        vStepInfo.param2 = (byte)(vData >> 4 & 3);
        StepInfos.Add(vStepInfo);
    }
    Clear();
    Invalidate();
}

(3) 通过方法SaveToFile将玩家的操作记录保存到指定文件中,变量AFileName是保存的文件。方法SaveToFile()的具体代码如下。

/// 将玩家操作记录保存到文件中
public void SaveToFile(string AFileName)
{
    FileStream vFileStream = new FileStream(AFileName,
        FileMode.Create, FileAccess.Write);
    SaveToStream(vFileStream);
    vFileStream.Close();
}

(4) 通过方法SaveToStream()将玩家的操作记录保存到指定流中,变量AStream是保存的流。方法SaveToStream()的具体代码如下。

/// 将玩家操作记录保存到流中
public void SaveToStream(Stream AStream)
{
    if (AStream == null) return;
    byte[] vBuffer = Encoding.ASCII.GetBytes("trf");
    AStream.Write(vBuffer, 0, vBuffer.Length); // 写头信息
    AStream.WriteByte((byte)colCount);
    AStream.WriteByte((byte)rowCount);
    byte vByte = (byte)brickTemplets.Count;
    AStream.WriteByte(vByte);
    foreach (List<List<Point>> vList in brickTemplets)
    {
        vByte = (byte)vList.Count;
        AStream.WriteByte(vByte);
        foreach (List<Point> vPoints in vList)
        {
            vByte = (byte)vPoints.Count;
            AStream.WriteByte(vByte);
            foreach (Point vPoint in vPoints)
            {
                vByte = (byte)(vPoint.Y << 4 | vPoint.X);
                AStream.WriteByte(vByte);
            }
        }
    }
    AStream.Write(BitConverter.GetBytes(StepInfos.Count), 0, sizeof(int));
    foreach (StepInfo vStepInfo in StepInfos)
    {
        AStream.WriteByte(vStepInfo.param1);
        AStream.Write(BitConverter.GetBytes(vStepInfo.timeTick), 0,
          sizeof(ushort));
        vByte = (byte)(vStepInfo.param2 << 4 | vStepInfo.command);
        AStream.WriteByte(vByte);
    }
}
5. 绘制方块处理

绘制方块处理是指在窗体内绘制指定的俄罗斯方块,并且在绘制过程中控制它的位置和显示样式。窗体图像绘制的具体实现流程如下。

(1)  通过方法DrawPoint()在窗体内绘制一个点的方块,其中变量AGraphics是绘制的图像,变量APoint是绘制的坐标,变量ABrick是绘制的方块图案,如果为空则表示清除。方法DrawPoint()的具体代码如下。

public void DrawPoint(Graphics AGraphics, Point APoint, byte ABrick)
{
    if (ImageList == null) return;
    if (ImageList.Images.Count <= 0) return;
    if (APoint.X < 0 || APoint.X >= colCount) return;
    if (APoint.Y < 0 || APoint.Y >= rowCount) return;
    Rectangle vRectangle = new Rectangle(
        APoint.X * brickWidth, APoint.Y * brickHeight,
        brickWidth, brickHeight);
    AGraphics.FillRectangle(new SolidBrush(BackColor), vRectangle);
    if (ABrick <= 0) return;
    ABrick = (byte)((ABrick - 1) % ImageList.Images.Count);
    Image vImage = ImageList.Images[ABrick];
    AGraphics.DrawImage(vImage, vRectangle.Location);
}

(2) 通过方法DrawPoints()在窗体内绘制整个点阵,其中变量AGraphics是绘制的图像。方法DrawPoints()的具体代码如下。

public void DrawPoints (Graphics AGraphics)
{
    if (ImageList == null) return;
    if (ImageList.Images.Count <= 0) return;
    for (int i = 0; i < colCount; i++)
        for (int j = 0; j < rowCount; j++)
            DrawPoint(AGraphics, new Point(i, j), points[i, j]);
}

(3) 通过方法DrawCurrent()在窗体内绘制当前被控制的方块,其中变量AGraphics是要绘制的图像,变量AClear用于设置是否清除绘制。方法DrawCurrent()的具体代码如下。

public void DrawCurrent(Graphics AGraphics, bool AClear)
{
    if (ImageList == null) return;
    if (ImageList.Images.Count <= 0) return;
    foreach (Point vPoint in brickTemplets[brickIndex][facingIndex])
        DrawPoint(AGraphics, new Point(vPoint.X + brickPoint.X,
            vPoint.Y + brickPoint.Y),
            AClear ? (byte)0 : (byte)(brickIndex + 1));
}

(4) 通过方法DrawNext()在窗体内绘制下一个出现的方块,其中变量AGraphics是要绘制的图像。方法DrawNext()的具体代码如下。

public void DrawNext(Graphics AGraphics)
{
    if (AGraphics == null) return;
    if (ImageList == null) return;
    if (ImageList.Images.Count <= 0) return;
    foreach (Point vPoint in brickTemplets[afterBrickIndex] [afterFacingIndex])
        DrawPoint(AGraphics, new Point(vPoint.X, vPoint.Y),
        (byte)(afterBrickIndex + 1));
}

(5) 通过方法DrawScore()在窗体内绘制积分框,其中变量AGraphics是要绘制的图像。方法DrawScore()的具体代码如下。

public void DrawScore(Graphics AGraphics)
{
    if (AGraphics == null) return;
    RectangleF vRectangleF = new Rectangle(0, 0, brickWidth * 4, brickHeight);
    StringFormat vStringFormat = new StringFormat();
    vStringFormat.FormatFlags |= StringFormatFlags.LineLimit;
    vStringFormat.Alignment = StringAlignment.Center;
    AGraphics.DrawString("Score", new Font(Font, FontStyle.Bold),
        Brushes.White, vRectangleF, vStringFormat);
    vRectangleF.Offset(0, brickHeight);
    AGraphics.DrawString(score.ToString(), Font,
        Brushes.White, vRectangleF, vStringFormat);
    vRectangleF.Offset(0, brickHeight);
    AGraphics.DrawString("Level", new Font(Font, FontStyle.Bold),
        Brushes.White, vRectangleF, vStringFormat);
    vRectangleF.Offset(0, brickHeight);
    AGraphics.DrawString(level.ToString(), Font,
        Brushes.White, vRectangleF, vStringFormat);
    vRectangleF.Offset(0, brickHeight);
    AGraphics.DrawString("Lines", new Font(Font, FontStyle.Bold),
        Brushes.White, vRectangleF, vStringFormat);
    vRectangleF.Offset(0, brickHeight);
    AGraphics.DrawString(lines.ToString(), Font,
        Brushes.White, vRectangleF, vStringFormat);
}

6. 游戏过程处理

游戏过程处理是指窗体内的游戏在玩的过程中的处理控制,即对窗体内方块的移动操作和行数的变化处理。游戏过程处理的具体实现流程如下。

(1) 通过方法CheckBrick()检查方块是否可以移动或变化到此位置,其中变量ABrickIndex表示模板序号,变量AFacingIndex表示朝向序号,变量ABrickPoint表示位置坐标。方法CheckBrick()的具体代码如下。

public bool CheckBrick(byte ABrickIndex, byte AFacingIndex, Point ABrickPoint)
{
    foreach (Point vPoint in brickTemplets[ABrickIndex][AFacingIndex])
    {
        if (vPoint.X + ABrickPoint.X < 0 ||
            vPoint.X + ABrickPoint.X >= colCount) return false;
        if (vPoint.Y + ABrickPoint.Y < 0 ||
            vPoint.Y + ABrickPoint.Y >= rowCount) return false;
        if (points[vPoint.X + ABrickPoint.X,
            vPoint.Y + ABrickPoint.Y] != 0) return false;
    }
    return true;
}

(2) 通过方法FreeLine()进行消行处理,即操作成功后将方块行删除。方法FreeLine()的具体实现代码如下。

public void FreeLine()
{
    int vFreeCount = 0;
    for (int j = rowCount - 1; j >= 0; j--)
    {
        bool vExistsFull = true; // 是否存在满行
        for (int i = 0; i < colCount && vExistsFull; i++)
            if (points[i, j] == 0)
                vExistsFull = false;
        if (!vExistsFull) continue;
        #region 图片下移
        Graphics vGraphics = Graphics.FromImage(backBitmap);
        Rectangle srcRect = new Rectangle(0, 0, backBitmap.Width, j * brickHeight);
        Rectangle destRect = srcRect;
        destRect.Offset(0, brickHeight);
        Bitmap vBitmap = new Bitmap(srcRect.Width, srcRect.Height);
        Graphics.FromImage(vBitmap).DrawImage(backBitmap, 0, 0);
        vGraphics.DrawImage(vBitmap, destRect, srcRect, GraphicsUnit.Pixel);
        vGraphics.FillRectangle(new SolidBrush(BackColor), 0, 0,
            backBitmap.Width, brickHeight);
        #endregion 图片下移
        lines++;
        vFreeCount++;
        for (int k = j; k >= 0; k--)
        {
            for (int i = 0; i < colCount; i++)
                if (k == 0)
                    points[i, k] = 0;
                else points[i, k] = points[i, k - 1];
        }
        j++;
    }
    score += scoress[vFreeCount];
    if (vFreeCount > 0)
    {
        level = Math.Min(lines / 30, speeds.Length - 1);
        timer.Interval = speeds[level];
        Invalidate();
    }
    if (youxiScore != null) youxiScore.Update(this);
}

(3) 通过方法BrickOperate()设置对方块的变化处理,即变化方块的旋转位置。其中参数ABrickOperates代表变化指令,具体含义是上、下、左、右移动和翻转处理。方法BrickOperate()的具体实现代码如下。

public bool BrickOperate(BrickOperates ABrickOperates)
{
    byte vFacingIndex = facingIndex;
    Point vBrickPoint = brickPoint;
    switch (ABrickOperates)
    {
        case BrickOperates.boTurnLeft:
            vFacingIndex = (byte)((vFacingIndex + 1) %
                brickTemplets[brickIndex].Count);
            break;
        case BrickOperates.boTurnRight:
            vFacingIndex = (byte)((brickTemplets[brickIndex].Count +
                vFacingIndex - 1) % brickTemplets[brickIndex].Count);
            break;
        case BrickOperates.boMoveLeft:
            vBrickPoint.Offset(-1, 0);
            break;
        case BrickOperates.boMoveRight:
            vBrickPoint.Offset(+1, 0);
            break;
        case BrickOperates.boMoveDown:
            vBrickPoint.Offset(0, +1);
            break;
        case BrickOperates.boMoveBottom:
            vBrickPoint.Offset(0, +1);
            while (CheckBrick(brickIndex, vFacingIndex, vBrickPoint))
                vBrickPoint.Offset(0, +1);
            vBrickPoint.Offset(0, -1);
            break;
    }
    if (CheckBrick(brickIndex, vFacingIndex, vBrickPoint))
    {
        if (playing && recordMode && !reviewing)
        {
            StepInfos.Add(new StepInfo(Environment.TickCount -
                lastRecordTime, 2, (byte)ABrickOperates, 0));
            lastRecordTime = Environment.TickCount;
        }
       
        Graphics vGraphics = Graphics.FromImage(backBitmap);
        DrawCurrent(vGraphics, true);
        facingIndex = vFacingIndex;
        brickPoint = vBrickPoint;
        DrawCurrent(vGraphics, false);
        if (ABrickOperates == BrickOperates.boMoveBottom)
            Downfall();
        else Invalidate();
    }
    else if (ABrickOperates == BrickOperates.boMoveDown)
    {
        if (playing && recordMode && !reviewing)
        {
            StepInfos.Add(new StepInfo(Environment.TickCount -
                lastRecordTime, 2, (byte)ABrickOperates, 0));
            lastRecordTime = Environment.TickCount;
        }
        Downfall();
    }
    return true;
}

(4) 通过方法NextBrick()预览显示下一个出现的方框,即下一个要操作控制的方框。方法NextBrick()的具体实现代码如下。

private void NextBrick()
{
    brickIndex = afterBrickIndex;
    facingIndex = afterFacingIndex;
    brickPoint.X = colCount / 2 - 1;
    brickPoint.Y = 0;
    afterBrickIndex = (byte)random.Next(brickTemplets.Count);
    afterFacingIndex = (byte)random.Next(brickTemplets
        [afterBrickIndex].Count);
    if (playing && recordMode && !reviewing)
    {
        StepInfos.Add(new StepInfo(0, 1, afterBrickIndex, afterFacingIndex));
        lastRecordTime = Environment.TickCount;
    }
    if (youxiNext != null && afterBrickIndex != brickIndex)
        youxiNext.Update(this);
    DrawCurrent(Graphics.FromImage(backBitmap), false);
    if (!CheckBrick(brickIndex, facingIndex, brickPoint))
    {
        if (playing && recordMode && !reviewing)
        {
            StepInfos.Add(new StepInfo(0, 3, 0, 0));
            lastRecordTime = Environment.TickCount;
        }
        GameOver();
    }
    Invalidate();
}

(5) 通过方法Downfall()执行方框落底后的处理,如果某行都满则执行行的删除方法程序。方法Downfall()的具体实现代码如下。

private void Downfall()
{
    foreach (Point vPoint in brickTemplets[brickIndex][facingIndex])
        points[vPoint.X + brickPoint.X,
            vPoint.Y + brickPoint.Y] = (byte)(brickIndex + 1);
    FreeLine();
    if (playing && !reviewing) NextBrick();
}
private void ImageListRecreateHandle(object sender, EventArgs e)
{
    DoChange();
}
private void DetachImageList(object sender, EventArgs e)
{
    ImageList = null;
}
public ImageList ImageList
{
    get
    {
        return imageList;
    }
    set
    {
        if (value != imageList)
        {
            EventHandler handler = new EventHandler
               (ImageListRecreateHandle);
            EventHandler handler2 = new EventHandler(DetachImageList);
            if (imageList != null)
            {
                imageList.RecreateHandle -= handler;
                imageList.Disposed -= handler2;
            }
            imageList = value;
            if (value != null)
            {
                brickWidth = ImageList.ImageSize.Width;
                brickHeight = ImageList.ImageSize.Height;
                DoChange();
                if (!playing && !reviewing) GameOver();
                if (youxiNext != null)
                {
                    youxiNext.BackColor = BackColor;
                    youxiNext.SetSize(brickWidth * 4, brickHeight * 4);
                    youxiNext.Update(this);
                }
                value.RecreateHandle += handler;
                value.Disposed += handler2;
            }
        }
    }
}

(6) 定义TetrisScore ()函数,设置得到游戏的积分,具体实现代码如下。

private void DetachyouxiScore(object sender, EventArgs e)
{
    youxiScore = null;
}
public youxiScore TetrisScore
{
    get
    {
        return youxiScore;
    }
    set
    {
        if (value != youxiScore)
        {
            EventHandler handler = new EventHandler(DetachyouxiScore);
            if (youxiScore != null) youxiScore.Disposed -= handler;
            youxiScore = value;
            if (value != null)
            {
                value.SetSize(4 * brickWidth, 6 * brickHeight);
                value.Update(this);
                value.Disposed += handler;
            }
        }
    }
}

(7) 定义ProgressBar()函数,设置实现回放进度条的处理,具体实现代码如下。

private void DetachProgressBar(object sender, EventArgs e)
{
    progressBar = null;
}
/// <summary>
/// 回放进度条
/// </summary>
public ProgressBar ProgressBar
{
    get
    {
        return progressBar;
    }
    set
    {
        if (value != progressBar)
        {
            EventHandler handler = new EventHandler
             (DetachProgressBar);
            if (progressBar != null) progressBar.Disposed -= handler;
            progressBar = value;
            if (value != null)
            {
                progressBar.Minimum = 0;
                progressBar.Maximum = StepInfos.Count;
                progressBar.Value = stepIndex < 0 ? 0 : stepIndex;
                value.Disposed += handler;
            }
        }
    }
}

(8) 定义各个热键事件处理方法,并通过方法OnKeyDown()设置按键按下时所要发生的对方块的控制方式,例如左移、右移和变换等方式,具体实现代码如下。

   protected override void OnPaint(PaintEventArgs e)
   {
       base.OnPaint(e);
       if (backBitmap != null) e.Graphics.DrawImage(backBitmap, 0, 0);
   }
   protected override bool IsInputKey(Keys keydata)
   {
       return (keydata == Keys.Down) || (keydata == Keys.Up) ||
           (keydata == Keys.Left) || (keydata == Keys.Right) ||
           (keydata == Keys.Escape) || base.IsInputKey(keydata);
   }
   protected override void OnMouseDown(MouseEventArgs e)
   {
       base.OnMouseDown(e);
       if (CanFocus) Focus();
   }
   protected override void Dispose(bool disposing)
   {
       if (threadReview != null) threadReview.Abort();
       base.Dispose(disposing);
   }
   protected override void OnKeyDown(KeyEventArgs e)
   {
       base.OnKeyDown(e);
       if (!playing || reviewing) return;
       switch (e.KeyCode)
       {
           case Keys.A:         // 左移
           case Keys.Left:
               BrickOperate(BrickOperates.boMoveLeft);
               break;
           case Keys.D:         // 右移
           case Keys.Right:
               BrickOperate(BrickOperates.boMoveRight);
               break;
           case Keys.W:         // 变化
           case Keys.Up:
               BrickOperate(BrickOperates.boTurnLeft);
                break;
            case Keys.Back:        // 变化
            case Keys.F:
                BrickOperate(BrickOperates.boTurnRight);
                break;
            case Keys.Down:        // 向下
            case Keys.S:
                BrickOperate(BrickOperates.boMoveDown);
                break;
            case Keys.Enter:        // 直下
            case Keys.Space:
            case Keys.End:
            case Keys.J:
                BrickOperate(BrickOperates.boMoveBottom);
                break;
        }
    }
}

(9) 通过类youxiNext来显示下一个出现的方块控件,并分别通过方法youxiNext()、Updata()、Clear()、SetSize()和OnPaint()来设置下一个方框。类youxiNext的具体实现代码如下。

public class youxiNext : Control
{
    public youxiNext()
    {
        BackColor = Color.Black;
        base.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
    }
    private Bitmap backBitmap;
    public void Clear()
    {
        Graphics vGraphics = Graphics.FromImage(backBitmap);
        vGraphics.FillRectangle(new SolidBrush(BackColor),
          vGraphics.ClipBounds);
    }
    public void Update(youxiControl AyouxiControl)
    {
        if (AyouxiControl == null) return;
        Clear();
        AyouxiControl.DrawNext(Graphics.FromImage(backBitmap));
        Invalidate();
    }
    public void SetSize(int AWidth, int AHeight)
    {
        Width = AWidth;
        Height = AHeight;
        backBitmap = new Bitmap(AWidth, AHeight);
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        if (backBitmap != null) e.Graphics.DrawImage(backBitmap, 0, 0);
    }
}

(10) 通过类youxiScore来显示游戏的当前积分,分别通过方法youxiScore()、Clear()、Update()、SetSize()和OnPaint()来进行积分的处理设置和显示。类youxiScore的具体实现代码如下。

// 显示积分信息的控件
public class youxiScore : Control
{
    private Bitmap backBitmap;
    public youxiScore()
    {
        BackColor = Color.Black;
        base.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
    }
    public void Clear()
    {
        Graphics vGraphics = Graphics.FromImage(backBitmap);
        vGraphics.FillRectangle(new SolidBrush(BackColor), vGraphics.
          ClipBounds);
    }
    public void Update(youxiControl AyouxiControl)
    {
        if (AyouxiControl == null) return;
        Clear();
        AyouxiControl.DrawScore(Graphics.FromImage(backBitmap));
        Invalidate();
    }
    public void SetSize(int AWidth, int AHeight)
    {
        Width = AWidth;
        Height = AHeight;
        backBitmap = new Bitmap(AWidth, AHeight);
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        if (backBitmap != null) e.Graphics.DrawImage(backBitmap, 0, 0);
    }
}

2005年7月19日,傍晚,要多学习

我历经16天的忙碌,今天终于完成了整个项目的开发工作。现在想来还唏嘘不已,仿佛是赶着鸭子上架的。当然这也不能完全怪自己,在过去的大学三年当中,平常学习编写的代码只有短短几十行,超过100行的代码寥寥无几。现在我完成了暑期作业,发现整个代码有700多行,要是在以前这是万万不能想象的。看来要想走向成功,勇敢迈出第一步还是很重要的!

其实在具体编码之前,在我脑海中已有很多思路,但是在具体编码时,我发现一行代码也写不出来。这种情况我相信在很多初学者身上都发生过,并且不只发生一次。我没有办法,只能自己搜集资料学习。

经过30分钟的学习后,我重新巩固了基本知识,我感觉我的效率不错!其实无论是在校学生,还是职场白领,都应该利用业余时间给自己充电,特别是开发人员。这样不但能提高自己,而且有利于自己以后的发展。

总结完毕之后,我决定早点休息,为接下来的调试工作养精蓄锐。

您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:1.6.1 事件处理程序
下一篇:1.7 最后的战役——测试运行
相关文章
图文推荐
排行
热门
最新书评
文章
下载
读书
特别推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站