1

I'm currently creating an 2d tilemap for my game. In order to do that I made a 2dArray filled with tiles. Those tiles are classes with an image stored in it.

Every time I move the sprite on the map, the map has to redraw itself every 1ms (I have a reason to do that). But it is too slow... It's running at 30 fps right now and I don't know what to do. I have tried many things but the results are the same.

This is how I make the map :

        public void makeBoard()
{
    for (int i = 0; i < tileArray.GetLength(0); i++)
    {
        for (int j = 0; j < tileArray.GetLength(1); j++)
        {
            tileArray[i, j] = new Tile() { xPos = j * 50, yPos = i * 50 };
        }
    }
}

Here I redraw each 1ms or higher the tiles and sprites :

      private void Wereld_Paint_1(object sender, PaintEventArgs e)
{
    //label1.Text = k++.ToString();
    using (Graphics grap = Graphics.FromImage(bmp))
    {
        for (int i = 0; i < tileArray.GetLength(0); i++)
        {
            for (int j = 0; j < tileArray.GetLength(1); j++)
            {
                grap.DrawImage(tileArray[i, j].tileImage, j * 50, i * 50, 50, 50);
            }
        }
        grap.DrawImage(player.movingObjectImage, player.xPos, player.yPos, 50, 50);
        grap.DrawImage(enemyGoblin.movingObjectImage, enemyGoblin.xPos, enemyGoblin.yPos, 50, 50);

        groundPictureBox.Image = bmp;
       // grap.Dispose();

    }
}

This is the Timer with a specific interval :

        private void UpdateTimer_Tick(object sender, EventArgs e)
{
    if(player.Update()==true) // true keydown event is fired
    {
        this.Invalidate();
    }
    label1.Text = lastFrameRate.ToString(); // for fps rate show
    CalculateFrameRate(); // for fps rate show
}
halfer
  • 19,824
  • 17
  • 99
  • 186
M Yil
  • 877
  • 1
  • 14
  • 35
  • 1. Check your images PixelFormat. It should be Format32bppARGB to avoid picture conversion on Draw. 2. Adjust the drawing Context Settings. Look for Keywords sourceOver, SourceCopy, NearestNeighbour, AntiAliased 3. Use Draw Image unscaled. This is faster than drawimage. But I fear you will not achieve 1ms refresh with gdi. – Thomas Voß Jul 18 '16 at 09:31
  • @Thomas Voß Allright thank you. But i have tried all of that already and unscaled makes it much worse for me. So i guess i can't do anything to this problem with windows forms? – M Yil Jul 18 '16 at 17:01
  • What you can try is: Draw all your tiles of one frame to a Bitmap, display that Bitmap at the end. On the next frame, Draw the old Bitmap to a new Bitmap but moved by the move offset. Then you only have to draw the remaining tiles and display the image again. By this you might save a lot of draw calls. For any figures you have, just draw them afterwards on top of the background. But that will only help if you have many tiles and if you are reusing some of them each frame. And in the end i don't think that you will see 1000 fps. Even 100 sounds much to me. – Thomas Voß Jul 18 '16 at 17:50
  • And you can try all of these settings: http://stackoverflow.com/a/38262796/6439999 Just in case you have missed some. – Thomas Voß Jul 18 '16 at 17:58
  • Another thing you can try: Let's say you have 40 tiles in width an 30 in height: With Parallel for you can draw to 30 row pictures and then you put them together afterwards. By this you can use all cores of the cpu. But you need your own Graphics from Image for each Thread. Otherwise you will see exceptions. – Thomas Voß Jul 18 '16 at 18:04
  • @Thomas Voß Ahh man... I don't reallt understand what you are trying to tell me with "What you can try is: Draw all your tiles of one frame to a Bitmap, display that Bitmap at the end. On the next frame, Draw the old Bitmap to a new Bitmap but moved by the move offset. Then you only have to draw the remaining tiles and display the image again. ". Can you please give me an example how I am supposed to do that, i really can't stop without making this optimal : / – M Yil Jul 18 '16 at 20:41
  • How many tiles do you have? Are they all always visible? – Thomas Voß Jul 19 '16 at 14:51
  • @Thomas Voß Hello thomas. Thank you for your help! Currently i draw 20 by 38 tiles, a screen full of tiles with 50 height and 50 width. Can u please tell me what i should do with this 1."backgroundRedrawRequired", 2."backGroundBitmap". Thank you for your kindness ! – M Yil Jul 19 '16 at 17:17
  • I thought that the tiles contain your background for your player and enemygoblin. As it only changes when you move out of the screen, there is usually no need to call DrawImage 760 times for each Frame. Just draw them all to one Bitmap (backGroundBitmap) and do a single drawImage call. Only if the map changes (when you move out of the screen) you have to redraw the background with the new visible area. – Thomas Voß Jul 19 '16 at 18:10
  • @Thomas Voß The map has to change when th player moves. So the only way is to grap.drawimage at every, lets say 1ms. Because the map is moving with a 1 pixel a time, so it has to be redrawn constantly. Or can i just creat the bitmap once, and use it to draw it every ms so i dont need to redraw each tile every 1ms? Is that possible you think? – M Yil Jul 19 '16 at 22:33
  • The question is is all tiles are always visible. If I multiply 38*50=1900 and 20*50=1000 you have almost a full hd resolution. So the whole map can be visible at the same time. Why do you try to move the background instead of the player/enemy? if your screen resolution is smaller than the background you can still draw the whole picture. even with a negative x/y coordinate. the minus part will then be discardd. So yes I think your best chance is to draw the whole background at once and to add the dynamic part afterwards. – Thomas Voß Jul 19 '16 at 22:57
  • Just one question: You say that you want to draw 1000fps. If now the player moves 1 pixel every frame, the player crosses the whole screen in 1.9 seconds. Isn't that a bit fast? – Thomas Voß Jul 19 '16 at 23:00
  • @Thomas Voß The reason that i want the background to move is because i want to set a camera movement. But that is not for now but for later. For now i want it to be fluent and not on 30 fps afther that i implement the camera function. I just want the player to move smooth. I dont know if it should be 1ms or 20. Just 60 fps. If i move the player it goes 50 pixels to that side so you can only move from tile to tile. – M Yil Jul 20 '16 at 11:27
  • @Thomas Voß "If i move the player it goes 50 pixels to that side so you can only move from tile to tile.". I mean : if i move the tiles... then it will look like player moves. – M Yil Jul 20 '16 at 12:44

1 Answers1

0

Concerning my comment:

private void Wereld_Paint_1(object sender, PaintEventArgs e)
{
    if(backgroundRedrawRequired)
    {
        Bitmap newBackground = new Bitmap(this.backGroundBitmap.Width, this.backGroundBitmap.Height, PixelFormat.Format32bppARGB);
        using (Graphics grap2 = Graphics.FromImage(newBackground))
        {
            // Draw the old background to the new image moved by the offset in X/Y 
            // MoveByNumberOfTilesX/Y can be negative depending on the direction you move the background.
            grap2.DrawImage(this.BackgroundImage, this.moveByNumberOfTilesX * 50, this.moveByNumberOfTilesY, this.BackgroundImage.Width, this.BackgroundImage.Height);

            // ToDo: Set the start i/j or the upper Bound of both for loops, so that only the tiles of the "white" area in the new background drawing get drawn.
            for (int i = 0; i < tileArray.GetLength(0); i++)
            {
                for (int j = 0; j < tileArray.GetLength(1); j++)
                {
                    grap2.DrawImage(tileArray[i, j].tileImage, j * 50, i * 50, 50, 50);
                }
            }
        }
        this.BackgroundImage = newBackground;
    }

    using (Graphics grap = Graphics.FromImage(bmp))
    {
        // Reuse the background as long as it doesn't change!
        grap.DrawImage(this.backGroundBitmap,0,0, groundPictureBox.Width, groundPictureBox.Height);
        grap.DrawImage(player.movingObjectImage, player.xPos, player.yPos, 50, 50);
        grap.DrawImage(enemyGoblin.movingObjectImage, enemyGoblin.xPos, enemyGoblin.yPos, 50, 50);
    }

    groundPictureBox.Image =bmp;
}
Thomas Voß
  • 1,145
  • 8
  • 20