I've been trying to better my lesser side of knowledge in the C# programming language by tackling graphics head on. I've learned a little bit, but I'm getting stumped right now because I can't explain 100% what's going on here. I've developed a Pong game to teach myself how to use the Graphics class; in this game I want particles to fly off into random locations when the ball hits a paddle or wall. This much I've managed myself, however I've encountered a problem. My particles are going away too quickly and are a position behind the ball. Below is my source code with the multiple classes I'm using. (Please note I've ported the Vector2 class from XNA and called it BallVelocity.

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 System.Threading;
using System.Diagnostics;
using System.Drawing;

namespace PONG
{
    public partial class Form1 : Form
    {
        // Threads
        private Thread ParticleThread;
        private Thread GameThread;

        // Game State
        private GameState gsState = new GameState();

        // Objects
        private Rectangle rBall;
        private Rectangle rLeftPlayer;
        private Rectangle rRightPlayer;

        // Aspect Ratio
        private const int iWidth = 820;
        private const int iHeight = 422;

        // Movement
        private int iLeftY = iHeight / 2 - 50;
        private int iRightY = iHeight / 2 - 50;
        private int iBallX = iWidth / 2 - 5;
        private int iBallY = iHeight / 2 - 5;

        // Velocity
        private BallVelocity bvVelocity = BallVelocity.Zero;

        // Scoring
        private int[] iaScores = new int[2];

        // Game Started and Activate Particles Booleans
        private bool bGameStarted = false;
        private bool ActivateParticles = false;

        public Form1()
        {
            InitializeComponent();
            CreateEvents();
            SetGraphics();
        }

        internal void ParticleEngine()
        {
            try
            {
                for (; ; )
                {
                    Thread.Sleep(50);
                    List<Particle> ToBeRemoved = new List<Particle>();

                    lock (gsState.Particles)
                    {
                        foreach (Particle p in gsState.Particles)
                        {
                            if (p.PerformFrame(gsState))
                                ToBeRemoved.Add(p);
                        }
                    }

                    foreach (var p in ToBeRemoved)
                    {
                        gsState.Particles.Remove(p);
                    }
                }
            }

            catch (ThreadAbortException ex)
            {
                Debug.Print("Game ending.");
            }
        }

        private void CreateEvents()
        {
            this.KeyDown += new KeyEventHandler(MovePlayer);
        }

        private void SetGraphics()
        {
            rBall = new Rectangle(iWidth / 2, iHeight / 2, 15, 15);
            rLeftPlayer = new Rectangle(5, iHeight / 2, 15, 100);
            rRightPlayer = new Rectangle(iWidth - 15 - 5, iHeight / 2, 15, 100);
        }

        private void MovePlayer(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Up)
                iLeftY -= 5;

            else if (e.KeyCode == Keys.Down)
                iLeftY += 5;

            rLeftPlayer = new Rectangle(5, iLeftY, 15, 100);
        }

        private void UpdateBall()
        {
            rBall.X += (int)bvVelocity.X;
            rBall.Y += (int)bvVelocity.Y;

            if (rBall.Y < 0 || rBall.Y + rBall.Height > iHeight)
            {
                bvVelocity.Y = -bvVelocity.Y;

                PointF BallLocation = new PointF(rBall.X, rBall.Y);

                for (int i = 0; i < 25; i++)
                {
                    Particle NewParticle = new Particle(BallLocation, 1);
                    gsState.Particles.Add(NewParticle);
                }

                ActivateParticles = true;
            }

            if (rBall.IntersectsWith(rLeftPlayer) || rBall.IntersectsWith(rRightPlayer))
            {
                bvVelocity.X = -bvVelocity.X;

                PointF BallLocation = new PointF(rBall.X, rBall.Y);

                for (int i = 0; i < 25; i++)
                {
                    Particle NewParticle = new Particle(BallLocation, 1);
                    gsState.Particles.Add(NewParticle);
                }

                ActivateParticles = true;
            }

            if (rBall.X < 0)
            {
                iaScores[1]++;
                InitiateBall();
            }

            else if (rBall.X + rBall.Width > iWidth)
            {
                iaScores[0]++;
                InitiateBall();
            }
        }

        private void InitiateBall()
        {
            int iSpeed = 15;

            Random rRandom = new Random();

            switch (rRandom.Next(4))
            {
                case 0:
                    bvVelocity.X = iSpeed;
                    bvVelocity.Y = iSpeed;
                    break;

                case 1:
                    bvVelocity.X = -iSpeed;
                    bvVelocity.Y = iSpeed;
                    break;

                case 2:
                    bvVelocity.X = iSpeed;
                    bvVelocity.Y = -iSpeed;
                    break;

                case 3:
                    bvVelocity.X = -iSpeed;
                    bvVelocity.Y = -iSpeed;
                    break;
            }

            rBall.X = iWidth / 2 - 5;
            rBall.Y = iHeight / 2 - 5;
        }

        private void mComputer()
        {
            if (rBall.X > rRightPlayer.X - 200)
            {
                if (rRightPlayer.Y > rBall.Y)
                    rRightPlayer.Y = rRightPlayer.Y - 5;

                else
                    rRightPlayer.Y = rRightPlayer.Y + 5;
            }

            else
            {
                if (rRightPlayer.Y == iHeight / 2)
                    rRightPlayer.Y = iHeight / 2;

                else if (rRightPlayer.Y > iHeight / 2)
                    rRightPlayer.Y = rRightPlayer.Y - 5;

                else
                    rRightPlayer.Y = rRightPlayer.Y + 5;
            }
        }

        private void PaintObjectsAndKeepScore(object sender, PaintEventArgs e)
        {
            e.Graphics.DrawRectangle(new Pen(Brushes.Lime), rLeftPlayer);
            e.Graphics.FillRectangle(Brushes.Green, rLeftPlayer);

            e.Graphics.DrawRectangle(new Pen(Brushes.Red), rRightPlayer);
            e.Graphics.FillRectangle(Brushes.Maroon, rRightPlayer);

            e.Graphics.DrawEllipse(new Pen(Brushes.Gray), rBall);
            e.Graphics.FillEllipse(Brushes.White, rBall);

            if (ActivateParticles)
            {
                lock (gsState.Particles)
                {
                    foreach (Particle p in gsState.Particles)
                        p.DrawParticles(e.Graphics);
                }

                ActivateParticles = false;
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ParticleThread = new Thread(ParticleEngine);
            ParticleThread.Start();

            InitiateBall();
            Updater.Start();
        }

        private void EndGame(object sender, FormClosingEventArgs e)
        {
            ParticleThread.Abort();
            ParticleThread = null;
        }

        private void UpdateGame(object sender, EventArgs e)
        {
            UpdateBall();
            mComputer();

            Invoke((MethodInvoker)(() =>
            {
                this.Invalidate();
                this.Update();
            }));
        }
    }

    internal class Particle
    {
        private TimeSpan ParticleLife = new TimeSpan(0, 0, 3);
        private DateTime? ParticleBirth;

        internal PointF Location { get; private set; }
        internal PointF Velocity { get; private set; }

        internal static Random Random = new Random();

        internal static PointF RandomVector(float Speed)
        {
            float RandomAngle = (float)(Random.NextDouble() * (Math.PI * 2));
            Speed *= (float)(Random.NextDouble());

            return new PointF((float)Math.Sin(RandomAngle) * Speed, (float)Math.Cos(RandomAngle) * Speed);
        }

        internal Particle(PointF Location, float Speed)
            : this(Location, RandomVector(Speed))
        {

        }

        internal Particle(PointF Location, PointF Velocity)
        {
            this.Location = Location;
            this.Velocity = Velocity;
        }

        internal bool PerformFrame(GameState gs)
        {
            if (ParticleBirth == null) ParticleBirth = DateTime.Now;

            Location = new PointF(Location.X + Velocity.X, Location.Y + Velocity.Y);
            Velocity = new PointF(Velocity.X * 0.99f, Velocity.Y * 0.99f);

            return DateTime.Now - ParticleBirth > ParticleLife;
        }

        internal void DrawParticles(Graphics g)
        {
            g.DrawRectangle(new Pen(Brushes.White), Location.X - 2, Location.Y - 2, 5, 5);
        }
    }

    internal class GameState
    {
        internal List<Particle> Particles;

        internal GameState()
        {
            Particles = new List<Particle>();
        }
    }

    internal class BallVelocity
    {
        internal int X;
        internal int Y;

        internal static BallVelocity Zero = new BallVelocity(0, 0);

        internal BallVelocity(int X, int Y)
        {
            this.X = X;
            this.Y = Y;
        }
    }
}

All help is greatly appreciated on this matter.

Thanks,
Jamie

I figured it out; remove the line setting ActivateParticles to false from the paint method.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.