Jak zrobić zakrzywioną powierzchnię z prostokątnych bloków?

12

W przypadku gry podobnej do Peggle chcę tworzyć bloki zgodne z krzywą:

bloki wzdłuż krzywej

Bloki znikałyby wtedy, gdy uderzyła w nie piłka.

Udało mi się narysować niektóre w poziomie, ale mam problem z zmuszeniem ich do podążania ścieżką:

moja próba blokowania ścieżek

Jak mam to zrobic? Czy muszę tworzyć obiekty Box2D z niestandardowymi wierzchołkami?

Moerin
źródło
Czy chcesz, aby pudełka po prostu się nie nakładały, czy też chcesz, aby nigdzie nie było żadnych przerw? (Nie jestem do końca pewien, co rozumiesz przez „przesunięcie osi Y obiektu względem kąta obiektu”).
Roy T.
1
Nie możesz wypełnić krzywej nienakładającymi się prostokątami , więc będziesz musiał stworzyć niestandardową geometrię, jeśli nie chcesz żadnych przerw.
Anko
@RoyT. Luki nie są ważne. Moim prawdziwym problemem jest obliczenie położenia bloku, które podążają za sobą pod innym kątem.
Moerin
Podejdę do tego, jak zdefiniować serię wierzchołków, które działają jak wspólne rogi między poszczególnymi ramkami. Nawet używając ścieżki do ich zdefiniowania, nadal potrzebujesz dodatkowych parametrów, aby określić odległość między wierzchołkami i długość każdego pola.
4
„Pudełka” na pierwszym zdjęciu nie są pudełkami, są parami trójkątów: i.stack.imgur.com/Tzuql.png
egarcia

Odpowiedzi:

14

Biorąc pod uwagę krzywą „root”, oto jak możesz wygenerować wierzchołki bloków.

Bézier z klockami

Krzywa korzenia znajduje się na środku, w kolorze czarnym. Punkty kontrolne są oznaczone czerwonymi Xs.

W skrócie : Zrobiłem Béziera i próbowałem go (w konfigurowalnym tempie). Następnie znalazłem wektor prostopadły wektora z każdej próbki do następnej, znormalizowałem go i przeskalowałem do (konfigurowalnej) połowy szerokości, najpierw w lewo, a potem odwrotnie w prawo. Potem narysowałem.

Rzeczy, które możesz dodać do tego:


Oto mój kod. Jest napisany w Lua (dla frameworka LÖVE ), ale myślę, że jest czytelny dla każdego.

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

Jeśli chcesz się nim bawić: Pobierz LÖVE i umieść powyższy kod main.luawe własnym katalogu. Umieść vector.luaz HUMPbiblioteki w tym samym katalogu. Uruchom go love <that-directory>z wiersza poleceń.

Poruszaj myszką! Środkowy punkt kontrolny jest ustawiony na położenie myszy:

Ustawianie punktu kontrolnego za pomocą myszy

Anko
źródło
Anko próbowałeś LibGdx? jeśli tak, czy wolisz Löve? Po mojej obecnej grze odchodzę od używania standardowego interfejsu API Androida i próbuję zdecydować między LibGdx a Löve. Ciekawa odpowiedź powyżej jak zawsze przy okazji
Green_qaue
@ Anko Dziękuję bardzo, to więcej niż się spodziewałem. Więcej myślę, że mogę łatwo zrozumieć twój kod, ponieważ używam MonkeyX do mojej gry, która jest podobna do LUA.
Moerin
1
@ iQ Nie korzystałem z Libgdx, ale dużo o nim czytałem i dobrze znam Javę. Libgdx jest duży . (Ma wsparcie akcelerometru, wbudowane generatory krzywych i wszystko), podczas gdy Love2D jest bardzo mały (nie ma żadnego z nich, nie ma obsługi shaderów itp.). Jednak dzięki swojej prostocie Love2D świetnie nadaje się do szybkich prototypów i małych gier, ale może być zbyt minimalistyczny w przypadku niektórych projektów. Kto wie. (Ty! Spróbuj i zobacz.: D)
Anko,
Świetna odpowiedź, a ten GIF to naprawdę niezły bonus!
Roy T.