Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Speeding up a background image

Status
Not open for further replies.

forrestcupp

Programmer
Jul 22, 2006
25
0
0
I am using c#/.NET with GDI to program my version of the classic snake game where the snake moves around trying not to crash, and everytime it eats something it gets longer.

I started off with just a solid background color:

this.BackColor = Color.SomeColor;

And I got pretty high speeds out of my game. But when I added a background image for a textured background, it slowed it down a lot. I started off by using:

this.BackgroundImage = Image.FromFile("file.jpg");

To try to speed it back up, I tried embedding the image, which didn't do any good. I tried using an uncompressed bmp file instead of a jpg, which didn't help. I tried using a Bitmap instead of an Image. I also tried drawing the image from the OnPaint handler. Using the OnPaint handler did speed things up a little, but not nearly enough. I need the speed, so that at higher levels of the game, I can speed the snake up.

Does anyone know a good way to have a background image without slowing things down, or is this just a major hindrance of .NET and GDI?
 
Create a class that extends your control - in this case a picturebox and set it to double buffer for you.

public class DoubleBufferPictureBox : PictureBox
{
public DoubleBufferPictureBox()
{
this.SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.DoubleBuffer,true);
}
}


Then set your background at the start of your level.

public void InitializeGame()
{
doublebufferedpicturebox1.Image = Image.FromFile("blah.jpg");
}

then on the paint event you would do

protected override void OnPaint(object sender, PaintEventArgs e)
{
base.Paint(e);

DrawSnake();
}

And that should move much faster for you.
 
Well, I tried what you suggested and putting the base.Paint(e); didn't work. It said "The event System.Windows.Forms.Control.Paint can only appear on the left hand side of += or -="

I'm new at c#/.NET so maybe I'm overlooking something obvious here?
 
I was wondering if maybe you meant that I should have
base.OnPaint(e);
in the OnPaint handler?

if so, I tried that and didn't get any errors, but it didn't display the image in my window. What am I leaving out?
 
Well, it's a jumbled mess, but I'll try to pick out what I did that pertains to what you wrote.

I made the DoubleBufferPictureBox class that you showed.

Then in the main window class, in the class scope, I defined:

DoubleBufferPictureBox myBackground = new DoubleBufferPictureBox();

then in my InitializeComponents method, I have:

myBackground.Image = Image.FromFile("background.jpg");
this.Controls.Add(myBackground);


then in the overrided OnPaint handler, I have:

protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);

all the rest of the code that controls the game

}

before I put in the this.Controls.Add thing, it just had a plain gray background. When I put this in, it has a plain gray background with weird things in the upper left corner.
 
Your problem is stemming from having to redraw the image every time you run a paint. That's a big opertion for the GDI.

What style of Snake are you creating? The classic 'tick' based snake or a smooth scrolling one? If you are using tick based snake then you should only redraw your image on a tick, not on every paint (which would happen on a timer update, on a window resize, when an apple appears, if windows scratches it's bum...).

If you are using a smooth scrolling window then you do really need to get your head around this double buffering issue. By the sounds of it you arn't writing your image property at the moment. Step back from loading your background as a control on the page and try using Graphics.DrawImage to draw the image directly onto the back of your playing field.

This will also give you some intersting options for only drawing a small portion of the image - opening up the possiblilty of refreshing only the part that was overwritten.

My knowledge of GDI is sketchy at best - mainly limited to image reprocessing for thumbnails and the like. You've got me wondering if I could do this now :)

I don't suppose you'd be willing to let slip what sort of structure you are using to store the previous locations of the snake body? The last time I did something like this was in PASCAL :)

Incidentaly - you may be better looking into Managed DirectX for this kind of thing, rather than the GDI. It's no where near as scary as it sounds - C# and DirectX are getting along just fine these days.


Yet another unchecked rambling brought to you by:
Oddball
 
I kind of thought DirectX might be the way to go, but I don't know anything about it yet. I have the DX SDL, but I haven't gotten into it yet.
As for using Graphics.DrawImage, I have already tried that, and it did speed things up, just not nearly enough. I am thinking about for this project using a solid background color, as that is a lot faster to process. I'll get into directX later.

As for how my snake body is structured, I am using an array of Rectangles about 90 segments long. The program keeps track of where each segment is located, and when the snake moves, each segment is assigned the position of the segment in front of it. Only the segments within the current length of the snake are drawn with the Graphics.FillEllipse method, and only those segments are active so that you can crash into yourself. The snake moves in the classic tick movement, but the problem is that you are supposed to eat numbers from 1 - 9 in order, and the numbers are smoothly moving around the screen, so I can't just do the background on a tick.

If nothing can be done, I'll just use a solid color background and figure out DirectX for another project.
 
I'll keep this thread monitored for a while - let us know how you get on.

I bet you forget to tell us when it's finished ;)


Yet another unchecked rambling brought to you by:
Oddball
 
Oddball,

You were right. I forgot to tell you when it was finished. I have been done for a while. I ended up sticking with the GDI and using a solid color background, but I think it turned out just as good. If you want to check it out, it is a free download on download.com. Just go there and enter number snake in the search, and I think it's the first thing that comes up. Of course you need .NET 2.0, and you also need DirectX 9.0 for the audio.

Since then, I have learned a little managed dx, and other forms of 3D programming. I am also meddling with C++ now, too.

p.s. I'm not trying to advertise here. The snake game is a free download, so I'm not going to benefit from an advertisement.
 
I should have gone to the book makers and put some money on ;) But then I guess they'd be asking for it back, now :D

*wonders off to download the game and give it a bash*


Yet another unchecked rambling brought to you by:
Oddball
 
Hmm - that didn't work. I submitted the error report to MS so that'll help :S

I have Windows XP Pro 64bit edition - that's the only thing I can think would make happy problems occuring.

Code:
EventType: clr20r3
P1: snakes.exe
P2: 1.0.2482.15184
P3: 45362b71
P4: snakes
P5: 1.0.2482.15184
P6: 45362b71
P7: 17
P8: 6
P9: system.badimageformatexception

Make of that what you will :S


Yet another unchecked rambling brought to you by:
Oddball
 
Sorry about that. I guess maybe it wouldn't work on 64bit.
 
Using a double buffer should work fine. I've got a program that loads background images of 8,000x8,000 pixels and allows me to drag around different shapped large icons (400x400 and larger) and it is all pretty smooth.

I do 2 things here. I use the inbuilt double buffering listed above and use GDI+ to draw directly to the window and I also maintian my own back buffer and draw from that.

so I initialise my back buffer
Code:
      if (bmOffScreen != null)
        bmOffScreen.Dispose();
      bmOffScreen = new System.Drawing.Bitmap(this.ClientRectangle.Width,
                                             this.ClientRectangle.Height);
      System.Drawing.Graphics grphOS = System.Drawing.Graphics.FromImage(bmOffScreen);
          grphOS.DrawImage((System.Drawing.Bitmap)cBattleMap.bmMap,0,0);

This basically sets up my back buffer. I'm actually doing more complicated stuff now by using tiles but this worked fine.

Then in the paint event of the window I did
Code:
      e.Graphics.DrawImage(bmOffScreen, 0, 0);
      if (tknDrag != null)
      {
        tknDrag.DrawDMToken(e.Graphics, dZoomLevel, rectSource);
      }

Basically the same as above but since my offscreen buffer is more complicated I need to initialise it on significant events. With a snakes game you could just use the original image directly and then just use DrawImage directly. Then you would do all your drawing of your snake and pill(s).

Another thing you could do to speed things up is find the bounding box of the super position your snake before and after it moves and only invalidate that box. then instead of
Code:
 e.Graphics.DrawImage(bmOffScreen, 0, 0);
drawing the whole image you'd limit it to the clip rectangle that you get via the "e" event object.


Hope I've been helpful,
Wayne Francis

If you want to get the best response to a question, please check out FAQ222-2244 first
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top