Как сделать изогнутую поверхность из прямоугольных блоков?

12

Для игры, подобной Peggle , я хочу сделать блоки, которые следуют за кривой, как это:

блоки вдоль кривой

Блоки тогда исчезнут, поскольку шар поражает их.

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

моя попытка блоков следования пути

Как мне это сделать? Нужно ли создавать объекты Box2D с пользовательскими вершинами?

Moerin
источник
Вы хотите, чтобы коробки просто не перекрывались, или вы хотите, чтобы в них не было никаких пробелов? (Я не совсем уверен, что вы подразумеваете под «смещением оси Y объекта в соответствии с углом объекта»).
Рой Т.
1
Вы не можете заполнить кривую неперекрывающимися прямоугольниками , поэтому вам придется создать некоторую нестандартную геометрию, если вы не хотите пропусков.
Анко
@RoyT. Пробелы не важны. Моя настоящая проблема - вычислить положение блока, который следует друг за другом под разным углом.
Мёрин
Способ, которым я бы подошел к этому, состоит в том, чтобы определить серию вершин, которые действуют как общие углы между каждой рамкой. Даже используя путь для их определения, вам все равно нужны дополнительные параметры, чтобы определить расстояние между вершинами и длину каждого блока.
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. Интересный ответ выше, как всегда между прочим
Green_qaue
@Anko Большое спасибо, это больше, чем я ожидал. Более того, я думаю, что могу легко понять ваш код, поскольку я использую MonkeyX для своей игры, которая похожа на LUA.
Мёрин
1
@iQ Я не использовал Libgdx, но я много об этом читал и хорошо знаю Java. Libgdx большой . (У него есть поддержка акселерометра, встроенные генераторы кривых и все остальное), в то время как Love2D очень маленький (у него его нет, нет поддержки шейдеров и т. Д.). Благодаря своей простоте, Love2D отлично подходит для быстрых прототипов и небольших игр, но он может быть слишком минималистичным для некоторых проектов. Кто знает. (Вы делаете! Попробуйте и посмотрите.: D)
Anko
Отличный ответ, и этот GIF действительно хороший бонус!
Рой Т.