Open4

Asepriteをプログラムで操作する方法

ailail

原理

Asperiteはピクセルアートを描くためのグラフィックスソフトですが、裏側でプログラムを動作させることが可能で、それによって通常Asepriteの有していない機能を利用することが可能になります。

記述言語はLua。

準備

  • まずは、適当なLuaプログラムをテキストエディタで編集します。(保存は.lua形式)
  • 次にそのプログラムをAseprite/scriptsフォルダ配下(OSによって異なる?)に置きます
    • Windows11では、C:¥User¥ユーザー名¥AppData¥Roaming¥Aseprite¥scriptsでした
    • Aseprite上部メニュー>File>Scripts>Open Scripts Folderをクリックすれば.luaファイルを置くべき場所がわかります
  • Asepriteを起動
  • Aseprite上部メニュー>File>Scriptsの内部にファイル名が記載されていれば成功
  • それをクリックすれば当該ファイルのluaで記述したプログラムが動作します
  • Aseprite上部メニュー>File>Scriptsの内部にファイル名が記載されていなければ、Aseprite上部メニュー>File>Scripts>RescanScripts Folder(ないしはF5キーを押下)をクリックしましょう
ailail

サンプル

以下は編集最中の画像を32x32サイズに分割し、それぞれ別名のpng形式で保存するプログラム
luaとかAsperiteのAPIよくわからん・・・

SpriteSplit.lua
local sprite = app.activeSprite
local width = 32 -- 分割する幅
local height = 32 -- 分割する高さ

for y = 0, sprite.height - 1, height do
  for x = 0, sprite.width - 1, width do
    local filename = string.format("SplitsResult/tile_%d_%d.png", y / height, x / width)
    sprite:saveCopyAs(filename)
  end
end

for y = 0, sprite.height - 1, height do
  for x = 0, sprite.width - 1, width do
    local filename = string.format("SplitsResult/tile_%d_%d.png", y / height, x / width)
    local newSprite = app.open(filename)
    local rect = Rectangle(x, y, math.min(width, sprite.width - x), math.min(height, sprite.height - y))
    newSprite:crop(rect)
    newSprite:saveAs(filename)
    newSprite:close()
  end
end

なお、ファイルの読み書きの基準はAseprite.exeの存在するフォルダ

ailail

編集最中の画像を対象に、Mainレイヤーに存在する現在のフォアグラウンドカラー(ペン色)と同じ色のピクセルを全て選択した後、選択したピクセルは白に、それ以外を黒に塗りつぶしたMaskレイヤーを作成する処理。(フレームが存在する場合は全フレームにその処理を繰り返す)

CreateMask.lua
local sprite = app.sprite -- アクティブなスプライトを取得

function main()
  if not sprite then
    app.alert("アクティブな画像ファイルが存在しません。")
    return
  end

  -- Mainレイヤーを探す
  local mainLayer = nil
  for _, layer in ipairs(sprite.layers) do
    if layer.name == "Main" then
      mainLayer = layer
      break
    end
  end
  if not mainLayer then
    app.alert("Mainレイヤーが見つかりません。")
    return
  end

  -- Maskレイヤーを取得または作成
  local maskLayer = nil
  for _, layer in ipairs(sprite.layers) do
    if layer.name == "Mask" then
      maskLayer = layer
      break
    end
  end
  if not maskLayer then
    maskLayer = sprite:newLayer()
    maskLayer.name = "Mask"
  end

  -- 記録色を取得
  local recordColor = app.fgColor
  local black = app.pixelColor.rgba(0, 0, 0, 255)
  local white = app.pixelColor.rgba(255, 255, 255, 255)

  for _, frame in ipairs(sprite.frames) do
    local mainCel = mainLayer:cel(frame)
    if not mainCel then goto continue end

    local mainImage = mainCel.image
    local offset = mainCel.position

    -- Mask画像を黒で塗りつぶし作成
    local maskImage = Image(sprite.width, sprite.height, ColorMode.RGBA)
    maskImage:clear(black)

    -- 指定色と一致するピクセルを白で描画(位置補正あり)
    for it in mainImage:pixels() do
      local pixelValue = mainImage:getPixel(it.x, it.y)

      local r = app.pixelColor.rgbaR(pixelValue)
      local g = app.pixelColor.rgbaG(pixelValue)
      local b = app.pixelColor.rgbaB(pixelValue)
      local a = app.pixelColor.rgbaA(pixelValue)

      if r == recordColor.red and g == recordColor.green and b == recordColor.blue and a == recordColor.alpha then
        local ex = it.x + offset.x
        local ey = it.y + offset.y

        if ex >= 0 and ey >= 0 and ex < maskImage.width and ey < maskImage.height then
          maskImage:putPixel(ex, ey, white)
        end
      end
    end

    -- MaskレイヤーのCelを作成または上書き
    local maskCel = maskLayer:cel(frame)
    if not maskCel then
      maskCel = sprite:newCel(maskLayer, frame, maskImage, Point(0, 0))
    else
      maskCel.image = maskImage
      maskCel.position = Point(0, 0)
    end
    maskCel.opacity = 255

    ::continue::
  end

  app.alert("処理が完了しました。")
end

main()

ailail

背景を単色(緑)で塗りつぶしたChromakeyレイヤーを作成する(フレームが存在する場合は全フレームにその処理を繰り返す)

CreateChromakey.lua
local sprite = app.sprite
if not sprite then
  app.alert("アクティブなスプライトがありません。")
  return
end

-- すでに存在している Chromakey レイヤーがあるか確認
local chromaLayer = nil
for _, layer in ipairs(sprite.layers) do
  if layer.name == "Chromakey" then
    chromaLayer = layer
    break
  end
end

-- なければ新規作成
if not chromaLayer then
  chromaLayer = sprite:newLayer()
  chromaLayer.name = "Chromakey"
end

-- 緑色 (r=0, g=255, b=0, a=255)
local green = app.pixelColor.rgba(0, 255, 0, 255)

for _, frame in ipairs(sprite.frames) do
  -- 全体を緑で塗ったイメージ作成
  local image = Image(sprite.width, sprite.height, ColorMode.RGBA)
  image:clear(green)

  -- 既存の Cel があれば更新、なければ新規作成
  local cel = chromaLayer:cel(frame)
  if cel then
    cel.image = image
    cel.position = Point(0, 0)
  else
    sprite:newCel(chromaLayer, frame, image, Point(0, 0))
  end
end

app.alert("Chromakeyレイヤーが作成され、背景が緑で塗られました。")