Los mapas de mosaico (Tile Maps en inglés), permiten crear escenarios de forma relativamente fácil. Suelen ser leídos de mapas de bits o de arreglos de valores (Númericos en el mayor de los casos), en donde cada valor distinto representa un tipo diferente de elemento a agregar en el mapa.
Un ejemplo de un mapa de mosaico sencillo de 4 bloques de ancho por 3 de alto, se representaría en Javascript de la siguiente forma:
Por supuesto, un mapa para nuestros juegos no pueden ser tan pequeño. Tomando en cuenta que estamos haciendo juegos con elementos de 10x10 pixeles, en un escenario de 300x200 pixeles, necesitaríamos crear un mapa de mosaico de 30x20 elementos.
Llevemos entonces a práctica lo anteriormente dicho. Comencemos por declarar un arreglo para cada tipo de elemento que vaya a existir en nuestro mapa de mosaico. En estos mapas, podemos agregar toda clase de elementos: Paredes, obstáculos, enemigos, objetos, etc... Para este práctico ejemplo, mostraré solo dos objetos con los cuales interactuar: Paredes y Lava.
Ahora, crearemos una función que lea los valores del mapa. En la primer línea recibimos los valores del mapa de mosaico a leer y el tamaño que tendrá cada bloque (que son de 10 pixeles).
Ahora que queda esto explicado, proseguimos con nuestra función. En las primeras líneas, crearemos las variables col y row que nos permitirán conocer nuestra posición actual, las variables columns y rows que nos ayudarán a manejar el total de filas y columnas en el mapa, y limpiaremos los arreglos que usaremos antes de volverlos a llenar:
Un ejemplo de un mapa de mosaico sencillo de 4 bloques de ancho por 3 de alto, se representaría en Javascript de la siguiente forma:
var map0=[
[1, 0, 0, 1],
[0, 0, 0, 0],
[1, 0, 0, 1]
];
El el ejemplo anterior, los 0 representan espacios en blanco, y los 1 representan paredes. Así pues, estaríamos creando un mapa de 4x3 con una pared en cada esquina.Por supuesto, un mapa para nuestros juegos no pueden ser tan pequeño. Tomando en cuenta que estamos haciendo juegos con elementos de 10x10 pixeles, en un escenario de 300x200 pixeles, necesitaríamos crear un mapa de mosaico de 30x20 elementos.
Llevemos entonces a práctica lo anteriormente dicho. Comencemos por declarar un arreglo para cada tipo de elemento que vaya a existir en nuestro mapa de mosaico. En estos mapas, podemos agregar toda clase de elementos: Paredes, obstáculos, enemigos, objetos, etc... Para este práctico ejemplo, mostraré solo dos objetos con los cuales interactuar: Paredes y Lava.
var wall = [],
lava = [],
map0 = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 2, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 1, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 1, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
];
He agregado también el mapa de mosaico que usaremos para este ejemplo. Si los cuentas, confirmarás que son 30 valores a lo ancho, y 20 a lo alto. Puedes modificar los valores dentro de este mapa para personalizar tu juego.Ahora, crearemos una función que lea los valores del mapa. En la primer línea recibimos los valores del mapa de mosaico a leer y el tamaño que tendrá cada bloque (que son de 10 pixeles).
function setMap(map, blockSize) {
Si quisiéramos usar objetos rectángulares en lugar de cuadrados, podríamos recibir blockWidth y blockHeight. Pero dado que en la mayoría de los juegos, los objetos suelen tener igual proporción, dejaremos así la función por ahora.Ahora que queda esto explicado, proseguimos con nuestra función. En las primeras líneas, crearemos las variables col y row que nos permitirán conocer nuestra posición actual, las variables columns y rows que nos ayudarán a manejar el total de filas y columnas en el mapa, y limpiaremos los arreglos que usaremos antes de volverlos a llenar:
var col = 0,
row = 0,
columns = 0,
rows = 0;
wall.length = 0;
lava.length = 0;
A continuación, leeremos cada uno de los valores en nuestro mapa de mosaico a través de un "for anidado", leyendo con el primero cada fila, y con el segundo cada columna dentro de la fila correspondiente. Si el valor de la posición actual en el arreglo del mapa corresponde a alguno de los elementos a agregar, lo empujamos al arreglo correspondiente en la posición que le corresponde. Noten que los ceros son simplemente ignorados: for (row = 0, rows = map.length; row < rows; row += 1) {
for (col = 0, columns = map[row].length; col < columns; col += 1) {
if (map[row][col] === 1) {
wall.push(new Rectangle2D(col * blockSize, row * blockSize, blockSize, blockSize, true));
} else if (map[row][col] === 2) {
lava.push(new Rectangle2D(col * blockSize, row * blockSize, blockSize, blockSize, true));
}
}
}
Con esta función, mandaremos a llamar a nuestro mapa al cargar el juego dentro de la función "init": // Set map
setMap(map0, 10);
De esta forma, leeremos todos los elementos de nuestro mapa de mosaico. Ahora que hemos concluido, solo debo recordarte que no olvides llamar a dibujar los elementos en la función paint: // Draw walls
ctx.fillStyle = '#999';
for (i = 0; i < wall.length; i += 1) {
wall[i].fill(ctx);
}
// Draw lava
ctx.fillStyle = '#f00';
for (i = 0; i < lava.length; i += 1) {
lava[i].fill(ctx);
}
Y agregar las acciones correspondientes a los elementos en el flujo de juego. El código que tomamos de la entrada pasada ya tiene la interacción con las paredes, así que solo resta agregar la intersección del personaje con la lava: // Player Intersects Lava
for (i = 0; i < lava.length; i += 1) {
if (player.intersects(lava[i])) {
gameover = true;
pause = true;
}
}
De esta forma, podremos crear con facilidad juegos en el futuro basados en mapas de mosaico.Codigo final:
/*jslint bitwise: true, es5: true */
(function (window, undefined) {
'use strict';
var KEY_ENTER = 13,
KEY_LEFT = 37,
KEY_UP = 38,
KEY_RIGHT = 39,
KEY_DOWN = 40,
canvas = null,
ctx = null,
lastPress = null,
pressing = [],
pause = false,
gameover = true,
player = null,
wall = [],
lava = [],
map0 = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 2, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 1, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 1, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
];
function Rectangle2D(x, y, width, height, createFromTopLeft) {
this.width = (width === undefined) ? 0 : width;
this.height = (height === undefined) ? this.width : height;
if (createFromTopLeft) {
this.left = (x === undefined) ? 0 : x;
this.top = (y === undefined) ? 0 : y;
} else {
this.x = (x === undefined) ? 0 : x;
this.y = (y === undefined) ? 0 : y;
}
}
Rectangle2D.prototype = {
constructor: Rectangle2D,
left: 0,
top: 0,
width: 0,
height: 0,
get x() {
return this.left + this.width / 2;
},
set x(value) {
this.left = value - this.width / 2;
},
get y() {
return this.top + this.height / 2;
},
set y(value) {
this.top = value - this.height / 2;
},
get right() {
return this.left + this.width;
},
set right(value) {
this.left = value - this.width;
},
get bottom() {
return this.top + this.height;
},
set bottom(value) {
this.top = value - this.height;
},
intersects: function (rect) {
if (rect !== undefined) {
return (this.left < rect.right &&
this.right > rect.left &&
this.top < rect.bottom &&
this.bottom > rect.top);
}
},
fill: function (ctx) {
if (ctx !== undefined) {
ctx.fillRect(this.left, this.top, this.width, this.height);
}
}
};
document.addEventListener('keydown', function (evt) {
lastPress = evt.which;
pressing[evt.which] = true;
}, false);
document.addEventListener('keyup', function (evt) {
pressing[evt.which] = false;
}, false);
function setMap(map, blockSize) {
var col = 0,
row = 0,
columns = 0,
rows = 0;
wall.length = 0;
lava.length = 0;
for (row = 0, rows = map.length; row < rows; row += 1) {
for (col = 0, columns = map[row].length; col < columns; col += 1) {
if (map[row][col] === 1) {
wall.push(new Rectangle2D(col * blockSize, row * blockSize, blockSize, blockSize, true));
} else if (map[row][col] === 2) {
lava.push(new Rectangle2D(col * blockSize, row * blockSize, blockSize, blockSize, true));
}
}
}
}
function reset() {
player.left = 40;
player.top = 40;
gameover = false;
}
function paint(ctx) {
var i = 0,
l = 0;
// Clean canvas
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw player
ctx.fillStyle = '#0f0';
player.fill(ctx);
// Draw walls
ctx.fillStyle = '#999';
for (i = 0; i < wall.length; i += 1) {
wall[i].fill(ctx);
}
// Draw lava
ctx.fillStyle = '#f00';
for (i = 0; i < lava.length; i += 1) {
lava[i].fill(ctx);
}
// Debug last key pressed
ctx.fillStyle = '#fff';
ctx.fillText('Last Press: ' + lastPress, 0, 20);
// Draw pause
if (pause) {
ctx.textAlign = 'center';
if (gameover) {
ctx.fillText('GAMEOVER', 150, 100);
} else {
ctx.fillText('PAUSE', 150, 100);
}
ctx.textAlign = 'left';
}
}
function act(deltaTime) {
var i = 0,
l = 0;
if (!pause) {
// GameOver Reset
if (gameover) {
reset();
}
// Move Rect
if (pressing[KEY_UP]) {
player.y -= 5;
for (i = 0; i < wall.length; i += 1) {
if (player.intersects(wall[i])) {
player.top = wall[i].bottom;
}
}
}
if (pressing[KEY_RIGHT]) {
player.x += 5;
for (i = 0; i < wall.length; i += 1) {
if (player.intersects(wall[i])) {
player.right = wall[i].left;
}
}
}
if (pressing[KEY_DOWN]) {
player.y += 5;
for (i = 0; i < wall.length; i += 1) {
if (player.intersects(wall[i])) {
player.bottom = wall[i].top;
}
}
}
if (pressing[KEY_LEFT]) {
player.x -= 5;
for (i = 0; i < wall.length; i += 1) {
if (player.intersects(wall[i])) {
player.left = wall[i].right;
}
}
}
// Out Screen
if (player.x > canvas.width) {
player.x = 0;
}
if (player.y > canvas.height) {
player.y = 0;
}
if (player.x < 0) {
player.x = canvas.width;
}
if (player.y < 0) {
player.y = canvas.height;
}
// Player Intersects Lava
for (i = 0; i < lava.length; i += 1) {
if (player.intersects(lava[i])) {
gameover = true;
pause = true;
}
}
}
// Pause/Unpause
if (lastPress === KEY_ENTER) {
pause = !pause;
lastPress = null;
}
}
function repaint() {
window.requestAnimationFrame(repaint);
paint(ctx);
}
function run() {
setTimeout(run, 50);
act(0.05);
}
function init() {
// Get canvas and context
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
canvas.width = 300;
canvas.height = 200;
// Create player
player = new Rectangle2D(40, 40, 10, 10, true);
// Set map
setMap(map0, 10);
// Start game
run();
repaint();
}
window.addEventListener('load', init, false);
}(window));
Regresa al índice
No tenía ni idea de esto, llego a saberlo hace unos años me hubiera ahorrado muchíiiiisimas horas de trabajo calculando manualmente los px que tengo que colocar en tal posición.
ResponderBorrarMe ha encantado amigo ^^ , como siga a este ritmo de aquí a fin de verano me habré echo un jueguecillo más que decente jaja
Pd: Como sugerencia creo que sería más fácil utilizar array bidimensional para fila y columna y nos ahorraríamos este algoritmo:
col++
if(col>=columns){
row++;
col=0;
}
}
}
Saludos!
¡Me alegro esté siendo de mucha ayuda para ti!
BorrarLo del arreglo bidimensional, eso usaba originalmente, pero descubrí es mucho más sencillo y menos propenso a errores fatales el usar esta clase de arreglo, así que este algoritmo facilita la tarea de su creación y mantenimiento.
Personalmente usaba un algoritmo más simplificado que involucraba módulos, pero lo consideré muy complicado para la gente en general, y por eso hice esta versión poco más larga, pero sencilla de comprender.
Por supuesto, si te sientes más cómodo con arreglos bidimensionales, es cuestión de gustos, y lo que sientas mejor para el desarrollo de tus creaciones. Éxito ;)
¿Cuales son los peligros de los arrays bidimensionales, me puedes explicar?, aparentemente son una solución ideal no requieren de ningún condicional ni variable extra.
BorrarDado que requiere un forma anidado para leerlo, mucha gente no toma en cuenta las excepciones posibles en caso que una fila sea más corta o larga de lo deseado, creando errores de objeto no encontrado cuando se intenta usar los valores faltantes. Pero so haces uso adecuado de estos arreglos, no deberías tener problema alguno.
BorrarJaja, bueno muchas gracias, me ha quedado claro, también se podría usar el operador modulo como me enseñaste en el capitulo de los sprites...
BorrarAsí es como lo hago yo personalmente, tal como le comentaba a Dryken. Pero preferí usar esta versión poco más larga pero comprensiva para que todo el mundo pudiera entenderla con facilidad.
BorrarTe hago una consulta con el tema del mapa.Estoy usando la misma función para definir tanto los bloques, pero en vez de agregar lava, agrego cajas que me gustaría que desaparezcan al colisionar con otro elemento. Te dejo el código que había armado para que se vea mejor la problemática.
ResponderBorrarLink : http://www.antworksdesign.com/clientes/game/boom.js
Gracias por tu tiempo.
¡Me he divertido bastante con tu código! Resolver el problema de la intersección entre ambos elementos ha sido un reto, pero tras varios minutos, ¡Finalmente he llegado al origen del problema!
BorrarAnalicemos esto un minuto: Tenemos Rectángulos que colisionan con Rectángulos mediante Intersect (x,y,width,height)... Tenemos Círculos que colisionan con Círculos mediante Distance (x,y,radius). Ahora, intentas colisionar un Rectángulo con un Círculo, el cual uno no tiene Ancho ni Alto, y el otro no tiene Radio... ¿¡Cómo harías para que ambos pueda interactuar entre ellos entonces!?
Dejaré que intentes llegar por la conclusión por ti mismo. Escríbeme la solución a la que llegues, o si aun no puedes lograrlo, avísame y te daré la mía. ¡Suerte!
Excelente tutorial, lo poquito que se sobre videojuegos en lo he sacado de tu blog : ) .
ResponderBorrarOye, cuando movemos nuestro rectangulo, se comprueba en un 'for' cuatro veces para posicionarnos después de colisionar, pero, no seria mejor guardar en unas variables nuestra posición antes de oprimir cualquier tecla (las flechas) y al ultimo hacer un solo ciclo, y en caso de que colisione se regresa a la antigua posición?. Creo, no lo he probado, no se si se haga mas rápido o si simplemente no sirva lo que digo jajajaj.
Sale, se agradece estos tutoriales.
Aunque sean cuatro "for", solo se ejecuta uno por ciclo si acaso hubo movimiento, o ninguno en caso contrario, por lo que en este caso, resulta mas optimizado que en la sugerencia que dices. Además... ¿Como sabes si debes regresas en x o y en el caso que mencionas?
BorrarDe igual forma, te has dado cuenta que hay muchas posibles soluciones a un mismo problema, y en algunos casos, unas son mejores que otras, como veremos mas tarde. ¡Felices códigos!
con la ayuda de tus tutoriales y varias horas rompiendome el coco jeje creo que pude avanzar bastante en mi pequeño proyecto.. pero creo que no logro implementar correctamente esta tecnica de mapa en mosaico cuando los objetos a pintar tienen distintos tamaños.. podrias subir un ejemplo de como hacer esto pintando distintas imagenes en lugar de rectangulos?
ResponderBorrarEste es mi pequeño proyecto jeje probablemente sea demaaasiadoo pedir pero si algun dia tienes tiempo y ganas agradeceria le hecharas un ojo y me dieras una solucion o me comentaras en que puedo mejorar, si no puedes igual agradesco muchisimo toda la informacion que publicas en este blog.
PD: escribo tanto aca por que no se como enviarte un mensaje privado.
https://mega.co.nz/#!8J00DBZK!sAuZqt9_T4i89gFBpQiQmHP3VskaedtqhF7P3VMdpr0
¡Ah! ¡Estas tratanto de implementar un RPG!
BorrarPero verás, el secreto de los objetos grandes en los RPG clásicos, es que en realidad se tratan de grupos de objetos pequeños, tal como se puede ver en esta imagen: http://actiongames.coltgames.com/rmvx_pic02_full.png
Ve si esto te da una idea mas clara de como diseñar tu mapa. Si aun no estas seguro, avisame y veré como puedo ayudarte mejor.
¡Suerte con tu proyecto!
aaa gracias por reponder, no lo habia pensado... entonces tengo que crear el mapa como un imagen grande y luego recortarlo en partes iguales??? ,y si es asi ,como hago para pintarlo por medio de un vector? (map1)...
BorrarEs esta la forma mas optima de hacerlo? no me queda muy claro, me gustaria que pudieras ayudarme
PD: algun consejo para poder mejorar el codigo, hacerlo mas optimo o algo..?? habia pensado crear una clase base de la que hereden las demas.. por que todos(o la mayoria) de los objetos tienen posX,posY,funcion Draw, funcion Intersect, etc...
Cada elemento tiene su valor. Por ejemplo, cada bloque de pasto es 0, los troncos que parecen de dos, serían 1 y 2 los horizontales, 3 y 4 los verticales, la casa pequeña que está en 6 partes seria el techo 5, 6 y 7 y para el suelo 8, 9, y 10, y así con los demás elementos. Al final pones todo en el vector de mapa para ensamblarlo tal como quieres verlo.
BorrarSobre la herencia, si múltiples objetos tienen la mayor parte de sus métodos iguales, es muy buena idea considerarle. Recuerda que en JavaScript esto se consigue a través de prototipos.
¡Suerte en tu código!
claro, eso es lo que habia pensado.. pero pareciera ser que no es una forma muy practica, digamos, parece dificil de modificar y de armar.. pero lo voy a intentar.
BorrarDeberia armar el mapa como una imagen grande con photoshop o adobe fireworks y luego ir cortandola para luego ensamblarla por codigo??? lo que me resuslta mas dificil hasta ahora es encontrar los recursos necesarios jeje ya que no tengo idea de como hacer imagenes... no conoceras algun lugar para descargarlos y usar free?? o algun programa? el que se ve en la imagen ( http://actiongames.coltgames.com/rmvx_pic02_full.png ) que pusiste se ve bastante bueno, por lo menos proporcionas los tiles (segun parece).
Desde ya muchas gracias
Es la forma en que se hacía originalmente. Quizá ya pueda hacerse de otras formas mas "sencillas", pero que podrían ser complicadas en otros aspectos.
BorrarLa idea es no hacer todo el mapa, si no los bloques que sabes que se repetiran, como en esta imagen: http://i1238.photobucket.com/albums/ff481/hanzokimura/HanzoGraphics/Hanzo-TownSet02VS.png . Después la ensamblas en map1 asignando un numeró a cada tile.
¿Que clase de recursos estas buscando, por cierto? ¿Para armar el mapa? Se que existen varios, pero no se precisamente de alguno que pudiera recomendarte.
tratando de implementar lo que dijiste pude aclarar mis dudas y logre un avance.. pero no estoy seguro si lo estoy haciendo bien xq me surgio un problema. cuando encuentro en 1 pinto el pasto cuando encuentro otro numero pinto una parte del objeto.. pero si el objeto tiene orificios o es transparente debajo de el se ve el fondo negro (xq no pinto el pasto) como puedo arreglar esto? creando un array para la superficie y otro para los objetos??? o como, no se si me explico bien
Borraro deberia editar directamente la imagen incrustandole el fondo que corresponde?.. para q no trasparente y no aparescan partes negras
Borrartodos los recursos necesarios para esto tipo de juego.. personajes.. poderes.. fondos como pasto agua lava arena.. arboles arbustos.. animales, sonidos, etc lo que mas necesito en este momento serian paneles para lo que serian los mensajes ,el inventario del personaje ,slots para los poderes, etc.. trate de hacerlos pero definitivamente no es lo mio jeje me quedaron bastante repulsivos
BorrarLa forma clásica y lo mas sencillo, sería poner los objetos sin fondo transparente, si sabes que siempre irán sobre pasto como en este caso. Si existe la posibilidad que uses los mismos elementos en distintos terrenos, quizá si convenga más armarlos en dos capas como fue tu sugerencia.
BorrarSobre los recursos, no tengo un sitio en específico que pueda recomendarte, pero si buscas en Internet, encontraras muchos recursos gratuitos que puedes usar en tus juegos (Son mayores las posibilidades si lo buscas en ingles).
muchas gracias por aclarar mis dudas jeje.. ya solucione la mayoria de mis problemas... pero ahora una nueva piedra aparece en mi camino. comenc a atulizar el mouse para mover el personaje pero no se como hacer para que vaya a la casilla donde hice click.. caminando y respetando las casillas :/ espero sepas aconsejarme en esto tambien, desde ya muchas gracias...
BorrarVaya... Mapas con ratón. ¡Que gran reto te has metido!
BorrarPara empezar, ¿Estas usando camaras? Es muy importante que tomes en cuenta este factor, ya que el ratón es relativo a la pantalla, no a la cámara...
si, utilizo una camara. pero pense que no presentaria problema si sumo X e Y del raton con X e Y de la camara..
Borrarcomenc a pensar en algo pero me parece que no llegare a ningun lado..
pense en obtener la posicion del personaje al hacer click.. tomar las direcciones en las que puede moverse, elejir el que este mas cerca del objetivo y guardar su direccion.. luego en base a esa posicion volver a tomar las direcciones en las que puedo moverme y asi sucesivamente..
conoces de alguna forma mejor de hacerlo? gracias
Bueno, si estás sumando la cámara, debería darte el valor de la posicion real que deseas, pero viendo que no es así, significa que intentas algo mas complicado de lo que estas explicando, o al menos no me queda clara tu intension. ¿Hay alguna forma en que puedas mostrarme lo que haces y lo que deseas hacer para asi ayudarte?
BorrarAcabo de leer con mayor detenimiento, y creo que lo que deseas hacer es un path-finding; ¿algo como esto? http://qiao.github.io/PathFinding.js/visual/
Borrarsi exactamente! eso era justo lo que necesito! encontrar el camino mas optimo entre 2 puntos y hacer que el personaje se mueva.. quieres que suba mis codigos para que los veas?? gracias por tu ayuda.
Borrarcreo que seria mejor para mi caso implementar el algoritmo A*.. veo que el ejemplo que me pasaste esta hecho con JQuery =/
BorrarSi buscas en Internet el algoritmo, hay muchos resultados que te enseñan la teoría para implementarlo. Además, ya hay varias implementaciones de pathfinder para javascript además del ejemplo que te mostré. Seguro habrá al menos alguna de ellas que sirva.
BorrarConoceis la libreria rpg js v2 para crear juegos de ese tipo? lo he estado mirando pero no consigo entender como funciona. Creo que implementa los mapas con json. Estaria bien algun tuto de este tipo de juego, no se si es mejor empezar de cero o usar eso ya que parace estar muy bien pensado y optimizado.Que pensais? Un saludo.
BorrarLa librería no la he revisado a fondo, por lo que no he podido ver si cubre los aspectos que necesitas para el juego que desarrollas.
BorrarSobre decidir si usar una librería o hacerlo de cero, definitivamente una buena librería te ahorraría mucho tiempo y dolores de cabeza, pero modificar funcionalidades para una tarea personalizada, puede ser demasiado complejo. Por el contrario, hacerlo de cero te permitirá controlar y optimizar el juego tanto como lo desees. Es en realidad cuestión de poner cada caso en la balanza para decidir cual es mejor para dicho proyecto, de acuerdo a tus necesidades.
Mucha suerte, ¡Y felices códigos!
Saludo yo quiero saber por que a mi no se me visualiza el mapa de tu ejemplo
ResponderBorrar¿Que navegador estás usando? ¿Revisaste ya la consola de Javascript?
BorrarQue bueno que responde de una ces eso quiere decir que esta atento a la web...
BorrarYo estoy usando el chrome, yo apena estoy empesando a programar no se mucho de javascript pero creo que me se algunos concepto basico yo ya eh hecho algunas cosita practicando pero mire la forma en la que hace los mapa y me parecii muy interesantr y mas facil al a estar creando bloque por bloque manualmente pero intento instegrarlo y no me resulta... como te puedo enviar un codigo para que vea si el mapa esta bien o no y gracias por los tutoruales son muy bueno y ayudan a mejorar las ideas que tenemos sigue asi que te apoyo
window.onload = function(){
Borrar//variables globales
var canvas = document.getElementById('canvas');
var gameloop = setInterval(main, 10);
var cWidth = document.getElementById('canvas').width;
var cHeight = document.getElementById('canvas').height;
var mouse = { posX: cWidth / 2, posY: cHeight / 2};
var ctx = null;
var wall = [];
var lava = [];
var map0 = [
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,2,2,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,2,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,
1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,1,
1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,2,0,0,1,0,0,1,
0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,2,0,0,1,0,0,0,
0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,2,0,0,1,0,0,0,
1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,2,0,0,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,1,0,0,0,0,0,0,1,0,0,1,
1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,
1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,
1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
];
function setMap(map, columns, blockSize){
var col = 0;
var row = 0;
wall.length = 0;
lava.length = 0;
for(var i = 0; i < map.length; i++){
if(map[i] == 1){
wall.push(new Rectangle(col*blockSize, row*blockSize, blockSize, blockSize));
}else if(map[i] == 2){
lava.push(new Rectangle(col*blockSize, row*blockSize, blockSize, blockSize));
col++;
}
if(col >= columns){
row++;
col = 0;
}
}
}
document.addEventListener('mousemove',function(evt){
mouse.posX=evt.pageX-canvas.offsetLeft;
mouse.posY=evt.pageY-canvas.offsetTop;
},false);
if(canvas && canvas.getContext){
var ctx = canvas.getContext('2d');
if(ctx){
cbackground(ctx);
}
}
function cbackground(ctx){
//pintando el mapa
ctx.save();
ctx.fillStyle = '#999';
for(var i = 0; i < wall.length; i++){
wall[i].fill(ctx);
}
ctx.restore();
ctx.save();
ctx.fillStyle = '#f00';
for(var i = 0; i < lava.length; i++){
lava[i].fill(ctx);
}
ctx.restore();
//pintando fondo del canvas
ctx.save();
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, cWidth, cHeight);
ctx.restore();
ctx.save();
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.strokeRect(0, 0, cWidth, cHeight);
ctx.restore();
//pintando el puntero
ctx.save();
ctx.beginPath();
ctx.fillStyle = 'blue';
ctx.arc(mouse.posX,mouse.posY, 5, 0, 360, true);
ctx.stroke();
ctx.restore();
}
//gameloop - function of actualization for game
function main(){
cbackground(ctx);
setMap(map0, 30, 10);
}
};
Antes que nada, ¿Revisaste la consola de Javascript (F12)? ¿No te reporta nada?
BorrarA simple vista... ¿Si incluiste la función Rectangle? ¿Donde mandaste a llamar la función main? Y posiblemente debas dibujar cbackground después de setMap, o estarías dibujando un mala vacío. Avisame si alguno de estos era el problema que tenías.
No eh podido revisar la consola por que es que estoy editando desde mi cel... aparte de eso lo eh tratado todo, eh hecho los paso que me dijiste, la funcion rectangle no la puedo meter en donde estoy asiendo la llamada de main por que a main la estoy llamando desde un setInterval () pero luego de que me hablaste de la funcion rectangle me percate de que no la avia agregado al codigo script y la agregue y agregue las dos funciones mas la del Rectangle.prototype.fill=function() y Rectangle.prototype.intersects=function() pero aun asi mo se me visualiza... asi quedo el ultimo codico como te lo enciare despues de este comentario
Borrarwindow.onload = function(){
Borrar//variables globales
var canvas = document.getElementById('canvas');
var gameloop = setInterval(main, 10);
var cWidth = document.getElementById('canvas').width;
var cHeight = document.getElementById('canvas').height;
var mouse = { posX: cWidth / 2, posY: cHeight / 2};
var ctx = null;
var wall = [];
var lava = [];
var map0 = [
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,2,2,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,2,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,
1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,1,
1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,2,0,0,1,0,0,1,
0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,2,0,0,1,0,0,0,
0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,2,0,0,1,0,0,0,
1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,2,0,0,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,1,0,0,0,0,0,0,1,0,0,1,
1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,
1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,
1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
];
function setMap(map, columns, blockSize){
var col = 0;
var row = 0;
wall.length = 0;
lava.length = 0;
for(var i = 0; i < map.length; i++){
if(map[i] == 1){
wall.push(new Rectangle(col*blockSize, row*blockSize, blockSize, blockSize));
}else if(map[i] == 2){
lava.push(new Rectangle(col*blockSize, row*blockSize, blockSize, blockSize));
col++;
}
if(col >= columns){
row++;
col = 0;
}
}
}
document.addEventListener('mousemove',function(evt){
mouse.posX=evt.pageX-canvas.offsetLeft;
mouse.posY=evt.pageY-canvas.offsetTop;
},false);
if(canvas && canvas.getContext){
var ctx = canvas.getContext('2d');
if(ctx){
cbackground(ctx);
}
}
function cbackground(ctx){
//pintando el mapa
ctx.save();
ctx.fillStyle = '#999';
for(var i = 0; i < wall.length; i++){
wall[i].fill(ctx);
}
ctx.restore();
ctx.save();
ctx.fillStyle = '#f00';
for(var i = 0; i < lava.length; i++){
lava[i].fill(ctx);
}
ctx.restore();
//pintando fondo del canvas
ctx.save();
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, cWidth, cHeight);
ctx.restore();
ctx.save();
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.strokeRect(0, 0, cWidth, cHeight);
ctx.restore();
//pintando el puntero
ctx.save();
ctx.beginPath();
ctx.fillStyle = 'blue';
ctx.arc(mouse.posX,mouse.posY, 5, 0, 360, true);
ctx.stroke();
ctx.restore();
}
//gameloop - function of actualization for game
function main(){
setMap(map0, 30, 10);
Rectangle();
cbackground(ctx);
}
function Rectangle(x,y,width,height){
this.x=(x==null)?0:x;
this.y=(y==null)?0:y;
this.width=(width==null)?0:width;
this.height=(height==null)?this.width:height;
}
Rectangle.prototype.intersects=function(rect){
if(rect!=null){
return(this.xrect.x
&& this.yrect.y);
}
}
Rectangle.prototype.fill=function(ctx){
ctx.fillRect(this.x, this.y, this.width, this.height);
};
}
Ya probé el código y encontré el error: Estás dibujando el fondo encima del mapa. ¿Como esperas que se vea si le pones un rectángulo blanco encima? :P
BorrarCorregido eso, note que además modificaste la función setMap y pusiste col++ dentro de unas llaves en las que no debería ir. Corrige estos dos detalles, y tu mapa debería verse ya sin problemas.
Este comentario ha sido eliminado por el autor.
ResponderBorrarComo va? Yo aca intentando que me salga pero no encuentro mi error si alguien me puede ayudar se lo agradeceria...
ResponderBorrar$(document).ready(function(){
var canvas = $("#canvas")[0];
var cWidth = $("#canvas").width();
var cHeight = $("#canvas").height();
var ctx = canvas.getContext("2d");
function init(){
if(typeof game_loop != "undefined"){
clearInterval(game_loop);
}
game_loop = setInterval(main, 100);
}
function main(){
paint();
setMap(map0, 10);
}
var wall = [],
lava = [],
map0 = [
[1, 1, 1, 1, 1, 1, 1, 1],
[1, 2, 0, 0, 0, 0, 2, 1],
[1, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 2, 0, 0, 2, 0, 1],
[1, 0, 2, 0, 0, 2, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 1],
[1, 2, 0, 0, 0, 0, 2, 1],
[1, 1, 1, 1, 1, 1, 1, 1]
];
function setMap(map, blockSize){
var col = 0,
row = 0,
columns = 0,
rows = 0;
wall.length = 0;
lava.length = 0;
for (row = 0, rows = map.length; row < rows; row += 1) {
for (col = 0, columns = map[row].length; col < columns; col += 1) {
if (map[row][col] === 1) {
wall.push(new Rectangle2D(col * blockSize, row * blockSize, blockSize, blockSize, true));
} else if (map[row][col] === 2) {
lava.push(new Rectangle2D(col * blockSize, row * blockSize, blockSize, blockSize, true));
}
}
}
}
function paint(ctx){
var i = 0,
l = 0;
ctx.fillStyle = "#999";
for (i = 0; i < wall.length; i += 1){
wall[i].fill(ctx);
}
ctx.fillStyle = "#f00";
for(i = 0; i < lava.length; i += 1){
lava[i].fill(ctx);
}
}
function Rectangle2D(x, y, width, height, createFromTopLeft) {
this.width = (width === undefined) ? 0 : width;
this.height = (height === undefined) ? this.width : height;
if (createFromTopLeft) {
this.left = (x === undefined) ? 0 : x;
this.top = (y === undefined) ? 0 : y;
} else {
this.x = (x === undefined) ? 0 : x;
this.y = (y === undefined) ? 0 : y;
}
}
init();
})