BuscarEntrar al ChatboxPortal del foroÍndiceSpellsRegistrarseConectarseGrupos de Usuarios
Eventos Actuales
No hay eventos actualmente
¡ Bienvenido Invitado!

Editar Perfil

Tus temas
Tus Mensajes ()

Enlaces rápidos








Comparte | .
 

 Funciones Avanzadas y Matemática Aplicada ( Autor: WonderWoman)

Ver el tema anterior Ver el tema siguiente Ir abajo 
AutorMensaje
Sapphiron

avatar


Barra de Salud : Este usuario es invulnerable ¿Será que es fiel a las normas?

Mensajes Mensajes : 1430
Reputación Reputación : 208
Monedas de oro : 12058
Monedas de Platino : 0

Inventario :
Frostmourne Escudo lunar Armadura Lunar Avanzada
Diamante Encantado Anillo Sagrado Libro de Leyendas

Mensaje(#) Tema: Funciones Avanzadas y Matemática Aplicada ( Autor: WonderWoman) Lun Jul 25, 2016 9:37 pm

Matemática Aplicada al World Editor
por WonderWoman

Muchos la odian, aunque la verdad que no entiendo como siendo "programadores" o "semiprogramadores" no le encuentren su atractivo.
Los motivos por los que hago este tutorial son: Nunca hice un tutorial en este foro, y muchos usuarios no saben ciertas cosas porque no terminaron la escuela o no les gusta la matemática y la intentan evadir.
Antes de empezar:
Si no saben de matemáticas continúen leyendo
Si saben poco lean este tutorial de blur16 primero:
http://www.worldedit.net/t2802-aplicacion-matematicas-en-el-editor-mapa-tutorial-gui
Si no saben nada de matemáticas abandonen la pagina jajajaja, no, lean si quieren pueden usar el mapa y serles utiles ciertas cosas pero no van a entender un carajo.
También no recomiendo este tutorial si son demasiado novatos aun con el editor.
Y recuerden, al final esta el mapa para descargar...

Entre las cosas mas comunes a la hora de comparar y medir son los ángulos.
De que puede servir esto? Infinidad de cosas, supongan que quieren detectar desde que angulo una unidad golpeo a otra para reducir el daño o aplicar bonus de daño como las habilidades de rikimaru o bristleback en dota.
Como ejemplo explicare mi habilidad Escudo Cargado del mapa de pruebas....

1) Comparación de Ángulos
La habilidad es un escudo de rayos activable hecho sobre el spell base inmolación. Ademas de causar un daño mínimo por segundo a las unidades cercanas (que es el efecto de inmolación) hace que cualquier unidad cercana mele o rango que ataque al espectro sobre el escudo sufra una descarga eléctrica adicional.
Aqui una imagen de la habilidad funcionando:

Como lograremos que se detecte que una unidad esta golpeando al espectro del lado del escudo? pues sera mediante una comparación de ángulos...
Como se ve en la imagen el circulo azul indica la posición del espectro que recibirá el golpe, la flecha azul indica la dirección hacia donde mira, la linea verde indica donde se encuentra el escudo (aproximadamente 45º a la derecha de la dirección donde mira el espectro), y el circulo rojo indica la posición de la unidad agresora.
La sección naranja muestra el área de efecto de la habilidad, los atacantes sobre esa zona serán afectados y recibirán un rayo de contra.
El primer paso sera filtrar con una condicion si la unidad es apta para ser afectada por un rayo de contra (si es de tierra, si no es inmune, etc...), Luego con otra condicion interna se filtraran las unidades que se encuentren dentro del rango permitido (dentro del circulo naranja), Luego la ultima condición determinara si esta entre los ángulos permitidos.
Ahora a los detonadores... Advierto que si bien esta todo en un detonador GUI tiene bastante uso de jass mediante custom scripts, de todos modos la mayoría del jass usado pertenece a funciones matemáticas disponibles en GUI, la idea es que los usuarios que saben GUI pero no saben jass ni quieren aprenderlo vean como es en realidad el mismo lenguaje y se puede combinar y si bien en esta habilidad no son necesarias las constantes locales para hacerla MUI sin arrays, ya que no tiene tiempos de espera, voy a introducirlas de todos modos y mostrar como se declaran y usan.

Defino las variables locales reales: P1X, P1Y, P2X, P2Y, DeltaX, DeltaY, Modulo, Modulo2, Angulo1.
La forma de definirlas es en un custom script poner esto:
  local [tipo] [nombre]
Por ejemplo:
  Custom script:   local real P1X
Like a Star @ heaven Univar1 es la unidad que ataca
Like a Star @ heaven Univar2 es la unidad atacada (el espectro)


Para cambiar el contenido de una variable es igual que en GUI:
  set [variable] = [valor]
Por ejemplo:
  Custom script:   set P1X = GetLocationX(udg_P1)
Observen que use la funcion GetLocationX([punto])   que en GUI es   X of ([punto])
Like a Star @ heaven En P1X,P1Y guardo las coordenadas de la unidad agresora
Like a Star @ heaven En P2X,P2Y guardo las coordenadas del espectro
Like a Star @ heaven En DeltaX y en DeltaY la diferencia entre estos puntos.


La idea ahora es ver si la unidad esta en el rango efectivo de la habilidad, para eso tengo que ver si la distancia a la que se encuentra es menor a una constante y asi sabre si esta dentro del circulo. En un principio la distancia viene dada por el modulo del par (DeltaX,DeltaY) osea:
|(DeltaX,DeltaY)| = (DeltaX2+DeltaY2)^0.5
Por una simple regla que dice:
si a2 < b2 entonces |a| < |b|
Ya que a y b son distancias, siempre son positivos y las barra de modulo se pueden quitar
si a2 < b2 entonces a < b
De este modo se ve que en lugar de comparar contra la distancia se lo puede hacer contra la distancia al cuadrado y ahorrarse el calculo de una raiz cuadrada que ocupa mas tiempo que las sumas o las multiplicaciones. Este detalle es muyyyy técnico y si bien aca es absurdo y podría usar el modulo en lugar del modulo cuadrado sin notar nunca la diferencia sepan que internamente casi cualquier programa que compare distancias lo hará por módulos cuadrados y no por módulos simples, si luego necesita el modulo sera después del filtro condicional como lo haré yo y no sera un proceso extra.

Like a Star @ heaven En Modulo2 guardo el modulo cuadrado de (DeltaX,DeltaY)
Pow es la funcion de potencia, ahi se ve que elevo al cuadrado porque el exponente es 2. Vuelvo a aclarar todo esto se puede pasar a GUI totalmente ya que no usa vJASS ni se usarlo, si van viendo mis detonadores y quieren pasarlo todo a GUI lo haran sin problema.
Observen que use la funcion GetUnitFacing([unidad])    que en GUI es   Facing of ([unidad])

Si ven en la comparacion del if no estoy pidiendo que el modulo cuadrado sea menor que 200 (la constante de distancia) sino que le pido que sea menor a 40000 (la constante de distancia al cuadrado) ojo con eso.
Si se entro al primer if necesito usar el modulo y el angulo en el que esta el escudo asi que los calculo.
Like a Star @ heaven En Modulo guardo el modulo de (DeltaX,DeltaY)
Like a Star @ heaven En Angulo1 guardo el angulo hacia donde apunta el escudo
Yo tome DeltaX = P2X - P1X, lo mismo con DeltaY... eso quiere decir que tome (el coseno del angulo desde la unidad atacante hasta el espectro)*Modulo y (el seno del angulo desde la unidad atacante hasta el espectro)*Modulo, entonces no debo usar el angulo del escudo sino el angulo opuesto del escudo, esto es para que vean la relación si usaba P1X - P2X y P1Y - P2Y tenia que tomar el angulo del escudo pero como tome el angulo al revés también invierto el otro angulo que equivale a sumarle o restarle 180.

Ahora la parte mas compleja si entienden este if ya tienen cocinada casi cualquier otra cosa de este tutorial.
El vector del escudo marrón
El vector de la unidad agresora rojo oscuro
El segmento verde indica la distancia que se comparara contra una constante para determinar la diferencia de ángulos
Como se ve en la gráfica usare trigonometria para determinar si el angulo esta en el rango de valores.
Como dije recién:
DeltaX = Cos(angulodesde{(P1X,P1Y) hasta (P2X,P2Y)}) * |( (P2X-P2Y) , (P2X-P2Y) )|
DeltaY = Sin(angulodesde{(P1X,P1Y) hasta (P2X,P2Y)}) * |( (P2X-P2Y) , (P2X-P2Y) )|
Vamos a suponer que estoy usando angulo del escudo y angulo desde unidad atacada hasta unidad atacante ya que es lo mismo matemáticamente.
Entonces ya que Modulo = |( (P2X-P2Y) , (P2X-P2Y) )|:
DeltaX/Modulo = Cos(angulodesde{(P1X,P1Y) hasta (P2X,P2Y)})
DeltaY/Modulo = Cos(angulodesde{(P1X,P1Y) hasta (P2X,P2Y)})
En la imagen se ve el coseno y el seno del ángulo del escudo que son:
Cos(Angulo1) y Sin(Angulo1)
Ahora traslado el problema de hacer diferencia entre ángulos a un problema de calculo de distancia que si entendieron todo lo anterior no necesitare explicarlo con detalle.
Si me paro en el borde de la circunferencia unitaria y limito la distancia es equivalente a dibujar un circulo con centro en el punto sobre el borde que me restringe un grupo limitado de ángulos para otro posible punto sobre el borde. Pero como calculo cuanto vale tal angulo? si solo tengo una distancia.
Bien para eso vamos a suponer que queremos que nuestro escudo bloquee en un radio de 90º (es decir los ataques provenientes desde (frente  del espectro + 45º - 45º) hasta (frente  del espectro + 45º + 45º).
Suponemos que el escudo esta apuntando hacia los 0º, el seno a 45º es 0.70 y a -45º es -0,70, el coseno es una función par y vale 0.70 para ambos ángulos.
Ya que es simétrico el problema solo usaremos el seno y coseno de 45º, y el seno y coseno de 0º que valen 0 y 1 respectivamente.
[(cos(45)-cos(0))2 + (sin(45)-sin(0))2]0.5 = 0.765367 = Cte
Por lo tanto deberíamos ver si la distancia entre los puntos en azul y rojo es menor o igual que esa constante.
En lugar de normalizar todos los vectores para obtener vectores unitarios vamos a dejar que opere con números grandes y en lugar de dividir DeltaX y DeltaY entre el modulo, vamos a multiplicar por el modulo tanto a la constante obtenida (0,765367) como a Sin(Angulo1) y a Cos(Angulo1).
Ademas para no calcular una raíz compararemos distancias cuadradas teniendo en cuenta que ya que vamos a comparar con módulos cuadrados
(Cte*Modulo)2 = Cte2*Modulo2
Siendo K la nueva constante = Cte2 = 0.585787
La condición queda ASÍ. (yo use otro angulo menor para el escudo del ejemplo por eso mi K difiere):


Luego de esto se crea un dummy que conjura el rayo, y el resto es eliminación de leaks de punto y cierres de ifs.
[gui]Escudo Cargado
   Acontecimientos
       Unidad - A unit Es atacado
   Condiciones
       ((Attacked unit) has buff Escudo Cargado ) Igual a True
   Acciones
       Custom script:   local real P1X
       Custom script:   local real P1Y
       Custom script:   local real P2X
       Custom script:   local real P2Y
       Custom script:   local real DeltaX
       Custom script:   local real DeltaY
       Custom script:   local real Modulo
       Custom script:   local real Modulo2
       Custom script:   local real Angulo1
       Set UniVar1 = (Attacking unit)
       Set UniVar2 = (Attacked unit)
       If (All Conditions are True) then do (Then Actions) else do (Else Actions)
           Si: Condiciones
               (UniVar1 is Una unidad voladora) Igual a False
               (UniVar1 is Mecánica) Igual a False
               (UniVar1 is Inmune a la magia) Igual a False
               (UniVar1 has buff Invulnerable) Igual a False
           Entonces: Acciones
               Set P1 = (Position of UniVar1)
               Custom script:   set P1X = GetLocationX(udg_P1)
               Custom script:   set P1Y = GetLocationY(udg_P1)
               Custom script:   call RemoveLocation (udg_P1)
               Set P1 = (Position of UniVar2)
               Custom script:   set P2X = GetLocationX(udg_P1)
               Custom script:   set P2Y = GetLocationY(udg_P1)
               Custom script:   set DeltaX = P2X - P1X
               Custom script:   set DeltaY = P2Y - P1Y
               Custom script:   set Modulo2 = Pow((DeltaX), 2.00) + Pow((DeltaY), 2.00)
               -------- IF 1 --------
               Custom script:   if ((Modulo2) <= 40000.00) then
               Custom script:   set Modulo = SquareRoot(Modulo2)
               Custom script:   set Angulo1 = ( GetUnitFacing(udg_UniVar2) + 135 )
               -------- IF 2 --------
               Custom script:   if ((Pow((DeltaX-(CosBJ(Angulo1)*Modulo)), 2.00) + Pow((DeltaY-(SinBJ(Angulo1)*Modulo)), 2.00)) <= (0.37*Modulo2)) then
               Unidad - Create 1 DUM for (Owner of UniVar2) at P1 facing ((Facing of UniVar2) - 45.00) degrees
               Unidad - Add a 1.00 second Genérico expiration timer to (Last created unit)
               Unidad - Order (Last created unit) to Bruja de mar naga neutral - Rayo cónico UniVar1
               Custom script:   endif
               -------- END IF 2 --------
               Custom script:   endif
               -------- END IF 1 --------
               Custom script:   call RemoveLocation (udg_P1)
           Otros: Acciones
[/gui]

Seguro que estarán viendo esto mientras se preguntan por que????? tan complejo es?
2 respuestas si y no. Si en realidad es así, le saque casi toda la abstracción que oculta la matemática y se aproxima mas al proceso real. No porque también se podría obviar el uso de locales haciendo mas amigable el código y se podrían usar funciones para determinar ángulos según puntos, pero esto trae otra complicación que es el problema del paso de 360º a 0º y es muy sucio y poco eficiente.
Para el que lea esto y sepa jass se dara cuenta que soy un asco en el lenguaje, no se un carajo, no uso newgen y hay funciones que se pueden reemplazar por nativas, al final dejo una invitación para quien quiera traducirlo a vJASS, ya que no es mucho trabajo nada de calculos.



2) Punto de Intersección Recta-Circulo
Esto no es de uso muy frecuente pero si algún día hacen un mapa con una zona circular de juego como el arena que estoy haciendo lo van a usar muchísimo.
Es mucho mas amigable en código que el anterior, no porque en realidad lo deba ser sino porque ya seria muy complejo explicar en un tutorial tantos pasos con relaciones trigonométricas así que el ejemplo esta completamente en GUI y es un detonador corto.
Detectar el punto de corte de una recta con un circulo puede servir en un principio para delimitar donde tiene que conjurar un dummy por ejemplo si el punto especificado sale del circulo, en el mapa de ejemplo hay un espectro rodeado de barriles que no lo dejan pasar, un halcón revela todo el tiempo donde esta el punto de corte entre el circulo de barriles y la dirección hacia la que mira el espectro.

Primero lo primero, todos saben que para mover una unidad a un punto se puede usar en gui la siguiente función: Unidad - Move [unidad] instantly to [punto]
Pero también se habrán dado cuenta que esta función detiene a la unidad trasladada y anula sus ordenes, sin embargo en muchos mapas las unidades pueden ser desplazadas sin que sus ordenes se corten. Los que jueguen dota lo habrán notado en el campo kinetico del trall, el vacío del dark seer etc...
Esto se logra mediante estas funciones que determinan las coordenadas a donde se quiere ubicar a la unidad:
SetUnitX([unidad], [real])
SetUnitY([unidad], [real])
Como primer paso las primeras lineas detectan si el espectro salio del circulo y si es asi lo vuelven hacia adentro, repito los que jueguen dota notaran que se comporta muy similar al campo kinetico del trall.

Ahora paso a explicar como sabremos donde esta este punto de corte y cual elegiremos ya que toda recta con un punto INTERIOR estrictamente en un circulo lo corta a este en 2 puntos.
Bien, empiezo con la idea y los detonadores serán una traducción de la misma al usar un nivel de abstracción mas alto que el spell anterior.

Hemos tomado como el origen el centro de la circunferencia (punto negro)
P1 Es la el punto donde se encuentra la unidad y se indica tambien la dirección hacia donde mira, teniendo un punto y una dirección tenemos una recta, que es la recta1.
Como primer paso se calcula la medida del angulo de P1 con respecto al sistema de referecia al que llamaremos Angulo y esta marcado claramente en la gráfica.
Luego llamamos Direccion al angulo correspondiente a la dirección de recta1.
En el caso particular del gráfico de la recta Angulo vale aproximadamente 60º y Direccion vale 315º o -45º. (Estos valores no serán usados en ningún momento solo son para ejemplificar).
Ahora procederemos a hacer una traslación angular de P1, "lo moveremos" - Direccion grados en contra de las agujas del reloj sin cambiar su distancia al origen. A este nuevo punto cuyo angulo es (Angulo - Direccion) lo llamaremos P2, dejando en realidad a P1 tal y como estaba al principio.
Cual es la gracia de hacer esto?? bueno como se puede ver la nueva recta recta2 esta rotada - Direccion grados respecto a recta1 por lo tanto su direccion es 0. En fin lo que hice fue cambiar mi sistema de referencia, esto es equivalente a haber rotado toda la grafica dejando a P1 sobre P2. Ahora se vera como trabajar con la recta recta2 cuya pendiente es 0 y su ecuacion es  Y=K (siendo K constante) facilita ampliamente las cosas, eso si hay que recordar "anti-transformar" el sistema una vez que se obtenga el punto de corte para recta2 con la circunferencia.
Ahora observen esta gráfica para comprender como se obtiene ese punto de corte que llamaremos P3
El triangulo rojo es la respuesta, nosotros conocemos 2 vertices de este triangulo y el restante es P3. El primer vértice es el punto en el origen (0,0), el segundo vértice que esta en el angulo recto tiene coordenada X = 0 como se ve. La coordenada en Y tambien es conocida, es la coordenada en Y de P2 porque gracias a la traslacion y rotacion estan a la misma altura quedando este vertice en (0, Y de (P2)). Aprovechando esto tambien se sabe la coordenada en Y del ultimo vertice que es el punto P3, como se ve tiene la misma Y del vertice anterior y de P2. Para calcular su coordenada en X usaremos trigonometria básica:
El teorema de pitagoras dice que en un triangulo rectángulo (como el de la figura) siendo A y B los lados menores y C la hipotenusa: A2 + B2 = C2
Despejando uno de los lados menones:
A2 = C2 - B2
A = (C2 - B2 )0.5
----------------------------------------------------------------
Esto es lo que usaremos, la hipotenusa al cuadrado la tenemos, es una constante y es el radio del circulo al cuadrado, la llamamos H2 por ahora.
El lado al cuadrado que esta restando dentro de la raiz corresponde al lado vertical en nuestra grafica y su medida es la distancia entre los vertices, ya que uno de ellos es el origen solo necesito el modulo del vertice restante que como su coordenada en X es 0 equivale a su coordenada en Y que como dijimos es Y de (P2).
Por el mismo motivo ya teniendo A, se sabe la coordenada en X de P3.
P3 = ((H22 - Y de (P2)2 )0.5, Y de (P2)).
Por ultimo, volvemos rotar el sistema pero ahora + Direccion grados, si AnguloP3 es el angulo de P3 entonces sea P4 el punto con distancia al origen igual al radio e igual a la distancia al origen de P3 con dirección (AnguloP3 + Direccion). Este punto P4 es el punto de corte que buscabamos.

La implementacion de esto en GUI es casi transparente ya que como dije voy a usar un nivel alto de abstracción y no me voy a poner a hacer matrices de rotación o cosas así porque ya seria una clase de álgebra 2 o no se que jajajaja.
Como únicos detalle fíjense que el punto de referencia no es (0,0) como en la explicación sino que es el centro del circulo indicado por el punto _CentroCirculo, y que angulo2 no esta declarado en cambio uso la variable Angulo para los 2 valores ya que al primer angulo no lo necesito mas después de usarlo.
Si ven que falta algo es porque esto tiene una inicializacion que carga _CentroCirculo, _Espectro, _Pajaro, hace el circulo con barriles y ademas tiene un loop que lo dispara por eso la falta de eventos.
[gui]Calculo de Punto
   Acontecimientos
   Condiciones
   Acciones
       -------- Limite de movimiento --------
       Set P5 = (Position of _Espectro)
       If (All Conditions are True) then do (Then Actions) else do (Else Actions)
           Si: Condiciones
               (Distance between P5 and _CentroCirculo) Mayor que 500.00
           Entonces: Acciones
               Set P1 = (_CentroCirculo offset by 500.00 towards (Angle from _CentroCirculo to P5) degrees)
               Custom script:   call SetUnitX(udg__Espectro, GetLocationX(udg_P1))
               Custom script:   call SetUnitY(udg__Espectro, GetLocationY(udg_P1))
           Otros: Acciones
               Set P1 = (Position of _Espectro)
       Custom script:   call RemoveLocation (udg_P5)
       -------- Calculo del punto de corte --------
       Set Angulo = (Angle from _CentroCirculo to P1)
       Set Direccion = (Facing of _Espectro)
       Set Distancia = (Distance between _CentroCirculo and P1)
       Set P2 = (_CentroCirculo offset by Distancia towards (Angulo - Direccion) degrees)
       Set P3 = (_CentroCirculo offset by ((Square root((250000.00 - (Power(((Y of P2) - (Y of _CentroCirculo)), 2.00))))), ((Y of P2) - (Y of _CentroCirculo))))
       Set Angulo = (Angle from _CentroCirculo to P3)
       Set P4 = (_CentroCirculo offset by 550.00 towards (Angulo + Direccion) degrees)
       Custom script:   call SetUnitX(udg__Pajaro, GetLocationX(udg_P4))
       Custom script:   call SetUnitY(udg__Pajaro, GetLocationY(udg_P4))
       Custom script:   call RemoveLocation (udg_P1)
       Custom script:   call RemoveLocation (udg_P2)
       Custom script:   call RemoveLocation (udg_P3)
       Custom script:   call RemoveLocation (udg_P4)
[/gui]




3) Limitación por Rectas
Sabemos que en GUI es posible detectar grupos de unidades que se encuentren en areas circulares o zonas rectangulares con sus lados paralelos a los ejes x/y del sistema de coordenadas del mapa.
Sin embargo a la hora de formar grupos que se encuentren en regiones de otra forma las funciones de las que disponemos quedan muy limitadas, en el primer punto se vio como verificar que una unidad este en una seccion circular de cualquier amplittud y cualquier angulo. Ahora voy a presentar otro problema igual de comun, el cual he visto que muchos solucionan de una manera muy ineficiente y poco practica sacrificando el rendimiento del mapa por el funcionamiento de un spell.

En una de las zonas de prueba del mapa hay un Señor del Fuego del cual salen 2 rayos azules que rotan barriendo un área circular. También hay varios espectros del jugador rojo, cuando el rayo pasa por sobre uno de ellos un efecto en rojo se marca sobre el espectro como si lo estuvieran dañando.

Como podrán imaginar en un detonador periódico tengo que determinar que unidades se encuentran en una región rectangular alargada que seria el área de impacto del rayo. Por un motivo de simplificar esto tomare las unidades en regiones de esta forma que es casi lo mismo y mejor aun para esta habilidad en particular.


Pero antes les voy a decir que es lo que he visto que algunos hacen para determinar unidades sobre lineas rectas con dirección cualquiera.
El siguiente algoritmo no esta en ningún lenguaje solo es demostrativo:
Código:
(K1, K2 constantes; x real; P punto; G1; G2 grupo)
Loop exterior
   Loop x desde 1 hasta longitud/K1  
      P = vertice1 de la linea + x*K1 (con direccion de la recta)
      G1 = Unidades en radio K2 de P que no este en G2
      Para cada unidad de G1
         (HACER LO QUE SEA)
      Fin del para cada unidad
      Añadir las unidades de G1 a G2
   Fin del Loop
........
Bueno para los que no se dieron cuenta son varios bucles anidados contando el del grupo de unidad, la región obtenida no tiene la forma optima sino mas bien es algo así:

Y quita muchísimo rendimiento la creaciones de miles de grupos por segundo 3 niveles de bucle, puntos comparaciones de bucle al determinar si una unidad esta en otro grupo etc.

Bien la manera mucho mas eficiente de hacer esto sin necesidad de tanta vuelta es verificar  para cada unidad en un grupo circular si esta esta por debajo de una recta y por encima de otra. Acá un gráfico mostrando la idea, las unidades en la zona blanca quedan por afuera del grupo en un principio, luego se filtran las unidades que estén en la zona celeste, y solo quedan las unidades en la zona verde.

Claro que las unidades en en circulo son las unidades con radio R, esto es una sola linea de GUI, la gracia es crear las rectas limitadoras.
Like a Star @ heaven _AnguloDeGiro es el angulo de dirección del segmento
Like a Star @ heaven _CentroReloj (Constante)es el punto central del circulo y el punto medio de los vértices.
Like a Star @ heaven _XRel (Constante) es la coordenada X de _CentroDeReloj
Like a Star @ heaven _YRel (Constante) es la coordenada Y de _CentroDeReloj
Como se ve en el código el angulo va cambiando cada vez que se ejecuta el detonador.
Like a Star @ heaven En P1 guardo el vértice con dirección _AnguloDeGiro
Like a Star @ heaven En P2 guardo el vértice con dirección opuesta
Like a Star @ heaven DX (Global)
Like a Star @ heaven DY (Global)
E inicializo DX y DY de manera tal que (DX,DY) = P1-P2


Luego creo el grupo de las unidades en un radio = 500 de _CentroDeReloj
Y hago la iteracion para cada unidad del grupo:
Defino las variables locales reales: K, F
Like a Star @ heaven En P3 guardo la posicion de la unidad tomada
Like a Star @ heaven En XP guardo la distancia en X de P3 a _CentroDeReloj
Like a Star @ heaven En YP guardo la distancia en Y de P3 a _CentroDeReloj
Observese que para calcular esta distancias:
Set XP = ((X of P3) - _XRel)
Set YP = ((Y of P3) - _YRel)
Esto se debe a que estoy tomando _CentroDeReloj como el origen para facilitar cálculos.

El segmento rojo indica el rayo que tiene vertices P1 y P2.
Las rectas en negro son las que necesito calcular para que me limiten el area permitida.
Como se ve estas rectas son paralelas a la recta roja y tienen un desplazamiento en Y similar pero de signo opuesto (siempre tomando como referencia virtual al punto _CentroDeReloj)
Que haremos entonces?
Bien la ecuacion de una recta en funcion de x es:
y = m*x + b  (con m y b constantes)
Centremosnos en m por ahora. m es lo que se dice "pendiente de la recta" e indica cuanto desplazamiento en Y corresponde a un desplazamiento en X. He aqui el primer problema... Que pasa si la recta esta justo sobre Y? la pendiente tiende a infinito, es mas no tiene que ser exactamente sobre Y, con que este lo suficientemente proxima puede causar un desbordamiento en el calculo si P3 esta alejado del origen en X.
Solucionamos esto dividiendo el problema en dos partes.

Que es esa cosa? no es el simbolito de los dummys de los crash test, esas gráficas indican como vamos a dividir el problema de manera tal que la pendiente es siempre menor o igual que 1  (m <= 1). Para eso tenemos que detectar si una unidad esta en una zona u otra.
Cuando m = 1 la recta tiene un angulo de 45º, y cuando m = -1 la recta tiene un angulo de -45º. Asi que esta sera la manera de determinar si esta en la primera zona, de lo contrario estará en la segunda (el área en rojo es la zona tomada).
Alguno se habrá avivado y dirá: Pero Wonder acaso no podías resolver el problema del caso 1 usando esto? osea ya que se esta limitando una sección. Bien la respuesta es SI, pero claro que no seria tan simple como limitar la sección de la imagen que tiene angulo 0 y una amplitud de 90º, aparte hay que decidir cual de las dos secciones opuestas considerar... en resumen, si pero si lo expandimos para resolver el primer caso es la misma dificultad.

Vease que:
m = DY/DX
Pero no haremos este cociente para ver si |m| es mayor o menor que 1 porque estariamos en la misma, solo compararemos si |DY| < |DX| que es lo mismo sin riesgo de división por 0.
Si la condición es verdadera analizaremos las cosas en función de x, si es falsa sera en función de y, de ese modo la pendiente siempre es menor o igual que 1.
Primero haré el análisis para el primer caso ya que función de x es lo mas común, y la otra es casi igual.
Volvamos a ver la gráfica con la recta roja y las otras 2 rectas negras...
Recuerdan las variables locales F, K que defini al principio del bucle del grupo de unidad?
Bueno F sera la funcion de la recta roja evaluada en XP, esto nos permite comparar si el punto P3 esta por encima o por debajo de la misma. Si YP > F entonces el punto P3 esta por sobre la recta, sino lo contrario.
Sin embargo la recta roja no es la recta que necesitamos para comparar, sino que necesitamos las rectas negras, pero estas valen (F + K) y (F - K) ya que las tres rectas tienen la misma pendiente. Por lo tanto K es la ordenada al origen.
Para calcular F:
F = m*XP
F = m*(DY/DX)
Para calcular K:
K = |DX|*C (con C constante)
Mientras mas grande es C, mas ancho es el rayo.
Ahora lo unico que queda es verificar que YP sea menor que (F + K) y que YP sea mayor que (F + K).


El ultimo paso, es hacer lo mismo para el caso de que la unidad se encuentre el la segunda zona, pero esto es solo replantear la función de x para transformarla en función de y.
Si ven en el código es el MISMO formato, solo que donde decía "X" dice "Y" y viceversa


Aca el detonador:
[gui]Giro del Reloj
   Acontecimientos
   Condiciones
   Acciones
       Set _AnguloDeGiro = (_AnguloDeGiro + 1.00)
       Custom script:   if ((udg__AnguloDeGiro) >= 360.00) then
       Set _AnguloDeGiro = (_AnguloDeGiro - 360.00)
       Custom script:   endif
       Set P1 = (_CentroReloj offset by 500.00 towards _AnguloDeGiro degrees)
       Set P2 = (_CentroReloj offset by 500.00 towards (_AnguloDeGiro + 180.00) degrees)
       Set DX = ((X of P1) - (X of P2))
       Set DY = ((Y of P1) - (Y of P2))
       Rayo - Move _Rayo to source P1 and target P2
       Set GrupoR = (Units within 500.00 of _CentroReloj matching ((Owner of (Matching unit)) Igual a Jugador 1 (rojo)))
       Grupo de unidad - Pick every unit in GrupoR and do (Actions)
           Bucle: Acciones
               Custom script:   local real K
               Custom script:   local real F
               Set P3 = (Position of (Picked unit))
               Set XP = ((X of P3) - _XRel)
               Set YP = ((Y of P3) - _YRel)
               -------- ----------------------------- --------
               Custom script:   if (RAbsBJ(udg_DX) >= RAbsBJ(udg_DY)) then
               Custom script:   set F = (udg_XP*udg_DY)/udg_DX
               Custom script:   set K = RAbsBJ(udg_DX*0.08)
               Custom script:   if (udg_YP <= (F + K)) then
               Custom script:   if (udg_YP >= (F - K)) then
               Efecto especial - Create a special effect at P3 using Abilities\Spells\Demon\DemonBoltImpact\DemonBoltImpact.mdl
               Efecto especial - Destroy (Last created special effect)
               Custom script:   endif
               Custom script:   endif
               Custom script:   else
               Custom script:   set F = (udg_YP*udg_DX)/udg_DY
               Custom script:   set K = RAbsBJ(udg_DY*0.08)
               Custom script:   if (udg_XP <= (F + K)) then
               Custom script:   if (udg_XP >= (F - K)) then
               Efecto especial - Create a special effect at P3 using Abilities\Spells\Demon\DemonBoltImpact\DemonBoltImpact.mdl
               Efecto especial - Destroy (Last created special effect)
               Custom script:   endif
               Custom script:   endif
               Custom script:   endif
               -------- ----------------------------- --------
               Custom script:   call RemoveLocation (udg_P3)
       Custom script:   call DestroyGroup (udg_GrupoR)
       Custom script:   call RemoveLocation (udg_P1)
       Custom script:   call RemoveLocation (udg_P2)
[/gui]




4) Fisica de Proyectiles y Velocidad Inicial
Probablemente han usado, o algunos han hecho algún sistema de proyectiles...
En el mapa de pruebas puse una versión simplificada de uno de mis sistemas en GUI.

Realmente no explicare como funciona el sistema a pesar de que es simple, sino que me concentrare en el concepto del proyectil y su matemática asociada, pero primero algunas cosas del Warcraft para tener en cuenta:

La imagen corresponde a un corte lateral de un pedazo de terreno en warcraft.
Cada linea representa la altura REAL de una unidad moviéndose sobre dicho terreno. La altura de movimiento para cada unidad se fija en un valor constante e igual.
El area oscura es el terreno.
La linea roja indica la altura a la que se encuentra una unidad voladora sobre el terreno.
La linea azul indica la altura de un dummy con habilidad langosta sobre el terreno.
La linea verde indica la altura ideal.

Como hacemos entonces para que los proyectiles se muevan a una altura predecible y real?
La función GetLocationZ([punto]) devuelve la altura a la que se encuentra un punto del terreno. Si cambiamos la altura de vuelo de una unidad a {100}, solo estaremos cambiando su altura de vuelo relativa al suelo, en cambio si cambiamos la altura de vuelo de esa unidad a {100 - GetLocationZ(Posición de la unidad)} estamos estableciendo la altura absoluta que es lo que nos importa. Esto no solo es una cuestión estética, también sirve para determinar cuando un proyectil ha alcanzado el suelo efectivamente.

Vamos a suponer el caso mas simple en la física de proyectiles, no hay viento, no hay rozamiento.
Sea ZA la altura absoluta del proyectil
El proyectil se dispara a una altura z1 (altura absoluta) con un angulo de direccion AD,
Ya que no hay rozamiento la velocidad plana (en el plano XY) es constante porque ninguna fuerza la afecta, sin embargo en el eje Z la fuerza de gravedad acelera al proyectil hacia abajo. Cuando ZA = 0 + AlturadelTerreno(Posición del proyectil), se considera que el proyectil alcanzo el suelo.
Ya que el en plano XY la velocidad es constante usaremos un sistema de 2 ejes solamente por lo que analizaremos todo desde el corte de terreno que expuse al principio.

En conclusión UN proyectil afectado por gravedad necesita minimamente:
Arrow Un dummy
Arrow Una variable de altura
Arrow Una variable de velocidad vertical
Arrow (Una variable de velocidad horizontal y una de dirección) o (Dos variables de velocidad horizontal una para X y otra para Y)
En un loop se moverá el proyectil lo que indique la velocidad horizontal (que es constante), y su altura cambiara a lo que indique la variable de altura - la altura en la posición donde esta. Tener en cuenta que a la variable de altura se le suma la de velocidad vertical cada vez que se entra al bucle, y a su vez a la variable de velocidad vertical se le resta la gravedad por lo tanto el crecimiento de altura es una función cuadrática.

Implementar eso no es difícil, la idea ahora sera que cuando el espectro indique la zona a la que quiera lanzar la bolita azul (el proyectil) en el mismo detonador de conjuracion de la habilidad se calcule cuanto tiene que valer la velocidad inicial vertical para que el proyectil alcance ese punto.
En la imagen se ve lo que se intenta hacer, hay que determinar cuanto vale la velocidad vertical inicial fijando los valores de la velocidad horizontal, el angulo, la altura inicial, y la altura final.
La linea negra indica el 0 en altura del sistema de referecia para el juego.
Las lineas grises miden cuanta altura hay desde el 0 absoluto.
La linea rosa oscuro mide la diferencia de altura entre el punto inicial y el punto final.
La linea naranja mide la distancia entre los puntos final e inicial.
Observese que el punto de lanzamiento es el punto verde que se encuentra a cierta altura del punto donde esta el espectro.
Esto se convierte en un problema de física de movimiento acelerado en este punto:
Esta parte si que no es apta para los que no tengan conocimiento de que es una integral ni nunca hayan tenido calculo o análisis, así que si no se entiende, no intenten entenderlo, luego se presenta de una manera mas sencilla la forma de calcularlo.
Sea z(x) la función que determina la altura absoluta del proyectil para una distancia x desde el punto de lanzamiento. La gravedad es una fuerza que actúa por tiempo, pero ya que el tiempo es proporcional al desplazamiento horizontal que es constante, se puede considerar que la gravedad actúa por distancia plana recorrida también.
Entonces sean v = velocidad ascendente, x = deslpazamiento en el plano XY, gx = gravedad, zi = altura inicial, zf = altura final.

Lo que necesito despejar es v, ya que los demás valores los puedo obtener midiendo. Así que con todo lo demás constante la formula queda así:

Esto también se puede aplicar para que la AI calcule cuanta velocidad ascendente le tiene que dar a su proyectil si hiciéramos un mapa de puntería tipo worms por ejemplo.
Pero ahora supongamos que queremos hacer que una unidad con AI sepa si una zona va a ser alcanzada por uno de estos proyectiles. Tendriamos que despejar x y dejar el resto constante de este modo.

Esta ultima forma es raro que sea usada, pero la anterior es fundamental.

De la segunda formula en rojo deduciremos como implementar en un mapa esta teoría.
zi es la altura en el punto que habiamos llamado P2 en la imagen de la trayectoria + alguna constante que indica la altura sobre el suelo a la que el espectro lanza el proyectil en nuestro caso particular esta constante es 150.
zf es la altura en el punto seleccionado por la habilidad (Target point of ability being cast) PG en el grafico.
x es la distancia entre P2 y PG sobre la velocidad horizontal que es constante y en nuestro caso particular vale 26.
gx es constante y vale 1 en este caso particular.

[gui]Granada
   Acontecimientos
       Unidad - A unit Inicia el efecto de una habilidad
   Condiciones
       (Ability being cast) Igual a Bolita Azul
   Acciones
       Set P2 = (Position of (Casting unit))
       Set P1 = (Target point of ability being cast)
       Set NroProyectiles = (NroProyectiles + 1)
       If (All Conditions are True) then do (Then Actions) else do (Else Actions)
           Si: Condiciones
               (Distance between P1 and P2) Mayor que o igual a 950.00
           Entonces: Acciones
               Set PG = (P2 offset by 950.00 towards (Angle from P2 to P1) degrees)
           Otros: Acciones
               Set PG = (Target point of ability being cast)
       Unidad - Create 1 DUM 2 for (Owner of (Casting unit)) at P2 facing P1
       Unidad - Add Forma de Cuervo de tormenta to (Last created unit)
       Unidad - Remove Forma de Cuervo de tormenta from (Last created unit)
       Unidad - Turn collision for (Last created unit) Apagado
       Set Proyectiles[NroProyectiles] = (Last created unit)
       Set ProyDir[NroProyectiles] = (Angle from P2 to P1)
       Set VelAscendente[NroProyectiles] = (0.50 x ((Distance between PG and P2) / 26.00))
       Set VelPlana[NroProyectiles] = 26.00
       Custom script:   set udg_ZPUNT = 150 + GetLocationZ(udg_P2) - GetLocationZ(udg_PG)
       Set VelAscendente[NroProyectiles] = (VelAscendente[NroProyectiles] - (ZPUNT / ((Distance between PG and P2) / 26.00)))
       Custom script:   set udg_ZPUNT = GetLocationZ(udg_P2) + 150
       Set Altura[NroProyectiles] = ZPUNT
       Unidad - Add a 2.20 second Genérico expiration timer to (Last created unit)
       Custom script:   call RemoveLocation (udg_P1)
       Custom script:   call RemoveLocation (udg_P2)
       Custom script:   call RemoveLocation (udg_PG)
[/gui]

En el código incluí una etapa que limita la distancia máxima a 950, no tiene sentido en este caso, pero si por ejemplo combinara esto con la intersección recta-circulo podría hacer que el espectro encerrado no pueda disparar proyectiles fuera del circulo ya que si lo hiciera esto lo limitaría a la distancia entre su posición y el punto de intersección.

Descarga aqui:



Ultimo Comentario
Este tutorial queda abierto, a quien haya tenido alguna experiencia matemática con el editor que no sea de muy bajo nivel lo invito a comentar y puede entrar dentro del post si es adecuado. Asi también yo seguiré añadiendo cosas con el tiempo, aun quedan muchas pero es laborioso hacer esto.
Para los que usan vJASS, si alguien tiene ganas de traducir estos detonadores a jass seria genial así posteo también el código. No tendrán ningún problema ya que es muy transparente la traducción, no hay que pensar nada o casi nada.
Por ultimo, si algo de toda esta porqueria les sirvio dejen  +rep , y si no lo hacen nada puedo hacer jajajaja.


Lets take a moment to break the ice, so my intentions are known..


¡Hola Invitado!
Tal vez los siguientes vínculos puedan interesante:
:star:Reglas del foro:star:
:star:Listado de Tutoriales:star:
:star:Consultas del editor de mundos:star:
Like a Star @ heaven Requisitos de Tutoriales y Spells:star:


Última edición por Sapphiron el Lun Jul 25, 2016 9:50 pm, editado 1 vez
Volver arriba Ir abajo
http://www.worldofeditors.net
Sapphiron

avatar


Barra de Salud : Este usuario es invulnerable ¿Será que es fiel a las normas?

Mensajes Mensajes : 1430
Reputación Reputación : 208
Monedas de oro : 12058
Monedas de Platino : 0

Inventario :
Frostmourne Escudo lunar Armadura Lunar Avanzada
Diamante Encantado Anillo Sagrado Libro de Leyendas

Mensaje(#) Tema: Re: Funciones Avanzadas y Matemática Aplicada ( Autor: WonderWoman) Lun Jul 25, 2016 9:47 pm

A pedido de @Marcos_M posteo el contenido de este post extraído de la antigua pagina WorldEdit. Todos los créditos son para @WonderWoman .

El post necesita que le resuban las imágenes ya que imageshack le estiró la pata a mucha gente .

Saludos.


Lets take a moment to break the ice, so my intentions are known..


¡Hola Invitado!
Tal vez los siguientes vínculos puedan interesante:
:star:Reglas del foro:star:
:star:Listado de Tutoriales:star:
:star:Consultas del editor de mundos:star:
Like a Star @ heaven Requisitos de Tutoriales y Spells:star:
Volver arriba Ir abajo
http://www.worldofeditors.net
[NtP]NtP
Soldado
avatar


Barra de Salud : Su salud está al 100% - Este usuario no ha recibido infracciones

Mensajes Mensajes : 73
Reputación Reputación : 0
Monedas de oro : 137
Monedas de Platino : 0

Inventario :



Mensaje(#) Tema: Re: Funciones Avanzadas y Matemática Aplicada ( Autor: WonderWoman) Jue Jul 28, 2016 2:17 pm

El Mejor Sapphiron-Sempai!!!
Volver arriba Ir abajo
armando368
Caballero
avatar


Barra de Salud : Su salud está al 80% - Este usuario ha recibido una infracción

Mensajes Mensajes : 289
Reputación Reputación : 15
Monedas de oro : 1092
Monedas de Platino : 0

Inventario :



Mensaje(#) Tema: Re: Funciones Avanzadas y Matemática Aplicada ( Autor: WonderWoman) Jue Dic 15, 2016 11:13 am

Comento ver imágenes


HOLA VOLVI ! ! !
Como los Viejos tiempos eh ?

{Username}, estas leyendo esto -.- , tanta lectura ? No tienes nada mejor que hacer ?
Volver arriba Ir abajo
Contenido patrocinado




Mensaje(#) Tema: Re: Funciones Avanzadas y Matemática Aplicada ( Autor: WonderWoman)

Volver arriba Ir abajo
 

Funciones Avanzadas y Matemática Aplicada ( Autor: WonderWoman)

Ver el tema anterior Ver el tema siguiente Volver arriba 
Página 1 de 1.

 Temas similares

-
» Necesito algunas nociones de matemática femenina.
» Misiones (Avanzadas)
» Máscara subacuática echa y sin la urgente!!!
» [Actualizado][HECHO]Misiones avanzadas, 5 estrellas
» [Tutorial] Crear una Cinemática

Permisos de este foro:No puedes responder a temas en este foro.
Warcraft III - WorldEditor :: Academia :: Nuevos tutoriales :: Tutoriales nuevos-