2
\$\begingroup\$

I'm using C# and I'm drawing shapes to a PictureBox.

I'm not drawing anything in 3D. Instead, I draw 2D shapes and have a "depth" value. (which changes the size of the shape and its opacity)

My problem is:

The shapes in the "back" (that are "deep", or basically have high Z value) overlap the shapes in the "front" (the ones with a low Z value).

How can I draw my shapes so that objects with a high Z value overlap the objects with a lower Z value, if they meet.

I want the green ball to render on top of the red ball

I want the green ball to render on top of the red ball.

What I tried for this problem:

Ordering the shapes by their Z value, (descending and ascending) and then drawing them.

Attached here is some code:

My Game.cs: (basically just has a List of Particles)

class Game
{
    public List<Particle> Particles;

    public Game()
    {
        Particles = new List<Particle>();
    }
}

My Particle.cs:

class Particle
{
    //NOTES--------------------------------------------------
    //Z should always be between 0 and 10.
    //When setting the location locally, make sure to use Location
    //because it changes the other properties with it.
    //-------------------------------------------------------
    #region Properties
    public Pen spawnPen;
    //currentPen for when the color is changing
    //and we need to keep the initial color in spawnPen
    public Pen currentPen;

    public Pen borderPen;

    public Brush spawnBrush;
    //currentBrush for when the color is changing
    //and we need to keep the initial color in spawnBrush.
    public Brush currentBrush;

    public bool ColorChanging = false;
    //The center point of the GraphicsPath
    //that we generated in Window.cs, the main file
    //possibly used for calculating the distance
    //to know by how much to change the color??????
    public PointF ColorChangingRegionCenter;

    //2D movement so we don't need a Z axis for speed.
    private PointF speed;
    public PointF Speed
    { get { return speed; } set { speed = value; } }

    private Vector3D location;
    public Vector3D Location
    {
        get { return location; }
        set
        {
            location = value;
            //50 is the width and height of the shapes at depth 0
            //(the maximum width and height, basically).
            rect = new RectangleF(location.X, location.Y, 50.0f - (location.Z * 4), 50.0f - (location.Z * 4));
            if (spawnPen == null)
                spawnPen = new Pen(Color.FromArgb(180 - (int)location.Z * 15, 76, 247, 219));
            if (spawnBrush == null)
                spawnBrush = new SolidBrush(Color.FromArgb(180 - (int)location.Z * 15, 76, 247, 219));
            if (borderPen == null)
                borderPen = new Pen(Color.FromArgb(235, 76, 247, 219), 1);
        }
    }

    private RectangleF rect;
    public RectangleF Rectangle
    { get { return rect; } set { rect = value; } }
    #endregion
    #region Constructors
    //Must explicity set the location
    //otherwise it's random.
    public Particle()
    {
        Random rng = new Random();
        float X, Z;

        //Lock the Random object
        //so it doesn't generate the same value!
        lock (rng)
        {
            X = (float)rng.NextDouble();
        }
        lock (rng)
        {
            Z = (float)rng.NextDouble();
        }
        Location = new Vector3D(X * 1100, 0, Z * 10);
        //We must generate the speed for 2 reason:
        //1.It is not set by default.
        //2.It depends on the Z value of the particle.
        speed = new PointF(2 - (location.Z * 2 / 10), 2 - (location.Z * 2 / 10));
    }
    public Particle(Pen spawnPen) : this()
    { this.spawnPen = spawnPen; }
    public Particle(Brush spawnBrush) : this()
    { this.spawnBrush = spawnBrush; }

    public Particle(float X, float Y, float Z)
    { Location = new Vector3D(X, Y, Z); }
    public Particle(float X, float Y, float Z, Pen spawnPen)
            : this(X, Y, Z)
    { this.spawnPen = spawnPen; }
    public Particle(float X, float Y, float Z, Brush spawnBrush)
            : this(X, Y, Z)
    { this.spawnBrush = spawnBrush; }
    #endregion
    #region Methods
    public void PerformFrame()
    {
        /*//If it is in the ColorChangingRegion
        if (ColorChanging)
        {
            double dLocationColorChangingCenter = Math.Sqrt(Math.Pow((location.X - ColorChangingRegionCenter.X), 2) + Math.Pow(location.Y - ColorChangingRegionCenter.Y, 2));
            if (currentBrush == null)
                currentBrush = spawnBrush;
            Pen brushColor = new Pen(currentBrush);
            //currentBrush = new SolidBrush(brushColor.Color.A + (255 - brushColor.Color.A) * );
            //Remember to dispose of unused objects.
        }*/
        //If traveling UP
        if (speed.Y < 0)
        {
            //If goes offscreen TOP
            if (location.Y < 0 - rect.Height)
            {
                //Send it to offscreen BOTTOM.
                //Location.Y = 500 + rect.Height;
                Location = new Vector3D(location.X, 500 + rect.Height, location.Z);
            }
        }
        //If traveling DOWN
        else if (speed.Y > 0)
        {
            //If goes offscreen BOTTOM
            if (location.Y > 500 + rect.Height)
            {
                //Send it to offscreen TOP.
                //Location.Y = -rect.Height;
                Location = new Vector3D(location.X, -rect.Height, location.Z);
            }
        }
        //If traveling LEFT
        if (speed.X < 0)
        {
            //If goes offscreen LEFT
            if (location.X < 0 - rect.Width)
            {
                //Send it to offscreen RIGHT.
                //Location.X = 1100 + rect.Width;
                Location = new Vector3D(1100 + rect.Width, location.Y, location.Z);
            }
        }
        //If traveling RIGHT
        else if (speed.X > 0)
        {
            //If goes offscreen RIGHT
            if (location.X > 1100 + rect.Width)
            {
                //Send it to offscreen LEFT.
                //Location.X = -rect.Width;
                Location = new Vector3D(-rect.Width, location.Y, location.Z);
            }
        }
        //Change the particle's location.
        //Location.X += speed.X;
        //Location.Y += speed.Y;
        Location = new Vector3D(location.X + speed.X, location.Y + speed.Y, location.Z);
    }
    public void RenderEmptyRectangle(Graphics g)
    {
        g.DrawRectangle(spawnPen, rect.X - rect.Width, rect.Y - rect.Height, rect.Width, rect.Height);
    }
    public void RenderFilledRectangle(Graphics g)
    {
        g.FillRectangle(spawnBrush, rect.X - rect.Width, rect.Y - rect.Height, rect.Width, rect.Height);
    }
    public void RenderEmptyCircle(Graphics g)
    {
        g.DrawEllipse(spawnPen, rect.X - rect.Width, rect.Y - rect.Height, rect.Width, rect.Height);
    }
    public void RenderFilledCircle(Graphics g)
    {
        g.FillEllipse(spawnBrush, rect.X - rect.Width, rect.Y - rect.Height, rect.Width, rect.Height);
    }
    public void RenderCircleBorder(Graphics g)
    {
        g.DrawEllipse(borderPen, rect.X - rect.Width - 1, rect.Y - rect.Height - 1, rect.Width + 1, rect.Height + 1);
    }
    #endregion
}

Where drawing is done: (inside a PictureBox's Paint method, the PictureBox is called "DrawingRegion")

    //Rendering is done here because
    //the Graphics object is here.
    private void DrawingRegion_Paint(object sender, PaintEventArgs e)
    {
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        e.Graphics.CompositingMode = CompositingMode.SourceCopy;
        e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
        e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
        lock (game.Particles)
        {
            //Looping backwards because the collection can be
            //modified and that throws an exception.
            for (int i = game.Particles.Count - 1; i > 0; i--)
            {
                game.Particles[i].RenderFilledCircle(e.Graphics);
                game.Particles[i].RenderCircleBorder(e.Graphics);
            }
        }
    }

My game-loop:

    //The game process
    //which updates the particles' location
    //and checks whether or not it should start changing colors
    private void GameProc()
    {
        while (true)
        {
            lock (game.Particles)
            {
                //Looping backwards because the collection can be
                //modified and that throws an exception.
                for (int i = game.Particles.Count - 1; i > 0; i--)
                {
                    game.Particles[i].PerformFrame();
                }
            }
            Invoke((MethodInvoker)(() =>
            {
                DrawingRegion.Invalidate();
                DrawingRegion.Update();
            }));
        }
    }

If you think any other chunk of code is relevant, ask me to post it and I will.

\$\endgroup\$
  • \$\begingroup\$ Where do you call the drawing methods in Particle.cs? \$\endgroup\$ – ChrisUC Feb 19 '16 at 13:23
  • \$\begingroup\$ It's called in the PictureBox's Paint method. @AlexandreVaillancourt Why would I do that when it relates to the same application? People can answer which ever question they know the answer to! =) \$\endgroup\$ – Moeyy AS Feb 19 '16 at 13:25
  • \$\begingroup\$ It doesn't seem like you call OrderParticlesByZ() anywhere \$\endgroup\$ – ChrisUC Feb 19 '16 at 13:37
  • \$\begingroup\$ @ChrisUC I tried using it, and it didn't work. So I excluded it from the code because it didn't change the result. \$\endgroup\$ – Moeyy AS Feb 19 '16 at 14:22
  • 1
    \$\begingroup\$ @Krythic or Monogame. \$\endgroup\$ – Felsir Feb 19 '16 at 14:34
0
\$\begingroup\$

For some reason, ordering the list works NOW.

Solution to this weird problem: ordering the list (on insertion, not in the game loop that would be a waste) by the list members' Z value like this: NameOfList = NameOfList.OrderBy(P => P.Location.Z).ToList();

All the comments were right, I was just so sure I tried it and it didn't work! proof that I tried it is that I had already written this method in my class, sorry for the misinformation on my part!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.