zoukankan      html  css  js  c++  java
  • c#版flappybird 未完全实现

    这些天开始在深圳找工作,想着把从前有些淡忘的技术再温故下。看到尊敬的《传智播客》有一期公开课,讲的是用c#编写flappybird小游戏,也就自己搜了下游戏资源,也来试试看。

    其实用到的技术就是传智播客讲的。只不过项目结构和一些逻辑稍微修改了下,更加符合原游戏的特点。

    ————————再次声明:非本人独创方案————————————

    导入资源

    QQ截图20141208172348

    窗体背景设置为background图片382*681,然后在窗体上加个picturebox,放入road图片,大小拉伸.

    QQ截图20141208172705

    而后加三个timer,依次设置100ms,10ms,10ms

    QQ截图20141213110101

    如此,游戏界面搭建完成。

    下面是代码结构,所有游戏元素的父类,抽象类:public abstract class GameObj

    小鸟类:public class Bird : GameObj

    管道类:public class Pipe : GameObj

    重力类:public static class Gravity

    GameObj类:添加两个抽象方法void Move()  和 void Draw(Graphics g)

    下面是整个类的代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Drawing;
    namespace flappybird
    {
        /// <summary>
        /// 游戏对象的父类
        /// </summary>
        public abstract class GameObj
        {
            public int X { get; set; }
            public int Y { get; set; }
            public int Height { get; set; }
            public int Width { get; set; }
    
            public GameObj(int x,int y,int height,int width)
            {
                this.X = x;
                this.Y = y;
                this.Height = height;
                this.Width = width;
            }
            public abstract void Move();
            public abstract void Draw(Graphics g);
        }
    }
     

    下面是Bird类源码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Drawing;
    using flappybird.Properties;
    
    namespace flappybird
    {
        public class Bird : GameObj
        {
            /// <summary>
            /// 各种小鸟形态数组
            /// </summary>
            private static Image[] imgs ={
                             Properties.Resources.bird_blue_0,
                             Properties.Resources.bird_blue_1,
                             Properties.Resources.bird_blue_2
                         };
            /// <summary>
            /// 当前绘制的小鸟形态的索引
            /// </summary>
            public int BirdIndex { get; set; }
            /// <summary>
            /// 存储当前下落速度
            /// </summary>
            public float Speed { get; set; }
            /// <summary>
            /// 存储已下落持续时间
            /// </summary>
            public float Time { get; set; }
    
    
    
    
            private static Bird bird = null;
            /// <summary>
            /// 单例模式,确保只实例化一个Bird对象
            /// </summary>
            /// <returns></returns>
            public static Bird GetSingleBird()
            {
                if (bird == null)
                {
                    bird = new Bird(100, 200, 0);
                }
                return bird;
            }
            /// <summary>
            /// 调用父类的构造函数
            /// </summary>
            /// <param name="x"></param>
            /// <param name="y"></param>
            /// <param name="birdIndex"></param>
            private Bird(int x, int y, int birdIndex)
                : base(x, y, imgs[0].Height, imgs[0].Width)
            {
                this.BirdIndex = birdIndex;
                this.Time = 10f;
                this.Speed = 0f;
            }
    
            /// <summary>
            /// 重写父类的抽象Draw方法
            /// </summary>
            public override void Draw(Graphics g)
            {
                switch (this.BirdIndex)
                {
                    case 0:
                        g.DrawImage(imgs[0], this.X, this.Y);
                        break;
                    case 1:
                        g.DrawImage(imgs[1], this.X, this.Y);
                        break;
                    case 2:
                        g.DrawImage(imgs[2], this.X, this.Y);
                        break;
                    default:
                        break;
                }
                this.BirdIndex++;
                this.BirdIndex %= 3;
            }
            /// <summary>
            /// 重写父类的抽象Move方法
            /// </summary>
            public override void Move()
            {
                this.Y = this.Y <= 60 ? 0 : this.Y - 60;
                //this.Y -= 30;
            }
            /// <summary>
            /// 获取图形区域,用于碰撞检测
            /// </summary>
            /// <returns></returns>
            public static Rectangle GetRectangle()
            {
                return new Rectangle(bird.X, bird.Y, Resources.bird_blue_0.Width, Resources.bird_blue_0.Height);
            } 
        }
    }
    

    结合注释,相信大家都能搞懂。。。

    Pipe类:

    此类中,我定义了一个pipes数组,用来向画面中呈现3列水管(每列包含上下两个水管)

    然后用InitPipes()函数初始化。此函数中,用了一种比较讨巧的方法,就是先判断数组中最后一个元素是否为null,类似Bird类中使用的单例模式。

    具体代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Drawing;
    using flappybird.Properties;
    namespace flappybird
    {
        public class Pipe : GameObj
        {
            public override void Move()
            {
                for (int i = 0; i < 3; i++)
                {
                    pipes[i, 0].X -= 1;
                    pipes[i, 1].X -= 1;
                }
                // this.X -= 1;
            }
            public override void Draw(System.Drawing.Graphics g)
            {
                for (int i = 0; i < 3; i++)
                {
                    //画出上水管
                    g.DrawImage(Resources.bound_up, pipes[i, 0].X, pipes[i, 0].Y);
                    //画出下水管
                    g.DrawImage(Resources.bound_down, pipes[i, 1].X, pipes[i, 1].Y);
                }
    
    
            }
            private Pipe(int x, int y)
                : base(x, y, Resources.bound_down.Height, Resources.bound_down.Width)
            {
    
            }
    
            private static Pipe[,] pipes = new Pipe[3, 2];
            private static void InitPipes()
            {
                if (pipes[2, 1] == null)
                {
                    Random rand = new Random(DateTime.Now.Millisecond);
                    for (int i = 0; i < 3; i++)
                    {
                        int y = rand.Next(80, 380);
                        //上水管
                        pipes[i, 0] = new Pipe(300 + i * 200, -y);
                        //下水管
                        pipes[i, 1] = new Pipe(300 + i * 200, -y + Resources.bound_up.Height + 170);
                    }
                }
            }
            public static Pipe GetPipes(int row, int col)
            {
                if (row > 1 || col > 2)
                {
                    return null;
                }
                InitPipes();
                return pipes[col, row];
            }
            public static Rectangle GetRectangle(int row,int col)
            {
                return new Rectangle(pipes[col, row].X, pipes[col, row].Y,
                    Resources.bound_up.Width, Resources.bound_up.Height);
            }
        }
    }
    

    然后Gravity类:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace flappybird
    {
        public static class Gravity
        {
            private const float g=9.8f;
            private const float a = 40f;
            /// <summary>
            /// 获取下落的距离
            /// </summary>
            /// <param name="time"></param>
            /// <param name="speed"></param>
            /// <returns></returns>
            public static int GetDropHeight(float time,float speed)
            {
                
                if (speed<0)
                {
                    return (int)(0.5f * a * time * time + speed * time);
    
                }
                return (int)(0.5f * g * time * time + speed * time);
    
            }
            /// <summary>
            /// 获取下落后的速度
            /// </summary>
            /// <param name="time"></param>
            /// <param name="speed"></param>
            /// <returns></returns>
            public static float GetDropSpeed(float time,float speed)
            {
                if (speed<0)
                {
                    return speed + a * time;
                }
                return speed + g * time;
            }
        }
    }
    

    定义一个 float a;这个表示小鸟向上飞的时候受到的向下的加速度。之所以这样设计,是为了小鸟向上飞的时候,不是一下就跳到某个高度,让它连续上升,但是减速特快的那种效果。

    我看传智播客里赵老师是直接设置个上升高度,给人的感觉是小鸟一下就”飞“上去了,这样设计会简单些,缺点是没有连续效果。

    大餐来了,Form1代码:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using flappybird.Properties;
    namespace flappybird
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
            private bool IsGameOver = false;
            private void Form1_Load(object sender, EventArgs e)
            {
                timer1.Enabled = true;
                timer2.Enabled = true;
                timer3.Enabled = true;
    
            }
    
            private void Form1_Paint(object sender, PaintEventArgs e)
            {
                if (IsGameOver)
                {
                    return;
                }
                Bird.GetSingleBird().Draw(e.Graphics);
                Pipe.GetPipes(0, 0).Draw(e.Graphics);
            }
    
            private void timer1_Tick(object sender, EventArgs e)
            {
                if (IsGameOver)
                {
                    return;
                }
                this.Invalidate();
            }
    
            private void Form1_MouseClick(object sender, MouseEventArgs e)
            {
                if (IsGameOver)
                {
                    return;
                }
                //Bird.GetSingleBird().Move();
                Bird.GetSingleBird().Speed = -80f;
            }
    
            private void Form1_KeyPress(object sender, KeyPressEventArgs e)
            {
    
            }
    
            private void Form1_KeyDown(object sender, KeyEventArgs e)
            {
                if (IsGameOver)
                {
                    return;
                }
                if (e.KeyCode == Keys.Space)
                {
                    //Bird.GetSingleBird().Move();
                    Bird.GetSingleBird().Speed = -80f;
                }
            }
            /// <summary>
            /// 计算重力影响
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void timer2_Tick(object sender, EventArgs e)
            {
                if (IsGameOver)
                {
                    return;
                }
                int dropHeight = Gravity.GetDropHeight(Bird.GetSingleBird().Time * 0.01f, Bird.GetSingleBird().Speed);
    
                int currentY = Bird.GetSingleBird().Y + dropHeight;
                int maxY = ClientRectangle.Height - pictureBox1.Height - Bird.GetSingleBird().Height;
                if (currentY >= maxY)
                {
                    Bird.GetSingleBird().Y = maxY;
                }
                else if (currentY < 0)
                {
                    Bird.GetSingleBird().Y = 0;
                }
                else
                {
                    Bird.GetSingleBird().Y = currentY;
                }
                Bird.GetSingleBird().Speed = Gravity.GetDropSpeed(Bird.GetSingleBird().Time * 0.01f, Bird.GetSingleBird().Speed);
            }
    
            Random rand = new Random(DateTime.Now.Millisecond);
    
            private void timer3_Tick(object sender, EventArgs e)
            {
                if (IsGameOver)
                {
                    return;
                }
                Pipe.GetPipes(0, 0).Move();
                //最靠近左边的管道列,为了下面的碰撞检测做准备。
                //因为只有最左边的管道才需要碰撞检测。避免不必要的操作。
                int left = 0;
                for (int i = 0; i < 3; i++)
                {
                    //若某一列管道左移出了屏幕范围,则把其放到屏幕右端外
                    //然后再进行左移
                    if (Pipe.GetPipes(0, i).X < -90)
                    {
                        //若i列移出了屏幕,则现在是(i + 1) % 3列在最左侧
                        left = (i + 1) % 3;
                        Console.WriteLine(left);
                        int y = rand.Next(80, 380);
                        //上面的管道
                        Pipe.GetPipes(0, i).X = 500;
                        Pipe.GetPipes(0, i).Y = -y;
                        //下面的管道
                        Pipe.GetPipes(1, i).X = 500;
                        Pipe.GetPipes(1, i).Y = -y + Resources.bound_up.Height + 170;
                    }
                }
                bool isInterUp = Bird.GetRectangle().IntersectsWith(Pipe.GetRectangle(0, left));
                bool isInterDown = Bird.GetRectangle().IntersectsWith(Pipe.GetRectangle(1, left));
                bool isInterGround = Bird.GetRectangle().IntersectsWith(new Rectangle(pictureBox1.Location, pictureBox1.Size));
                if (isInterUp||isInterDown||isInterGround)
                {
                    timer1.Enabled = false;
                    timer2.Enabled = false;
                    timer3.Enabled = false;
                    IsGameOver = true;
                }
    
            }
        }
    }
    

    结合前面的几个类,这里就留给大家自己体会吧。(绝不是我懒得写说明。哈哈哈)

    QQ截图20141213142407

    传送门:http://download.csdn.net/detail/geeking/8255401

  • 相关阅读:
    各种各样的轮播图
    让你分分钟学会Javascript中的闭包
    [数据清洗]-混乱的邮编数据
    [数据分析工具] Pandas 功能介绍(二)
    [机器学习]-[数据预处理]-中心化 缩放 KNN(二)
    [机器学习]-[数据预处理]-中心化 缩放 KNN(一)
    [数据分析工具] Pandas 不可不知的功能(一)
    [总结]-2018 w1
    [数据清洗]-看上去一样的数字
    [数据清洗]- Pandas 清洗“脏”数据(三)
  • 原文地址:https://www.cnblogs.com/geeking/p/4161272.html
Copyright © 2011-2022 走看看