Как рисовать только плитки (спрайты), видимые на экране

Я пытаюсь создать простую 2D-игру с прокруткой в ​​​​качестве учебного упражнения в моноигре (XNA). В настоящее время я загружаю информацию о плитке из текстового файла, создавая массив (tiles[,]) с объектом плитки, хранящим информацию о том, где записана плитка и какую текстуру следует использовать при вызове метода рисования. В настоящее время я рисую каждую плитку в массиве сразу, а затем просто обновляю положение плитки, чтобы перемещать ее по экрану. Очевидно, рисовать все плитки одновременно довольно сложно, и я думаю, что было бы лучше, если бы я рисовал только те плитки, которые видны на экране, чтобы по мере перемещения плиток по экрану отрисовывалось больше плиток. Я не уверен, как лучше это сделать? Я немного потерялся в том, как я могу сказать, какие плитки должны быть нарисованы. Любая помощь будет принята с благодарностью. Вот мои методы рисования и обновления в моем классе уровня - он просто вызывает методы в классе Tile для рисования/обновления позиции.

    /// <summary>
    /// Draws each individual tile in the level.
    /// </summary>
    private void DrawTiles(SpriteBatch spriteBatch)
    {
        // For each tile position
        for (int y = 0; y < Height; ++y)
        {
            for (int x = 0; x < Width; ++x)
            {
               tiles[x, y].Draw(spriteBatch);
            }
        }
    }



    public void Update(GameTime gameTime, int scrollSpeed, ScrollDirection direction)
    {
        // Update the positions of each of the Background sprites
        foreach (Tile tile in tiles)
        {
            tile.Update(gameTime, new Vector2(0, scrollSpeed), scrollDirection);
        }
    }
}

заранее спасибо

Теперь я пытаюсь прокручивать только видимые на экране плитки для рисования, используя окно просмотра, переданное с графического устройства. В основном я пытаюсь получить количество плиток, которые могут поместиться на экране, прежде чем пытаться их нарисовать, разделив высоту экрана на высоту плитки и то же самое на ширину. Это прекрасно работает и рисует все тайлы, видимые в точке обзора.

Единственная проблема заключается в том, что он больше не рисует плитки, которые начинались за пределами области просмотра, поэтому, даже если в обновлении плитки перемещаются в область просмотра, они не рисуются, поскольку цикл использует их исходное местоположение. Я не могу придумать способ исправить это, не перебирая все плитки :-(

Поскольку уровень может быть большим, было бы очень неэффективно перебирать все плитки, чтобы рисовать только то, что было на экране.

Мы будем очень признательны за любые советы.

вот обновленный розыгрыш:

    private void DrawTiles(SpriteBatch spriteBatch)
    {
        float tileWidth = 40;
        float tileHeight = 32;


        for (int y = 0; y < (int)Math.Ceiling(mViewport.Height / tileHeight); ++y)
        {

           for (int x = 0; x < (int)Math.Ceiling(mViewport.Width / tileWidth); ++x)
            {
                tiles[x, y].Draw(spriteBatch);
            }
        }
    }

person Pectus Excavatum    schedule 02.06.2013    source источник
comment
Простой пример 2D-камеры, который я только что создал: pastebin.com/jrGXWfNh Он только демонстрирует, что делает камера и как она работает. работает с нарисованным прямоугольником в качестве эталона. С ним довольно просто работать, достаточно продвинуто, чтобы полностью понять. Если затем вы знаете, что ваш экран имеет ограничения, вы просто проверяете, где находится камера, и проверяете, находится ли другой прямоугольник внутри экрана, а затем просто рисуете или не рисуете.   -  person Deukalion    schedule 03.06.2013
comment
Спасибо, Девкалион, кажется, я понял.   -  person Pectus Excavatum    schedule 04.06.2013


Ответы (2)


Вам нужна информация о вашей камере. Если вы знаете, на чем сосредоточена ваша камера и какой у вас размер экрана, вызов отрисовки может быть таким:

private void DrawTiles(SpriteBatch spriteBatch)
{
    // For each tile position
    for (int y = cameraOffset.Y - screenHeight / 2; y < cameraOffset.Y + (int)Math.Ceiling(screenHeight / 2); ++y)
    {
        for (int x = cameraOffset.X - screenWidth / 2; x < cameraOffset.X + (int)Math.Ceiling(screenWidth / 2); ++x)
        {
           tiles[x, y].Draw(spriteBatch);
        }
    }
}

Причина, по которой я использую Math.Ceiling, заключается в том, чтобы скрыть, если плитки частично находятся на экране. Подразумевается Math.Floor с другой стороны, так как это целочисленное деление.

Обратите внимание, что предполагается, что cameraOffset, screenWidth и screenHeight выражены в тайлах. Если это не так, преобразуйте их соответствующим образом.

person Mike Precup    schedule 02.06.2013
comment
Еще раз спасибо, Майк, я немного запутался в этой концепции камеры. В настоящее время я размещаю плитку только на основе позиции массива, а затем изменяю положение плитки в соответствии с: theSpriteBatch.Draw(mSpriteTexture, Position * sizeVector, Color.White); Знаете ли вы какие-либо учебные пособия, которые я мог бы просмотреть, чтобы понять, как настроить камеру? - person Pectus Excavatum; 02.06.2013
comment
В этом примере камера находится в центре экрана, поэтому просто измените ее на позицию вашего игрока или что-то в этом роде, чтобы она следовала за ней. - person Cyral; 03.06.2013
comment
@Pectus Excavatum Если в вашем spriteBatch установить матрицу для мира, вы можете легко преобразовать весь 2D-экран. Если ваш экран 400x400 и ваша камера находится в X = 0, Y = 0, она начнет отображать все в диапазоне от 0,0 до 400, 400. Если вы не масштабируете его или не поворачиваете. Матричная камера = Matrix.CreateScale(Zoom) * Matrix.CreateRotationZ(MathHelper.ToRadians(градусы)) * Matrix.CreateTranslation(PositionX, PositionY, 0); а затем вы просто настраиваете положение на то, что хотите показать. И теперь вы можете установить позицию объекта на 400, 0 (не визуализируется), но если вы немного переместите камеру, это будет видно. - person Deukalion; 03.06.2013
comment
Вокруг целая куча примеров 2D-камеры. Я не могу найти ту, которую использовал сам, но вот похожая adambruenderman.wordpress.com/2011/04/05/ - person craftworkgames; 04.06.2013
comment
хорошо, я понимаю предпосылку, которую предлагает Майк, зацикливаться только на плитках на экране. Думаю, это хороший план. Но я не могу понять, почему я не могу заставить его работать с помощью окна просмотра - см. мой пересмотренный вопрос для кода. Любые идеи будут очень признательны, так как, когда я пытаюсь перебирать только плитки на экране, плитки не рисуются :-( - person Pectus Excavatum; 05.06.2013

Создайте константы с именами WINDOW_WIDTH и WINDOW_HEIGHT, которые хранят ширину и высоту окна. Затем нарисуйте плитки, как вы показали, но только с теми, которые находятся в пределах значений WINDOW_WIDTH и WINDOW_HEIGHT. Вам также нужны свойства X и Y в вашем классе Tile.

private void DrawTiles(SpriteBatch spriteBatch)
{
    float tileWidth = 40;
    float tileHeight = 32;


    for (int y = 0; y < (int)Math.Ceiling(mViewport.Height / tileHeight); ++y)
    {

       for (int x = 0; x < (int)Math.Ceiling(mViewport.Width / tileWidth); ++x)
        {
            if (tiles[x, y].X <= WINDOW_WIDTH &&
                tiles[x, y].X >= 0 &&
                tiles[x, y].Y <= WINDOW_HEIGHT &&
                tiles[x, y].Y >= 0)
            {
                tiles[x, y].Draw(spriteBatch);
            }
        }
    }
}
person Daniel G    schedule 20.03.2016