-- 《崩坏童话:蜜糖村》全部像素素材生成脚本(Aseprite Lua) -- 运行:aseprite -b --script tools/gen_art.lua -- 输出:public/assets/*.png + art/*.aseprite(源文件) -- 全部素材为本脚本逐像素绘制的原创内容,无任何外部素材。 local pc = app.pixelColor local function C(hex, a) a = a or 255 local r = tonumber(hex:sub(1, 2), 16) local g = tonumber(hex:sub(3, 4), 16) local b = tonumber(hex:sub(5, 6), 16) return pc.rgba(r, g, b, a) end -- ============ 糖果色板(村庄)+ 灰白色板(白色层) ============ local PAL = { outline = C("5a4232"), black = C("2a2420"), white = C("ffffff"), -- 草地/植物 grass = C("8fd05a"), grassD = C("79b94a"), grassL = C("a5e070"), leaf = C("5da53f"), leafD = C("4a8a32"), trunk = C("8a5a34"), trunkD = C("6e4526"), -- 路面/石 path = C("e8d5a8"), pathD = C("cbb488"), stone = C("c8c0a8"), stoneD = C("a8a088"), -- 水 water = C("7ec8e8"), waterD = C("5fb0d8"), waterL = C("b8e6f8"), -- 花 pink = C("ff9ecb"), red = C("e05858"), yellow = C("ffd94a"), berry = C("e8497a"), -- 建筑 wall = C("fff1d0"), wallD = C("e8d8b0"), roof = C("f4924a"), roofD = C("d97733"), wood = C("c98a4b"), woodD = C("a86a34"), woodL = C("e0aa6a"), -- 室内 floor = C("e6c896"), floorD = C("d4b47e"), clinic = C("eaf2ec"), clinicD = C("d0e0d6"), inwall = C("f6e8c8"), inwallD = C("e0cca0"), -- 灰白层 wgray = C("e8e8ea"), wgrayD = C("cfcfd4"), wgrayDD = C("aeaeb6"), wdark = C("8a8a92"), -- 其他 fog = C("e8ecf0"), fogD = C("cdd6de"), metal = C("9aa4ac"), blue = C("5a88c8"), sky = C("8ecae6"), } -- ============ 绘制助手 ============ local function makeCtx(img, ox, oy) local ctx = {} function ctx.px(x, y, c) img:putPixel(ox + x, oy + y, c) end function ctx.rect(x, y, w, h, c) for j = y, y + h - 1 do for i = x, x + w - 1 do img:putPixel(ox + i, oy + j, c) end end end function ctx.hline(x, y, w, c) ctx.rect(x, y, w, 1, c) end function ctx.vline(x, y, h, c) ctx.rect(x, y, 1, h, c) end function ctx.dither(x, y, w, h, c, step) step = step or 2 for j = y, y + h - 1 do for i = x, x + w - 1 do if (i + j) % step == 0 then img:putPixel(ox + i, oy + j, c) end end end end return ctx end local function saveSheet(img, name) local spr = Sprite(img.width, img.height, ColorMode.RGB) spr.cels[1].image = img spr:saveAs("art/" .. name .. ".aseprite") spr:saveCopyAs("public/assets/" .. name .. ".png") spr:close() end -- ============ 瓦片集:10 列 × 8 行 = 80 瓦(16×16) ============ local T = {} T[0] = function(c) -- 草地1 c.rect(0, 0, 16, 16, PAL.grass) c.px(3, 4, PAL.grassL); c.px(11, 9, PAL.grassL); c.px(6, 13, PAL.grassD); c.px(13, 2, PAL.grassD) end T[1] = function(c) -- 草地2(草茎) c.rect(0, 0, 16, 16, PAL.grass) c.px(4, 5, PAL.grassD); c.px(4, 4, PAL.grassD); c.px(10, 10, PAL.grassD); c.px(10, 9, PAL.grassD) c.px(13, 5, PAL.grassL); c.px(2, 12, PAL.grassL) end T[2] = function(c) -- 草地+白花 T[0](c); c.px(5, 6, PAL.white); c.px(4, 5, PAL.white); c.px(6, 5, PAL.white); c.px(5, 4, PAL.white); c.px(5, 5, PAL.yellow) c.px(12, 12, PAL.white) end T[3] = function(c) -- 草地+粉花 T[1](c); c.px(11, 4, PAL.pink); c.px(10, 3, PAL.pink); c.px(12, 3, PAL.pink); c.px(11, 2, PAL.pink); c.px(11, 3, PAL.yellow) c.px(3, 10, PAL.pink) end T[4] = function(c) -- 石板路 c.rect(0, 0, 16, 16, PAL.path) c.hline(0, 7, 16, PAL.pathD); c.vline(7, 0, 8, PAL.pathD); c.vline(3, 8, 8, PAL.pathD); c.vline(12, 8, 8, PAL.pathD) c.px(2, 3, PAL.pathD); c.px(10, 12, PAL.pathD) end T[5] = function(c) -- 石板路2 c.rect(0, 0, 16, 16, PAL.path) c.hline(0, 3, 16, PAL.pathD); c.hline(0, 11, 16, PAL.pathD); c.vline(5, 4, 7, PAL.pathD); c.vline(11, 12, 4, PAL.pathD); c.vline(11, 0, 3, PAL.pathD) end T[6] = function(c) -- 路缘(草→路) c.rect(0, 0, 16, 8, PAL.grass); c.rect(0, 8, 16, 8, PAL.path) c.hline(0, 7, 16, PAL.grassD); c.hline(0, 8, 16, PAL.pathD) c.px(3, 6, PAL.grassL); c.px(11, 12, PAL.pathD) end T[7] = function(c) -- 湖水1 c.rect(0, 0, 16, 16, PAL.water) c.hline(2, 4, 4, PAL.waterL); c.hline(9, 10, 4, PAL.waterL); c.hline(5, 13, 3, PAL.waterD) end T[8] = function(c) -- 湖水2 c.rect(0, 0, 16, 16, PAL.water) c.hline(8, 3, 4, PAL.waterD); c.hline(1, 9, 4, PAL.waterL); c.hline(11, 14, 3, PAL.waterL) end T[9] = function(c) -- 岸边(上草下水) c.rect(0, 0, 16, 6, PAL.grass); c.rect(0, 6, 16, 10, PAL.water) c.hline(0, 5, 16, PAL.grassD); c.hline(0, 6, 16, PAL.waterL) end T[10] = function(c) -- 花丛 c.rect(0, 0, 16, 16, PAL.grass) c.rect(1, 2, 14, 12, PAL.leaf) c.px(1, 2, PAL.grass); c.px(14, 2, PAL.grass); c.px(1, 13, PAL.grass); c.px(14, 13, PAL.grass) c.px(4, 5, PAL.pink); c.px(8, 4, PAL.yellow); c.px(12, 6, PAL.white); c.px(3, 9, PAL.yellow) c.px(7, 8, PAL.pink); c.px(11, 10, PAL.pink); c.px(5, 12, PAL.white); c.px(10, 12, PAL.yellow) end T[11] = function(c) -- 莓果丛(有果) c.rect(0, 0, 16, 16, PAL.grass) c.rect(2, 3, 12, 11, PAL.leaf); c.rect(3, 2, 10, 13, PAL.leaf) c.dither(3, 3, 10, 10, PAL.leafD, 3) c.px(5, 5, PAL.berry); c.px(10, 4, PAL.berry); c.px(7, 8, PAL.berry); c.px(11, 9, PAL.berry); c.px(4, 10, PAL.berry) end T[12] = function(c) -- 莓果丛(已采) c.rect(0, 0, 16, 16, PAL.grass) c.rect(2, 3, 12, 11, PAL.leaf); c.rect(3, 2, 10, 13, PAL.leaf) c.dither(3, 3, 10, 10, PAL.leafD, 3) end T[13] = function(c) -- 树·左上 c.rect(0, 0, 16, 16, PAL.grass) c.rect(4, 2, 12, 14, PAL.leaf) c.px(4, 2, PAL.grass); c.px(5, 2, PAL.grass); c.px(4, 3, PAL.grass) c.dither(5, 4, 11, 12, PAL.leafD, 4); c.px(8, 5, PAL.grassL); c.px(12, 8, PAL.grassL) end T[14] = function(c) -- 树·右上 c.rect(0, 0, 16, 16, PAL.grass) c.rect(0, 2, 12, 14, PAL.leaf) c.px(11, 2, PAL.grass); c.px(10, 2, PAL.grass); c.px(11, 3, PAL.grass) c.dither(0, 4, 11, 12, PAL.leafD, 4); c.px(4, 6, PAL.grassL) end T[15] = function(c) -- 树·左下 c.rect(0, 0, 16, 16, PAL.grass) c.rect(4, 0, 12, 6, PAL.leaf); c.dither(4, 0, 12, 6, PAL.leafD, 4) c.px(4, 5, PAL.grass) c.rect(10, 6, 6, 10, PAL.trunk); c.vline(10, 6, 10, PAL.trunkD) end T[16] = function(c) -- 树·右下 c.rect(0, 0, 16, 16, PAL.grass) c.rect(0, 0, 12, 6, PAL.leaf); c.dither(0, 0, 12, 6, PAL.leafD, 4) c.px(11, 5, PAL.grass) c.rect(0, 6, 6, 10, PAL.trunk); c.vline(5, 6, 10, PAL.trunkD) end T[17] = function(c) -- 小灌木 c.rect(0, 0, 16, 16, PAL.grass) c.rect(3, 5, 10, 9, PAL.leaf); c.rect(4, 4, 8, 11, PAL.leaf) c.dither(4, 5, 8, 8, PAL.leafD, 3) end T[18] = function(c) -- 房屋外墙 c.rect(0, 0, 16, 16, PAL.wall) c.hline(0, 15, 16, PAL.wallD); c.vline(0, 0, 16, PAL.wallD) c.px(4, 4, PAL.wallD); c.px(11, 9, PAL.wallD) end T[19] = function(c) -- 外墙+窗 T[18](c) c.rect(4, 4, 8, 8, PAL.woodD); c.rect(5, 5, 6, 6, PAL.sky) c.vline(7, 5, 6, PAL.woodD); c.hline(5, 8, 6, PAL.woodD) c.px(6, 6, PAL.waterL) end T[20] = function(c) -- 门 c.rect(0, 0, 16, 16, PAL.wall) c.rect(3, 1, 10, 15, PAL.wood); c.rect(4, 2, 8, 14, PAL.woodL) c.vline(4, 2, 14, PAL.wood); c.px(10, 9, PAL.woodD); c.hline(4, 2, 8, PAL.wood) end T[21] = function(c) -- 屋顶·左 c.rect(0, 0, 16, 16, PAL.roof) c.hline(0, 0, 16, PAL.roofD); c.hline(0, 5, 16, PAL.roofD); c.hline(0, 10, 16, PAL.roofD); c.hline(0, 15, 16, PAL.roofD) c.vline(0, 0, 16, PAL.roofD) end T[22] = function(c) -- 屋顶·右 c.rect(0, 0, 16, 16, PAL.roof) c.hline(0, 0, 16, PAL.roofD); c.hline(0, 5, 16, PAL.roofD); c.hline(0, 10, 16, PAL.roofD); c.hline(0, 15, 16, PAL.roofD) c.vline(15, 0, 16, PAL.roofD) end T[23] = function(c) -- 屋檐 c.rect(0, 0, 16, 8, PAL.roof); c.hline(0, 7, 16, PAL.roofD); c.hline(0, 6, 16, PAL.roofD) c.rect(0, 8, 16, 8, PAL.wall); c.hline(0, 8, 16, PAL.wallD) end T[24] = function(c) -- 木地板 c.rect(0, 0, 16, 16, PAL.floor) c.hline(0, 4, 16, PAL.floorD); c.hline(0, 9, 16, PAL.floorD); c.hline(0, 14, 16, PAL.floorD) c.px(5, 2, PAL.floorD); c.px(12, 7, PAL.floorD); c.px(3, 12, PAL.floorD) end T[25] = function(c) -- 木地板2 c.rect(0, 0, 16, 16, PAL.floor) c.hline(0, 2, 16, PAL.floorD); c.hline(0, 7, 16, PAL.floorD); c.hline(0, 12, 16, PAL.floorD) c.px(9, 4, PAL.floorD); c.px(2, 9, PAL.floorD) end T[26] = function(c) -- 诊所地砖 c.rect(0, 0, 16, 16, PAL.clinic) c.hline(0, 7, 16, PAL.clinicD); c.vline(7, 0, 16, PAL.clinicD); c.hline(0, 15, 16, PAL.clinicD); c.vline(15, 0, 16, PAL.clinicD) end T[27] = function(c) -- 室内墙 c.rect(0, 0, 16, 16, PAL.inwall) c.dither(0, 0, 16, 16, PAL.inwallD, 8) end T[28] = function(c) -- 室内墙裙 c.rect(0, 0, 16, 10, PAL.inwall) c.rect(0, 10, 16, 6, PAL.wood); c.hline(0, 10, 16, PAL.woodD) end T[29] = function(c) -- 地毯 c.rect(0, 0, 16, 16, PAL.pink) c.rect(1, 1, 14, 14, C("ffc4de")); c.rect(3, 3, 10, 10, PAL.pink) c.px(7, 7, PAL.white); c.px(8, 8, PAL.white) end T[30] = function(c) -- 床头(枕头) c.rect(0, 0, 16, 16, PAL.wood) c.rect(1, 1, 14, 15, C("f8f4e8")) c.rect(3, 3, 10, 7, PAL.white); c.hline(3, 9, 10, C("d8d4c8")) c.hline(1, 0, 14, PAL.woodD) end T[31] = function(c) -- 床尾(被子) c.rect(0, 0, 16, 16, PAL.wood) c.rect(1, 0, 14, 15, C("8ec8e0")) c.hline(1, 3, 14, C("6fb0d0")); c.hline(1, 8, 14, C("6fb0d0")); c.hline(1, 13, 14, C("6fb0d0")) c.hline(1, 15, 14, PAL.woodD) end T[32] = function(c) -- 柜台 c.rect(0, 0, 16, 16, PAL.wood) c.rect(0, 0, 16, 6, PAL.woodL); c.hline(0, 6, 16, PAL.woodD) c.vline(0, 0, 16, PAL.woodD); c.vline(15, 0, 16, PAL.woodD); c.hline(0, 15, 16, PAL.woodD) end T[33] = function(c) -- 烤炉 c.rect(0, 0, 16, 16, PAL.stoneD) c.rect(1, 1, 14, 14, PAL.stone) c.rect(3, 6, 10, 7, PAL.black) c.rect(4, 8, 8, 4, C("f4924a")); c.px(6, 9, PAL.yellow); c.px(9, 10, PAL.yellow) c.hline(3, 3, 10, PAL.stoneD) end T[34] = function(c) -- 货架(空) c.rect(0, 0, 16, 16, PAL.wood) c.rect(1, 1, 14, 14, PAL.woodL) c.hline(1, 5, 14, PAL.woodD); c.hline(1, 10, 14, PAL.woodD); c.hline(1, 14, 14, PAL.woodD) c.vline(0, 0, 16, PAL.woodD); c.vline(15, 0, 16, PAL.woodD) end T[35] = function(c) -- 货架(同一商品重复排列,标签空白) T[34](c) for _, y in ipairs({ 2, 7, 11 }) do c.rect(2, y, 3, 3, PAL.berry); c.rect(6, y, 3, 3, PAL.berry); c.rect(10, y, 3, 3, PAL.berry) c.px(3, y, PAL.white); c.px(7, y, PAL.white); c.px(11, y, PAL.white) end end T[36] = function(c) -- 桌子 c.rect(0, 0, 16, 16, PAL.floor) c.rect(2, 3, 12, 9, PAL.woodL); c.hline(2, 11, 12, PAL.woodD) c.vline(3, 12, 3, PAL.woodD); c.vline(12, 12, 3, PAL.woodD) c.hline(2, 3, 12, PAL.wood) end T[37] = function(c) -- 椅子 c.rect(0, 0, 16, 16, PAL.floor) c.rect(4, 3, 8, 3, PAL.wood) c.rect(4, 6, 8, 6, PAL.woodL) c.vline(4, 12, 3, PAL.woodD); c.vline(11, 12, 3, PAL.woodD) end T[38] = function(c) -- 井·顶(小屋顶) c.rect(0, 0, 16, 16, PAL.grass) c.rect(2, 2, 12, 6, PAL.roofD); c.rect(3, 1, 10, 2, PAL.roof) c.vline(3, 8, 8, PAL.trunkD); c.vline(12, 8, 8, PAL.trunkD) end T[39] = function(c) -- 井·底(石圈) c.rect(0, 0, 16, 16, PAL.grass) c.rect(2, 2, 12, 12, PAL.stone) c.rect(4, 4, 8, 8, PAL.black) c.px(3, 3, PAL.stoneD); c.px(12, 3, PAL.stoneD); c.px(3, 12, PAL.stoneD); c.px(12, 12, PAL.stoneD) c.hline(2, 2, 12, PAL.stoneD) end T[40] = function(c) -- 雾(轻) c.rect(0, 0, 16, 16, PAL.fog) c.dither(0, 0, 16, 16, PAL.fogD, 3) end T[41] = function(c) -- 雾(浓) c.rect(0, 0, 16, 16, PAL.fogD) c.dither(0, 0, 16, 16, PAL.fog, 2); c.dither(1, 0, 16, 16, C("bcc8d2"), 5) end T[42] = function(c) -- 白色层·墙 c.rect(0, 0, 16, 16, PAL.wgray) c.hline(0, 12, 16, PAL.wgrayD); c.rect(0, 13, 16, 3, PAL.wgrayDD) end T[43] = function(c) -- 白色层·地 c.rect(0, 0, 16, 16, PAL.wgrayD) c.hline(0, 7, 16, PAL.wgrayDD); c.vline(7, 0, 16, PAL.wgrayDD) end T[44] = function(c) -- 白色层·门 c.rect(0, 0, 16, 16, PAL.wgray) c.rect(3, 1, 10, 15, PAL.wgrayDD); c.rect(4, 2, 8, 14, PAL.wgrayD) c.px(10, 9, PAL.wdark) end T[45] = function(c) -- 长椅·左 c.rect(0, 0, 16, 16, PAL.grass) c.rect(2, 4, 14, 3, PAL.woodL); c.rect(2, 7, 14, 4, PAL.wood) c.vline(3, 11, 3, PAL.woodD); c.hline(2, 7, 14, PAL.woodD) end T[46] = function(c) -- 长椅·右 c.rect(0, 0, 16, 16, PAL.grass) c.rect(0, 4, 14, 3, PAL.woodL); c.rect(0, 7, 14, 4, PAL.wood) c.vline(12, 11, 3, PAL.woodD); c.hline(0, 7, 14, PAL.woodD) end T[47] = function(c) -- 公告板·左 c.rect(0, 0, 16, 16, PAL.grass) c.rect(2, 1, 14, 12, PAL.wood); c.rect(4, 3, 12, 8, C("f8f0d8")) c.hline(5, 5, 8, PAL.stoneD); c.hline(5, 7, 6, PAL.stoneD); c.hline(5, 9, 9, PAL.stoneD) c.vline(4, 13, 3, PAL.trunkD) end T[48] = function(c) -- 公告板·右 c.rect(0, 0, 16, 16, PAL.grass) c.rect(0, 1, 14, 12, PAL.wood); c.rect(0, 3, 12, 8, C("f8f0d8")) c.hline(1, 5, 8, PAL.stoneD); c.hline(1, 7, 9, PAL.stoneD); c.hline(1, 9, 5, PAL.stoneD) c.vline(11, 13, 3, PAL.trunkD) end T[49] = function(c) -- 邮筒 c.rect(0, 0, 16, 16, PAL.grass) c.rect(4, 2, 8, 10, PAL.red); c.rect(5, 1, 6, 2, C("c04040")) c.rect(6, 5, 4, 2, PAL.black) c.vline(6, 12, 3, C("c04040")); c.vline(9, 12, 3, C("c04040")) end T[50] = function(c) -- 栅栏·横 c.rect(0, 0, 16, 16, PAL.grass) c.hline(0, 6, 16, PAL.woodL); c.hline(0, 7, 16, PAL.wood); c.hline(0, 10, 16, PAL.woodL); c.hline(0, 11, 16, PAL.wood) c.rect(2, 4, 2, 10, PAL.wood); c.rect(12, 4, 2, 10, PAL.wood) c.px(2, 4, PAL.woodL); c.px(12, 4, PAL.woodL) end T[51] = function(c) -- 栅栏·柱 c.rect(0, 0, 16, 16, PAL.grass) c.rect(6, 2, 3, 12, PAL.wood); c.px(6, 2, PAL.woodL); c.px(7, 2, PAL.woodL) end T[52] = function(c) -- 路牌 c.rect(0, 0, 16, 16, PAL.grass) c.rect(2, 3, 12, 6, PAL.woodL); c.hline(3, 5, 10, PAL.woodD); c.hline(3, 7, 7, PAL.woodD) c.rect(7, 9, 2, 6, PAL.wood) end T[53] = function(c) -- 野餐布1 c.rect(0, 0, 16, 16, C("f8f0e0")) for j = 0, 15 do for i = 0, 15 do if (math.floor(i / 4) + math.floor(j / 4)) % 2 == 0 then c.px(i, j, C("f0a0a8")) end end end end T[54] = function(c) -- 野餐布2(有食物) T[53](c) c.rect(5, 5, 6, 6, C("fff8e8")); c.px(7, 7, PAL.berry); c.px(9, 8, PAL.berry); c.px(6, 9, PAL.yellow) end T[55] = function(c) -- 花坛(盛开) c.rect(0, 0, 16, 16, PAL.grass) c.rect(1, 1, 14, 14, PAL.stoneD); c.rect(2, 2, 12, 12, C("8a6a48")) c.px(4, 4, PAL.pink); c.px(8, 5, PAL.yellow); c.px(11, 4, PAL.red); c.px(5, 8, PAL.yellow) c.px(9, 9, PAL.pink); c.px(12, 8, PAL.white); c.px(4, 11, PAL.red); c.px(8, 12, PAL.pink); c.px(11, 11, PAL.yellow) end T[56] = function(c) -- 花田·土 c.rect(0, 0, 16, 16, C("a8825a")) c.hline(0, 3, 16, C("94714c")); c.hline(0, 8, 16, C("94714c")); c.hline(0, 13, 16, C("94714c")) end T[57] = function(c) -- 花田·苗 T[56](c) c.px(3, 2, PAL.leaf); c.px(3, 1, PAL.leaf); c.px(9, 7, PAL.leaf); c.px(9, 6, PAL.leaf); c.px(13, 12, PAL.leaf); c.px(13, 11, PAL.leaf) c.px(4, 1, PAL.pink); c.px(10, 6, PAL.yellow) end T[58] = function(c) -- 彩带(挂起) c.rect(0, 0, 16, 16, pc.rgba(0, 0, 0, 0)) for i = 0, 15 do local y = math.floor(2 + 2 * math.sin(i / 4)) c.px(i, y + 1, PAL.woodD) end c.px(2, 4, PAL.red); c.px(2, 5, PAL.red); c.px(6, 5, PAL.yellow); c.px(6, 6, PAL.yellow) c.px(10, 4, PAL.pink); c.px(10, 5, PAL.pink); c.px(14, 5, PAL.sky); c.px(14, 6, PAL.sky) end T[59] = function(c) -- 灯笼杆 c.rect(0, 0, 16, 16, PAL.grass) c.rect(7, 2, 2, 12, PAL.trunk) c.rect(5, 2, 6, 5, PAL.yellow); c.rect(6, 1, 4, 1, PAL.roofD); c.px(7, 4, C("fff0a0")); c.px(8, 4, C("fff0a0")) end T[60] = function(c) -- 湖水·波光 T[7](c) c.px(4, 6, PAL.white); c.px(11, 8, PAL.white); c.hline(7, 2, 3, PAL.waterL) end T[61] = function(c) -- 室内窗 T[27](c) c.rect(3, 3, 10, 8, PAL.woodD); c.rect(4, 4, 8, 6, PAL.sky) c.vline(7, 4, 6, PAL.woodD); c.px(5, 5, PAL.waterL) end T[62] = function(c) -- 白色层·病床头 c.rect(0, 0, 16, 16, PAL.wgrayD) c.rect(1, 1, 14, 15, PAL.wgray) c.rect(3, 3, 10, 6, PAL.white); c.hline(3, 8, 10, PAL.wgrayD) c.hline(1, 0, 14, PAL.wgrayDD) end T[63] = function(c) -- 白色层·病床尾 c.rect(0, 0, 16, 16, PAL.wgrayD) c.rect(1, 0, 14, 15, C("dfe2e6")) c.hline(1, 4, 14, PAL.wgrayD); c.hline(1, 9, 14, PAL.wgrayD) c.hline(1, 15, 14, PAL.wgrayDD) end T[64] = function(c) -- 桌(诊所/办公) c.rect(0, 0, 16, 16, PAL.clinic) c.rect(1, 3, 14, 9, PAL.woodL); c.hline(1, 11, 14, PAL.woodD); c.hline(1, 3, 14, PAL.wood) c.rect(3, 5, 6, 4, PAL.white); c.hline(4, 6, 4, PAL.stoneD); c.hline(4, 7, 3, PAL.stoneD) c.vline(2, 12, 3, PAL.woodD); c.vline(13, 12, 3, PAL.woodD) end T[65] = function(c) -- 日历(墙面) T[27](c) c.rect(4, 2, 8, 11, PAL.white); c.rect(4, 2, 8, 3, PAL.red) c.hline(5, 7, 6, PAL.stoneD); c.hline(5, 9, 6, PAL.stoneD); c.rect(7, 10, 3, 2, PAL.black) end T[66] = function(c) -- 柜台+配方纸 T[32](c) c.rect(4, 1, 7, 5, C("f8f0d8")); c.hline(5, 2, 5, PAL.stoneD); c.hline(5, 4, 4, PAL.stoneD) end T[67] = function(c) -- 木箱 c.rect(0, 0, 16, 16, PAL.grass) c.rect(2, 3, 12, 12, PAL.wood); c.rect(3, 4, 10, 10, PAL.woodL) c.hline(3, 8, 10, PAL.wood); c.vline(8, 4, 10, PAL.wood) end T[68] = function(c) -- 篮子 c.rect(0, 0, 16, 16, PAL.floor) c.rect(3, 6, 10, 8, PAL.woodL); c.hline(3, 6, 10, PAL.woodD); c.hline(3, 13, 10, PAL.woodD) c.dither(4, 8, 8, 5, PAL.wood, 2) c.px(6, 5, PAL.berry); c.px(9, 5, PAL.berry) end T[69] = function(c) -- 花坛(干涸·待浇水) c.rect(0, 0, 16, 16, PAL.grass) c.rect(1, 1, 14, 14, PAL.stoneD); c.rect(2, 2, 12, 12, C("b09068")) c.px(5, 5, C("8a7a5a")); c.px(10, 7, C("8a7a5a")); c.px(6, 10, C("8a7a5a")) end T[70] = function(c) -- 白色层·窗 T[42](c) c.rect(4, 2, 8, 7, PAL.wgrayDD); c.rect(5, 3, 6, 5, C("c8ccd4")); c.vline(7, 3, 5, PAL.wgrayDD) end T[71] = function(c) -- 门垫 c.rect(0, 0, 16, 16, PAL.grass) c.rect(2, 4, 12, 8, C("c8a878")); c.rect(3, 5, 10, 6, C("dcc094")) end T[72] = function(c) -- 高草 c.rect(0, 0, 16, 16, PAL.grass) for i = 1, 14, 3 do c.vline(i, 6, 8, PAL.grassD); c.vline(i + 1, 8, 6, PAL.grassL) end end T[73] = function(c) -- 广场中心砖 c.rect(0, 0, 16, 16, PAL.path) c.rect(2, 2, 12, 12, C("f0c88a")); c.rect(5, 5, 6, 6, PAL.path) c.px(7, 7, PAL.roofD); c.px(8, 8, PAL.roofD) end T[74] = function(c) -- 白色层·帘 c.rect(0, 0, 16, 16, PAL.wgray) for i = 0, 15, 3 do c.vline(i, 0, 16, C("d8dade")) end c.hline(0, 0, 16, PAL.wgrayDD) end T[75] = function(c) -- 白色层·监护仪 c.rect(0, 0, 16, 16, PAL.wgrayD) c.rect(3, 2, 10, 9, PAL.wdark); c.rect(4, 3, 8, 6, C("354048")) c.px(5, 6, C("7ae0a0")); c.px(6, 5, C("7ae0a0")); c.px(7, 6, C("7ae0a0")); c.px(8, 6, C("7ae0a0")); c.px(9, 4, C("7ae0a0")); c.px(10, 6, C("7ae0a0")) c.rect(6, 11, 4, 4, PAL.wgrayDD) end T[76] = function(c) -- 石墙(村界) c.rect(0, 0, 16, 16, PAL.stone) c.hline(0, 5, 16, PAL.stoneD); c.hline(0, 11, 16, PAL.stoneD) c.vline(4, 0, 5, PAL.stoneD); c.vline(10, 6, 5, PAL.stoneD); c.vline(6, 12, 4, PAL.stoneD) end T[77] = function(c) -- 雾中草地(边缘) c.rect(0, 0, 16, 16, PAL.grass) c.dither(0, 0, 16, 16, PAL.fog, 2) end T[78] = function(c) -- 黑 c.rect(0, 0, 16, 16, PAL.black) end T[79] = function(c) -- 纯白灰 c.rect(0, 0, 16, 16, PAL.wgray) end local tilesImg = Image(160, 128, ColorMode.RGB) for idx = 0, 79 do local col, row = idx % 10, math.floor(idx / 10) T[idx](makeCtx(tilesImg, col * 16, row * 16)) end saveSheet(tilesImg, "tiles") -- ============ 主角:16×24 × 16 帧(下/上/左/右 × 4) ============ local SKIN = C("f6d2a8") local HAIR = C("6b4632") local SHIRT = C("e87858") local SHIRT_D = C("cc5f42") local PANTS = C("4a6a9a") local SHOE = C("7a4a2a") -- 画一帧主角;dir: 0下 1上 2左 3右;f: 0..3 行走帧(0 兼待机) local function paintPlayer(c, dir, f) -- 头(x4..11, y0..7):发 0..2,脸 3..7 c.rect(4, 0, 8, 3, HAIR) c.px(4, 0, pc.rgba(0, 0, 0, 0)); c.px(11, 0, pc.rgba(0, 0, 0, 0)) if dir == 1 then c.rect(4, 3, 8, 5, HAIR) -- 背面全是头发 c.px(4, 7, SKIN); c.px(11, 7, SKIN) else c.rect(4, 3, 8, 5, SKIN) c.vline(4, 3, 2, HAIR); c.vline(11, 3, 2, HAIR) -- 刘海两侧 if dir == 0 then c.px(6, 5, PAL.black); c.px(9, 5, PAL.black) c.px(7, 7, PAL.black); c.px(8, 7, PAL.black) -- 永远的微笑 c.px(6, 6, C("f8b8a0")); c.px(9, 6, C("f8b8a0")) elseif dir == 2 then c.rect(9, 3, 3, 5, HAIR) -- 侧脸:后脑 c.px(6, 5, PAL.black); c.px(5, 7, PAL.black); c.px(6, 7, PAL.black) elseif dir == 3 then c.rect(4, 3, 3, 5, HAIR) c.px(9, 5, PAL.black); c.px(9, 7, PAL.black); c.px(10, 7, PAL.black) end end -- 身体(y8..16) c.rect(5, 8, 6, 9, SHIRT) c.hline(5, 16, 6, SHIRT_D) -- 手臂:随帧摆动 local sway = ({ 0, 1, 0, -1 })[f + 1] if dir == 0 or dir == 1 then c.vline(4, 9 + (sway > 0 and 1 or 0), 5, SHIRT_D); c.px(4, 14 + (sway > 0 and 1 or 0), SKIN) c.vline(11, 9 + (sway < 0 and 1 or 0), 5, SHIRT_D); c.px(11, 14 + (sway < 0 and 1 or 0), SKIN) else c.vline(dir == 2 and 7 or 8, 9 + math.abs(sway), 5, SHIRT_D) c.px(dir == 2 and 7 or 8, 14 + math.abs(sway), SKIN) end -- 腿(y17..20)与鞋(y21..23) local lLift = (f == 1) and 1 or 0 local rLift = (f == 3) and 1 or 0 c.rect(5, 17, 3, 4 - lLift, PANTS) c.rect(8, 17, 3, 4 - rLift, PANTS) c.rect(5, 21 - lLift, 3, 2, SHOE) c.rect(8, 21 - rLift, 3, 2, SHOE) end local playerImg = Image(256, 24, ColorMode.RGB) for dir = 0, 3 do for f = 0, 3 do paintPlayer(makeCtx(playerImg, (dir * 4 + f) * 16, 0), dir, f) end end saveSheet(playerImg, "player") -- ============ 主角头像:24×24(微笑 / 真实的脸) ============ local function paintPortrait(c, real) c.rect(0, 0, 24, 24, C("f8ecd0")) -- 背景 c.rect(4, 2, 16, 6, HAIR) -- 头发 c.px(4, 2, C("f8ecd0")); c.px(19, 2, C("f8ecd0")) c.rect(4, 8, 16, 12, SKIN) -- 脸 c.vline(4, 8, 4, HAIR); c.vline(5, 8, 2, HAIR) c.vline(19, 8, 4, HAIR); c.vline(18, 8, 2, HAIR) c.rect(6, 20, 12, 4, SHIRT) -- 肩 if real then -- 真实的脸:疲惫——平直的嘴、眼下阴影、微乱的头发 c.px(3, 4, HAIR); c.px(20, 5, HAIR); c.px(2, 5, HAIR) c.rect(8, 12, 2, 2, PAL.black); c.rect(14, 12, 2, 2, PAL.black) c.hline(8, 15, 2, C("c8a080")); c.hline(14, 15, 2, C("c8a080")) -- 眼袋 c.hline(10, 18, 4, C("9a6a52")) -- 平直疲惫的嘴 else -- 永远的微笑 c.rect(8, 12, 2, 2, PAL.black); c.rect(14, 12, 2, 2, PAL.black) c.px(7, 14, C("f8b8a0")); c.px(16, 14, C("f8b8a0")) -- 腮红 c.px(9, 17, C("9a6a52")); c.px(14, 17, C("9a6a52")) c.hline(10, 18, 4, C("9a6a52")) -- 上弯的微笑 end end local p1 = Image(24, 24, ColorMode.RGB); paintPortrait(makeCtx(p1, 0, 0), false); saveSheet(p1, "portrait_smile") local p2 = Image(24, 24, ColorMode.RGB); paintPortrait(makeCtx(p2, 0, 0), true); saveSheet(p2, "portrait_real") -- ============ 具名 NPC:16×24 × 4 帧(smile/flat/off/blink) ============ -- off 档与 smile 档仅差 1–2 像素(右眼低 1 像素),绝不画成鬼脸。 local function paintAnimal(c, spec, expr) local top = spec.headTop or 2 -- 头 c.rect(2, top, 12, 10, spec.fur) c.px(2, top, pc.rgba(0, 0, 0, 0)); c.px(13, top, pc.rgba(0, 0, 0, 0)) c.px(2, top + 9, pc.rgba(0, 0, 0, 0)); c.px(13, top + 9, pc.rgba(0, 0, 0, 0)) -- 口鼻 c.rect(5, top + 5, 6, 4, spec.muzzle) -- 眼睛 local ey = top + 4 if expr == "blink" then c.px(5, ey + 1, PAL.black); c.px(10, ey + 1, PAL.black) else c.px(5, ey, PAL.black); c.px(5, ey + 1, PAL.black) local roff = (expr == "off") and 1 or 0 -- 崩坏档:右眼低 1 像素 c.px(10, ey + roff, PAL.black); c.px(10, ey + 1 + roff, PAL.black) end -- 鼻子 if spec.nose then c.px(7, top + 6, PAL.black); c.px(8, top + 6, PAL.black) end -- 嘴:微笑(小 U)或平直 local my = top + 8 if expr == "flat" then c.hline(6, my, 4, PAL.black) else c.px(6, my - 1, PAL.black); c.px(9, my - 1, PAL.black); c.px(7, my, PAL.black); c.px(8, my, PAL.black) end -- 身体 c.rect(4, 12, 8, 8, spec.cloth) -- 手 c.vline(3, 13, 4, spec.fur); c.vline(12, 13, 4, spec.fur) -- 脚 c.rect(4, 20, 3, 3, spec.furD); c.rect(9, 20, 3, 3, spec.furD) if spec.extra then spec.extra(c, expr) end end local NPCS = { { file = "npc_afu", fur = C("c49058"), furD = C("a87840"), muzzle = C("ecd0a8"), cloth = C("e0b060"), nose = true, extra = function(c) -- 狗村长:垂耳+红领巾 c.rect(1, 3, 2, 5, C("a87840")); c.rect(13, 3, 2, 5, C("a87840")) c.hline(4, 12, 8, PAL.red); c.px(7, 13, PAL.red); c.px(8, 13, PAL.red) end }, { file = "npc_yuanyuan", fur = C("f7f2ea"), furD = C("ddd4c8"), muzzle = C("ffffff"), cloth = C("f8c8d8"), headTop = 4, extra = function(c) -- 兔面包师:长耳+围裙 c.rect(4, 0, 2, 4, C("f7f2ea")); c.px(5, 1, C("f8b8c8")); c.px(5, 2, C("f8b8c8")) c.rect(10, 0, 2, 4, C("f7f2ea")); c.px(10, 1, C("f8b8c8")); c.px(10, 2, C("f8b8c8")) c.rect(5, 14, 6, 6, C("fff8f0")); c.px(6, 10, C("f0a0b0")) -- 围裙+腮红 c.px(9, 10, C("f0a0b0")) end }, { file = "npc_dabao", fur = C("9a6b42"), furD = C("7e5530"), muzzle = C("c89a68"), cloth = C("5a88c8"), nose = true, extra = function(c) -- 熊邮差:圆耳+挎包带 c.rect(3, 0, 3, 2, C("9a6b42")); c.rect(10, 0, 3, 2, C("9a6b42")) c.px(4, 1, C("c89a68")); c.px(11, 1, C("c89a68")) for i = 0, 6 do c.px(4 + i, 13 + i > 19 and 19 or 13 + math.floor(i * 0.9), C("3a5a88")) end end }, { file = "npc_mimi", fur = C("c8c0d8"), furD = C("a8a0c0"), muzzle = C("ece8f4"), cloth = C("e8a04a"), nose = true, extra = function(c) -- 猫店主:尖耳+胡须 c.px(3, 1, C("c8c0d8")); c.rect(3, 2, 2, 1, C("c8c0d8")); c.px(4, 1, C("c8c0d8")) c.px(12, 1, C("c8c0d8")); c.rect(11, 2, 2, 1, C("c8c0d8")); c.px(11, 1, C("c8c0d8")) c.hline(1, 8, 2, C("8a84a0")); c.hline(13, 8, 2, C("8a84a0")) end }, { file = "npc_fu", fur = C("d8b088"), furD = C("bc9268"), muzzle = C("f0d8b8"), cloth = C("f4f4f4"), nose = true, extra = function(c) -- 鹿医生:小角+白大褂 c.px(4, 1, C("8a6a4a")); c.px(4, 0, C("8a6a4a")); c.px(3, 0, C("8a6a4a")) c.px(11, 1, C("8a6a4a")); c.px(11, 0, C("8a6a4a")); c.px(12, 0, C("8a6a4a")) c.vline(7, 12, 8, C("d8d8d8")) -- 大褂门襟 end }, { file = "npc_gui", fur = C("a8c078"), furD = C("88a058"), muzzle = C("c8d8a0"), cloth = C("7a9a4a"), extra = function(c) -- 龟爷爷:龟壳纹+老花镜 c.hline(5, 14, 6, C("5f7c38")); c.hline(5, 17, 6, C("5f7c38")); c.vline(7, 13, 6, C("5f7c38")) local gy = 2 + 4 c.px(4, gy, C("6a6a6a")); c.px(6, gy, C("6a6a6a")); c.px(9, gy, C("6a6a6a")); c.px(11, gy, C("6a6a6a")) c.px(7, gy, C("6a6a6a")); c.px(8, gy, C("6a6a6a")) end }, } for _, spec in ipairs(NPCS) do local img = Image(64, 24, ColorMode.RGB) local exprs = { "smile", "flat", "off", "blink" } for i, e in ipairs(exprs) do paintAnimal(makeCtx(img, (i - 1) * 16, 0), spec, e) end saveSheet(img, spec.file) end -- ============ 双子鸟:16×16 × 2 帧(闭嘴/张嘴) ============ local function paintBird(c, open) local B = C("6ab0e8") local BD = C("4a90c8") c.rect(4, 5, 8, 7, B) -- 身体 c.rect(8, 2, 6, 6, B) -- 头 c.px(11, 4, PAL.black) -- 眼 c.rect(2, 6, 3, 4, BD) -- 尾 c.rect(5, 7, 4, 3, BD) -- 翅 if open then c.px(14, 5, PAL.yellow); c.px(15, 4, PAL.yellow); c.px(15, 6, PAL.yellow) else c.px(14, 5, PAL.yellow); c.px(15, 5, PAL.yellow) end c.vline(7, 12, 2, PAL.yellow); c.vline(10, 12, 2, PAL.yellow) -- 脚 end local birdImg = Image(32, 16, ColorMode.RGB) paintBird(makeCtx(birdImg, 0, 0), false) paintBird(makeCtx(birdImg, 16, 0), true) saveSheet(birdImg, "bird") -- ============ 灰麻雀(E3 结局,一只真的鸟):16×16 ============ local function paintSparrow(c) local G = C("9a9088") local GD = C("6e6660") c.rect(4, 6, 8, 6, G) c.rect(8, 3, 6, 5, G) c.px(11, 5, PAL.black) c.rect(2, 7, 3, 3, GD) c.rect(5, 8, 4, 2, GD) c.px(14, 6, C("7a6a58")); c.px(15, 6, C("7a6a58")) c.vline(7, 12, 2, GD); c.vline(10, 12, 2, GD) end local sparrowImg = Image(16, 16, ColorMode.RGB) paintSparrow(makeCtx(sparrowImg, 0, 0)) saveSheet(sparrowImg, "sparrow") -- ============ 群演 ×5:16×16 × 2 帧循环 ============ local function paintCritter(c, spec, f) local bob = f -- 第二帧整体下移 1px 形成循环 c.rect(4, 6 + bob, 8, 6 - bob, spec.body) c.rect(5, 2 + bob, 6, 5, spec.body) c.px(6, 4 + bob, PAL.black); c.px(9, 4 + bob, PAL.black) c.px(7, 6 + bob, spec.snout); c.px(8, 6 + bob, spec.snout) c.rect(4, 12, 3, 2, spec.dark); c.rect(9, 12, 3, 2, spec.dark) if spec.extra then spec.extra(c, f) end end local CRITTERS = { { file = "extra1", body = C("e0904a"), dark = C("b87030"), snout = C("f8c890"), -- 松鼠:大尾巴 extra = function(c, f) c.rect(12, 1 + f, 3, 9, C("b87030")); c.px(13, 2 + f, C("e0904a")) end }, { file = "extra2", body = C("b09068"), dark = C("8a6a48"), snout = C("e8cca8"), -- 刺猬:背刺 extra = function(c, f) for i = 4, 11, 2 do c.px(i, 1 + f + (i % 4 == 0 and 0 or 1), C("6e5236")) end end }, { file = "extra3", body = C("f4e04a"), dark = C("d8b830"), snout = C("f4a030"), -- 鸭子:扁嘴 extra = function(c, f) c.px(6, 5 + f, C("f4a030")); c.px(9, 5 + f, C("f4a030")) end }, { file = "extra4", body = C("b8b8c4"), dark = C("8e8e9c"), snout = C("e8c8c8"), -- 老鼠:圆耳 extra = function(c, f) c.rect(4, 0 + f, 2, 2, C("b8b8c4")); c.rect(10, 0 + f, 2, 2, C("b8b8c4")) end }, { file = "extra5", body = C("7ac86a"), dark = C("58a048"), snout = C("aee89e"), -- 青蛙:凸眼 extra = function(c, f) c.px(5, 1 + f, C("7ac86a")); c.px(10, 1 + f, C("7ac86a")) end }, } for _, spec in ipairs(CRITTERS) do local img = Image(32, 16, ColorMode.RGB) paintCritter(makeCtx(img, 0, 0), spec, 0) paintCritter(makeCtx(img, 16, 0), spec, 1) saveSheet(img, spec.file) end -- ============ 向日葵表盘:24×24 × 14 帧 ============ -- 帧 0..12 = 点亮花瓣数;帧 13 = 满格+花心黑籽 local function paintSunflower(c, lit, seeds) local cx, cy = 11.5, 11.5 for k = 0, 11 do local ang = (k / 12) * math.pi * 2 - math.pi / 2 local col = (k < lit) and PAL.yellow or C("7a7264") local colD = (k < lit) and C("e0b830") or C("635c50") for r = 7, 10 do local x = math.floor(cx + math.cos(ang) * r + 0.5) local y = math.floor(cy + math.sin(ang) * r + 0.5) c.px(x, y, r > 9 and colD or col) local px2 = math.floor(cx + math.cos(ang + 0.14) * r + 0.5) local py2 = math.floor(cy + math.sin(ang + 0.14) * r + 0.5) c.px(px2, py2, col) end end -- 花心 for j = -4, 4 do for i = -4, 4 do if i * i + j * j <= 18 then c.px(12 + i, 12 + j, C("9a6a2a")) end end end for j = -3, 3 do for i = -3, 3 do if i * i + j * j <= 9 then c.px(12 + i, 12 + j, C("b8843a")) end end end if seeds then -- 过熟的黑籽 c.px(11, 11, PAL.black); c.px(13, 12, PAL.black); c.px(12, 14, PAL.black) c.px(10, 13, PAL.black); c.px(14, 10, PAL.black); c.px(12, 11, C("4a3a20")) end end local sunImg = Image(24 * 14, 24, ColorMode.RGB) for f = 0, 12 do paintSunflower(makeCtx(sunImg, f * 24, 0), f, false) end paintSunflower(makeCtx(sunImg, 13 * 24, 0), 12, true) saveSheet(sunImg, "sunflower") print("ALL ART GENERATED OK")