Mostrando las entradas con la etiqueta Canvas. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Canvas. Mostrar todas las entradas

miércoles, 3 de febrero de 2021

Simple RayCast 2D - javascript

Permite mostrar gráficos estilo Wolfenstein de los 80s, con el pequeño plus (o des, según se vea) de darle relevancia al piso y esconder el techo.

Puedes descarga el script desde la siguiente link Download

Instalación

Debe incluirse la clase dentro el cuerpo del html. Para usarse al insarse al instante debe ir dentro de la etiqueta <head> para cargarse antes del body. De lo contrario, puede cargarse en el body, y debe usarse hasta confirmar que ha cargado.

<script type="simple_raycast2D.js"></script>

Inicializar el motor

Para comenzar debemos hacer una instancia de simple_raycast2D

// inicia el motor gráfico RayCast2D
rc = new simple_raycast2D(
    canvas, // elemento html canvas
    maxx, // profundidad del rayo casteador
);

El primer argument es un elemento htm canvas, que es posible crearlo en el instante o tomar uno que ya se encuentre en el body. El segundo argumento es la profundidad de campo o área visible.

Por ejemplo:

rc = new simple_raycast2D(
    canvas = document.createElement('canvas')
    maxx = 16
);

Tamaño del canvas

El motor no usa la tarjeta gráfica, en cambio usa el procesador. Por esta razón el tamaño de dibujo del canvas debe ser limitado a usar baja resolución para que corra bien.

A parte, sabemos que se le puede dar un tamaño de impresión independiente al tamaño de dibujo del canvas. La función calcula automáticamente el ancho de dibujo del canvas.

Esto se especifica con el siguiente estilo y la siguiente función

canvas {
    width: 100vw;
    height: 100vh;
    position: fixed;
    left: 0;
    right: 0;
}

rc.resizeTo(scr_height = 90);

donde src_height especifica la altura del canvas. Recomiendo que sea entre 80 y 160 pixeles. 

Cada que la ventana cambie de tamaño o la orientación, debería llamarse.

window.addEventListener('resize',function(){
	rc.resizeTo(90);
});
rc.resizeTo(90);

El mapa

Para especificar un mapa debemos definir dos variables, tiles y mapa. Donde tiles es un arreglo de bits correspondiente a la imagen que se usara como tile map y puede extraerse usando la función imData. Y mapa en un arreglo bidimencional de 16bits.

rc.tiles = imData('mapTexture.png',true);
rc.mapa = [
    [0,0,0,0],
    [0,0,0,0],
    [0,0,0,0]
];

Por ejemplo, el array para el mapa es de 4x3. Y donde se esperan números de 16 bits.

  • El bit 1 indica si es pared.
  • El bit 2 indica si debe comportarse como diagonal,
  • Los bit 3,4,5,6,7,8 indican el id del sprite 0-63 para la pared,
  • Los bit 9,10,11,12,13,14 indican el id del sprite 0-63 para el piso, 
  • Los bit 15,16 indican como dibujar el piso:
    • 0 - normal,
    • 1 - rotación 90°,
    • 2 - rotación 180°,
    • 3 - rotación -90°


En seguida un ejemplo de cómo usar mapas separados

mapa_es_pared = [
    [1,0,0,1],
    [1,0,0,1],
    [1,1,1,1]
];
mapa_sprites_pared = [
    [5,0,0,5],
    [4,0,0,4],
    [5,6,5,5]
];
mapa_sprites_piso = [
    [0,4,6,0],
    [0,7,5,0],
    [0,0,0,0]
];
mapa_piso_rotacion = [
    [0,0,1,0],
    [0,3,2,0],
    [0,0,0,0]
];
mapa = [];
for(var y=0; y<3; y++){
    mapa[y] = [];
    for(var x=0; x<4; x++)
        mapa[y][x] = mapa_es_pared[y][x] +
            (mapa_sprites_pared[y][x] << 2) +
            (mapa_sprites_piso[y][x] << 8) +
            (mapa_piso_rotacion[y][x] << 14);
}
rc.mapa = mapa;

La camara básica

Para posicionar la cámara se puede usar el siguiente comando

rc.cam({x: 1, y: 1, a:0});

donde "xy" es la posición de la cámara dentro del mapa, y "a" es el ángulo en radianes que la cámara estará mirando.

Los objetos

Para agregar objetos a la escena, primero debes crear las texturas de las cuales se tomaran los gráficos. Eso se hace con el siguiente comando.

var sprite_texture = rc.textura(imData('sprite.png'), width, height);

La textura se carga asincronamente, de modo que no necesitas esperar a que cargue para poderla usar.

La textura debe contener los sprites del objeto organizados horizontalmente por cara y verticalmente por animación. Es decir, cada columna de sprites dentro de la imagen corresponde a un ángulo o cara del objeto, y cada renglón corresponde a un cuadro de animación.

Ahora, ya podemos crear un objeto con el siguiente comando

obj_n = rc.crear(
    sprite_texture, // indica la textura que usará el objeto
    angulos, // indica la cantidad de caras del objeto     angulos, // indica la cantidad de caras del objeto     divY, // indica la cantidad de cuadros del objeto     ancho, // indica el ancho con el que será dibujado el objeto     animacion, // es un array que indica el orden de los frames     inverso,     sorteable );

Para el array que indica el orden de frames de animación, en el caso deuna imagen de 4x3 sprites sería [0,4,8], pensando en que usa 4 caras, y una animación de 3 cuadros.

Posicionar y animación de objetos

Para indicar la posición de un objeto usa el siguiente comando

rc.pos(obj_n, {
    x: 2,    // posición x
    y: 1.5,    // posición y
    z: 0,    // posición altura
    a: 0,    // angulo
    v: true    // visible
});

El primer argumento es el índice devuelto por la función crear. El segundo atributo es un objeto con cualidades de la entidad, donde ningún atributo del objeto es obligatorio.

También puedes indicar una animación con el siguiente comando. Donde el primer argumento es el índice devuelto por la función crear. El segundo argumento es un objeto con la información de la animación.

rc.anim(obj_n, {
    c: 0, // indica el cuadro donde comienza la animación
    l: 3, // indica la duración de la animación
    r: 2, // indica cuántos cuadros rebobinará al terminar la animación
    s: 0.1, // indica la velocidad de la aniamción
});

por ejemplo,  en la imagen tenemos en el primer cuadro de pie, y en los siguientes dos la animación de caminar. Para ponerle a caminar, comenzamos en el cuadro 0, donde está de pie, y especificamos una duración de 3 cuadros, que son uno de pie y dos de caminado, y por último 2 cuadros de retroceso al terminar, para que se repitan los cuadros de caminado.

Loop Update

Para terminar, debemos llamar la función dibujar a cada instante para actualizar y redibujar

rc.dibujar(showWalls, showFloor);

donde el primer argumento es un valor booleano para indicar si debe dibujar las paredes, y el segundo indica si debe dibujar el piso.

DOCUMENTACION

/*

// inicia el motor gráfico RayCast2D
rc = new simple_raycast2D(
    canvas, // elemento html canvas
    maxx, // profundidad del rayo casteador
);

// para cargar especificar la textura del piso y las paredes
rc.tiles = imData('mapTexture.png',true);

// además se puede cargar una imagen escala de grises de protuberancia para el piso
rc.tilesDepth = imData('depthTexture.png',true);

// para cargar el mapa, se hace con un array bidimencional de W x H,
// donde se esperan numeros de 16bits. El bit(1) indica si es pared, el bit(2) indica si
// debe comportarse como diagonal, el bit(3,4,5,6,7,8) es el id del sprite 0-63 para la pared,
// el bit (9,10,11,12,13,14) indica el id del sprite 0-63 para el piso,  el bit(15,16) indican
// como dibujar el piso: 0 nada, 1 rotación 90°, 2 rotación 180°, 3 rotación -90°
rc.mapa = [[0]];

// posiciona la camara opc = {x, y, a}
rc.cam(opc);

// posiciona la camara aditivamente opc = {x, y, a}
rc.camMov(opc);

// posiciona la camara automáticamente con forme al mapa sobre el objecto n. A partir de
// la posición actual de la camara voltea a ver al objeto n y lo rodea "ang" grados,
// acercandose o alejandose a "z" disancia
rc.camAuto(n, ang, z);

// posiciona la camara en alineación camara, n, m, a una distancia z de n
rc.camAtack(n,m,z);

// carga una textura. "width, height" es el acho y
// alto en pixeles de la imagen. Es necesario proporcionarlos para la carga ascincrona
// si se precarga, basta con "width" 
rc.textura(imagenData, width, height);

// se encarga de actualizar el canvas. "showWalls" es un booleano que especifica si debe mostrar
// la paredm "showFloor" es un booleano que especifica si debe mostrar el piso, "es3D" es un booleano
// que especifica si dibujará lateralmente otra camará para efecto 3D, "_x,_y" especifica la posición en
// el canvas donde comenzará a dibujar, __paletteChange es un objecto que pretende cambia run color por
// otro, por ejemplo {0:20} (el 12 por el 20), pero tambien puede ser una sucición de colres {0:[20, 21, 15]}
rc.dibujar(showWalls, showFloor, false, false, false, es3D, _x, _y, __paletteChange);

// Crea un objecto con la textura "textura" create previamente, misma que está dividida en "angulos"
// o columnas que representan las caras del objeto, y dividida en "divY" renglones de animación, presentando en pantalla con un "ancho" factor
// correspondiente a el tamaño de los bloques, "animación" si es definido debe ser un array con el nuevo
// orden de los renglones de animación, "inverso" es un voleano que indica si las caras están en orden
// inverso, "sorteable" es un boleano que especifica si se dibujará en orden de distancia
rc.crear(textura, angulos, angulos, divY,ancho, animacion, inverso, sorteable);

// elimina el objeto "n"
rc.borrar(n){ this.obj_activo[n] = false; ;

// posiciona el objecto "n" { x:pos,y:pos,z:pos, a:angulo, v:boolean_visible }
rc.pos(n, opc);

// posiciona aditivamente el objecto "n" { x:pos,y:pos,z:pos, a:angulo, v:boolean_visible }
rc.mov(n, opc);

// anima un objeto "n", comienza en el renglon "opc.c" y avanza "opc.l" renglones, entonces retrocede
// "opc.r" renglones para realizar un loop
rc.anim(n, opc);

// crea un objecto emporal tomado el objeto "n" y lo anima desde el renglon "opc.c" hasta "opc.l"
// renglones, en la posición "opc.x, opc.y" cuando termina se destruye amenos que "isTemp" sea
// definido igual a false
rc.animX(n, opc, isTemp);

// devuelve los objectos que se encuentran en el arco de posición "x,y", de angulo "a", de arco "arc"
// a una distancia máxima "max"
rc.getObjectsByRayCast(x,y,a,arc_size,max);

// devuelve los objetos que tocan una barra que inicia en "x,y", de angulo "a", un grosor "w" y
// un largo "max"
rc.getObjectsByRayCastAndBar(x,y,a,w,max);

// devuelve la imagen de ruta "url", donde "isBlocks" es booleano  que especifica si servirá para
// paredes y techo (true - que pondra la secuencia por cuadro), o para objetos (false - que no
// pondra secuencia).
imageData = imData(url,isBlocks);

// actualiza el tamaño de la pantalla al tamaño html del canvas, donde scr_height es el alto en piexeles
// por default 90
rc.resizeTo(scr_height);
*/

jueves, 9 de noviembre de 2017

WebGl BufferGeometry Sprite

Éste pretende ser un motor de animación 3d usando cuadros, basado en THREE.js.

... el ejemplo lo tendré pronto ...

*** Download ***

https://drive.google.com/file/d/1hHsDfnmpmjUOaF87CF3IRwi6A84UaSjt/view?usp=sharing

*** Documentación ***

Cargar textura
```
var textureLoader = new THREE.TextureLoader();
textureLoader.load('photo.png', function (texture){
... código ...
});
```

Iniciar motor
Se le pasa la escena three js
```
var enSp = new WebGl_BufferGeometry_Sprite(scene);
```

Crear grupo o familia de textura
Para aumentar la velocidad del plotéo, se me ocurre que todos los objetos tridimencionales que compartan una textura, serán el mismo objeto THREE.BufferGeometry y manipularé sus vertices para que funjan como objetos independientes.
```
var groupMesh = enSp.addTexture(texture);
```

Agregar un objeto 3d
Se le debe pasar el arreglo con las vertices del objeto, el arreglo del mapa de textura, el arreglo de los indices de los polígonos,  arreglo de vertices agrupadas,  arreglo de cuadros de animación.
```
vtx = [[0,2,0,2,-1,0,-2,-1,0], [0,2.2,0,2,-1,0,-2.2,-1,0]];
uvmap = [0.5,0,1,1,0,1];
index = [0,1,2];
groups = [[{x:0,y:0,z:0},2,1],[..  ..],..  ..];
animation = [0,1,0];
var sprite = enSp.addSprite(groupMesh, vtx, uvmap, index, groups, animation);
```

  • Array con las vertices del objeto. Es un array unidimencional que contiene los componentes xyz de las vertices uno tras otro. Además, para agregar cuadros de animación, es posible pasarle un array de arrays donde cada sub array es un nuevo array unidimencional que contiene los componentes xyz de cada cuadro. Importante. Se respeta el orden de las vertices, esperando que la diferencia de entre ellas sea la animación.
  • Array del mapa de textura. Es un array unidimencional que contiene los componentes uv de cada vértice. Importante. nótese que es por vértice y no por polígono. de modo que si queremos tener dos polígonos vecinos con distinto mapa de textura, se requerirá que cada uno tenga sus vertices particulares. Tratando de evitar el caso anterior porque al aumentar las vertices, se reduce el desempeño.
  • Array de indices de polígonos. Es un array unidimencional que contiene los componentes abc de cada polígono, uno tras de otro.
  • Array de vertices agrupadas. Es un array bidimencional que en sus subarreglos contiene un objeto {x,y,z} en su primera posición que especifica el origen del grupo, y seguido, el indice de las vertices que conforman el grupo. Esto es útil para animar o mover por separado un grupo de un objeto.
  • Array de cuadros de animación. Es un array bidimencional que contiene el orden en que el indice de los cuadros de animación se sucederán (los cuadros son los subarrays del Array de vertices). Por ejemplo, si tengo una animación de maniar en tres cuadros, y deseo que se repitan en loop, usaría 0,1,2,1  ... de modo que al animar se sucederían 012101210 ...

Un sprite Objeto o sprite, tiene funciones que permiten modificar su posición, rotación, etc.

  • remove. Remueve el sprite u objeto del motor.
    ```
    sprite.remove();
    ```
  • pos. Especifica la posición de un sprite pasándole un elemento del tipo {x,y,z} y un valor booleano positivo si queremos que sea aditivo en vez de imperativo.
    ```
    sprite.pos({x:10, y:1, z:0,1}, false);
    ```
  • ang. Especifica el ángulo de un sprite pasándole un elemento del tipo {x,y,z} y un valor booleano positivo si queremos que sea aditivo en vez de imperativo. Usa radianes.
    ```
    sprite.ang({x:10, y:1, z:0,1}, false);
    ```
  • scale. Especifica la escala de un sprite pasándole un elemento del tipo {x,y,z} y un valor booleano positivo si queremos que sea aditivo en vez de imperativo. Por default cada uno valen 1. Además es posible especificar todos de un solo golpe pasándole un número en vez del objeto xyz.
    ```
    sprite.scale({x:2, y:2, z:2}, false);
    sprite.scale(2, false);
    ```
  • scroll. Permite recorrer la textura de un objeto o sprite pasándole la cantidad que se moverá en uv el mapa de las vertices.
    ```
    sprite.scroll(u,v);
    ```
  • anim. Anima un sprite según los siguientes parámetros (solo el cuadro inicial es obligatorio -start-):
    ```
    sprite.anim(start, stopin, back, step, onsuccess,  targetx, targety, targetz, targeteasefn, onstep);
    ```
    • cuadro inicial. cuadro con el que arranca la animación.
    • cuadro final. cuadro donde se detendrá la animación.
    • retroceso. si se especifica, volverá el puntero de animación la cantidad `retroceso` permitiendo con ello hacer un loop.
    • velocidad. es la velocidad del puntero en cambiar de cuadro. Por default es 1, pero puedes hacer que dure el doble pasándolo a 0.5
    • animación success. Es una función que se ejecuta cada que la animación supera el cuadro final, o bien, si se reinicia el loop.
    • target x. Especifica si se debe trasladar a algún punto mientras dura la animación.
    • target y. Especifica si se debe trasladar a algún punto mientras dura la animación.
    • target z. Especifica si se debe trasladar a algún punto mientras dura la animación.
    • target ease. si se especifica un punto de traslado, es la función ease que usara para realizar el traslado. Por default es lineal. 
    • función onstep. Es ta función se ejecuta cada que el puntero de la animación avanza, siendo sus argumentos:
      • tEase. un numero entre 0 y 1 que refleja el avance de la animación de forma ease.
      • t. un numero entre 0 y 1 que refleja el avance de la animación de forma real.
  • setGroup. permite cambiar la posición, ángulo o escala de un grupo de vertices según los siguientes parámetros:
    ```
    sprite.setGroup(group, obj, isAdd, _fn, _success, _step);
    ```
    • group. es un numero que indica el indice del grupo de vertices
    • obj. es un elemento de tipo {x,y,z,ax,ay,az,ex,ey,ez} que especifica las nuevas propiedades del grupo.
    • isAdd. si se especifica, es un booleano que indica si las nuevas propiedades son aditivas.
    • _fn. Si se especifica es la función ease con laque sucederá el cambio de especificaciones.
    • _success. si se especifica, es la función que se ejecuta cuando las nuevas especificaciones sucedan.
    • _step. es la velocidad con la que sucederán las nuevas especificaciones. Por default es uno (de inmediato). Por ejemplo, si se quisiera hacer en dos pasos, usaríamos 0.5
  • clone. permite clonar un sprite existente. A veces resulta útil clonar enemigos o efectos.
    ```
    sprite.clone();
    ```
  • join. permite fusionar un sprite en otro para construir sprites más complicados. Conserva los grupos de vertices sólo recorriendo el indice de grupo. Al fusionarlo podemos indicar la posición, ángulo y escala.
    ```
    sprite.join(obj,pos,ang,scale);
    ```
Eejecutar un efecto especial
Clona un sprite para animarlo y auto destruirlo al concluir la animación. (solo sprite es obligatorio)
```

enSp.fx(sprite,x,y,z,e,flip,endX,endY,endZ,step,startFrame,endFrame,callBackFnOnEnd,easeFn,onStep)
```
    • sprite. sprite recurso donde se hará el clon.
    • x. posición donde aparecerá.
    • y. posición donde aparecerá.
    • z. posición donde aparecerá.
    • e. escala que tendrá.
    • flip. no es nada.
    • endX. posición que alcanzará durante la animación
    • endY. posición que alcanzará durante la animación
    • endZ. posición que alcanzará durante la animación
    • step. velovidad con la que el puntero del cuadro avanza. Por default es 1.
    • startFrame. cuadro inicial de la animación
    • endFrame. cuadro final de la animación.
    • callBackFnOnEnd. funcion que se ejecuta justo al terminar la animación.
    • easeFn. funcion ease que se usa para suceder la traslación
    • onStep. funcion que se ejecuta a cada paso de la traslación (tEase, t)

Refrescar cuadro
Es importante refrescar el cuadro constantemente para que todos las acciones tomen efecto.
```
enSp.update()
```

jueves, 23 de septiembre de 2010

Demo de Zelda en JavaScript, juego RPG

Hi, pues si, aquí les presento una pequeña demostración de un RPG que mi hermano me invito a hacer. Escogí JavaScript y Canvas para desarrollarlo.

Primero, hago uso de algunas imagenes que aquí les pongo


El escenario, link, los monstros y el boss final (el cual aún no es vencible o atacable, jejeje ).





Se juega con el mouse!! n //// n