Little Help Fellow Programmers ?

Miscellaneous. No spam or advertisements, constructive discussion encouraged.
Post Reply
User avatar
Iv121
Vice Admiral
Vice Admiral
Posts:2414
Joined:Fri Dec 07, 2012 3:40 pm
Affiliation:UTN
Location:-> HERE <-
Little Help Fellow Programmers ?

Post by Iv121 » Fri May 03, 2013 8:20 am

So basically as my programming finals project I am making a small game (we are working with C# windows form) with a bunch of stuff. I have a map with a size of 6400x6400 pixels (or 100x100 Tiles) which is basically a huge bitmap made out of the textures loaded for each tile from a map file. I try to make the map move around like in standard RTS games when the mouse hovers near the end of the screen and made a rather simple method that paints the bitmap in other co-ordinates depending on the mouse location (Like viewing that map through a small window and moving the map in front of it in order to see other parts of it). Unfortunately moving a 6400x6400 bitmap is quite costy and when I tried to make the process more efficient I ended up with even more costy methods crashing the application with "out of memory" :[ .

Anyway that is what I made

Spoiler:

Code: Select all

        private void Form1_MouseMove(object sender, MouseEventArgs e) // checks if the mouse hovers near the edges of the screen
        {

            if (e.Y <= 1)
            {
                mouseHoverSide[0] = true;  //   mouseHoverSide is a bool array that stores if the mouse hovers near the edge of the screen
            }                              // directions 0 - up, 1 - right, 2 - down , 3 - left
            else
            {
                mouseHoverSide[0] = false;

                if (e.Y >= this.Height - 1)
                    mouseHoverSide[2] = true;
                else
                    mouseHoverSide[2] = false;
            }

            if (e.X >= this.Width - 1)
            {
                mouseHoverSide[1] = true;
            }
            else
            {
                mouseHoverSide[1] = false;

                if (e.X <= 1)
                    mouseHoverSide[3] = true;
                else
                    mouseHoverSide[3] = false;
            }

        }

Code: Select all

        private void timer1_Tick(object sender, EventArgs e) // moves the background around the screen. Ticks every 10 milliseconds.
        {
            if (mouseHoverSide[0])
            {
                currentAnchor.Y += 8; // currentAnchor is a point object that stores the co-ordinates of the top left corner of the BITMAP.
                if (currentAnchor.Y > 0)  // the anchor is like a nail in the bitmap and if it "bumps" into the edge of the screen it cannot go any further.
                    currentAnchor.Y = 0;

                g.DrawImage(background, currentAnchor); // background is the map textures themselves
                g.DrawImage(tankLayer, currentAnchor);   // the tanks are drawn separately so it is easier to clean them and draw elsewhere.
                g.DrawImage(foreground, currentAnchor); // foreground is used for temporary effects such as animations.
            }
            if (mouseHoverSide[1])
            {
                currentAnchor.X -= 8;
                if (currentAnchor.X < this.Width - background.Width)
                    currentAnchor.X = this.Width - background.Width;

                g.DrawImage(background, currentAnchor);
                g.DrawImage(tankLayer, currentAnchor);
                g.DrawImage(foreground, currentAnchor);
            }
            if (mouseHoverSide[2])
            {
                currentAnchor.Y -= 8;
                if (currentAnchor.Y < this.Height - panelMenu2.Height - background.Height)
                    currentAnchor.Y = this.Height - panelMenu2.Height - background.Height;

                g.DrawImage(background, currentAnchor);
                g.DrawImage(tankLayer, currentAnchor);
                g.DrawImage(foreground, currentAnchor);
            }
            if (mouseHoverSide[3])
            {
                currentAnchor.X += 8;
                if (currentAnchor.X > panelMenu1.Width)
                    currentAnchor.X = panelMenu1.Width;

                g.DrawImage(background, currentAnchor);
                g.DrawImage(tankLayer, currentAnchor);
                g.DrawImage(foreground, currentAnchor);
            }

            label3.Text = currentAnchor.ToString(); // used for debugging.
        }
How can I make it more efficiently ?
They're watching ... Image

"I am forbidden tag" -CvN

Luna
Lieutenant
Lieutenant
Posts:583
Joined:Thu Dec 06, 2012 7:21 pm

Re: Little Help Fellow Programmers ?

Post by Luna » Fri May 03, 2013 9:18 am

I have a very limited knowledge of code so i didn't see anything wrong, your going to have to wait until frostbyte comes around.
"The Sky is the Limit"
Image
Commander Error wrote:"Titan" - Moves slightly quicker than a glacier, on a good day.
Prototype wrote:F-14s are just gay Tornados.
Catsonmeth wrote:Which meant every two weeks, Tuesday night was reserved for mainlining coffee and getting sensual with a keyboard

User avatar
Iv121
Vice Admiral
Vice Admiral
Posts:2414
Joined:Fri Dec 07, 2012 3:40 pm
Affiliation:UTN
Location:-> HERE <-

Re: Little Help Fellow Programmers ?

Post by Iv121 » Fri May 03, 2013 9:26 am

I know this piece of code works (it is actually perfectly fine with small maps). The problem is in the way in which I do it which is not efficient enough. I need to find a more efficient way to do it .
They're watching ... Image

"I am forbidden tag" -CvN

User avatar
fr0stbyte124
Developer
Posts:727
Joined:Fri Dec 07, 2012 3:39 am
Affiliation:Aye-Aye

Re: Little Help Fellow Programmers ?

Post by fr0stbyte124 » Fri May 03, 2013 9:43 am

I've never used System.Drawing.Graphics before, so I can't really advise on how to get the best performance overall, but you should definitely not be working with the entire map at any given time.

You said the map was divided into tiles of 64x64 pixels, so keep them individual images. Then, when it is time to draw, calculate the range of tiles which will actually be seen within the viewport and draw those independently. If the graphics library belongs anywhere near a game, multiple draws shouldn't add any perceivable overhead.

Additionally, if you can, draw tank sprites directly to the screen rather than to an image layer. If they move significantly, storing old image data is a waste of resources. Same goes for effects. Only where needed.


If you'd like, send me a copy of your project and I can see if anything else stands out. When is it due?

User avatar
Iv121
Vice Admiral
Vice Admiral
Posts:2414
Joined:Fri Dec 07, 2012 3:40 pm
Affiliation:UTN
Location:-> HERE <-

Re: Little Help Fellow Programmers ?

Post by Iv121 » Fri May 03, 2013 10:11 am

OH depends on what kind of teacher you have O.o . The situation is a bit dire because just like you I didn't work with graphics before and it turned out a nightmare - a lot of problems and limitations that appear out of nowhere (like this one). If not for those I could complete it two months ago but making all the animation using it is not easy (thank god at least the textures can be made in Photoshop).

Also I tried to make a central way to do animations which mostly divide into move and rotate. I store the needed data in variables (such as the image to move and the direction to move it) , after that I start a timer IsBusy which is the set frame of time given to perform the animation. During this time an animTimer ticks every 10 milliseconds redrawing the image elsewhere until the IsBusy timer ticks and stops himself and that timer. In the meantime I stopped the main program with a while loop, waiting until the timer is not "Busy" anymore.

Here it gets complicated as it turns out that while I'm in that loop the timer does not work (I always thought in windows Form they already have threads and such built in) so now I need to add threads (which I'm not swimming in and would be glad to some direction). The two problems I encounter that my tries to use threads and thread.sleep in windows form in general freezed the whole form so I'm not sure I know how to do it correctly and also the timer takes parameters when called (which god knows where it takes them from , and probably frost)

That is if we use timers that I tried to use to avoid thread.Sleep anyway which in our case is the best way to do it. That is why if we talk about it already let's try to make this work instead:
Spoiler:

Code: Select all

        private void TurnAnim()  // turns the given tank at the given location
        {
            processedArea = background.Clone(ActiveRec, background.PixelFormat); // used to repaint the background behind the tank that we move to avoid tank duplication
            foreground = new Bitmap(animImage); // creates a foreground for this specific animation

            tlGraphics.Clip = new Region(ActiveRec);
            tlGraphics.Clear(Color.FromArgb(0, 255, 255, 255)); // deletes the static tank picture from the tank layer

            if (animDir == Direction.left) // decides the turning direction
                animSpeed = -animSpeed;

            for (int i = 0; i < 90 / animSpeed; i++) // keep rotating until the tank is turned 90 deg
            {
                fgGraphics.RotateTransform((float)animSpeed); // turns the tank
                g.DrawImage(processedArea, ActiveRec); // cleans the tank off the window
                g.DrawImage(foreground, ActiveRec);      // draws the new tank
                Thread.Sleep(10); 
            }

            tlGraphics.DrawImage(background, ActiveRec); // now that the picture is in its new place stores in tank layer
        }
That would be best to make this work in a windows form thread.
They're watching ... Image

"I am forbidden tag" -CvN

User avatar
fr0stbyte124
Developer
Posts:727
Joined:Fri Dec 07, 2012 3:39 am
Affiliation:Aye-Aye

Re: Little Help Fellow Programmers ?

Post by fr0stbyte124 » Fri May 03, 2013 2:42 pm

http://www.codeproject.com/Articles/814 ... ker-Thread

It's hard to say how you should do it with the information you provided. The first thing, though, is that you really don't need to thread every single entity. Use a thread-safe collection from here http://msdn.microsoft.com/en-us/library ... rrent.aspx to store your tanks. In the main thread run your game logic, but give the tanks rotational and directional velocities. Then, do all your frame updating in that timer's tick event.

And again, don't do anything that involves handling a single world-sized image.

User avatar
Iv121
Vice Admiral
Vice Admiral
Posts:2414
Joined:Fri Dec 07, 2012 3:40 pm
Affiliation:UTN
Location:-> HERE <-

Re: Little Help Fellow Programmers ?

Post by Iv121 » Fri May 03, 2013 3:35 pm

My mind doesn't work properly this late and the article you gave is a bit harder to understand because he uses too many unrelated classes and threads, why could he just write <OUTPUT> instead of all that MSG stuff in the first part of the example, makes it much more user friendly.

I guess I could just disable player controls until isBusy ticks and this way stopping the player from taking any actions while the anim is playing but problem is that usually an anim is a sequence of a few parts including rotation first and movement later, so I need to find a way to return to the second part of the anim after I'm done with the first part. The thread.Sleep could be perfect for this but I already get that this and painting doesn't go well.

Actually might I ask - If I keep a timer that each 10 milliseconds draws an image in a var at a given location in a var and update the info of those vars from a thread (using thread.Sleep to synchronize it correctly) will it work ?

Ah we'll see tomorrow, I'm off now.
They're watching ... Image

"I am forbidden tag" -CvN

User avatar
fr0stbyte124
Developer
Posts:727
Joined:Fri Dec 07, 2012 3:39 am
Affiliation:Aye-Aye

Re: Little Help Fellow Programmers ?

Post by fr0stbyte124 » Fri May 03, 2013 5:03 pm

I don't know. It would depend a lot on how it is implemented, and I can't tell enough from your description whether you are doing it correctly or not. I'd have to see the code firsthand to really understand.

I would recommend having a longer interval than 10ms if you are still having performance issues. The events will begin piling up in the queue if they don't finish before the next one is invoked.

Also, avoid any direct communication between threads unless you really know what you are doing; it can cause instability issues if you are not careful. Instead, use one of those thread-safe collections I linked you to store your entities. To handle moving after rotating, just introduce a value to track the action state inside the tank class. If you are ROTATING and hit the correct orientation, change the state to MOVING and continue the action on each subsequent tick. Don't try to use Thread.Sleep() in the drawing thread.

--------------------
Actually, I'm wondering how much you actually need threading. The way Minecraft does it is it runs draw frames back-to-back as quickly as it can. During this process, mouse interrupts are read and the camera is updated, and terrain and entities are drawn. Entities all have a velocity component, and during each frame their position is tweaked based on the amount of time since the last tick. When a set amount of time has passed, the tick routine is executed at the end of draw routine. If you look at the profiler in the F3 menu, the game ticks are the white marks and the red/green marks are the frame draws. Both run on the same thread.


Say your game logic tick is 10/sec, or 100ms intervals. If you wanted to do the non-threaded version, do this:
-At the start of your program record System.DateTime.Now.
-Add 100 ms to that time and make it your TargetTime.
-Run draw updates back to back until DateTime.Now > TargetTime.
-Add another 100ms to TargetTime. More accurately, keep adding 100ms until TargetTime >DateTime.Now if something unexpected happened. (Going off the previous target time instead of the current time will give us a more consistent average tick rate.)


Mouse events could pose a problem if everything is running in the UI thread, as the main loop would starve the other UI events, which I am beginning to suspect is what you were describing before. Instead of looping, try manually raising the same event once it is completed. This should give the other events a chance to fire, but I am not completely sure this won't cause threading issues. You'll need to try it and find out.


Once you have that, I would start trying to optimize the drawing method some more. For a 2D sprite game, it should be very easy to run on a modern computer.

User avatar
Iv121
Vice Admiral
Vice Admiral
Posts:2414
Joined:Fri Dec 07, 2012 3:40 pm
Affiliation:UTN
Location:-> HERE <-

Re: Little Help Fellow Programmers ?

Post by Iv121 » Sat May 04, 2013 1:32 am

I'm really not sure I'm doing it efficiently :tongue: . I don't think there is a need to add velocity and such to tanks because I move each one individually. I will try some of the stuff in here and return to you later.
They're watching ... Image

"I am forbidden tag" -CvN

User avatar
Iv121
Vice Admiral
Vice Admiral
Posts:2414
Joined:Fri Dec 07, 2012 3:40 pm
Affiliation:UTN
Location:-> HERE <-

Re: Little Help Fellow Programmers ?

Post by Iv121 » Sat May 04, 2013 6:12 am

So basically the way I tried to implement it doesn't work because if I update those vars in another thread and the main one is processing it in the same time it crashes. I need a way to leave the info for the main thread in such way it does not interfere with the processes going on there.
They're watching ... Image

"I am forbidden tag" -CvN

User avatar
Iv121
Vice Admiral
Vice Admiral
Posts:2414
Joined:Fri Dec 07, 2012 3:40 pm
Affiliation:UTN
Location:-> HERE <-

Re: Little Help Fellow Programmers ?

Post by Iv121 » Sat May 04, 2013 1:08 pm

While I am waiting for help on the thread matter I tried to work on the screen. Painting the visible tiles one by one proved to be more efficient but the graphical results of it were quite dodgy. The pictures were spazzing out, blinking and moving individually. It seems like there is no way around storing them in a bigger bitmap... I need to join up new pictures and delete the old ones every 64 pixels I pass which is a bit a pain in the butt but I can make it I think.

Edit: I'm not sure it works, every 64 pixels it does freeze for a moment while it makes the new background out of tiles.
They're watching ... Image

"I am forbidden tag" -CvN

User avatar
fr0stbyte124
Developer
Posts:727
Joined:Fri Dec 07, 2012 3:39 am
Affiliation:Aye-Aye

Re: Little Help Fellow Programmers ?

Post by fr0stbyte124 » Sat May 04, 2013 10:39 pm

Copy the image to a separate frame the size of the viewport. Copy that frame to the viewport only when you are finished drawing. You may also want to experiment with mixing it up. Drawing the terrain to the buffer, but drawing the tanks to the viewport, or using having two buffers which updated independently, for instance.

The thing where one thread interferes with another is because the objects are not thread-safe, and only one thread can interract with it at a time or the data gets corrupted. If your objects are not thread-safe, and most aren't, you need to access them from a thread-safe container like I've been saying. The container will prevent multiple threads from touching the contents at the same time. Sometimes even reading from it will cause a crash. If you need to do multiple operations, consider swapping entities out instead of accessing them directly.

If you haven't covered programming with threads, I would recommend avoiding them altogether. It's really easy for things to go wrong when threads get involved.

User avatar
Iv121
Vice Admiral
Vice Admiral
Posts:2414
Joined:Fri Dec 07, 2012 3:40 pm
Affiliation:UTN
Location:-> HERE <-

Re: Little Help Fellow Programmers ?

Post by Iv121 » Sat May 04, 2013 11:03 pm

Ah we covered everything but in such haste we basically covered nothing - you learn on your own just like in RL :[ . You say if I store them in a thread-safe container both can access it simultaneously ?

Also I already use a buffer bitmap. The problem is again that ever 64 pixels it freezes while it paints the new bitmap.
They're watching ... Image

"I am forbidden tag" -CvN

User avatar
fr0stbyte124
Developer
Posts:727
Joined:Fri Dec 07, 2012 3:39 am
Affiliation:Aye-Aye

Re: Little Help Fellow Programmers ?

Post by fr0stbyte124 » Sun May 05, 2013 12:48 am

A thread-safe container doesn't let you access objects at the same time. It makes sure that only one thread is accessing the object at any given time. Any other thread trying to access the object will be stalled until the object is freed up. As long as you are in and out quickly, this is the normal way of doing it.

You are saying that it stutters even when drawing the entire image? That's not good. You may need to look into using a different library. How much time do you have left?

User avatar
Iv121
Vice Admiral
Vice Admiral
Posts:2414
Joined:Fri Dec 07, 2012 3:40 pm
Affiliation:UTN
Location:-> HERE <-

Re: Little Help Fellow Programmers ?

Post by Iv121 » Sun May 05, 2013 4:00 am

Not enough for this I guess. I'm already way over my head thinking about stuff ppl that do similar projects shouldn't think about (such as that threading or efficiency).

I think I can finish rather quickly if I get the graphics figured but for this I have only 3 weeks left. Man next project is text based -.- . In case stuff doesn't go well I can simply go and cut out stuff. I think that the way to do this with the map moving is to move it all , but move it 64 pixels at a time or a whole tile. This way it will be still usable with the freezing. If we are doing it this way I guess we can simply paint the selected tiles onto the bitmap and then paint it. It will still freeze a bit like it froze before when I tried to load separate tiles but I guess it will be better than moving the whole bitmap.
They're watching ... Image

"I am forbidden tag" -CvN

Post Reply