Игра Маленький C # понг

Недавно я создал 140-футовую игру Pong в C #, используя формы.

Так как я очень новичок в программировании, я бы хотел, чтобы кто-нибудь захотел прочитать его и дать мне советы о том, где улучшить, что, по вашему мнению, я сделал не так, и больше всего, что должно было быть сделано лучше и как .

Плюс, было бы немного полезно реализовать делегатов где-нибудь в коде выше? Я все еще изучаю их, но не нашел полезного способа их реализовать.

Наконец, я не нашел способа использовать метод Dispose() на созданной графике в Pong Класс без нарушения кода. Это действительно так полезно? Разве сборщик мусора не собирается делать это в любом случае?

namespace WindowsFormsApplication2
{
    public class PongGame
    {  
        public static Rectangle paddle1 = new Rectangle(14, 180, 20, 100);
        public static Rectangle paddle2 = new Rectangle(566, 180, 20, 100);
        public static Rectangle ball = new Rectangle(290, 115, 16, 16);
        static Font Drawfont = new Font("Arial", 40);
        public static bool p1movesUp, p1movesDown, p2movesUp, p2movesDown;
        public static SolidBrush sb = new SolidBrush(Color.White);
        public static double p1p;                           //Double that will store player 1 score
        public static double p2p;                           //Double that will store player 2 score
        static int RandoMin = 1;                            //Those 2 random integers are used to randomize ball directions
        static int RandoMax = 3;                            //in the Randomize() method to avoid repetition of ball movement
        public static double Xspeed = -1;                   //Beginning Initial speed
        public static double Yspeed = 1;

        public static void DrawIt(Graphics Draw)
        {   //Draws both paddles and ball
            Draw.Clear(Color.Black);
            Draw.FillRectangle(sb, paddle2);
            Draw.FillRectangle(sb, paddle1);
            Draw.FillRectangle(sb, ball);
            //Draw Score
            Draw.DrawString(p1p.ToString(), Drawfont, sb, 180, 10);
            Draw.DrawString(p2p.ToString(), Drawfont, sb, 380, 10);
        }

        public static void CheckIfMoving()                      //If player press the key to move the paddle, this method
        {                                                       //changes the Y position of the paddle Accordingly
            if (p1movesUp == true)
            { int z = paddle1.Y <= 0 ? paddle1.Y = 0 : paddle1.Y -= 3; }
            if (p1movesDown == true)
            { int z = paddle1.Y >= 381 ? paddle1.Y = 381 : paddle1.Y += 3; }
            if (p2movesUp == true)
            { int z = paddle2.Y <= 0 ? paddle2.Y = 0 : paddle2.Y -= 3; }
            if (p2movesDown == true)
            { int z = paddle2.Y >= 381 ? paddle2.Y = 381 : paddle2.Y += 3; }
        }

        public static void Restart()                            //Method called upon player scoring, to reset speed values
        {                                                       //and ball position
            ball.X = 290;   Yspeed = 1;
            ball.Y = 115;   RandoMin = 1;
            Xspeed = -1;    RandoMax = 3;
        }

        public static void CheckScore()                         //Check if any player has scored, and increase p1p accordingly
        {
            if (ball.X < 1)
            { p2p += 1; Restart(); }
            else if (ball.X > 579)
            { p1p += 1; Restart(); }
        }

        public static void IncreaseSpeed()                      //Increase both the normal speed and the results of
        {                                                       //any possible randomization in the Randomize() method
            RandoMin += 1;
            RandoMax += 1;
            Xspeed = Xspeed < 0 ? Xspeed -= 1 : Xspeed += 1;
        }

        public static void MoveBall(Timer t1)
        {
            ball.X += (int)Xspeed;                                     //Changes ball coordinates based on speed in both x & y axis
            ball.Y += (int)Yspeed;
            if (ball.Y > 465 || ball.Y < 0) { Yspeed = -Yspeed; }     //If ball touch one of the Y bounds, it's y speed gets a change in sign, and ball rebounce
            if (ball.X > 579 || ball.X < 1) { Xspeed = -Xspeed; }     //Same for X bounds, with x speed
            if (ball.IntersectsWith(paddle1) || ball.IntersectsWith(paddle2))
            {
                int dst = paddle1.Y + 100;
                int Distance = dst - ball.Y;
                if (Distance > 75 || Distance < 25) { Randomize(); }  //If the ball intersects the paddle "away" from the centre, the ball movement get randomized
                else { Xspeed = -Xspeed; }                             //else, it's speed on the X axis gets simply reverted
            }
        }

        static void Randomize()
        {
            Random r = new Random();

            double s = r.Next(RandoMin, RandoMax);                     //Uses RandoMin & RandoMax values to randomize the X speed of the ball
            Xspeed = ball.IntersectsWith(paddle1) ? Xspeed = s : Xspeed = -s;

            if (Yspeed < 0)                                            //If ball is moving upward, (so y speed is negative) the random value assigned
            {                                                          //will be changed in sign, so the ball can still go upward
                double t = r.Next(RandoMin, RandoMax);
                Yspeed = -t;
            }
            else                                                       //Else, directly change the Y speed to a positive value
            { Yspeed = r.Next(RandoMin, RandoMax); }
        }                                                              //End of PongGame Class
    }

    public partial class Form1 : Form
    {
        Graphics Draw;
        SolidBrush sb = new SolidBrush(Color.White);
        public Form1()
        {
            InitializeComponent();
            pictureBox1.BackColor = Color.Black;
            Draw = pictureBox1.CreateGraphics();

            timer1.Interval = 10;
            timer1.Start();
            timer2.Start();
        }


        private void timer1_Tick(object sender, EventArgs e)
        {  
            PongGame.DrawIt(Draw);                      //Draws paddles & ball
            PongGame.MoveBall(timer1);                  //Moves the ball
            PongGame.CheckScore();                      //Check if one player scored
            PongGame.CheckIfMoving();                   //Method that check if player is moving up or down the paddle
        }

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.S)
            { PongGame.p1movesDown = true; }
            if (e.KeyData == Keys.W)
            { PongGame.p1movesUp = true; }
            if (e.KeyData == Keys.L)
            { PongGame.p2movesDown = true; }
            if (e.KeyData == Keys.P)
            { PongGame.p2movesUp = true; }
        }

        private void Form1_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.S)
            { PongGame.p1movesDown = false; }
            if (e.KeyData == Keys.W)
            { PongGame.p1movesUp = false; }
            if (e.KeyData == Keys.L)
            { PongGame.p2movesDown = false; }
            if (e.KeyData == Keys.P)
            { PongGame.p2movesUp = false; }
        }

        private void timer2_Tick(object sender, EventArgs e)
        { PongGame.IncreaseSpeed(); }     //Every 3 seconds, this timer will increase Overall speed
    }
}
11 голосов | спросил Rphysx 21 MarpmFri, 21 Mar 2014 23:40:34 +04002014-03-21T23:40:34+04:0011 2014, 23:40:34

1 ответ


11

Вот несколько замечаний:

  1. В Form1_KeyDown и Form1_KeyUp, логика была бы организована в виде switch:

    private void Form1_KeyDown(object sender, KeyEventArgs e)
    {
        switch (e.KeyData)
        {
            case Keys.S:
                PongGame.p1movesDown = true;
                return;
            case Keys.W:
                PongGame.p1movesUp = true;
                return;
            case Keys.L:
                PongGame.p2movesDown = true;
                return;
            case Keys.P:
                PongGame.p2movesUp = true;
                return;
        }
    }
    
    private void Form1_KeyUp(object sender, KeyEventArgs e)
    {
        switch (e.KeyData)
        {
            case Keys.S:
                PongGame.p1movesDown = false;
                return;
            case Keys.W:
                PongGame.p1movesUp = false;
                return;
            case Keys.L:
                PongGame.p2movesDown = false;
                return;
            case Keys.P:
                PongGame.p2movesUp = false;
                return;
        }
    }
    
  2. В CheckIfMoving переменная z объявляется просто для использования причудливой версии тернарного оператора (?:). Это нужно переписать, чтобы делать то, что вы на самом деле хотите, а не пытаться быть умным:

    public static void CheckIfMoving()
    {
        // If player press the key to move the paddle, this method
        // changes the Y position of the paddle Accordingly
        if (p1movesUp)
        {
            if (paddle1.Y <= 0)
            {
                paddle1.Y = 0;
            }
            else
            {
                paddle1.Y -= 3;
            }
        }
    
        if (p1movesDown)
        {
            if (paddle1.Y >= 381)
            {
                paddle1.Y = 381;
            }
            else
            {
                paddle1.Y += 3;
            }
        }
    
        if (p2movesUp)
        {
            if (paddle2.Y <= 0)
            {
                paddle2.Y = 0;
            }
            else
            {
                paddle2.Y -= 3;
            }
        }
    
        if (p2movesDown)
        {
            if (paddle2.Y >= 381)
            {
                paddle2.Y = 381;
            }
            else
            {
                paddle2.Y += 3;
            }
        }
    }
    
  3. «Я не нашел способа использовать метод Dispose() на созданной графике в Pong Класс, не нарушающий код. Действительно ли это полезно? Разве сборщик мусора не должен так делать? »

    Это сравнение яблок и апельсинов, но не чувствую себя плохо! Многие люди смешивают или связывают Dispose() с сборщиком мусора. Иногда они переплетены, во многих случаях это не так. Очень простое эмпирическое правило: если класс реализует IDisposable, вызовите Dispose() в какой-то момент. Период. Глубоко в недрах этого класса (или того, который он имеет как член) используется неуправляемый неуправляемый ресурс. Dispose() гарантирует, что ресурс возвращается детерминированным способом. Я настоятельно рекомендую прочитать и понять этот ответ .

    Итак, как вы это исправите? Просто. Другая часть partial вашего класса Form1 имеет метод Dispose(). Обычно это выглядит примерно так:

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }
    

    Добавьте следующее:

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        this.Draw.Dispose();
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }
    

    Однако это своего рода обман. Вы сохраняете объект Graphics для срока службы приложения. Лучшим способом было бы удалить Draw как член класса Form1 и используя его только вокруг части, которая рисует (timer1_Tick). Обратите внимание, что using будет вызывать Dispose() на конец области автоматически для вас:

    private void timer1_Tick(object sender, EventArgs e)
    {
        using (Graphics Draw = this.pictureBox1.CreateGraphics())
        {
            PongGame.DrawIt(Draw);      // Draws paddles & ball
        }
    
        PongGame.MoveBall(this.timer1); // Moves the ball
        PongGame.CheckScore();          // Check if one player scored
        PongGame.CheckIfMoving();       // Method that check if player is moving up or down the paddle
    }
    
  4. Не постоянно обновлять генератор случайных чисел в методе Randomize:

        Random r = new Random();
    

    , сделайте это один раз как член класса:

        private static readonly Random r = new Random();
    

    Это предотвратит возможные повторяющиеся рандомы за короткий промежуток времени.

  5. У всех членов PongGame действительно должно быть public? Я не думаю. Единственными членами, доступными вне этого класса, являются p1movesUp, p1movesDown, p2movesUp и p2movesDown , Даже тогда я сделаю все из них private и вместо этого сделаю эти четыре автоматически реализованных свойства, чтобы помочь с инкапсуляцией:

    public static bool p1movesUp { get; set; }
    public static bool p1movesDown { get; set; }
    public static bool p2movesUp { get; set; }
    public static bool p2movesDown { get; set; }
    
  6. Так как все в классе PongGame static, объявить класс статически: public static class PongGame. У меня есть некоторые будущие мысли о том, чтобы сделать все не так-статичным, реализовать интерфейс и впрыскивать зависимости, но давайте сделаем это простым.

  7. Я немного запутался в использовании double s в PongGame. Они содержат только целые числа. Вы ожидаете, что счет превысит два миллиарда? Если нет, сделайте их все int. Лучшая производительность и намерение.

Надеюсь, это поможет!

ответил Jesse C. Slicer 22 MaramSat, 22 Mar 2014 00:52:20 +04002014-03-22T00:52:20+04:0012 2014, 00:52:20

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132