x01.Game.Dragon.1: 恐龙飞行

 与其白手起家,不如学习高手。恐龙飞行是个不错的入门例子,实现如下。

1.运行 VS2010,新建 WinForm 项目 x01.Game.Dragon,将 Form1.cs 改名为 MainForm.cs,在 MainForm 类中,添加字段

  bool  isOver = false;

  添加引擎函数 Run(),代码如下:

Run()
void Run()
{
    while (!isOver)
    {
        Application.DoEvents();
    }
    Application.Exit();
}

  添加 override 函数 OnKeyDown(),代码如下:

OnKeyDown()
        protected override void OnKeyDown(KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Enter:
                    Run();
                    break;
                case Keys.Escape:
                    GameOver();
                    break;
                case Keys.Up:
                    break;
                case Keys.Right:
                    break;
                case Keys.Down:
                    break;
                case Keys.Left:
                    break;
            }
        }

  添加退出函数 GameOver(),代码如下:

GameOver()
        private void GameOver()
        {
            isOver = true;
        }

  如此,游戏框架完成。虽然现在什么都不干,但看 OnKeyDown() 函数,不难发现,Enter 键开始,Esc 键结束,箭头键控制飞行方向。

2.游戏说白了,就是操作图形,GDI+ 是个不错的选择。Graphics 提供操作方法,Bitmap 提供被操作对象,将 Bitmap 提供给 PictureBox 的 Image 属性,即可实现游戏的基本场景。添加类 GameBox,继承自 PictureBox。其代码文件 GameBox.cs 的内容如下:

GameBox
    class GameBox : PictureBox
    {
        Graphics device;
        Bitmap surface;
        Font font;

        public GameBox(int width, int height)
        {
            BackColor = Color.Black;

            surface = new Bitmap(width, height);
            Image = surface;
            device = Graphics.FromImage(surface);

            font = new Font("Arial", 18, FontStyle.Regular, GraphicsUnit.Pixel);
        }

        protected override void Dispose(bool disposing)
        {
            device.Dispose();
            surface.Dispose();
            font.Dispose();

            base.Dispose(disposing);
        }

        public Graphics Device
        {
            get { return device; }
        }

        public void Refresh()
        {
            Image = surface;
        }

        public void Print(int x, int y, string text, Brush brush = null)
        {
            if (brush == null)
            {
                brush = Brushes.White;
            }

            Device.DrawString(text, font, brush, x, y);
        }
    }

  Print() 是为了输出文本,Device 是为了提供操作,如此而已。
3.终于到了核心的部分:Sprite 类。其代码文件 Sprite.cs 内容如下:

Sprite
    class Sprite
    {
        public enum FrameDirectionE
        {
            Forward = 1, Backward = -1, None = 0
        }
        public enum FrameWrapE
        {
            Wrap, Bounce
        }

        GameBox gameBox;

        public Sprite(ref GameBox box)
        {
            gameBox = box;
            Rate = 30;
            FrameDirection = FrameDirectionE.Forward;
        }

        public PointF Position { get; set; }
        public PointF Offset { get; set; }
        public Size Size { get; set; }
        public Bitmap Image { get; set; }
        public int Columes { get; set; }
        public int Frames { get; set; }
        public int CurrentFrame { get; set; }
        public FrameDirectionE FrameDirection { get; set; }
        public FrameWrapE FrameWrap { get; set; }
        public int Rate { get; set; }
        public Rectangle Bound
        {
            get { return new Rectangle((int)Position.X, (int)Position.Y, Size.Width, Size.Height); }
        }

        public void Animation(int startFram, int endFrame)
        {
            int lastTime = 0;
            int time = Environment.TickCount;
            if (time > lastTime + Rate)
            {
                lastTime = time;
                CurrentFrame += (int)FrameDirection;
                switch (FrameWrap)
                {
                    case FrameWrapE.Wrap:
                        if (CurrentFrame < startFram)
                        {
                            CurrentFrame = endFrame;
                        }
                        else if (CurrentFrame > endFrame)
                        {
                            CurrentFrame = startFram;
                        }
                        break;
                    case FrameWrapE.Bounce:
                        if (CurrentFrame < startFram)
                        {
                            CurrentFrame = startFram;
                            FrameDirection = FrameDirectionE.Forward;
                        }
                        else if (CurrentFrame > endFrame)
                        {
                            CurrentFrame = endFrame;
                            FrameDirection = FrameDirectionE.Backward;
                        }
                        break;
                    default:
                        break;
                }
            }
        }

        public void Draw()
        {
            Rectangle frame = new Rectangle();
            frame.X = (CurrentFrame % Columes) * Size.Width;
            frame.Y = (CurrentFrame / Columes) * Size.Height;
            frame.Width = Size.Width;
            frame.Height = Size.Height;
            gameBox.Device.DrawImage(Image, Bound, frame, GraphicsUnit.Pixel);
        }
    }

  仔细一看,只有两个方法:Animation() 和 Draw()。Draw() 比较简单,就是把需要的 Frame 绘制到指定的位置。Animation() 则是通过设置控制 Frame 的移动,以形成动画效果。
4.有了 GameBox 和 Sprite,再回到 MainForm 就简单了。最后的代码文件 MainForm.cs 内容如下:

MainForm
    public partial class MainForm : Form
    {
        const string ImgDir = @"F:\Resource\Image\game\";

        GameBox gameBox;
        Sprite sprite;
        Bitmap dragon;
        Bitmap grass;
        PointF offset;

        bool isOver = false;
        int direction = 2;

        public MainForm()
        {
            InitializeComponent();
            Init();
        }

        protected override void OnClosed(EventArgs e)
        {
            GameOver();
        }
        protected override void OnKeyDown(KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Enter:
                    Run();
                    break;
                case Keys.Escape:
                    GameOver();
                    break;
                case Keys.Up:
                    direction = 0;
                    break;
                case Keys.Right:
                    direction = 2;
                    break;
                case Keys.Down:
                    direction = 4;
                    break;
                case Keys.Left:
                    direction = 6;
                    break;
            }
        }

        void Run()
        {
            int currentTime = 0, startTime = 0;
            while (!isOver)
            {
                currentTime = Environment.TickCount;
                if (currentTime > startTime + 16)
                {
                    startTime = currentTime;
                    Draw();
                    Application.DoEvents();
                    gameBox.Refresh();
                }
            }
            Application.Exit();
        }
        private void GameOver()
        {
            isOver = true;
        }
        private void Init()
        {
            Text = "Dragon Game";
            Size = new Size(800, 600);

            gameBox = new GameBox(800, 600);
            gameBox.Parent = this;
            gameBox.Dock = DockStyle.Fill;
            gameBox.Print(10, 10, "Enter: 开始");
            
            dragon = new Bitmap(ImgDir + "dragon.png");
            grass = new Bitmap(ImgDir + "grass.bmp");

            sprite = new Sprite(ref gameBox);
            sprite.Image = dragon;
            sprite.Size = new Size(256, 256);
            sprite.Columes = 8;
            sprite.Frames = 64;
            sprite.Position = new PointF(250, 150);
            
        }

        void Draw()
        {
            gameBox.Device.DrawImageUnscaled(grass, 0, 0, 800, 600);

            switch (direction)
            {
                case 0: // up
                    offset = new PointF(0, -1);
                    break;
                case 2: // right
                    offset = new PointF(1, 0);
                    break;
                case 4: // down
                    offset = new PointF(0, 1);
                    break;
                case 6: // left
                    offset = new PointF(-1, 0);
                    break;
                default:
                    break;
            }

            sprite.Position = new PointF(sprite.Position.X + offset.X, sprite.Position.Y + offset.Y);
            sprite.Animation(direction * 8 + 1, direction * 8 + 8);
            sprite.Draw();

            gameBox.Print(0, 0, "Esc: 结束\t->: 方向");
        }
    }

  其中 ImgDir 应作适当调整。dragon.png 文件是 8*8 的 Frame 集合,0,2,4,6 行分别表示上、右、下、左方向。相关资源,可到我的 x01.download 中下载。

5.本例学自《Virtual C# Game Programming for Teens》,但作了点精简,以突出主干。一本好书胜过一个好老师,的确如此。运行效果图如下:

 

posted on 2012-05-09 21:03  x01  阅读(1621)  评论(2编辑  收藏  举报

导航