2015年11月12日 星期四

C# - 泡泡射擊遊戲

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsForms_Game1
{
    public partial class Form1 : Form
    {
        private Point player; //存放玩家x,y       
        private Point[] vMonster; //存放怪物x,y
        private Bullet[] vBullet; //存放子彈x,y       

        private Random random;

        private Graphics wndGraphic; //視窗畫布
        private Graphics backGraphic; //背景頁畫布
        private Bitmap backBmp; //背景點陣圖

        private const int VIEW_W = 1024;
        private const int VIEW_H = 768;

        private const float MAKE_MONSTER_TIME = 3;
        private float makeMonsterTime; //生怪的時間

        private const float MAKE_BULLET_TIME = 0.1f;
        private float makeBulletTime; //再過多久生子彈的時間

        private const float REQUIRE_FPS = 60; //預期的畫面更新率(每秒呼叫幾次onTimer)

        private const int BALL_SIZE = 50; //球的直徑

        private const int MAX_ENEMY = 30;
        private const int MAX_BULLET = 10;

        private Point mousePos;
       
        private KeyState keySpace;//空白鍵
        private KeyState keyUp;//
        private KeyState keyDown;//
        private KeyState keyLeft;//
        private KeyState keyRight;//

        public Form1()
        {
            InitializeComponent();

            keySpace = new KeyState(Keys.Space);
            keyUp = new KeyState(Keys.Up);
            keyDown = new KeyState(Keys.Down);
            keyLeft = new KeyState(Keys.Left);
            keyRight = new KeyState(Keys.Right);

            makeBulletTime = 0;

            makeMonsterTime = MAKE_MONSTER_TIME;

            wndGraphic = CreateGraphics(); //建立視窗畫布

            backBmp = new Bitmap(VIEW_W, VIEW_H);
            backGraphic = Graphics.FromImage(backBmp);

            mousePos = new Point();

            random = new Random();
            random = new Random();

            player = new Point();
            player.x = 100;
            player.y = 100;

            vMonster = new Point[MAX_ENEMY];
            vBullet = new Bullet[MAX_BULLET];

            for (int i = 0; i < MAX_ENEMY; i++)
            {
                vMonster[i] = new Point();
                vMonster[i].x = random.Next(0, VIEW_W);
                vMonster[i].y = random.Next(0, VIEW_H);
            }
           
            timer1.Interval = 1000 / (int)REQUIRE_FPS; // 1/30
            timer1.Start();
        }

        private void onPaint(object sender, PaintEventArgs e)
        {
           drawGame();
        }

        private void moveMonster()
        {
            for (int i = 0; i < MAX_ENEMY; i++)
            {
                if (vMonster[i] != null)
                {
                    vMonster[i].move(player);

                    /*碰到玩家球就消失
                    if (vMonster[i].getDistance(player) < BALL_SIZE)
                    {
                        vMonster[i] = null;
                    }
                    */
                }
            }
        }

        private void moveBullet_KillMonster()
        {
            for (int i = 0; i < MAX_BULLET; i++)
            {
                if (vBullet[i] != null)
                {
                    vBullet[i].move();

                    //有沒有打到怪
                    for (int m = 0; m < MAX_ENEMY; m++)
                    {
                        //子彈跟怪都是存在的
                        if (vMonster[m] != null)
                        {
                            if (vMonster[m].getDistance(vBullet[i]) < BALL_SIZE)
                            {
                                vBullet[i] = null;
                                vMonster[m] = null;
                                break;
                            }
                        }
                    }
                    if (vBullet[i] != null)
                    {
                        if (vBullet[i].x > VIEW_W) //檢查出界                    
                            vBullet[i] = null;                     
                        else if (vBullet[i].y > VIEW_H) //檢查出界
                            vBullet[i] = null;
                        else if (vBullet[i].x < 0) //檢查出界
                            vBullet[i] = null;
                        else if(vBullet[i].y < 0) //檢查出界                       
                            vBullet[i] = null;                      
                    }
                }
            }
        }

        private void drawGame()
        {
            backGraphic.FillRectangle(Brushes.White, 0, 0, VIEW_W, VIEW_H);
            backGraphic.DrawEllipse(Pens.Red, player.x, player.y, BALL_SIZE, BALL_SIZE); //畫出玩家

            int total = 0;

            //畫出怪物
            for (int i = 0; i < MAX_ENEMY; i++)
            {
                if (vMonster[i] != null)
                {
                    total++;
                    backGraphic.DrawEllipse(Pens.Blue, vMonster[i].x, vMonster[i].y, BALL_SIZE, BALL_SIZE);
                }
            }
                       
            String str = "怪物數量:" + total;
            backGraphic.DrawString(str, SystemFonts.CaptionFont, Brushes.Black, 0, 0);

            total = 0;

            //畫出子彈
            for (int i = 0; i < MAX_BULLET; i++)
            {
                if (vBullet[i] != null)
                {
                    total++;
                    backGraphic.DrawEllipse(Pens.Black, vBullet[i].x, vBullet[i].y, BALL_SIZE, BALL_SIZE);
                }                    
            }

            //顯示子彈的數量
            total = MAX_BULLET - total;
            str = "剩餘子彈:" + total;
            backGraphic.DrawString(str, SystemFonts.CaptionFont, Brushes.Black, 0, 20);

            if (keySpace.isDown())
            {
                backGraphic.DrawString("space down", SystemFonts.CaptionFont, Brushes.Black, 0, 40);
            }
            else
            {
                backGraphic.DrawString("space up", SystemFonts.CaptionFont, Brushes.Black, 0, 40);
            }

            //將背景頁畫到視窗上面
            wndGraphic.DrawImageUnscaled(backBmp, 0, 0);

            //Invalidate(); //通知重繪畫面
        }

        private void onTimer(object sender, EventArgs e)
        {
            //就是主迴圈:main
            keySpace.onTimer();
            keyUp.onTimer();
            keyDown.onTimer();
            keyLeft.onTimer();
            keyRight.onTimer();

            if (keySpace.isPress()) //剛剛壓下去
            {
                //也要做發射
                fireBullet();
            }

            // 連發
            if (keySpace.isDown()) //壓著空白鍵
            {
                makeBulletTime -= 1.0f / REQUIRE_FPS; // 1/30
                if(makeBulletTime <= 0)//發射子彈的時間到了
                {
                    fireBullet();
                }
            }
          
            if (keyUp.isDown()) player.y -= 5;
            if (keyDown.isDown()) player.y += 5;
            if (keyLeft.isDown()) player.x -= 5;
            if (keyRight.isDown()) player.x += 5;

            makeMonsterTime -= 1.0f / 30.0f;
            if(makeMonsterTime <= 0)
            {
                //生怪時間到了
                for (int i = 0; i < MAX_ENEMY; i++)
                {
                    if (vMonster[i] == null)
                    {
                        vMonster[i] = new Point();
                        vMonster[i].x = random.Next(0, VIEW_W);
                        vMonster[i].y = random.Next(0, VIEW_H);
                        break;
                    }
                }
                makeMonsterTime = MAKE_MONSTER_TIME;
            }
                       
            moveMonster();
            moveBullet_KillMonster();
            drawGame();
        }

        void fireBullet()
        {
            //原本放開,剛剛按下去
            for (int i = 0; i < MAX_BULLET; i++)
            {
                if (vBullet[i] == null)
                {
                    vBullet[i] = new Bullet(player, mousePos);
                    vBullet[i].x = player.x;
                    vBullet[i].y = player.y;

                    break;
                }
            }
            makeBulletTime = MAKE_BULLET_TIME;
        }

        //滑鼠移動的通知
        private void onMouseMove(object sender, MouseEventArgs e)
        {
            mousePos.x = e.X;
            mousePos.y = e.Y;
        }

        //按鍵放開的通知
        private void onKeyUp(object sender, KeyEventArgs e)
        {
            keySpace.onKeyUp(e.KeyCode);
            keyUp.onKeyUp(e.KeyCode);
            keyDown.onKeyUp(e.KeyCode);
            keyLeft.onKeyUp(e.KeyCode);
            keyRight.onKeyUp(e.KeyCode);
        }

        //按下某個按鍵
        private void onKeyDown(object sender, KeyEventArgs e)
        {
            keySpace.onKeyDown(e.KeyCode);
            keyUp.onKeyDown(e.KeyCode);
            keyDown.onKeyDown(e.KeyCode);
            keyLeft.onKeyDown(e.KeyCode);
            keyRight.onKeyDown(e.KeyCode);
        }
    }

    class Point
    {
       // public static float staticData = 1.7f; //共享
        
        public float x, y;

        public float getDistance(Point pnt)
        {
            //x,y是指呼叫者的x,y
            //pnt是指傳入的物件
            float L = (float)Math.Sqrt((x - pnt.x) * (x - pnt.x) + (y - pnt.y) * (y - pnt.y));
            return L;
        }

        public void move(Point target) //target > 玩家
        {
            float L = getDistance(target);

            float tx, ty;
            if (L > 1)
            {
                tx = x + (target.x - x) * 1 / L;
                ty = y + (target.y - y) * 1 / L;

                x = tx;
                y = ty;
            }
            else //距離很近
            {
                x = target.x;
                y = target.y;
            }
        }
    }

    class Bullet : Point //Bullet繼承Point:加強或修改父類別
    {
        private Point moveDir; //移動的向量
        private const int BULLET_SPEED = 10;
        //方法名稱與類別名稱相同:建構
        //new某個物件(實體)的時候會呼叫這個物件(實體)的建構
        public Bullet(Point pos, Point mousePos)
        {
            x = pos.x;
            y = pos.y;

            float dist = getDistance(mousePos);

            moveDir = new Point();
            moveDir.x = (mousePos.x - x) * BULLET_SPEED / dist;
            moveDir.y = (mousePos.y - y) * BULLET_SPEED / dist;
        }

        public void move()
        {
            //使用父類別的
            x += moveDir.x;
            y += moveDir.y;
        }
    }

    //按鍵狀態(指定特定按鍵來做偵測)
    class KeyState
    {
        //Keys不是一個類別,是一個列舉(enum)
        private Keys theKey; //存放一個對應的按鍵編號

        //剛剛壓下去 / 目前這次是壓著的 / 上次onTimer是否壓著
        bool bPress, bDown, bPreDown;

        public KeyState(Keys k)
        {
            theKey = k;
            bPress = false;
            bDown = false;
            bPreDown = false;
        }

        public void onKeyDown(Keys k)
        {
            if(theKey == k)
            {               
                bDown = true; //剛剛按下去
            }
        }

        public void onKeyUp(Keys k)
        {
            if (theKey == k)
            {
                bDown = false;
                bPress = false;
            }
        }
       
        public bool isPress() //是否剛剛按下去
        {
            return bPress;
        }
       
        public bool isDown() //是否壓著
        {
            return bDown;
        }

        public void onTimer() //timer通知時呼叫
        {
            if(bDown == true)//此時是壓著
            {
                if (bPreDown == false) bPress = true;
                else bPress = false;
            }
            else
            {
                bPress = false;
            }
            bPreDown = bDown; //把這次的狀態記下來,下次就可以用這個狀態
        }
    }
}

沒有留言:

張貼留言