Comment créer une surface incurvée à partir de blocs rectangulaires?


12

Pour un jeu de type Peggle , je veux faire des blocs qui suivent une courbe, comme ceci:

blocs le long d'une courbe

Les blocs disparaissent alors lorsque la balle les frappe.

J'ai réussi à en dessiner horizontalement, mais j'ai du mal à leur faire suivre un chemin:

ma tentative de blocs de suivi de chemin

Comment puis-je faire cela? Dois-je créer des objets Box2D avec des sommets personnalisés?


Voulez-vous simplement que les cases ne se chevauchent pas ou voulez-vous qu'il n'y ait aucun espace nulle part? (Je ne sais pas exactement ce que vous entendez par "décalage de l'axe Y de l'objet en fonction de l'angle de l'objet").
Roy

1
Vous ne pouvez pas remplir une courbe avec des rectangles qui ne se chevauchent pas , vous devrez donc créer une géométrie personnalisée si vous ne voulez pas d'espace.
Anko

@RoyT. Les écarts ne sont pas importants. Mon vrai problème est de calculer la position du bloc qui se succèdent sous différents angles.
Moerin

La façon dont j'aborderais cela est de définir une série de sommets qui agissent comme les coins communs entre chaque boîte. Même en utilisant un chemin pour les définir, vous avez toujours besoin de paramètres supplémentaires pour définir la distance entre les sommets et la longueur de chaque boîte.

4
Les "cases" sur la première image ne sont pas des cases, ce sont des paires de triangles: i.stack.imgur.com/Tzuql.png
egarcia

Réponses:


14

Étant donné une courbe "racine", voici comment générer des sommets de bloc.

Bézier avec des blocs

La courbe racine est au milieu, en noir. Ses points de contrôle sont indiqués par un Xs rouge .

En bref : j'ai fait un Bézier et l' ai échantillonné (à un rythme configurable). J'ai ensuite trouvé le vecteur perpendiculaire du vecteur de chaque échantillon au suivant, normalisé et mis à l' échelle à une demi-largeur (configurable), d'abord à gauche, puis inversement à droite. Puis l'a dessiné.

Des choses que vous pourriez ajouter à cela:


Voici mon code. Il est écrit en Lua (pour le framework de jeu LÖVE ), mais je pense qu'il est lisible pour tout le monde.

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 vous souhaitez jouer avec: Obtenez LÖVE et placez le code ci-dessus main.luadans son propre répertoire. Mettez vector.luade la HUMPbibliothèque dans le même répertoire. Exécutez-le à love <that-directory>partir d'une ligne de commande.

Déplacez la souris! Le point de contrôle central est défini à l'emplacement de la souris:

Définition du point de contrôle avec la souris


Anko avez-vous essayé LibGdx? si oui, préférez-vous Löve? Je m'éloigne de l'utilisation de l'API Android standard après mon jeu actuel et j'essaie de décider entre LibGdx et Löve. Réponse intéressante ci-dessus comme toujours btw
Green_qaue

@Anko Merci beaucoup, c'est plus que ce à quoi je m'attendais. Plus je pense que je peux facilement comprendre votre code puisque j'utilise MonkeyX pour mon jeu qui est similaire à LUA.
Moerin

1
@iQ Je n'ai pas utilisé Libgdx, mais j'en ai beaucoup lu et je connais bien Java. Libgdx est grand . (Il a un support d'accéléromètre, des générateurs de courbes intégrés et tout), tandis que Love2D est très petit (il n'en a aucun, il n'y a pas de support de shader, etc.). Grâce à sa simplicité, Love2D a été idéal pour les prototypes rapides et les petits jeux, mais il pourrait être trop minimaliste pour certains projets. Qui sait. (Vous le faites! Essayez-le et voyez.: D)
Anko

Excellente réponse, et ce GIF est vraiment un bon bonus!
Roy T.
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.