2015年1月8日 星期四

103學期 - 逢甲大學 - 物件導向設計C++ - 期末作品 - 劉俊易 - 貪吃蛇

前言:
        我自己本身是逢甲機電系,因為在大二的時候學到自己系內的「計算機程式」,學得蠻有心得的,因此在系外選修的部分就選了有關程式設計的課程。
         那老師上課的時候出了一份加分的期末作業給我們同學,一心想拿滿分的我就帶有這一份衝勁來撰寫這套程式 - 貪吃蛇,因為我小時候第一次接觸的遊戲就是貪吃蛇,就決定我的期末作品的主題了。

程式說明:
        因為我自以本身比較常在寫數值分析的程式,對於以From視窗的事件發生比較不熟悉,卡BUG卡得比較久花了點時間。~"~
         好了進入正題,一開始我宣告了以下幾個物件:

Button Start_Btn                         是為了按下開始
Button CtrlSz_Btn                        控制邊界大小,不過BUG太多了,暫時先不啟用這個功能
GroupBox Bdry_GB                    為了設定貪吃蛇可以行走的邊界範圍
Label Snk_Lbl                             貪吃蛇的頭
Label FdRdom_Lbl                     食物每一次出現都必須隨機
Label[] SkB_Lbl                         貪吃蛇的身體,食物吃得越多,身體就越長,是一個矩陣label
Label Msg_Lbl                            訊息Label
Timer Mv_Timer                         讓貪吃蛇移動,宣告Timer事件移動
Random SnakePositionX_Rand   隨機的食物位置X
Random SnakePositionY_Rand   隨機的食物位置Y

         我當然還有一些變數,不過那之後就再介紹就行了。進入Form1_Load裡面,當然就是先設定每個物件的參數,包含:位置、尺寸等等,以及非常重要的三個事件,開始鍵的設定時間的前進以及按鍵的事件等。

         Start_Btn_Click:
         當「Start」按下去時,必須先重新設定「SnakeHead」、「FoodRandom」、「SnakeBody」、「Grade」、「Timer」、「Direction」。
                  SnakeHead:包含了可視化、尺寸、位置、顏色以及秀出在GroupBox上的語句。
                  FoodRandom:同上
                  SnakeBody:因為身體的數量會不斷增加,因此必須利用矩陣的方式宣告,宣告完後也必須設定參數,同上。但是不能先宣告位置跟加入控制,因為還沒吃到食物。
                  Grade:就是分數,一開始的分數一定等於零。
                  Timer:設定每次發生之間的間隔以及開啟時間計時。
                  Determined_Direction:顧名思義,就是設定貪吃蛇前進的方向。
         Boundary_GBox_KeyUp:
         其實就是設定「W」「A」「S」「D」的方向控制,不過必須要注意一點,方向在轉的時候,每次就只能夠轉九十度、不能夠一百八十度。
         Move_Timer_Tick:
         「SnakeBody moving」:貪吃蛇的身體會隨著「FN_int」增加而增加,增加之後必須要讓前面的身體往後補到後面的位置,必須要做位置交換的動作。這時,為了要存起第一個位置的變數,必須先宣告一個暫時的變數儲存。
         「SnakeHead Moving」:貪吃蛇開始往前進。控制方向已經由按鍵事件控制了,左右一個控制變數、上下一個控制變數,分別以1、0、-1來做表示。
         「Setting the mesh」:為了確保貪吃蛇的位置都能夠在整數上面,因此在此程式統一為20像素為一個單位,無論上下左右前進步距皆為20個像素,因此在這裡做設定。
         「Boundary Line」:在這裡必須設定兩個遊戲的限制條件,第一個不能夠超過邊界、第二個不能夠接觸到貪吃蛇自己本身的身體。所以特別宣告了布林變數
         「Eaing the Food」:當吃到食物所發生的事件,也就意味著貪吃蛇的頭與食物的位置相等,這個假設成立後,「FN_int」增加。因為難度設計的關係速度也要變快,意味著事件發生的時間間距縮短。在秀出貪吃蛇的身體。


貪吃蛇:程式碼
========================================================================
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;

namespace 貪吃蛇
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        //=======================================
        Button Start_Btn = new Button();                           // Start_Button
        Button CtrlSz_Btn = new Button();                          // ControlSize_Button
        GroupBox Bdry_GB = new GroupBox();                         // Boundary_GroupBox
        Label Snk_Lbl = new Label();                               // SnakeHead_Label
        Label FdRdom_Lbl = new Label();                            // FoodRandom_Label
        Label[] SkB_Lbl;                                           // SnakeBody_Label
        Label Msg_Lbl = new Label();                               // Message_Label
        Timer Mv_Timer = new Timer();                              // Move_Timer
        Random SnakePositionX_Rand = new Random(RandY_int);        // SnakePositionX_Random
        Random SnakePositionY_Rand = new Random(FN_int);           // SnakePositionY_Random
        private int Level_int = 1;                                 // Game Level
        private int SnakeMoveStep_int = 20;                        // Move the 20 unit
        public int TopBottom_int = 0, RightLeft_int = 1;           // Determine the direction
        private static int RandX_int = 200, RandY_int = 200;       // FoodLocation
        private static int FN_int = 0;                             // FoodNumber_int
        private int GBox_X, GBox_Y, GBox_h, GBox_w, Btn_X, Btn_Y;  //
        private int Tmp_X = 0, Tmp_Y = 0;                          // Temporary Position
        private int Grade = 0;                                     //
        //=======================================
        private void Form1_Load(object sender, EventArgs e)
        {
            //--Boundary_GBox--
            GBox_X = 400 + 20; GBox_Y = 400 + 10;          // Getting the X , Y
            GBox_h = 20; GBox_w = 20;                      // Getting the h , w
            Bdry_GB.Text = "";                             // Setting the name
            Bdry_GB.Location = new Point(GBox_w, GBox_h);  // Setting the point
            Bdry_GB.Size = new Size(GBox_X, GBox_Y);       // Setting the Size
            this.Controls.Add(Bdry_GB);                    // Showing the Boundary_GBox

            //--Start_Btn_Setting--
            Btn_X = 100; Btn_Y = 30;
            Start_Btn.Text = "Start Game";                            // Setting the Name
            Start_Btn.Location = new Point(GBox_h, GBox_h + GBox_X);  // Setting the position
            Start_Btn.Size = new Size(Btn_X, Btn_Y);                  // Setting the size
            Start_Btn.Click += Start_Btn_Click;                       // Adding the Event
            this.Controls.Add(Start_Btn);                             // Showing this Start_Btn

            //--ControlSize_Btn_Setting--  視窗放大
            //CtrlSz_Btn.Text = "Choice Size";                                   // Setting the name
            //CtrlSz_Btn.Location = new Point(GBox_h, GBox_h + GBox_X + Btn_Y);  // Setting the point
            //CtrlSz_Btn.Size = new Size(Btn_X, Btn_Y);                          // Setting the Size
            //CtrlSz_Btn.Click += ChoiceSize_Btn_Click;                          // Adding the Eevnt
            //this.Controls.Add(CtrlSz_Btn);                                     // Showing this ControlSize_Btn

            //--Message_Lbl--
            Msg_Lbl.AutoSize = true;                                       // Setting the AutoSize is true
            Msg_Lbl.Location = new Point(Btn_X + GBox_h, GBox_X + Btn_Y);  // Setting the Location
            this.Controls.Add(Msg_Lbl);                                    // Showing in the this from

            //--Event--
            Mv_Timer.Tick += Move_Timer_Tick;  // Adding the Event
            Bdry_GB.KeyUp += Boundary_GBox_KeyUp; //  Adding the Event

            //--From Setting--
            this.Text = "貪吃蛇";               // Setting the windows's Name
            this.Width = GBox_Y + 3 * GBox_w;   // Setting the windows's Width
            this.Height = 5 * GBox_X / 3;       // Setting the windows's Height
        }
        //--Event--
        void Start_Btn_Click(object sender, EventArgs e)
        {
            //--SnakeHead--
            int w = this.Bdry_GB.Size.Width;                             // Getting the this width
            int h = this.Bdry_GB.Size.Height;                            // Getting the this Height
            Snk_Lbl.Enabled = true;                                      // Setting the Enabled is true
            Snk_Lbl.Size = new Size(20, 20);                             // Setting the Size
            Snk_Lbl.Location = new Point(0, w / 2 - Snk_Lbl.Size.Width); // Setting the Location
            Snk_Lbl.BackColor = Color.Red;                               // Setting the Color red
            this.Bdry_GB.Controls.Add(Snk_Lbl);                          // Showing the GBox

            //--FoodRandom_Lbl--
            FdRdom_Lbl.Size = new Size(20, 20);
            FdRdom_Lbl.BackColor = Color.Blue;
            FdRdom_Lbl.Location = new Point(200, 190);
            this.Bdry_GB.Controls.Add(FdRdom_Lbl);

            //--SnakeBooy--
            SkB_Lbl = new Label[w * h / 400 + 1];
            for (int i = 1; i < SkB_Lbl.Length; i++)
            {
                SkB_Lbl[i] = new Label();
                SkB_Lbl[i].Size = new Size(20, 20);
                SkB_Lbl[i].BackColor = Color.Black;
            }

            //--Grade--
            Grade = 0;

            //--Timer--
            //Mv_Timer.Interval = 15 * (10 - Level_int);  // Following the Level with decreasing
            Mv_Timer.Interval = 150;                      // Following the Level with decreasing
            Mv_Timer.Enabled = true;                      // Trun on the Enabled

            //--Determined_Direction--
            TopBottom_int = 0; RightLeft_int = 1; // Initial direction
            Bdry_GB.Focus();                      //  Getting the Boundary Control
        }
        /*void ChoiceSize_Btn_Click(object sender, EventArgs e)
        {
            // Control this windows Size
            if (Bdry_GB.Size.Width == GBox_X) Bdry_GB.Size = new Size(GBox_X * 13 / 10, GBox_Y * 13 / 10);
            else if (Bdry_GB.Size.Width == GBox_X * 13 / 10) Bdry_GB.Size = new Size(GBox_X * 16 / 10, GBox_Y * 16 / 10);
            else if (Bdry_GB.Size.Width == GBox_X * 16 / 10) Bdry_GB.Size = new Size(GBox_X, GBox_Y);

            // follow chance
            //---form---
            this.Width = Bdry_GB.Size.Width + 3 * Bdry_GB.Location.X;  // Setting the windows's Width
            this.Height = 5 * Bdry_GB.Size.Height / 3;                 // Setting the windows's Height
            //---Btn---                                              
            Start_Btn.Location = new Point(Bdry_GB.Location.X, Bdry_GB.Location.Y + Bdry_GB.Size.Height);           // Setting the position
            CtrlSz_Btn.Location = new Point(Bdry_GB.Location.X, Bdry_GB.Location.X + Bdry_GB.Size.Height + Btn_Y);  // Setting the point
            //---Lbl---
            Msg_Lbl.Location = new Point(Btn_X + Bdry_GB.Location.X, Bdry_GB.Size.Width + Btn_Y);  // Setting the Location
            Snk_Lbl.Location = new Point(0, this.Bdry_GB.Size.Width / 2 - Snk_Lbl.Size.Width);     // Setting the Location

            // Restart
            if (Mv_Timer.Enabled == true)
            {
                Mv_Timer.Enabled = false;                              // Trun off the Enabled
                Snk_Lbl.Enabled = false;                               // Hidding the SnakeHead
                Snk_Lbl.Location = new Point(0, Bdry_GB.Size.Height);  // Setting the Location
                TopBottom_int = 0; RightLeft_int = 1;                  // Initial direction
                MessageBox.Show("Restart!!");                          // MessageBox
            }
        }*/
        void Boundary_GBox_KeyUp(object sender, KeyEventArgs e)
        {
            // Control the direction
            switch (e.KeyCode)
            {
                case Keys.A:
                    {
                        if (RightLeft_int == 1) break;
                        TopBottom_int = 0;
                        RightLeft_int = -1;
                        break;
                    }
                case Keys.D:
                    {
                        if (RightLeft_int == -1) break;
                        TopBottom_int = 0;
                        RightLeft_int = 1;
                        break;
                    }
                case Keys.W:
                    {
                        if (TopBottom_int == 1) break;
                        TopBottom_int = -1;
                        RightLeft_int = 0;
                        break;
                    }
                case Keys.S:
                    {
                        if (TopBottom_int == -1) break;
                        TopBottom_int = 1;
                        RightLeft_int = 0;
                        break;
                    }
            }
        }
        void Move_Timer_Tick(object sender, EventArgs e)
        {
            // SnakeBody moving
            for (int i = FN_int; i >= 1; i--)
            {
                Tmp_X = SkB_Lbl[i].Location.X; Tmp_Y = SkB_Lbl[i].Location.Y;
                SkB_Lbl[i + 1].Location = new Point(Tmp_X, Tmp_Y);
            }
            //  Body will follow the SnakeHead
            SkB_Lbl[1].Location = new Point(Snk_Lbl.Location.X, Snk_Lbl.Location.Y);

            // SnakeHead moving
            // OK
            Snk_Lbl.Left += SnakeMoveStep_int * RightLeft_int;   // SnakeHead move
            Snk_Lbl.Top += SnakeMoveStep_int * TopBottom_int;    // SnakeHead move

            // Make sure meshes are 20 unit Setting the mesh
            // OK
            int[] X = new int[(Bdry_GB.Width - FdRdom_Lbl.Size.Width) / 20];
            int[] Y = new int[(Bdry_GB.Width - FdRdom_Lbl.Size.Width) / 20];
            for (int i = 0; i < X.Length; i++)
            {
                X[i] = i * ((Bdry_GB.Width - FdRdom_Lbl.Size.Width) / 20);
                Y[i] = 10 + i * ((Bdry_GB.Width - FdRdom_Lbl.Size.Width) / 20);
            }

            // Can't be over the boundary line
            // OK
            bool BoundaryLine_bool =
                (
                   Snk_Lbl.Left <= -10 |                  //  Left Boundary
                   Snk_Lbl.Left >= Bdry_GB.Size.Width |  //  Right Boundary
                   Snk_Lbl.Top <= -10 |                   //  Top Boundary
                   Snk_Lbl.Top >= Bdry_GB.Size.Height    //  Button Boundary
                );
            // Can't touch Snake Body
            // OK
            for (int i = 1; i <= FN_int; i++)
                if (Snk_Lbl.Location == SkB_Lbl[i].Location)
                    BoundaryLine_bool = true;
            // If that's true
            if (BoundaryLine_bool == true)
            {
                int w = Bdry_GB.Width;                                       // Simpifity
                Snk_Lbl.Location = new Point(0, w / 2 - 2 * Snk_Lbl.Width);  // Setting the Location
                TopBottom_int = 0; RightLeft_int = 0;                        // Initial direction  and  Speed equal zero
                Snk_Lbl.Location = new Point(0, w / 2 - Snk_Lbl.Size.Width); // Setting the Location
                //
                Mv_Timer.Enabled = false;                                    // Close the Timer
                for (int i = 1; i <= FN_int; i++) SkB_Lbl[i].Visible = false;// Body
                FN_int = 0;                                                  // Restart
                this.Bdry_GB.Controls.Remove(FdRdom_Lbl);                    // Adding the FoodRandom
                MessageBox.Show("Game Over");                                // Show "You over the Boundary Line"
                return;
            }

            // Eating the Food
            // OK
            if (Snk_Lbl.Location == FdRdom_Lbl.Location)
            {
                FN_int++;                                                                 // Number plus one
                Mv_Timer.Interval -= 2;                                                   // Level Faster
                for (int i = 1; i <= FN_int; i++) this.Bdry_GB.Controls.Add(SkB_Lbl[i]);  // Adding the Snaek Body
                //
                RandX_int = SnakePositionX_Rand.Next(0, X.Length);                        // Setting the New Random X
                RandY_int = SnakePositionY_Rand.Next(0, Y.Length);                        // Setting the New Random Y
                FdRdom_Lbl.Location = new Point(X[RandX_int], Y[RandY_int]);              // Setting the New Position
                this.Bdry_GB.Controls.Add(FdRdom_Lbl);                                    // Show the FdRdom_Lbl
                for (int i = 1; i <= FN_int; i++) Grade += 200 - Mv_Timer.Interval;       // Plus Grade
            }

            // MessageBox
            Msg_Lbl.Text = "Your Grade is " + Grade.ToString();
        }
    }
}
========================================================================