Управление текстовыми картами в 2D-массиве для окрашивания на холсте HTML5.

Итак, я делаю RPG для HTML5 только для удовольствия. Карта представляет собой <canvas> (ширина 512 пикселей, высота 352 пикселя 16 плиток, 11 плиток сверху вниз). Я хочу знать, есть ли более эффективный способ рисовать <canvas>.

Вот как я это делаю прямо сейчас.

Как плитки загружаются и окрашиваются на карту

Карту окрашивают плитки (32x32) с помощью фрагмента Image(). Файлы изображений загружаются с помощью простого цикла for и помещаются в массив с именем tiles[], чтобы быть ПОКРЫТО, используя drawImage().

Сначала мы загружаем плитки ...

введите описание изображения здесь>> </p>

<p> и вот как это делается: </p>

<pre><code>//НАСТРОЙКА & ИЗОБРАЖИТЕ КАРТЫ КАРТЫ
tiles = [];
var loadedImagesCount = 0;
для (x = 0; x <= NUM_OF_TILES; x ++) {
  var imageObj = new Image (); //новый экземпляр для каждого изображения
  imageObj.src =

Естественно, когда игрок начинает игру, он загружает карту, на которой они остановились. Но здесь, это все травяная карта.

В настоящее время на картах используются двумерные массивы. Вот пример карты.

// SET UP THE & DRAW THE MAP TILES
tiles = [];
var loadedImagesCount = 0;
for (x = 0; x <= NUM_OF_TILES; x++) {
  var imageObj = new Image(); // new instance for each image
  imageObj.src = "js/tiles/t" + x + ".png";
  imageObj.onload = function () {
    console.log("Added tile ... " + loadedImagesCount);
    loadedImagesCount++;
    if (loadedImagesCount == NUM_OF_TILES) {
      // Onces all tiles are loaded ...
      // We paint the map
      for (y = 0; y <= 15; y++) {
        for (x = 0; x <= 10; x++) {
          theX = x * 32;
          theY = y * 32;
          context.drawImage(tiles[5], theY, theX, 32, 32);
        }
      }
    }
  };
  tiles.push(imageObj);
}

Я получаю разные карты, используя простую структуру [[4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 1, 1, 1, 1], [1, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 1], [13, 13, 13, 13, 1, 1, 1, 1, 13, 13, 13, 13, 13, 13, 13, 1], [13, 13, 13, 13, 1, 13, 13, 1, 13, 13, 13, 13, 13, 13, 13, 1], [13, 13, 13, 13, 1, 13, 13, 1, 13, 13, 13, 13, 13, 13, 13, 1], [13, 13, 13, 13, 1, 13, 13, 1, 13, 13, 13, 13, 13, 13, 13, 1], [13, 13, 13, 13, 1, 1, 1, 1, 13, 13, 13, 13, 13, 13, 13, 1], [13, 13, 13, 13, 13, 13, 13, 1, 13, 13, 13, 13, 13, 13, 13, 1], [13, 13, 13, 13, 13, 11, 11, 11, 13, 13, 13, 13, 13, 13, 13, 1], [13, 13, 13, 1, 1, 1, 1, 1, 1, 1, 13, 13, 13, 13, 13, 1], [1, 1, 1, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 1, 1, 1]]; . После того, как массив 2d выше if, соответствующее число в каждом массиве будет окрашено в соответствии с return, хранящимся внутри Image(). Затем появится tile[] и покрасьте в соответствии с drawImage() и x и раз нажмите y, чтобы нарисовать правильная координата 32.

Как происходит множественное переключение карт

В моей игре на картах есть пять вещей, чтобы отслеживать: x-y, currentID, leftID, rightID и upID.

  • currentID: Текущий идентификатор карты, на которой вы находитесь.
  • leftID: Какой идентификатор bottomID для загрузки, когда вы выходите слева от текущей карты.
  • rightID: Какой идентификатор currentID для загрузки, когда вы выходите справа текущая карта.
  • downID: Какой идентификатор currentID для загрузки при выходе внизу текущей карты.
  • upID: Какой идентификатор currentID для загрузки, когда вы выходите в верхней части текущей карты.

Что-то примечание: если либо currentID, leftID, rightID, либо upID) НЕ являются конкретными, это означает, что они bottomID. Это означает, что они не могут покинуть эту сторону карты. Это просто невидимая блокада.

Итак, как только человек выходит из карты, в зависимости от того, где они вышли ... например, если они вышли на дно,0 будет загружен номер карты bottomID и, таким образом, будет нарисован на карте.

Вот репрезентативный .GIF, который поможет вам лучше визуализировать:

введите описание изображения здесь>> </p>

<p> Как вы можете видеть, рано или поздно со многими картами я буду иметь дело с  many  идентификаторами. И это может стать немного запутанным и беспокойным. </p>

<p> Очевидными плюсами являются то, что он загружает 176 фрагментов за раз, обновляет небольшой холст размером 512x352 и обрабатывает одну карту во времени. Кон является то, что идентификаторы MAP при работе со многими картами могут иногда запутываться. </p>

<h2> Мой вопрос </h2>

<ul>
<li> Это эффективный способ хранения карт (учитывая использование плиток) или лучший способ обработки карт? </li>
</ul>
<p> Я думал вдоль линии гигантской карты. Размер карты большой, и это все одно 2D-массив. Однако в окне просмотра все еще 512x352 пикселей. </p>

<p> Вот еще один .gif, который я сделал (для этого вопроса), чтобы помочь визуализировать: </p>

<p> <img src =

44 голоса | спросил nn2 10 J000000Tuesday12 2012, 06:35:51

7 ответов


5

Изменить: просто увидел, что мой ответ основан на вашем коде, но на самом деле не ответил на ваш вопрос. Я сохранил старый ответ, если вы можете использовать эту информацию.

Изменить 2: Я исправил 2 вопроса с исходным кодом:  - Добавление +1 в вычисление конца x и y было ошибочно внутри скобок, но оно должно быть добавлено после деления.  - Я забыл проверить значения x и y.

Вы можете просто иметь текущую карту в памяти и загружать окружающие карты. Представьте себе мир, состоящий из 2d массива одиночных уровней размером 5x5. Игрок начинает в поле 1. Когда верхняя и левая границы уровня находятся на краю света, их не нужно загружать. Таким образом, в этом случае уровень 1/1 активен, а уровни 1/2 и 2/1 загружаются ... Если игрок теперь перемещается вправо, все уровни (кроме того, что вы переместили) выгружаются, а новое окружение загружен. Значения уровня 2/1 теперь активны, теперь загружаются 1/1, 2/2 и 3/1.

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

Старый ответ:

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

//Mock values
//camera position x
//viewport.x = 233f;
//camera position y
//viewport.y = 100f;
//viewport.w = 640
//viewport.h = 480
//levelWidth = 10
//levelHeight = 15
//tilesize = 32;

//startX defines the starting index for the x axis.
// 7 = 233 / 32
int startX = viewport.x / tilesize;

//startY defines the starting index
// 3 = 100 / 32
int startY = viewport.y / tilesize;

//End index y
// 28 = (233 + 640) / 32 + 1
int endX = ((viewport.x + viewport.w) / tilesize) + 1;

//End index y
// 19 = (100 + 480) / 32 + 1
int endX = ((viewport.x + viewport.w) / tilesize) + 1;

//Validation
if(startX < 0) startX = 0;
if(startY < 0) startY = 0;
//endX is set to 9 here
if(endX >= levelWidth) endX = levelWidth - 1;
//endX is set to 14 here
if(endY >= levelHeight) endY = levelHeight - 1;

for(int y = startY; y < yEnd; y++)
    for(int x = startX; x < xEnd; x++)
          [...]

Примечание: Код выше не проверен, но он должен дать представление о том, что делать.

Следуя базовому примеру для представления в представлении:

public class Viewport{
    public float x;
    public float y;
    public float w;
    public float h;
}

Представление javascript будет выглядеть как

<script type="text/javascript">
//Declaration
//Constructor
var Viewport = function(xVal, yVal, wVal, hVal){
    this.x = xVal;
    this.y = yVal;
    this.w = wVal;
    this.h = hVal;
}
//Prototypes
Viewport.prototype.x = null;
Viewport.prototype.y = null;
Viewport.prototype.w = null;
Viewport.prototype.h = null;
Viewport.prototype.toString = function(){
    return ["Position: (", this.x, "/" + this.y + "), Size: (", this.w, "/", this.h, ")"].join("");
}

//Usage
var viewport = new Viewport(23, 111, 640, 480);
//Alerts "Position: (23/111), Size: (640/480)
alert(viewport.toString());
</script>

Если вы примете мой пример кода, вам просто нужно заменить объявления int и float на var. Также убедитесь, что вы используете Math.floor для этих вычислений, которые присваиваются значениям на основе int, чтобы получить такое же поведение.

Одна вещь, которую я бы рассмотрел (хотя я не уверен, что это имеет значение в javascript) заключается в том, чтобы поместить все плитки (или как можно больше) в одну большую текстуру вместо использования нескольких отдельных файлов.

Вот ссылка, объясняющая, как это сделать: http://thiscouldbebetter.wordpress.com/2012/02/25/slicing-an-image-into-tiles-in-javascript/

ответил tom van green 10 J000000Tuesday12 2012, 13:15:27
2

Во всех реализациях, которые я видел, карты плитки часто генерируются как одно большое изображение, а затем «разбиваются» на более мелкие куски.

https://gamedev.stackexchange.com/a/25035/10055

Эти куски обычно имеют степень 2, поэтому они оптимально подходят в памяти.

ответил Romoku 10 J000000Tuesday12 2012, 07:11:21
2

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

Сохраните всю карту как один большой массив. Нет смысла иметь карты и подмаски, а также потенциально суб-под-карты (например, если вы входите в здания). Вы все равно загружаете все это, и это держит все хорошо и просто.

Отобразить карту сразу. У вас есть экранный холст, на котором вы делаете карту, а затем используйте ее в своей игре. Эта страница показывает вам, как это сделать с помощью простой функции javascript:

var renderToCanvas = function (width, height, renderFunction) {
  var buffer = document.createElement('canvas');
  buffer.width = width;
  buffer.height = height;
  renderFunction(buffer.getContext('2d'));
  return buffer;
};

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

var map = renderToCanvas( map_width*32, map_height*32, renderMap );

var MapSizeX = 512;
var MapSizeY = 352;

var draw = function(canvas, mapCentreX, mapCentreY) {
  var context = canvas.getContext('2d');

  var minX = playerX - MapSizeX/2;
  var minY = playerY - MapSizeY/2;

  context.drawImage(map,minX,minY,MapSizeX,MapSizeY,0,0,MapSizeX,MapSizeY);
}

Это нарисует небольшую часть карты с центром вокруг mapCentreX, mapCentreY. Это позволит вам плавно прокручивать всю карту, а также движение подтипов - вы можете сохранить позицию игрока как 1/32 части плитки, чтобы вы могли получить плавное движение по карте (очевидно, если вы хотите перемещайте плитки по-черепице как стилистический выбор, вы можете просто увеличить количество кусков 32).

Вы можете по-прежнему размещать свою карту, если хотите, или если ваш мир огромен и не будет помещаться в память ваших целевых систем (помните, что даже для 1 байт на пиксель карта 512x352 составляет 176 КБ) но, предварительно создавая свою карту, вы увидите большое увеличение производительности в большинстве браузеров.

Это дает вам гибкость повторного использования плиток во всем мире без головной боли при редактировании одного большого изображения, но также позволяет вам быстро и легко запускать это и рассматривать только одну «карту» (или столько же «карт», сколько вам нравится).

ответил Matt Kemp 12 J000000Thursday12 2012, 13:46:26
1

Простым способом отслеживания идентификаторов было бы просто использовать другой массив 2d с ними: поддерживая только x, y в этом «мета-массиве», вы можете легко найти другие идентификаторы.

Итак, вот что взято у Google на двумерных черепичных холстевых играх (ну, на самом деле, многопользовательский режим HTML5, но также и механизм черепицы) от их недавнего ввода-вывода 2012: http://www.youtube.com/watch?v=Prkyd5n0P7k Он также имеет исходный код.

ответил Tapio 10 J000000Tuesday12 2012, 10:05:10
1

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

Я тоже работаю над ролевой игрой, так как я загружаю свою карту через Ajax со страницы PHP, которая загружает карту из базы данных, где говорится о X, Y, позиции Z и значение плитки, нарисуйте ее и, наконец, игрок движется.

Я все еще работаю над этим, чтобы сделать его более эффективным, но карта загружается достаточно быстро, чтобы сохранить его таким образом.

ответил BernaMariano 13 J000000Friday12 2012, 00:25:06
1

Нет , массив является наиболее эффективным способом хранения карты плитки .

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


Однако нужно учитывать, когда вы собираетесь загрузить следующую карту. Учитывая, что карты не особо значительны, то, что на самом деле займет самое долгое время, ждет, пока сервер выполнит доставку карты. Фактическая загрузка займет всего несколько миллисекунд. Но это ожидание можно сделать асинхронным, ему не нужно блокировать поток игры. Поэтому лучше всего не загружать данные по мере необходимости, но раньше. Игрок находится на одной карте, теперь загружает все смежные карты. Игрок перемещается на следующей карте, снова загружает все соседние карты и т. Д. И т. Д. Игрок даже не заметит, что ваша игра загружается в фоновом режиме.

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

ответил API-Beast 13 J000000Friday12 2012, 23:55:05
0

Я не уверен, почему вы думаете: как вы можете видеть, рано или поздно, со многими картами, я буду иметь дело со многими идентификаторами. И это может стать немного запутанным и беспокойным.

LeftID всегда будет -1 из идентификатора currentID, rightID всегда будет +1 текущего идентификатора.

UpID всегда будет - (общая ширина карты) текущего ID и downID всегда будет + (общая ширина карты) текущего идентификатора, за исключением нуля, что означает, что вы попали в край.

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

Я написал редактор карт Javascript, который прокручивается во всех направлениях, размером 1000 х 1000 (я бы не рекомендовал этот размер), и он по-прежнему перемещается так же хорошо, как и карта 50 х 50, и имеет 3 слоя, включая прокрутку параллакса , Он сохраняет и читает в формате json. Я пришлю вам копию, если вы скажете, что не возражаете против электронной почты примерно 2 месяца. Он предназначен для github, но у меня нет приличного (законного) набора плиток, поэтому я еще не потрудился разместить его там.

ответил John 14 J000000Saturday12 2012, 00:51:32

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132