Last active
May 13, 2026 19:33
-
-
Save Clemapfel/cde6f9e3c8585f0559da1b35d651724e to your computer and use it in GitHub Desktop.
9-Slice Frame in Love2D
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| local create_frame = function( | |
| frame_x, frame_y, frame_w, frame_h, -- dimensions of the frame in world space, in px | |
| frame_x_thickness, -- width of the vertical part of the frame in world space, in px | |
| frame_y_thickness, -- height of the horizontal part of the frame in world space, in px | |
| texture_atlas, -- love.Texture | |
| -- pixel coordinates of the sprites in the texture atlas, in texture space, non-normalized | |
| top_x, top_y, top_w, top_h, -- top horizontal bar | |
| right_x, right_y, right_w, right_h, -- right vertical bar | |
| bottom_x, bottom_y, bottom_w, bottom_h, -- bottom horizontal bar | |
| left_x, left_y, left_w, left_h, -- left vertical bar | |
| top_left_corner_x, top_left_corner_y, -- top left corner sprite | |
| top_left_corner_w, top_left_corner_h, | |
| top_right_corner_x, top_right_corner_y, -- top right corner sprite | |
| top_right_corner_w, top_right_corner_h, | |
| bottom_right_corner_x, bottom_right_corner_y, -- bottom right corner sprite | |
| bottom_right_corner_w, bottom_right_corner_h, | |
| bottom_left_corner_x, bottom_left_corner_y, -- bottom left corner sprite | |
| bottom_left_corner_w, bottom_left_corner_h, | |
| backdrop_x, backdrop_y, backdrop_w, backdrop_h -- full frame background, optional | |
| ) | |
| assert(texture_atlas:typeOf("Texture"), "In create_frame: wrong argument type for argument #7: expected `Texture`") | |
| local data = {} | |
| local indices = {} | |
| local index_offset = 1 | |
| local atlas_w, atlas_h = texture_atlas:getDimensions() | |
| local add_quad = function(min_x, min_y, max_x, max_y, tx, ty, tw, th) | |
| local w, h = max_x - min_x, max_y - min_y | |
| table.insert(data, { | |
| min_x + 0, min_y + 0, -- vertex xy | |
| (tx + 0) / atlas_w, (ty + 0) / atlas_h, -- normalize px coordinates to [0, 1] texture uv | |
| 1, 1, 1, 1 -- vertex rgba | |
| }) | |
| table.insert(data, { | |
| min_x + w, min_y + 0, | |
| (tx + tw) / atlas_w, (ty + 0) / atlas_h, | |
| 1, 1, 1, 1 | |
| }) | |
| table.insert(data, { | |
| min_x + w, min_y + h, | |
| (tx + tw) / atlas_w, (ty + th) / atlas_h, | |
| 1, 1, 1, 1 | |
| }) | |
| table.insert(data, { | |
| min_x + 0, min_y + h, | |
| (tx + 0) / atlas_w, (ty + th) / atlas_h, | |
| 1, 1, 1, 1 | |
| }) | |
| table.insert(indices, index_offset + 0) | |
| table.insert(indices, index_offset + 1) | |
| table.insert(indices, index_offset + 2) | |
| table.insert(indices, index_offset + 0) | |
| table.insert(indices, index_offset + 2) | |
| table.insert(indices, index_offset + 3) | |
| index_offset = index_offset + 4 | |
| end | |
| if backdrop_x ~= nil and backdrop_y ~= nil and backdrop_w ~= nil and backdrop_h ~= nil then | |
| add_quad( | |
| frame_x + frame_x_thickness, frame_y + frame_y_thickness, | |
| frame_x + frame_w - frame_x_thickness, frame_y + frame_h - frame_y, | |
| backdrop_x, backdrop_y, backdrop_w, backdrop_h | |
| ) | |
| end | |
| add_quad( | |
| frame_x, frame_y, | |
| frame_x + frame_x_thickness, frame_y + frame_y_thickness, | |
| top_left_corner_x, top_left_corner_y, top_left_corner_w, top_left_corner_h | |
| ) | |
| add_quad( | |
| frame_x + frame_w - frame_x_thickness, frame_y, | |
| frame_x + frame_w, frame_y + frame_y_thickness, | |
| top_right_corner_x, top_right_corner_y, top_right_corner_h, top_right_corner_h | |
| ) | |
| add_quad( | |
| frame_x + frame_w - frame_x_thickness, frame_y + frame_h - frame_y_thickness, | |
| frame_x + frame_w, frame_y + frame_h, | |
| bottom_right_corner_x, bottom_right_corner_y, bottom_right_corner_w, bottom_right_corner_h | |
| ) | |
| add_quad( | |
| frame_x, frame_y + frame_h - frame_y_thickness, | |
| frame_x + frame_x_thickness, frame_y + frame_h, | |
| bottom_right_corner_x, bottom_right_corner_y, bottom_right_corner_w, bottom_right_corner_h | |
| ) | |
| add_quad( | |
| frame_x + frame_x_thickness, frame_y, | |
| frame_x + frame_w - frame_x_thickness, frame_y + frame_y_thickness, | |
| top_x, top_y, top_w, top_h | |
| ) | |
| add_quad( | |
| frame_x + frame_w - frame_x_thickness, frame_y + frame_y_thickness, | |
| frame_x + frame_w, frame_y + frame_h - frame_y_thickness, | |
| right_x, right_y, right_w, right_h | |
| ) | |
| add_quad( | |
| frame_x + frame_x_thickness, frame_y + frame_h - frame_y_thickness, | |
| frame_x + frame_w - frame_x_thickness, frame_y + frame_h, | |
| bottom_x, bottom_y, bottom_w, bottom_h | |
| ) | |
| add_quad( | |
| frame_x, frame_y + frame_y_thickness, | |
| frame_x + frame_x_thickness, frame_y + frame_h - frame_y_thickness, | |
| left_x, left_y, left_w, left_h | |
| ) | |
| local mesh = love.graphics.newMesh(data, "triangles", "dynamic") | |
| mesh:setVertexMap(indices) | |
| mesh:setTexture(texture_atlas) | |
| return mesh | |
| end | |
| -- ### usage ### | |
| local mesh = nil | |
| local allocate_mesh = function() | |
| local atlas = love.graphics.newImage("assets/sprites/barboach.png") | |
| atlas:setFilter("nearest") -- not required | |
| -- pixel positions for the sprites inside the texture atlas | |
| local sprite_x, sprite_y = 0, 0 | |
| local sprite_w, sprite_h = atlas:getDimensions() | |
| local frame_x_thickness = 2 * sprite_w | |
| local frame_y_thickness = 2 * sprite_h | |
| -- pixel positions of the area the frame should cover | |
| local margin = 50 | |
| local x, y = margin, margin | |
| local w = love.graphics.getWidth() - 2 * margin | |
| local h = love.graphics.getHeight() - 2 * margin | |
| -- inside of the frame is the area at position: x + frame_x_thickness, y + frame_y_thickness, | |
| -- with width / height: w - 2 * frame_x_thickness, y - 2 * frame_y_thickness | |
| mesh = create_frame( | |
| x, y, w, h, | |
| frame_x_thickness, frame_y_thickness, | |
| atlas, | |
| -- dummy pixel positions, replace these with ones from an actual spritesheet | |
| sprite_x, sprite_y, sprite_w, sprite_h, | |
| sprite_x, sprite_y, sprite_w, sprite_h, | |
| sprite_x, sprite_y, sprite_w, sprite_h, | |
| sprite_x, sprite_y, sprite_w, sprite_h, | |
| sprite_x, sprite_y, sprite_w, sprite_h, | |
| sprite_x, sprite_y, sprite_w, sprite_h, | |
| sprite_x, sprite_y, sprite_w, sprite_h, | |
| sprite_x, sprite_y, sprite_w, sprite_h, | |
| nil, nil, nil, nil -- no backdrop | |
| ) | |
| end | |
| love.load = function() | |
| allocate_mesh() | |
| end | |
| love.draw = function() | |
| love.graphics.setColor(1, 1, 1, 1) | |
| love.graphics.draw(mesh) | |
| end | |
| love.resize = function() | |
| allocate_mesh() | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment