¿Cómo hago una superficie curva con bloques rectangulares?

12

Para un juego tipo Peggle , quiero hacer bloques que sigan una curva, así:

bloques a lo largo de una curva

Los bloques desaparecerían cuando la pelota los golpeara.

Logré dibujar algunos horizontalmente, pero tengo problemas para que sigan un camino:

mi intento de bloquear caminos que siguen

¿Cómo hago esto? ¿Necesito crear objetos Box2D con vértices personalizados?

Moerin
fuente
¿Desea que las cajas simplemente no se superpongan o desea que no haya espacios en ninguna parte? (No estoy exactamente seguro de lo que quiere decir con "desplazamiento del eje Y del objeto de acuerdo con el ángulo del objeto").
Roy T.
1
No puede rellenar una curva con rectángulos no superpuestos , por lo que tendrá que crear algo de geometría personalizada si no desea espacios.
Anko
@RoyT. Las brechas no son importantes. Mi verdadero problema es calcular la posición del bloque que se sigue con un ángulo diferente.
Moerin
La forma en que abordaría esto es definir una serie de vértices que actúen como las esquinas comunes entre cada cuadro. Incluso utilizando una ruta para definirlos, aún necesita parámetros adicionales para definir la distancia entre los vértices y la longitud de cada cuadro.
44
Las "cajas" en la primera imagen no son cajas, son pares de triángulos: i.stack.imgur.com/Tzuql.png
egarcia

Respuestas:

14

Dada una curva "raíz", así es como puede generar vértices de bloque.

Bézier con bloques

La curva raíz está en el medio, en negro. Sus puntos de control se muestran con Xs rojo .

En resumen : hice un Bézier y lo probé (a una velocidad configurable). Luego encontré el vector perpendicular del vector de cada muestra a la siguiente, lo normalicé y lo escalé a un ancho medio (configurable), primero a la izquierda, luego inversamente a la derecha. Luego lo dibujó.

Cosas que podrías agregar a esto:


Aquí está mi código. Está escrito en Lua (para el marco del juego LÖVE ), pero creo que es legible para cualquiera.

local v = require "vector"

-- A function that makes bezier functions
-- Beziers have start point     p0
--              control point   p1
--              end point       p2
local function makeBezierFunction(p0,p1,p2)
    return function (t)
        local pow = math.pow
        return pow( (1-t),2 ) * p0
               + 2 * (1-t) * t * p1
               + pow(t,2) * p2
    end
end

love.graphics.setBackgroundColor(255, 255, 255)
function love.draw()
    local line = love.graphics.line
    local colour = love.graphics.setColor

    -- Bezier sampling parameters
    local nSegments = 10
    local segIncr = 1/nSegments

    -- Bezier definition: Start (`p0`), control (`p1`) and end `p2`) point
    local p0 = v(100,100)
    local p1 = v( love.mouse.getX(), love.mouse.getY() )
    local p2 = v(500,100)
    local controlPoints = {p0,p1,p2}
    local bez = makeBezierFunction(p0,p1,p2)

    -- Sample the bezier
    for i=0,1-segIncr,segIncr do
        colour(0, 0, 0)
        local x1,y1 = bez(i        ):unpack()
        local x2,y2 = bez(i+segIncr):unpack()
        line(x1,y1,x2,y2)

        -- Find left and right points.
        local center = v(x1, y1)
        local forward = v(x2, y2) - center
        local left = center + forward:perpendicular():normalize_inplace() * 10
        local right = center - forward:perpendicular():normalize_inplace() * 10

        -- Draw a line between them.
        line(left.x, left.y, right.x, right.y)

        -- Find *next* left and right points, if we're not beyond the end of
        -- the curve.
        if i + segIncr <= 1 then
            local x3, y3 = bez(i+segIncr*2):unpack()
            local center2 = v(x2, y2)
            local forward2 = v(x3, y3) - center2
            local left2 = center2 + forward2:perpendicular():normalize_inplace() * 10
            local right2 = center2 - forward2:perpendicular():normalize_inplace() * 10

            -- Connect the left and right of the current to the next point,
            -- forming the top and bottom surface of the blocks.
            colour(0, 0xff, 0)
            line(left.x, left.y, left2.x, left2.y)
            colour(0, 0, 0xff)
            line(right.x, right.y, right2.x, right2.y)
        end
    end

    -- Draw an X at the control points
    for _,p in ipairs(controlPoints) do
        local x,y = p:unpack()
        colour(0xff,0x00,0x00)
        line(x-5,y-5, x+5,y+5)
        line(x-5,y+5, x+5,y-5)
    end
    -- Draw lines between control points
    for i=1,#controlPoints do
        colour(0xff,0x00,0x00, 100)
        local cp1 = controlPoints[i]
        local cp2 = controlPoints[i+1]
        if cp1 and cp2 then
            line(cp1.x, cp1.y
                ,cp2.x, cp2.y)
        end
    end
end

Si desea jugar con él: Obtenga LÖVE y coloque el código anterior main.luaen su propio directorio. Poner vector.luade la HUMPbiblioteca en el mismo directorio. Ejecútelo como love <that-directory>desde una línea de comando.

Mueve el mouse! El punto de control central está configurado en la ubicación del mouse:

Establecer el punto de control con el mouse

Anko
fuente
Anko, ¿has probado LibGdx? si es así, ¿prefieres Löve? Me estoy alejando del uso de la API estándar de Android después de mi juego actual y estoy tratando de decidir entre LibGdx y Löve. Interesante respuesta arriba como siempre por cierto
Green_qaue
@ Annko Muchas gracias, es más de lo que esperaba. Más creo que puedo entender fácilmente su código ya que uso MonkeyX para mi juego que es similar a LUA.
Moerin
1
@ iQ No he usado Libgdx, pero he leído mucho al respecto y conozco bien Java. Libgdx es grande . (Tiene soporte de acelerómetro, generadores de curvas incorporados y todo), mientras que Love2D es muy pequeño (no tiene ninguno de ellos, no hay soporte de sombreador, etc.). Sin embargo, gracias a su simplicidad, Love2D ha sido excelente para prototipos rápidos y juegos pequeños, pero puede ser demasiado minimalista para algunos proyectos. Quién sabe. (¡Sí! Pruébalo y verás.: D)
Anko
Gran respuesta, ¡y ese GIF es realmente una buena ventaja!
Roy T.