Wie mache ich aus rechteckigen Blöcken eine gekrümmte Fläche?

12

Für ein Peggle- ähnliches Spiel möchte ich Blöcke erstellen , die einer Kurve folgen:

Blöcke entlang einer Kurve

Die Blöcke würden dann verschwinden, wenn der Ball sie trifft.

Ich habe es geschafft, einige horizontal zu zeichnen, aber ich habe Probleme damit, sie einem Pfad zu folgen:

mein Versuch, Wegeblöcke zu verfolgen

Wie mache ich das? Muss ich Box2D- Objekte mit benutzerdefinierten Eckpunkten erstellen ?

Moerin
quelle
Möchten Sie, dass sich die Kästchen einfach nicht überlappen, oder möchten Sie, dass nirgendwo Lücken entstehen? (Ich bin mir nicht ganz sicher, was Sie unter "Versetzen der Y-Achse des Objekts um den Objektwinkel" verstehen.)
Roy T.
1
Sie können eine Kurve nicht mit nicht überlappenden Rechtecken füllen. Wenn Sie also keine Lücken möchten , müssen Sie eine benutzerdefinierte Geometie erstellen.
Anko
@RoyT. Die Lücken sind nicht wichtig. Mein eigentliches Problem besteht darin, die Position des Blocks zu berechnen, die mit unterschiedlichen Winkeln aufeinander folgen.
Moerin
Ich würde das so angehen, indem ich eine Reihe von Scheitelpunkten definiere, die als gemeinsame Ecken zwischen den einzelnen Feldern dienen. Selbst wenn Sie einen Pfad verwenden, um sie zu definieren, benötigen Sie zusätzliche Parameter, um den Abstand zwischen den Scheitelpunkten und die Länge der einzelnen Felder zu definieren.
4
Die "Kästchen" auf dem ersten Bild sind keine Kästchen, sondern Paare von Dreiecken: i.stack.imgur.com/Tzuql.png
egarcia

Antworten:

14

Bei einer gegebenen "Wurzel" -Kurve können Sie wie folgt Blockscheitelpunkte generieren.

Bézier mit Blöcken

Die Wurzelkurve ist in der Mitte in Schwarz. Seine Kontrollpunkte werden mit einem roten Xs angezeigt .

Kurz gesagt : Ich habe ein Bézier gemacht und es abgetastet (mit einer konfigurierbaren Rate). Ich fand dann den senkrechten Vektor des Vektors von jedem Sample zum nächsten, normalisierte ihn und skalierte ihn auf eine (konfigurierbare) halbe Breite, zuerst nach links, dann umgekehrt nach rechts. Dann zeichnete es.

Dinge, die du hinzufügen könntest:


Hier ist mein Code. Es ist in Lua (für das LÖVE -Framework) geschrieben, aber ich denke, es ist für jeden lesbar.

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

Wenn Sie damit spielen möchten : Holen Sie sich LÖVE und speichern Sie den obigen Code in main.luaeinem eigenen Verzeichnis. Setzen Sie vector.luaaus der HUMPBibliothek in dem gleichen Verzeichnis. Führen Sie es love <that-directory>von einer Befehlszeile aus.

Bewege die Maus! Der mittlere Kontrollpunkt wird auf die Mausposition gesetzt:

Kontrollpunkt mit der Maus setzen

Anko
quelle
Anko hast du LibGdx ausprobiert? wenn ja, bevorzugst du Löve? Ich bin weg von der Verwendung von Standard-Android-API nach meinem aktuellen Spiel und ich versuche, mich zwischen LibGdx und Löve zu entscheiden. Interessante Antwort oben wie immer übrigens
Green_qaue
@Anko Vielen Dank, es ist mehr als ich erwartet hatte. Ich glaube, ich kann Ihren Code leichter verstehen, da ich MonkeyX für mein Spiel verwende, das LUA ähnelt.
Moerin
1
@iQ Ich habe Libgdx nicht benutzt, aber ich habe viel darüber gelesen und kenne Java gut. Libgdx ist groß . (Es hat Beschleunigungsmesser-Unterstützung, eingebaute Kurvengeneratoren und alles), während Love2D sehr klein ist (es hat keine von denen, es gibt keine Shader-Unterstützung usw.). Dank seiner Einfachheit eignet sich Love2D hervorragend für schnelle Prototypen und kleine Spiele, ist jedoch für einige Projekte möglicherweise zu minimalistisch. Wer weiß. (Sie tun! Versuchen Sie es und sehen.: D)
Anko
Tolle Antwort, und das GIF ist wirklich ein schöner Bonus!
Roy T.