raton posicion mover mostrar event div coordenada con javascript canvas mouse noise isometric

mover - posicion del raton javascript



posición del mouse en mosaico isométrico, incluida la altura (6)

Struggeling traduciendo la posición del mouse a la ubicación de las teselas en mi grilla. Cuando todo es plano, las matemáticas se ven así:

this.position.x = Math.floor(((pos.y - 240) / 24) + ((pos.x - 320) / 48)); this.position.y = Math.floor(((pos.y - 240) / 24) - ((pos.x - 320) / 48));

donde pos.x y pos.y son la posición del mouse, 240 y 320 son el desplazamiento, 24 y 48 del tamaño del mosaico. La posición entonces contiene la coordenada de la cuadrícula de la ficha sobre la que estoy pasando. Esto funciona razonablemente bien en una superficie plana.

Ahora estoy agregando altura, que las matemáticas no tienen en cuenta.

Esta cuadrícula es una cuadrícula 2D que contiene ruido, que se está traduciendo a altura y tipo de mosaico. La altura es realmente solo un ajuste a la posición ''Y'' de la baldosa, por lo que es posible que se formen dos baldosas en el mismo lugar.

No sé cómo determinar qué mosaico sobre el que me estoy moviendo.

editar:

Avancé un poco ... Antes, dependía del evento mouseover para calcular la posición de la grilla. Acabo de cambiar esto para hacer el cálculo en el bucle de sorteo, y verifico si las coordenadas están dentro de los límites del mosaico que se está dibujando actualmente. crea algo de sobrecarga aunque no estoy seguro si estoy muy contento con él, pero confirmaré si funciona.

editar 2018:

No tengo respuesta, pero como esta ha [sd] una recompensa abierta, sírvete con un código y una demo

La cuadrícula en sí es, simplificada;

let grid = [[10,15],[12,23]];

lo que lleva a un dibujo como:

for (var i = 0; i < grid.length; i++) { for (var j = 0; j < grid[0].length; j++) { let x = (j - i) * resourceWidth; let y = ((i + j) * resourceHeight) + (grid[i][j] * -resourceHeight); // the "+" bit is the adjustment for height according to perlin noise values } }

editar post-bounty:

Ver GIF. La respuesta aceptada funciona. El retraso es mi culpa, la pantalla no se actualiza en mousemove (todavía) y la velocidad de cuadro es baja. Está claramente devolviendo el azulejo correcto.

Source


Aquí está la entrada de la grilla que definiría por el bien de esta discusión. El resultado debe ser un mosaico (coordinate_1, coordinate_2) basado en la visibilidad en la pantalla de usuario del mouse:

Puedo ofrecer dos soluciones desde diferentes perspectivas, pero tendrá que convertir esto nuevamente en su dominio problemático. La primera metodología se basa en fichas de colores y puede ser más útil si el mapa cambia dinámicamente. La segunda solución se basa en dibujar recuadros delimitadores de coordenadas basados ​​en el hecho de que los mosaicos más cercanos al observador como (0, 0) nunca pueden ser ocluidos por mosaicos detrás de él (1,1).

Enfoque 1: azulejos de colores transparentes

El primer enfoque se basa en el dibujo y se desarrolla here . Debo darle el crédito a @haldagan por una solución particularmente hermosa. En resumen, se basa en dibujar una capa perfectamente opaca sobre el lienzo original y colorear cada azulejo con un color diferente. Esta capa superior debe estar sujeta a las mismas transformaciones de altura que la capa subyacente. Cuando el mouse se desplaza sobre una capa en particular, puedes detectar el color a través del lienzo y, por lo tanto, del mosaico mismo. Esta es la solución con la que probablemente iría y esto parece ser un problema no tan raro en la visualización de computadoras y los gráficos (encontrar posiciones en un mundo isométrico en 3D).

Enfoque 2: Encontrar el mosaico delimitador

Esto se basa en la conjetura de que la fila "frontal" nunca puede ser ocluida por filas "traseras" detrás de ella. Además, las fichas "más cerca de la pantalla" no pueden ocluirse con mosaicos "más alejados de la pantalla". Para precisar el significado de "frente", "atrás", "más cerca de la pantalla" y "más lejos de la pantalla", observe lo siguiente:

.

Basado en este principio, el enfoque es construir un conjunto de polígonos para cada azulejo. Entonces, primero determinamos las coordenadas en el lienzo de solo cuadro (0, 0) después de la escala de altura. Tenga en cuenta que la operación de escala de altura es simplemente un trapecio extendido verticalmente en función de la altura.

Luego determinamos las coordenadas en el lienzo de los cuadros (1, 0), (0, 1), (1, 1) después de la escala de altura (tendríamos que restar algo de esos polígonos que se superponen con el polígono (0, 0) )

Proceda a construir cada recuadro delimitando coordenadas restando oclusiones de polígonos más cerca de la pantalla, para finalmente obtener coordenadas de polígonos para todos los recuadros.

Con estas coordenadas y un poco de cuidado, en última instancia, puede determinar a qué mosaico apunta un estilo de búsqueda binario mediante la superposición de polígonos buscando entre las filas inferiores.


Por favor, no me juzguen ya que no estoy publicando ningún código. Solo sugiero un algoritmo que pueda resolverlo sin un alto uso de memoria.

El algoritmo:

En realidad, para determinar qué mosaico está sobre el mouse, no es necesario que verifiquemos todas las fichas. Al principio, creemos que la superficie es 2D y buscamos qué mosaico recorre el puntero del mouse con la fórmula OP publicada. Este es el probable mouse de mosaico más probable que el cursor puede señalar en esta posición del cursor.

Este mosaico puede recibir un puntero de mouse si está a 0 de altura, al verificar su altura actual podemos verificar si realmente está en la altura para recibir el puntero, lo marcamos y avanzamos.

Luego, encontramos el próximo mosaico probable que está más cerca de la pantalla al incrementar o disminuir los valores de cuadrícula x, y, dependiendo de la posición del cursor.

Luego seguimos avanzando en zigzag hasta llegar a un mosaico que no puede recibir puntero, incluso si está a su altura máxima.

Cuando llegamos a este punto, el último mosaico que se encontró a una altura para recibir el puntero es el mosaico que estamos buscando.

En este caso, solo verificamos 8 mosaicos para determinar qué mosaico está recibiendo puntero actualmente. Esto es muy eficiente desde el punto de vista de la memoria en comparación con la comprobación de todos los mosaicos presentes en la cuadrícula y produce un resultado más rápido.


También importa qué más hay en la pantalla. Las matemáticas intentan funcionar si tus fichas son bastante uniformes. Sin embargo, si muestra varios objetos y desea que el usuario los elija, es mucho más fácil tener un mapa de identificadores del tamaño de un lienzo.

function poly(ctx){var a=arguments;ctx.beginPath();ctx.moveTo(a[1],a[2]); for(var i=3;i<a.length;i+=2)ctx.lineTo(a[i],a[i+1]);ctx.closePath();ctx.fill();ctx.stroke();} function circle(ctx,x,y,r){ctx.beginPath();ctx.arc(x,y,r,0,2*Math.PI);ctx.fill();ctx.stroke();} function Tile(h,c,f){ var cnv=document.createElement("canvas");cnv.width=100;cnv.height=h; var ctx=cnv.getContext("2d");ctx.lineWidth=3;ctx.lineStyle="black"; ctx.fillStyle=c;poly(ctx,2,h-50,50,h-75,98,h-50,50,h-25); poly(ctx,50,h-25,2,h-50,2,h-25,50,h-2); poly(ctx,50,h-25,98,h-50,98,h-25,50,h-2); f(ctx);return ctx.getImageData(0,0,100,h); } function put(x,y,tile,image,id,map){ var iw=image.width,tw=tile.width,th=tile.height,bdat=image.data,fdat=tile.data; for(var i=0;i<tw;i++) for(var j=0;j<th;j++){ var ijtw4=(i+j*tw)*4,a=fdat[ijtw4+3]; if(a!==0){ var xiyjiw=x+i+(y+j)*iw; for(var k=0;k<3;k++)bdat[xiyjiw*4+k]=(bdat[xiyjiw*4+k]*(255-a)+fdat[ijtw4+k]*a)/255; bdat[xiyjiw*4+3]=255; map[xiyjiw]=id; } } } var cleanimage; var pickmap; function startup(){ var water=Tile(77,"blue",function(){}); var field=Tile(77,"lime",function(){}); var tree=Tile(200,"lime",function(ctx){ ctx.fillStyle="brown";poly(ctx,50,50,70,150,30,150); ctx.fillStyle="forestgreen";circle(ctx,60,40,30);circle(ctx,68,70,30);circle(ctx,32,60,30); }); var sheep=Tile(200,"lime",function(ctx){ ctx.fillStyle="white";poly(ctx,25,155,25,100);poly(ctx,75,155,75,100); circle(ctx,50,100,45);circle(ctx,50,80,30); poly(ctx,40,70,35,80);poly(ctx,60,70,65,80); }); var cnv=document.getElementById("scape"); cnv.width=500;cnv.height=400; var ctx=cnv.getContext("2d"); cleanimage=ctx.getImageData(0,0,500,400); pickmap=new Uint8Array(500*400); var tiles=[water,field,tree,sheep]; var map=[[[0,0],[1,1],[1,1],[1,1],[1,1]], [[0,0],[1,1],[1,2],[3,2],[1,1]], [[0,0],[1,1],[2,2],[3,2],[1,1]], [[0,0],[1,1],[1,1],[1,1],[1,1]], [[0,0],[0,0],[0,0],[0,0],[0,0]]]; for(var x=0;x<5;x++) for(var y=0;y<5;y++){ var desc=map[y][x],tile=tiles[desc[0]]; put(200+x*50-y*50,200+x*25+y*25-tile.height-desc[1]*20, tile,cleanimage,x+1+(y+1)*10,pickmap); } ctx.putImageData(cleanimage,0,0); } var mx,my,pick; function mmove(event){ mx=Math.round(event.offsetX); my=Math.round(event.offsetY); if(mx>=0 && my>=0 && mx<cleanimage.width && my<cleanimage.height && pick!==pickmap[mx+my*cleanimage.width]) requestAnimationFrame(redraw); } function redraw(){ pick=pickmap[mx+my*cleanimage.width]; document.getElementById("pick").innerHTML=pick; var ctx=document.getElementById("scape").getContext("2d"); ctx.putImageData(cleanimage,0,0); if(pick!==0){ var temp=ctx.getImageData(0,0,cleanimage.width,cleanimage.height); for(var i=0;i<pickmap.length;i++) if(pickmap[i]===pick) temp.data[i*4]=255; ctx.putImageData(temp,0,0); } } startup(); // in place of body.onload

<div id="pick">Move around</div> <canvas id="scape" onmousemove="mmove(event)"></canvas>

Aquí el "id" es un simple x+1+(y+1)*10 (por lo que es agradable cuando se muestra) y se ajusta en un byte (Uint8Array), que podría ir hasta 15x15 cuadrícula de visualización ya, y hay más amplio tipos disponibles también.

(Intenté dibujarlo pequeño, y se veía bien en la pantalla del editor de fragmentos, pero aparentemente todavía es demasiado grande aquí)


Tarea interesante.

Tratemos de simplificarlo, resolvamos este caso concreto

Solución

La versión de trabajo está aquí: https://github.com/amuzalevskiy/perlin-landscape (cambia https://github.com/jorgt/perlin-landscape/pull/1 )

Explicación

Primero, lo que vino a la mente es:

Solo dos pasos:

  • encuentra una columna vertical, que coincide con un conjunto de fichas
  • iterar los mosaicos en el conjunto de abajo hacia arriba, verificando si el cursor se coloca por debajo de la línea superior

Paso 1

Necesitamos dos funciones aquí:

Detecta la columna:

function getColumn(mouseX, firstTileXShiftAtScreen, columnWidth) { return (mouseX - firstTileXShiftAtScreen) / columnWidth; }

Función que extrae una matriz de mosaicos que corresponden a esta columna.

Gire la imagen 45 grados en mente. Los números rojos son columnNo. 3 columna está resaltada. Eje X es horizontal

function tileExists(x, y, width, height) { return x >= 0 & y >= 0 & x < width & y < height; } function getTilesInColumn(columnNo, width, height) { let startTileX = 0, startTileY = 0; let xShift = true; for (let i = 0; i < columnNo; i++) { if (tileExists(startTileX + 1, startTileY, width, height)) { startTileX++; } else { if (xShift) { xShift = false; } else { startTileY++; } } } let tilesInColumn = []; while(tileExists(startTileX, startTileY, width, height)) { tilesInColumn.push({x: startTileX, y: startTileY, isLeft: xShift}); if (xShift) { startTileX--; } else { startTileY++; } xShift = !xShift; } return tilesInColumn; }

Paso 2

Una lista de fichas para verificar está lista. Ahora, para cada azulejo, necesitamos encontrar una línea superior. También tenemos dos tipos de fichas: izquierda y derecha. Ya hemos almacenado esta información durante la construcción de juegos de fichas coincidentes.

function getTileYIncrementByTileZ(tileZ) { // implement here return 0; } function findExactTile(mouseX, mouseY, tilesInColumn, tiles2d, firstTileXShiftAtScreen, firstTileYShiftAtScreenAt0Height, tileWidth, tileHeight) { // we built a set of tiles where bottom ones come first // iterate tiles from bottom to top for(var i = 0; i < tilesInColumn; i++) { let tileInfo = tilesInColumn[i]; let lineAB = findABForTopLineOfTile(tileInfo.x, tileInfo.y, tiles2d[tileInfo.x][tileInfo.y], tileInfo.isLeft, tileWidth, tileHeight); if ((mouseY - firstTileYShiftAtScreenAt0Height) > (mouseX - firstTileXShiftAtScreen)*lineAB.a + lineAB.b) { // WOHOO !!! return tileInfo; } } } function findABForTopLineOfTile(tileX, tileY, tileZ, isLeftTopLine, tileWidth, tileHeight) { // find a top line ~~~ a,b // y = a * x + b; let a = tileWidth / tileHeight; if (isLeftTopLine) { a = -a; } let b = isLeftTopLine ? tileY * 2 * tileHeight : - (tileX + 1) * 2 * tileHeight; b -= getTileYIncrementByTileZ(tileZ); return {a: a, b: b}; }


Tuve la misma situación en un juego. primero probé con las matemáticas, pero cuando descubrí que los clientes quieren cambiar el tipo de mapa todos los días, cambié la solución con una solución gráfica y se la pasé al diseñador del equipo. Capturé la posición del mouse al escuchar el clic de los elementos SVG.

el gráfico principal utilizado directamente para capturar y traducir la posición del mouse al píxel requerido.

https://blog.lavrton.com/hit-region-detection-for-html5-canvas-and-how-to-listen-to-click-events-on-canvas-shapes-815034d7e9f8 https://code.sololearn.com/Wq2bwzSxSnjl/#html


Una forma de resolver esto sería seguir el rayo que va desde el píxel cliqueado en la pantalla al mapa. Para eso, simplemente determine la posición de la cámara en relación con el mapa y la dirección que está mirando:

const camPos = {x: -5, y: -5, z: -5} const camDirection = { x: 1, y:1, z:1}

El siguiente paso es obtener la Posición táctil en el mundo 3D. En esta cierta perspectiva, es bastante simple:

const touchPos = { x: camPos.x + touch.x / Math.sqrt(2), y: camPos.y - touch.x / Math.sqrt(2), z: camPos.z - touch.y / Math.sqrt(2) };

Ahora solo necesitas seguir el rayo en la capa (escala las direcciones para que sean más pequeñas que una de las dimensiones de tus mosaicos):

for(let delta = 0; delta < 100; delta++){ const x = touchPos.x + camDirection.x * delta; const y = touchPos.y + camDirection.y * delta; const z = touchPos.z + camDirection.z * delta;

Ahora solo toma la ficha en xz y comprueba si y es más pequeña que su altura;

const absX = ~~( x / 24 ); const absZ = ~~( z / 24 ); if(tiles[absX][absZ].height >= y){ // hanfle the over event }