用C#编写俄罗斯方块游戏-含代码

用C#编写俄罗斯方块游戏-含代码

效果展示

在这里插入图片描述
简介:
俄罗斯方块是很多人儿时玩过的游戏,作者本人也不例外,那是我玩的是手持式液晶屏游戏机,其趣味性极强,成为了儿时不可或缺的美好记忆。本文展示如何通过C#设计这样一款桌面游戏,同时也展示了如何规范的编写代码,对于C#初学者或迫切需要规范编码的人群非常友好。

项目结构

在这里插入图片描述
代码将游戏分成两部分:
1、Tetris.Core是一个动态链接库(DLL),他主要实现游戏的数据结构、逻辑以及算法,其实现与图形界面完全无关,所以可以方便的移植到别的UI实现方式,如WPF、Web,甚至可以移植到Web,而不需要修改Tetris.Core中的代码。
2、TetrisWF是实现展示游戏的UI模块,砖块的绘制和用户的交互动作都由他来实现,由于游戏功能已经在Core中实现,所以此模块的代码相对较少。

游戏核心类Game的实现代码

using System;
using System.Drawing;
using System.Timers;

namespace Tetris.Core
{
    /// <summary>
    /// 定义游戏对象
    /// </summary>
    public class Game
    {
        private Timer tmrEngine = null;//游戏执行时钟或引擎

        #region 基础数据结构
        /// <summary>
        /// 获取游戏当前的分数,从0开始
        /// </summary>
        public int Score { get; private set; }

        /// <summary>
        /// 获取游戏当前等级, 从1开始
        /// </summary>
        public int Level { get; private set; }

        /// <summary>
        /// 墙体对象
        /// </summary>
        public Wall Wall { get; }

        /// <summary>
        /// 获取当前砖块对象
        /// </summary>
        public Block Block { get; private set; }

        /// <summary>
        /// 获取当前砖块的中心点位置
        /// </summary>
        public Point CurrentBlockPosition { get; private set; } = Point.Empty;

        /// <summary>
        /// 获取下一个砖块对象
        /// </summary>
        public Block NextBlock { get; private set; }
        #endregion

        /// <summary>
        /// 设置或获取游戏的绘制宿主
        /// </summary>
        public IDrawing DrawingHost { get; set; } = null;

        /// <summary>
        /// 获取一个值,该值指示游戏是否已经结束
        /// </summary>
        public bool IsGameOver { get; private set; } = false;

        /// <summary>
        /// 获取砖块仓库对象
        /// </summary>
        private BlockStore BlockStore { get; } = null;

        /// <summary>
        /// 游戏结束时触发
        /// </summary>
        public event EventHandler GameOver = null;

        public Game(int wallWidth, int wallHeight, BlockStore blockStore)
        {
            this.Block = null;
            this.BlockStore = blockStore;
            this.NextBlock = blockStore.CreateNextBlock();
            this.Wall = new Wall(wallWidth, wallHeight, Color.Black);
        }

        /// <summary>
        /// 开始游戏
        /// </summary>
        public void Start()
        {
            if (IsGameOver)
            {
                Wall.Reset();
                IsGameOver = false;
            }
            if (tmrEngine == null)
            {
                tmrEngine = new Timer();
                tmrEngine.Interval = 500;
                tmrEngine.Elapsed += TmrEngine_Elapsed;
                tmrEngine.AutoReset = true;
                Reset();
            }
            tmrEngine.Start();
        }

        /// <summary>
        /// 获取一个值,该值指示游戏是否在运行
        /// </summary>
        public bool IsStarted
        {
            get
            {
                if (tmrEngine == null)
                    return false;
                return tmrEngine.Enabled;
            }
        }

        /// <summary>
        /// 暂停正在进行的游戏
        /// </summary>
        public void Stop()
        {
            if (tmrEngine != null)
            {
                tmrEngine.Stop();
            }
        }

        /// <summary>
        /// 复位游戏数据
        /// </summary>
        public void Reset()
        {
            if (tmrEngine != null)
                tmrEngine.Interval = 500;
            Score = 0;
            Level = 1;
        }

        private void TmrEngine_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (this.Block == null)
            {
                if (!PlaceBlockToTop(this.NextBlock))
                {
                    //当放不下新砖块时,游戏便可以结束了
                    IsGameOver = true;
                    Stop();
                    Redraw();
                    GameOver?.Invoke(this, EventArgs.Empty);
                    return;
                }
                else
                    this.CreateNextBlock();
            }
            else
            {
                if (!MoveToBottom())
                {
                    Merge();
                    int count = CleanUpFullLines();
                    UpdateScoreAndLevel(count);
                }
            }
            Redraw();
        }

        private void UpdateScoreAndLevel(int count)
        {
            Score += count * 10;
            if (Score >= 100)
            {
                Score = 0;
                Level++;
                tmrEngine.Interval = tmrEngine.Interval * 0.8;
            }
        }

        /// <summary>
        /// 重绘游戏对象,他用来通知宿主程序绘制游戏本身
        /// 它可以是WinForm,也可以是WPF和其它
        /// </summary>
        public void Redraw()
        {
            if (DrawingHost != null)
                DrawingHost.Draw(this);
        }

        /// <summary>
        /// 创建下一个砖块,预备砖块
        /// </summary>
        private void CreateNextBlock()
        {
            this.NextBlock = BlockStore.CreateNextBlock();
            Redraw();
        }

        /// <summary>
        /// 放置一个新的砖块
        /// </summary>
        /// <param name="block"></param>
        /// <returns></returns>
        private bool PlaceBlockToTop(Block block)
        {
            int left = (this.Wall.Width - Block.Width) / 2;

            if (!Measure(new Point(left, 0), block))
                return false;

            this.Block = block.Copy();
            this.CurrentBlockPosition = new Point(left, 0);
            Redraw();
            return true;
        }

        #region 移动和旋转砖块
        /// <summary>
        /// 将当前砖块向下移动一格
        /// </summary>
        /// <returns></returns>
        public bool MoveToBottom()
        {
            return Move(0, 1);
        }

        /// <summary>
        /// 快速丢下砖块
        /// </summary>
        public void FastDown()
        {
            while (MoveToBottom()) ;
        }

        /// <summary>
        /// 将当前砖块向左移动一格
        /// </summary>
        /// <returns></returns>
        public bool MoveToLeft()
        {
            return Move(-1, 0);
        }

        /// <summary>
        /// 将当前砖块向右移动一格
        /// </summary>
        /// <returns></returns>
        public bool MoveToRight()
        {
            return Move(1, 0);
        }

        /// <summary>
        /// 旋转当前砖块
        /// </summary>
        /// <param name="direction">指定旋转方向</param>
        /// <returns>返回值确认是否旋转成功</returns>
        public bool Rotate(RotationDirection direction)
        {
            if (this.Block == null)
                return false;
            Block newBlock = this.Block.Copy();

            bool cw = direction == RotationDirection.CW;
            for (int x = 0; x < Block.Width; x++)//X
            {
                for (int y = 0; y < Block.Height; y++)//Y
                {
                    newBlock[x, y] = cw ? this.Block[y, 2 - x] : this.Block[2 - y, x];
                }
            }

            //确认旋转后的砖块有效部分是否超出了墙体
            if (!Measure(this.CurrentBlockPosition, newBlock))
                return false;

            this.Block = newBlock;
            Redraw();

            return true;
        }

        /// <summary>
        /// 将当前砖块移动指定的偏移量
        /// </summary>
        /// <param name="xOffset"></param>
        /// <param name="yOffset"></param>
        /// <returns></returns>
        private bool Move(int xOffset, int yOffset)
        {
            if (this.Block == null)
                return false;

            int left = CurrentBlockPosition.X + xOffset;
            int top = CurrentBlockPosition.Y + yOffset;

            if (!Measure(new Point(left, top), this.Block))
                return false;

            this.CurrentBlockPosition = new Point(left, top);
            Redraw();
            return true;
        }
        #endregion

        #region 砖块与墙体合并/清除满行
        /// <summary>
        /// 将当前砖块融合到墙体
        /// </summary>
        private void Merge()
        {
            int left = CurrentBlockPosition.X;
            int top = CurrentBlockPosition.Y;
            Block block = this.Block;

            for (int x = left; x < left + Block.Width; x++)
            {
                for (int y = top; y < top + Block.Height; y++)
                {
                    if (block.Data[x - left, y - top])
                    {
                        Wall[x, y] = true;
                        Wall.Colors[x, y] = block.Color;
                    }
                }
            }
            this.Block = null;
            Redraw();
        }

        /// <summary>
        /// 清理满行,并返回清理的行数
        /// </summary>
        /// <returns></returns>
        private int CleanUpFullLines()
        {
            int count = 0;
            for (int y = Wall.Height - 1; y >= 0; y--)
            {
                if (IsFullLine(y))
                {
                    CleanUpLine(y);
                    y++;
                    count++;
                }
            }
            return count;
        }

        /// <summary>
        /// 清理指定行
        /// </summary>
        /// <param name="lineNo"></param>
        private void CleanUpLine(int lineNo)
        {
            for (int x = 0; x < Wall.Width; x++)
                Wall[x, lineNo] = false;
            for (int y = lineNo; y >= 0; y--)
            {
                for (int x = 0; x < Wall.Width; x++)
                {
                    if (y == 0)
                    {
                        Wall[x, y] = false;
                        Wall.Colors[x, y] = Wall.BackColor;
                    }
                    else
                    {
                        Wall[x, y] = Wall[x, y - 1];
                        Wall.Colors[x, y] = Wall.Colors[x, y - 1];
                    }
                }
            }
        }

        /// <summary>
        /// 判断是否为满行
        /// </summary>
        /// <param name="lineNo"></param>
        /// <returns></returns>
        private bool IsFullLine(int lineNo)
        {
            bool isFullLine = true;
            for (int x = 0; x < Wall.Width; x++)
            {
                if (!Wall[x, lineNo])
                {
                    isFullLine = false;
                    break;
                }
            }
            return isFullLine;
        }
        #endregion

        /// <summary>
        /// 测试当前位置是否可以容纳当前砖块(碰撞测试)
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private bool Measure(Point position, Block block)
        {
            int left = position.X;
            int top = position.Y;

            for (int x = left; x < left + Block.Width; x++)
            {
                for (int y = top; y < top + Block.Height; y++)
                {
                    //越界判断,当出现越界时,则表示当前砖块的有效部分已经超出墙体
                    bool indexIsOk = (x >= 0 && x < this.Wall.Width &&
                        y >= 0 && y < this.Wall.Height);
                    if (block.Data[x - left, y - top] 
                        && (!indexIsOk || this.Wall.Data[x, y]))
                        return false;
                }
            }
            return true;
        }
    }
}

Game 类主要实现游戏的算法,生产新的砖块、左右移动等逻辑动作,碰撞检测、砖块消除以及消除后的积分动作都由他来实现。

以上供参考,谢谢。

代码连接

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值