Як зробити вигнуту поверхню з прямокутних блоків?


12

Для гри, схожої на Peggle , я хочу зробити блоки, що слідують за кривою, наприклад:

блоки вздовж кривої

Потім блоки зникнуть, коли куля вдарить по них.

Мені вдалося намалювати горизонтально, але у мене виникають проблеми змусити їх слідувати шлях:

моя спроба блоків, що слідують за маршрутом

Як це зробити? Чи потрібно мені створювати об'єкти Box2D зі спеціальними вершинами?


Ви хочете, щоб ящики просто не перетиналися або ви хочете, щоб ніде не було прогалин? (Я не точно впевнений, що ви маєте на увазі під "зміщенням осі Y об'єкта відповідно до кута об'єкта").
Рой Т.

1
Ви не можете заповнити криву прямокутниками , що не перекриваються , тому вам доведеться створити певну геометричну геометію, якщо не потрібно прогалин.
Анко

@RoyT. Прогалини не важливі. Моя реальна проблема - обчислити положення блоку, які слідують один за одним під різним кутом.
Moerin

Я б підійшов до цього - визначити ряд вершин, які виступають загальними кутами між кожним полем. Навіть використовуючи шлях для їх визначення, вам все одно потрібні додаткові параметри для визначення відстані між вершинами та довжиною кожного поля.

4
"Ящики" на першому зображенні не поля, це пари трикутників: i.stack.imgur.com/Tzuql.png
egarcia

Відповіді:


14

З огляду на "кореневу" криву, ось як можна генерувати вершини блоків.

Безьє з блоками

Корінь кривої знаходиться посередині, чорного кольору. Його контрольні точки показані червоним Xs.

Коротше кажучи : я зробив Безьє і взяв пробу (з можливістю налаштування). Потім я знайшов перпендикулярний вектор вектора від кожного зразка до наступного, нормалізував його і змістив його до (настроюваної) півширини, спочатку ліворуч, потім навпаки праворуч. Потім намалював це.

Ви можете додати до цього речі:


Ось мій код. Це написано на Lua (для ігор LÖVE ), але я думаю, що це читається для будь-кого.

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

Якщо ви хочете пограти з ним: Отримайте LÖVE і вставте вищевказаний код main.luaу свою власну каталог. Помістіть vector.luaз HUMPбібліотеки в той самий каталог. Запустити його як love <that-directory>з командного рядка.

Перемістіть мишку! Середня контрольна точка встановлюється в розташування миші:

Встановлення контрольної точки за допомогою миші


Анко ви спробували LibGdx? якщо так, то чи віддаєте перевагу Леві? Я відмовляюся від використання стандартного API для Android після своєї поточної гри, і я намагаюся вирішити між LibGdx та Löve. Цікава відповідь вище, як завжди btw
Green_qaue

@Anko Спасибі велике, це більше, ніж я очікував. Більше я думаю, що я можу легко зрозуміти ваш код, оскільки я використовую MonkeyX для своєї гри, яка схожа на LUA.
Moerin

1
@iQ Я не користувався Libgdx, але я багато читав про нього і добре знаю Java. Libgdx великий . (Він має підтримку акселерометра, вбудований в генератори кривих і все), в той час як Love2D дуже маленький (у нього немає жодної з них, немає підтримки шейдерів тощо). Завдяки своїй простоті, Love2D чудово підходить для швидких прототипів та невеликих ігор, але для деяких проектів це може бути занадто мінімалістично. Хто знає. (Ви робите! Спробуйте і подивіться.: D)
Анко

Чудова відповідь, і що GIF - це справді приємний бонус!
Рой Т.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.