Question How to detect collision with generated pictureboxes

MrRobot

Member
Joined
Sep 20, 2019
Messages
5
Programming Experience
1-3
Hey! I'm new around here, not sure if I'm doing it right but I need some help solving a problem here pretty please.

So basically I have a character which I can control, and I have multiple pictureboxes being generated randomly. My question is, how do I make a collision for all of them?!

Everytime I move the Character.Bounds code away from where the Picturebox are being generated, it says my Picturebox has no context. I know I have to generate something with it but I don't know what and where... can you help me?

I'll use images since I don't know how to make the code look fancy on the foruns yet, sorry :p

codeHelp.png
 
Please post your code in code tags so we are able to copy and edit the text. Otherwise attaching it as an image thus restricts us from doing so, and I doubt anyone will rewrite all that just to check for you.

Also be sure to post the exact error you are receiving?

You can post code in code tags as demonstrated :

nD918n6.gif


If your project is still in early stages, consider doing this in WPF, as WF is nearing end of life, and WPF would be more suitable in my opinion.
 
A direct answer that does not address any of the issues with your code/design: Notice how you added your pictureSeed into the Controls collection of your Terrain? You will have to find that control that you added and perform your collision detection with the found control.

Now an indirect answer:
You are writing code as if you were back in the 90's and early 2000's. Using your UI as your data model is one of the worse things you can do when compared to the more modern way of writing code. Yes, back then we had to write code that way because of the limited resource available to computers, but not only have computers gotten better, we programmers have also learned that tightly coupling the data into the UI is a poor idea. Your UI should simply be a visual representation of your actual data model. So in this case, using your PictureBox and Form to keep track of where objects are and how big they are is not a great approach. The modern approach is to have a data structure that holds your game objects. If a really simple game, a simple list or array would be a sufficient data structure. In a more complex game, do a few searches on various approaches for "scene graphs". A common one you may find is a k-d tree.

Any which way, that data structure is not tied to the UI. That means that you can either use a class variable, or pass around that data structure where you need it like in your collision detection, scene rendering, or simulation/game state updates.

A bit of advice: Although .NET Core 3.0 includes WinForms, you should really treat it as technology that is at end-of-life. Even though you could write WinForms code following more modern MVC or MVP patterns, it will require a lot of discipline not to backslide. Choose WPF or Xamarin instead. These frameworks could be used in the old WinForms style, but they really encourage you to write code using the modern MVVM or MVC patterns. Learning these patterns is a skill that is transferable to other technologies. Learning how to do WinForms will forever lock you into doing maintenance of old code (granted there are some people making very good livings maintaining old COBOL and FORTRAN code).
 
Last edited:
I'll use images since I don't know how to make the code look fancy on the foruns yet, sorry :p
If you've ever used Microsoft Word, Wordpad, NotePad or any text editor ever, you should know that the formatting tools are on the toolbar above the text area. Did you look through that toolbar to see what was available?
 
You will have to find that control that you added and perform your collision detection with the found control.
C#:
Character.Controls.Add(pictureSeed);
So does that mean I would have to add my Character onto the seeds?! Like this? ^

-------- // -------- // -------- //
If your project is still in early stages, consider doing this in WPF, as WF is nearing end of life, and WPF would be more suitable in my opinion.
You are writing code as if you were back in the 90's and early 2000's. Using your UI as your data model is one of the worse things you can do when compared to the more modern way of writing code.
It's alright :d, I know I'm super blend with my code but nowadays I just code for fun, when I'm bored and such... I don't learn code for +4 years after I finished school.

-------- // -------- // -------- //
Please post your code in code tags so we are able to copy and edit the text.

Ok, thank you for the help. Here's the code in text.
C#:
private void Global_Slow_Timer_Tick(object sender, EventArgs e)
        {
            //RANDOMIZE CHANCE SEED TO APPEAR
            Random seedRandom = new Random();
            int seedRandomNumber = seedRandom.Next(1, 20);

            // SET SEED SPAWN T OR F
            if (seedRandomNumber == 19)
            {
                seedspawn = true;
            }
            else
            {
                seedspawn = false;
            }
            // ------------------------------

            // SEED SPAWN IF TRUE
            if (seedspawn == true)
            {
                // SEED RANDOMIZE X
                int seedRandomX = seedRandom.Next(24, 450);
                Seed_X_label.Text = seedRandomX.ToString();
                // SEED RANDOMIZE Y
                int seedRandomY = seedRandom.Next(22, 420);
                Seed_Y_label.Text = seedRandomY.ToString();

                //GENERATE SEED
                PictureBox pictureSeed = new PictureBox();
                pictureSeed.Tag = "Seeds";
                pictureSeed.BackColor = Color.Transparent;
                pictureSeed.Size = new Size(40, 40);
                pictureSeed.SizeMode = PictureBoxSizeMode.Zoom;
                pictureSeed.Image = new Bitmap(@"C:\Users\pc\Documents\Visual Studio 2015\Projects\Game8_Waterfall\Waterfall\Waterfall\bin\Debug\imgs\seed.png");
                pictureSeed.Left = Convert.ToInt32(Seed_X_label.Text);
                pictureSeed.Top = Convert.ToInt32(Seed_Y_label.Text);

                Terrain.Controls.Add(pictureSeed);
                // --------------                  
            }
            // ------------------

                //CHARACTER COLLISION WITH SEEDS
                if (Character.Bounds.IntersectsWith(pictureSeed.Bounds))
                {
                    MessageBox.Show("It Worked!");
                    Action.Text = "Gather Seeds (F)";
                    Resource.Text = "Seeds";
                    Action.Visible = true;
                }
                // ----------------------------
        }
 
Also be sure to post the exact error you are receiving?

Yes my bad. I basically want my Character to collide with these random generated Seeds (pictureboxes) around the Terrain which is a Panel so I can pick them up.

.
game.png


However, If I move away the code of collision between the Char and the Seeds, it doesn't recognize "pictureSeeds" which are the Seeds. I don't know what to code to make it work.
error.png
 
Try moving your PictureBox pictureSeed = new PictureBox(); into global scope. Then you also need to take into consideration how :
You will have to find that control that you added and perform your collision detection with the found control.
How do you plan on doing that?
 
No, you don't have to add your character into the seeds. Why would you?

If all you need is for line 44 to have access to the variable you declared on line 29, then simply move the declaration to a scope that is shared by both chunks of code to satisfy the compiler.

Of course, that solution will only fix the immediate compiler problem. You still have a logic problem about how do you deal with other seeds that you had created previously in past timer tick event firings. Again, please re-read my post above #3 regarding using some kind of data structure to keep track of your objects.

Out of curiosity, what tutorial or book did you use to learn how to write code in C#? This issue about scope should have been covered by it relatively early on shortly after introducing methods and classes. If needed, review the section regard scopes.

Since you are writing a game, I highly suggest also learning about the game loop. It'll likely help you better organize your game logic.
 
How do you plan on doing that?
Notice that on line 30, he fills in the Tag property of the control with the value of "Seeds". So a simple:
C#:
var seeds = Terrain.Controls.Where(c => c.Tags == "Seeds");
will return an enumerable of all the controls that he had created with that tag.

Even better, if the OP had set the Name property instead of the Tag property, then he wouldn't even need to use C# LINQ extension methods. He could use the pre-LINQ, WinForms built-in Control.ControlCollection.Find() method to get back an array of all the controls that he created with that name.

But again, that approach of using the WinForms controls as the data model for the game is a really poor approach if brand new code is going to be written. It would be better to simply add all the seed bounds into a List<Rectangle>.
 
What if the seed spawn is not true? How does the compiler access something it was never instructed (allowed) to create?

Notice (as an example of scope, and not a recommendation) if you move line 40 onto line 50, the error goes away? Why do you think the compiler is satisfied with that now it can access the non existent object in that context?

Out of curiosity, what tutorial or book did you use to learn how to write code in C#?
Not being funny, but obviously none, or so I would assume, and our OP said they only do this for fun.
So a simple:
Code:
var seeds = Terrain.Controls.Where(c => c.Tags == "Seeds");
will return an enumerable of all the controls that he had created with that tag.
I was trying to encourage learning by making our OP consider my question. lol But that ship sailed. Thank's for making that one easier em. :LOL::rolleyes:
 
Need to toss out a bone occasionally. :)
 
You guys really helped me a ton. Thank you so much!

I finally managed to fix the problem by searching for the Name property of the picturebox. It works perfectly!
C#:
            //CHARACTER COLLISION WITH SEEDS
            // Loop through all controls
            foreach (Control control in Terrain.Controls)
            {
                // Determine what the control is, and if it is, remove it.
                if (control.Name == "Seeds")
                {
                    if (Character.Bounds.IntersectsWith(control.Bounds))
                    {
                        Terrain.Controls.Remove(control);
                    }
                }
            }
            // ----------------------------------
 
Does that really work? Normally, you'll run into issues when you remove items from a collection while you are still iterating over the collection. As I recall an exception is thrown. For example, the following fails:
C#:
        var list = new List<int>() { 1, 2, 3 };
        foreach(var number in list)
        {
            if (number == 2)
                list.Remove(number);
        }

If it works for your particular situation, it looks like you lucked out.
 
Correct indeed, I'm a bit behind, so only getting a chance to follow up on this now. Skydiver is right, don't try to modify a collection in a loop. However, this can be avoided by doing the following :
C#:
            var list = new List<int>() { 1, 2, 3 }; int i = 0; bool removeValue = false;
            foreach (var number in list)
            {
                i++; /* Report the position */
                if (number == 2)
                {
                    removeValue = true;
                    break; /* Let me out, I'm claustrophobic ;) */
                }
            }
            if (removeValue) list.Remove(i); /* Remove without exception */
You will notice number two is now gone safely without InvalidOperationException flopping a toss, leaving only one and three on index zero and one.
 
I generally recommend using a for loop with a decreasing counter if there's a desire to remove items, e.g.
C#:
// Create a list containing numbers 1-10.
var items = Enumerable.Range(1, 10).ToList();

Console.WriteLine(string.Join(", ", items));

// Remove all even numbers.
for (int i = items.Count - 1; i >= 0; i--)
{
    if (items[i] % 2 == 0)
    {
        items.RemoveAt(i);
    }
}

Console.WriteLine(string.Join(", ", items));
Console.ReadLine();
 
Back
Top Bottom