Оптимизация изометрического тайлового движка для лучшего FPS

У меня есть изометрический тайловый движок, написанный на XNA (Monogame). Он может рисовать только мозаичную поверхность карты. Но когда у меня большая карта (например, 50x50 тайлов), то это очень медленно (около 15 кадров в секунду). Когда у меня небольшая карта (например, 10x10 тайлов), то частота кадров идеальна (60 FPS).

Я пытаюсь найти способ оптимизировать свой код, но понятия не имею, как это сделать.

Это мой код:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using System;

namespace IsoEngine
{
    public class Game1 : Game
    {
        GraphicsDeviceManager _graphics;
        SpriteBatch _spriteBatch;

        Texture2D Tile1;
        Texture2D Tile2;
        MouseState mouseState;
        bool isMousePressed = false;

        int[,] map = { {1, 1, 1, 1},
                       {1, 0, 0, 1},
                       {1, 0, 0, 1},
                       {1, 1, 1, 1} };

        int tileWidth = 64;
        int tileHeight = 32;
        Vector2 scrollSpan = new Vector2(0, 0);
        Vector2 mouseDragPos = new Vector2(0, 0);

        public Game1()
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            base.IsMouseVisible = true;
            base.Initialize();
        }

        protected override void LoadContent()
        {
            _spriteBatch = new SpriteBatch(GraphicsDevice);

            Tile1 = Content.Load<Texture2D>("1");
            Tile2 = Content.Load<Texture2D>("2");
        }

        protected override void UnloadContent()
        {
        }

        protected override void Update(GameTime gameTime)
        {
            mouseState = Mouse.GetState();

            if (mouseState.LeftButton == ButtonState.Pressed && !isMousePressed)
            {
                isMousePressed = true;
                mouseDragPos.X = mouseState.X;
                mouseDragPos.Y = mouseState.Y;
            }

            if (mouseState.LeftButton == ButtonState.Pressed && isMousePressed)
            {
                if (mouseDragPos.X < mouseState.X)
                {
                    scrollSpan.X += mouseState.X - mouseDragPos.X;
                    mouseDragPos.X = mouseState.X; 
                }
                if (mouseDragPos.X > mouseState.X)
                {
                    scrollSpan.X -= mouseDragPos.X - mouseState.X;
                    mouseDragPos.X = mouseState.X;
                }
                if (mouseDragPos.Y < mouseState.Y)
                {
                    scrollSpan.Y += (mouseState.Y - mouseDragPos.Y) * 2;
                    mouseDragPos.Y = mouseState.Y;
                }
                if (mouseDragPos.Y > mouseState.Y)
                {
                    scrollSpan.Y -= (mouseDragPos.Y - mouseState.Y) * 2;
                    mouseDragPos.Y = mouseState.Y;
                }
            }

            if (mouseState.LeftButton == ButtonState.Released && isMousePressed)
                isMousePressed = false;

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            _spriteBatch.Begin();
            DrawMap();
            _spriteBatch.End();

            base.Draw(gameTime);
        }

        private void DrawMap()
        {
            for (int osaY = 0; osaY < map.GetLength(0); osaY++)
            {
                for (int osaX = 0; osaX < map.GetLength(1); osaX++)
                {
                    int x = osaX * 32;
                    int y = osaY * 32;

                    Texture2D thisTile = Tile1;

                    if (map[osaY, osaX] == 0)
                        thisTile = Tile1;

                    if (map[osaY, osaX] == 1)
                        thisTile = Tile2;

                    PlaceTile(thisTile, CartToIso(new Vector2(x, y)), new Vector2(osaX, osaY));
                }
            }
        }

        public void PlaceTile(Texture2D tileImage, Vector2 tilePos, Vector2 tileCoords)
        {
            _spriteBatch.Draw(tileImage, new Vector2(tilePos.X - (tileWidth / 2), tilePos.Y - tileHeight), Color.White);
        }

        public Vector2 CartToIso(Vector2 cartCoords)
        {
            Vector2 isoCoords = new Vector2(0, 0);

            isoCoords.X = (cartCoords.X + scrollSpan.X) - cartCoords.Y;
            isoCoords.Y = (cartCoords.X + scrollSpan.Y + cartCoords.Y) / 2;

            return isoCoords;
        }

        public Vector2 IsoToCart(Vector2 isoCoords)
        {
            Vector2 cartCoords = new Vector2(0, 0);

            cartCoords.X = (2 * isoCoords.Y + isoCoords.X - scrollSpan.X - scrollSpan.Y) / 2;
            cartCoords.Y = (2 * isoCoords.Y - isoCoords.X + scrollSpan.X - scrollSpan.Y) / 2;

            return cartCoords;
        }
    }
}

person Earlgray    schedule 14.01.2014    source источник


Ответы (3)


Я предлагаю вам взглянуть на ответ, который я написал некоторое время назад, он рисует только единственную видимую часть уровня, независимо от того, насколько велик уровень:

Я не копирую и не вставляю ответ здесь, поскольку я уже написал его, поэтому идите и посмотрите на него здесь:

https://gamedev.stackexchange.com/a/29930/16262

person aybe    schedule 14.01.2014
comment
Означает ли это, что вы должны были пометить вопрос соответствующим образом, а не отвечать на него только ссылкой на отдельный вопрос и ответ? - person DonBoitnott; 15.01.2014

Как правило, для повышения производительности избегайте создания ненужных объектов и не делайте ничего, что вам не нужно. Например, вы создаете одну бесполезную локальную текстуру в DrawMap(), также за один вызов метода PlaceTile вы создаете 3 новых вектора, а вам нужен один и т.д. также обратите внимание, что это лишь незначительные улучшения.
Другим способом ускорения может быть использование буферов (не знаю, что по умолчанию для XNA)
Но самое главное, распараллеливайте везде, где это возможно. .

person wondra    schedule 14.01.2014

Прежде чем делать что-либо еще, профилируйте свой код и посмотрите, на что тратится больше всего времени. Только после этого можно приступать к оптимизации.

person Community    schedule 14.01.2014
comment
Больше всего времени тратится на метод PlaceTile(), он занимает 95% всего потраченного времени. - person Earlgray; 15.01.2014