Então, eu estou fazendo um RPG HTML5 apenas por diversão. O mapa é um <canvas>
(largura de 512 px, altura de 352 px | 16 blocos de largura, 11 blocos de cima para baixo). Quero saber se existe uma maneira mais eficiente de pintar o <canvas>
.
Aqui está como eu o tenho agora.
Como os blocos são carregados e pintados no mapa
O mapa está sendo pintado por azulejos (32x32) usando a Image()
peça. Os arquivos de imagem são carregados através de um for
loop simples e colocados em uma matriz chamada tiles[]
PAINTED ao usar drawImage()
.
Primeiro, carregamos os ladrilhos ...
e aqui está como está sendo feito:
// 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);
}
Naturalmente, quando um jogador inicia um jogo, ele carrega o mapa que ele deixou por último. Mas por aqui, é um mapa de grama.
No momento, os mapas usam matrizes 2D. Aqui está um exemplo de mapa.
[[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]];
Recebo mapas diferentes usando uma if
estrutura simples . Uma vez que o 2d array acima seja return
, o número correspondente em cada array será pintado de acordo com o Image()
armazenado no interior tile[]
. Em seguida, drawImage()
irá ocorrer e pintar de acordo com o x
e y
e vezes por 32
pintar na correta x-y
coordenadas.
Como ocorre a troca de mapas múltiplos
Com o meu jogo, mapas têm cinco coisas para manter o controle de: currentID
, leftID
, rightID
, upID
, e bottomID
.
- currentID: O ID atual do mapa em que você está.
- leftID: qual ID de
currentID
carregar quando você sai à esquerda do mapa atual. - rightID: qual ID de
currentID
carregar quando você sai à direita do mapa atual. - downID: qual ID de
currentID
carregar quando você sai na parte inferior do mapa atual. - upID: qual ID de
currentID
carregar quando você sai na parte superior do mapa atual.
Algo a nota: Se qualquer um leftID
, rightID
, upID
, ou bottomID
não são específicos, isso significa que eles são um 0
. Isso significa que eles não podem sair desse lado do mapa. É apenas um bloqueio invisível.
Assim, uma vez que uma pessoa sai de um lado do mapa, dependendo de onde saiu ... por exemplo, se saiu na parte inferior, bottomID
será o número do map
carregamento a ser carregado e, assim, será pintado no mapa.
Aqui está um .GIF representacional para ajudá-lo a visualizar melhor:
Como você pode ver, mais cedo ou mais tarde, com muitos mapas, lidarei com muitos IDs. E isso pode ficar um pouco confuso e agitado.
Os profissionais óbvios são que ele carrega 176 blocos de cada vez, atualiza uma pequena tela de 512x352 e manipula um mapa de cada vez. A desvantagem é que os IDs do MAP, ao lidar com muitos mapas, podem ficar confusos às vezes.
Minha pergunta
- É uma maneira eficiente de armazenar mapas (dado o uso de blocos) ou existe uma maneira melhor de lidar com mapas?
Eu estava pensando nas linhas de um mapa gigante. O tamanho do mapa é grande e é uma matriz 2D. A janela de exibição, no entanto, ainda tem 512x352 pixels.
Aqui está outro .gif que eu fiz (para esta pergunta) para ajudar a visualizar:
Desculpe se você não consegue entender meu inglês. Por favor, pergunte qualquer coisa que você tenha problemas para entender. Felizmente, deixei claro. Obrigado.
fonte
Respostas:
Editar: Acabei de ver que minha resposta foi baseada no seu código, mas na verdade não respondeu à sua pergunta. Eu mantive a resposta antiga, caso você possa usar essas informações.
Editar 2: Corrigi dois problemas com o código original: - A adição de +1 no cálculo das extremidades x e y foi por engano dentro dos colchetes, mas precisa ser adicionada após a divisão. - Esqueci de validar os valores x e y.
Você pode simplesmente ter o mapa atual na memória e carregar os mapas ao redor. Imagine um mundo que consiste em uma matriz 2D de níveis únicos do tamanho 5x5. O jogador começa no campo 1. Como os limites superior e esquerdo do nível estão na extremidade do mundo, eles não precisam ser carregados. Portanto, nesse caso, o nível 1/1 está ativo e os níveis 1/2 e 2/1 são carregados ... Se o jogador agora se mover para a direita, todos os níveis (além daquele para o qual você está se mudando) serão descarregados e o novo ambiente será carregado. carregado. O nível 2/1 agora está ativo, 1/1, 2/2 e 3/1 agora estão carregados.
Espero que isso lhe dê uma idéia de como isso poderia ser feito. Mas essa abordagem não funcionará muito bem, quando todos os níveis tiverem que ser simulados durante o jogo. Mas se você pode congelar níveis não utilizados, isso deve funcionar bem.
Resposta antiga:
O que faço ao renderizar o nível (também baseado em blocos) é calcular quais itens da matriz de blocos interceptam a viewport e, então, eu apenas processo esses blocos. Dessa forma, você pode ter mapas grandes, mas precisa renderizar a parte na tela.
Nota: O código acima não foi testado, mas deve ter uma ideia do que fazer.
Seguindo um exemplo básico para uma representação de viewport:
Uma representação em javascript se pareceria
Se você pegar meu exemplo de código, basta substituir as declarações int e float por var. Além disso, certifique-se de usar Math.floor nesses cálculos, atribuídos aos valores baseados em int, para obter o mesmo comportamento.
Uma coisa que eu consideraria (embora não tenha certeza se isso importa em javascript) é colocar todos os blocos (ou o máximo possível) em uma grande textura em vez de usar muitos arquivos únicos.
Aqui está um link que explica como fazê-lo: http://thiscouldbebetter.wordpress.com/2012/02/25/slicing-an-image-into-tiles-in-javascript/
fonte
640, 480
.. mas pode funcionar com512, 352
certo?Armazenar mapas como blocos é útil para permitir que você reutilize ativos e redesenha o mapa. Realisticamente, isso realmente não oferece muitos benefícios ao jogador. Além disso, você está redesenhando cada bloco de cada vez. Aqui está o que eu faria:
Armazene o mapa inteiro como uma grande matriz. Não há nenhum ponto em ter mapas e submapas, bem como potencialmente submapas (se você estiver entrando em edifícios, por exemplo). Você está carregando tudo de qualquer maneira e isso mantém tudo agradável e simples.
Renderize o mapa de uma só vez. Tenha uma tela fora da tela para a qual você renderiza o mapa e use-a no seu jogo. Esta página mostra como fazer isso com uma simples função javascript:
Isso pressupõe que seu mapa não mude muito. Se você quiser renderizar submapas no local (por exemplo, o interior de um edifício), você também o renderiza em uma tela e depois desenha-a no topo. Aqui está um exemplo de como você pode usar isso para renderizar seu mapa:
Isso desenhará uma pequena parte do mapa centralizada
mapCentreX, mapCentreY
. Isso oferecerá rolagem suave em todo o mapa, além de movimento de sub-mosaico - você pode armazenar a posição do jogador como 1/32 de um mosaico, para que você possa obter um movimento suave no mapa (claramente, se desejar mova lado a lado como uma opção estilística, você pode aumentar em pedaços de 32).Você ainda pode agrupar seu mapa, se quiser, ou se o seu mundo for enorme e não caber na memória dos sistemas de destino (lembre-se de que mesmo para 1 byte por pixel, um mapa de 512x352 tem 176 KB), mas por Ao renderizar seu mapa assim, você verá um grande ganho de desempenho na maioria dos navegadores.
O que isso oferece é a flexibilidade de reutilizar blocos em todo o mundo, sem a dor de cabeça de editar uma imagem grande, mas também permitindo que você execute isso de maneira rápida e fácil e considere apenas um 'mapa' (ou tantos 'mapas' quanto desejar) .
fonte
Em todas as implementações, vi mapas de blocos frequentemente serem gerados como uma imagem grande e depois "divididos" em pedaços menores.
https://gamedev.stackexchange.com/a/25035/10055
Esses pedaços geralmente têm potências de 2, então eles se encaixam perfeitamente na memória.
fonte
Uma maneira fácil de acompanhar os IDs seria simplesmente usar outro array 2D com eles: mantendo apenas x, y nesse "meta array", você pode facilmente pesquisar os outros IDs.
Dito isto, aqui está a opinião do Google sobre os jogos de tela 2D em mosaico em mosaico (bem, na verdade, o multiplayer HTML5, mas o mecanismo de mosaico também é coberto) de sua recente I / O 2012: http://www.youtube.com/watch?v=Prkyd5n0P7k It também possui código fonte disponível.
fonte
Sua idéia de carregar o mapa inteiro primeiro em uma matriz é uma maneira eficiente, mas será fácil obter o JavaScript Injection'd, qualquer pessoa poderá conhecer o mapa e a aventura se inicia.
Também estou trabalhando em um jogo de RPG baseado na Web, do jeito que carrego meu mapa via Ajax a partir de uma página PHP, que carrega o mapa do banco de dados, onde diz a posição X, Y, Z e vlaue do bloco, desenhe-o e, finalmente, o jogador se move.
Ainda estou trabalhando para torná-lo mais eficiente, mas o mapa carrega rápido o suficiente para mantê-lo dessa maneira agora.
fonte
Não , uma matriz é a maneira mais eficiente de armazenar um mapa de blocos .
Pode haver algumas estruturas que requerem um pouco menos de memória, mas apenas à custa de custos muito mais altos para carregar e acessar os dados.
No entanto, o que precisa ser considerado é quando você carregará o próximo mapa. Dado que os mapas não são especialmente grandes, o que realmente levará mais tempo é esperar o servidor entregar o mapa. O carregamento real levará apenas alguns milissegundos. Mas essa espera pode ser feita de forma assíncrona, não precisa bloquear o fluxo do jogo. Portanto, o melhor é não carregar os dados quando necessário, mas antes. O jogador está em um mapa, agora carregue todos os mapas adjacentes. O jogador se move no próximo mapa, carrega todos os mapas adjacentes novamente, etc., etc. O jogador nem notará que seu jogo está carregando em segundo plano.
Dessa forma, você também pode criar a ilusão de um mundo sem fronteiras, também desenhando os mapas adjacentes; nesse caso, você precisará carregar não apenas os mapas adjacentes, mas também os adjacentes.
fonte
Não sei por que você pensa: como você pode ver, mais cedo ou mais tarde, com muitos mapas, lidarei com muitos IDs. E isso pode ficar um pouco confuso e agitado.
LeftID sempre será -1 do currentID, o rightID sempre será +1 do currentID.
O UpID sempre será - (largura total do mapa) do ID atual e o downID será sempre + (largura total do mapa) do ID atual, com exceção de zero, o que significa que você atingiu o limite.
Um único mapa pode ser dividido em muitos mapas e é salvo seqüencialmente com mapIDXXXX, onde XXXX é o ID. Dessa forma, não há nada para se confundir. Estive no seu site e parece, posso estar errado, que o problema está no editor de mapas que impõe uma limitação de tamanho, o que está impedindo sua automação de dividir um mapa grande em vários pequenos ao salvar e essa limitação é provavelmente restringindo uma solução técnica.
Eu escrevi um editor de mapa Javascript que rola em todas as direções, blocos de 1000 x 1000 (eu não recomendaria esse tamanho) e ele ainda se move tão bem quanto um mapa de 50 x 50, com 3 camadas, incluindo rolagem de paralaxe. Ele salva e lê no formato json. Enviar-lhe-ei uma cópia se você disser que não se importa com um e-mail de aproximadamente 2meg. Ele deve estar no github, mas eu não tenho nenhum conjunto de peças decente (legal), e é por isso que ainda não me preocupei em colocá-lo lá.
fonte