Open7
Godot4.4の自分用メモ書き

ボタンの中心で拡縮するだけのシェーダー
CC0
shader_type canvas_item;
uniform float scale : hint_range(0.1, 10.0, 0.1) = 1.0;
uniform vec2 pivot_point = vec2(0.5, 0.5);
uniform vec2 size = vec2(0,0);
void vertex() {
vec2 pivot_pixel = size * pivot_point;
VERTEX = VERTEX - pivot_pixel;
VERTEX = vec2((VERTEX.x * scale) + pivot_pixel.x,((VERTEX.y) * scale) + pivot_pixel.y);
}
使い方
拡縮する前にnode.sizeをシェーダーのsizeに渡す。
拡縮する際はscaleを好きなようにいじる。Tweenとかで。

Godot4以降でShoeBoxで作ったfntファイルを読み込む時は
common lineHeight=96 base=26 scaleW=256 scaleH=256 pages=1 packed=0 alphaChnl=0 redChnl=0 greenChnl=0 blueChnl=0
のalphaChnlを0にしないと色が読み込まれない。なんてこった。
参考 : https://github.com/godotengine/godot/issues/68458
なのでこういうスクリプトを作りました
エディタプラグインでボタン押すと自動的にいい感じにしてくれるようになんかこうしといてください
var dir : DirAccess = DirAccess.open("res://UI/Fonts/Bitmap/")
if dir:
for file_name in dir.get_files():
if file_name.ends_with(".fnt"):
var file : FileAccess = FileAccess.open("res://UI/Fonts/Bitmap/" + file_name,FileAccess.READ_WRITE)
var clean_file : String = ""
while file.get_position() < file.get_length():
var line : String = file.get_line()
# カラー対応させる
line = line.replace("alphaChnl=1","alphaChnl=0")
# ファイル名を相対パスにする
if line.ends_with('.png"'):
var regex : RegEx = RegEx.new()
regex.compile('(.*)(file=")(.*)')
line = regex.sub(line,"$1") + regex.sub(line,"$2") + regex.sub(line,"$3").get_file()
clean_file += line + "\n"
file.store_line(clean_file)
file.close()
var new_file : FileAccess = FileAccess.open("res://UI/Fonts/Bitmap/" + file_name,FileAccess.WRITE)
new_file.store_string(clean_file)
new_file.close()
await get_tree().process_frame
# 再インポートする
EditorInterface.get_resource_filesystem().reimport_files(["res://UI/Fonts/Bitmap/" + file_name])

乗算に関するあれこれ
- 乗算に設定したノードはmodulateでアルファをいじっても反応しないが、RGBを上げることで擬似的に薄くすることができる。
- アルファがカットアウトされているので、ぼかした丸とかを影として使いたい時は背景を白にするとよい。

なんかこう、いい感じにスプライトシートを切り出してSpriteFramesのリソースに変換するやつ
フォルダ構成は
- IMGs
- CharacterSprite
- After
- Before
- Resouce
- CharacterSprite
ファイル名は
- 32_うんたらかんたら.png
または
- 32x48_うんたらかんたら.png
になってて、Beforeに画像を入れてなんかこう、いい感じにエディタ内で実行するとResourceに.tresを吐き出してAfterに画像を移動させてくれる。
いい感じにエディタアドオンにしといてください。CC0です。
空フレームは追加されないようになってたり……なんなり……
func dir_png_find(path:String):
var dir : DirAccess = DirAccess.open(path)
if dir:
dir.list_dir_begin()
var file_name : String = dir.get_next()
while file_name != "":
if dir.current_is_dir():
dir_png_find(path + file_name + "/")
elif file_name.get_extension() == "png":
png_to_spriteframe(path + file_name)
elif file_name.get_extension() == "tres":
not_use_spriteframe_remove(path + file_name)
file_name = dir.get_next()
func png_to_spriteframe(path:String):
var img : CompressedTexture2D = load(path)
if img:
var file_name_Array : Array = Array(path.get_file().split("_"))
var cell_size_array : Array = file_name_Array.pop_front().split("x")
var clean_file_name_regex : RegEx = RegEx.new()
clean_file_name_regex.compile("[0-9]*x[0-9]*_(.*)")
# なんかもっとスマートな方法がほしい
var file_path_array : Array = Array(path.split("/"))
var ext : String = file_path_array.back().get_extension()
var file_name : String = file_path_array.pop_back().get_file().get_basename()
var folder_path : String = str("/".join(file_path_array)).replace("res://IMGs/CharacterSprite/Before","")
var exp_dir_path : String = "res://IMGs/CharacterSprite/Resource" + folder_path + "/"
var after_dir_path : String = "res://IMGs/CharacterSprite/After" + folder_path
var exp_dir : DirAccess = DirAccess.open("res://IMGs/")
var exp_file_name = file_name
while clean_file_name_regex.search(exp_file_name):
exp_file_name = clean_file_name_regex.sub(exp_file_name,"$1$2")
print(exp_file_name)
if cell_size_array.size() > 0:
# シートサイズの計算
var sprite_sheet : Vector2 = Vector2(1,1)
var sprite_sheet_size : String = file_name_Array.pop_front()
# 一コマ横x一コマ縦_シート横数xシート縦数の場合は代入して色々計算する
var sprite_sheet_regex : RegEx = RegEx.new()
sprite_sheet_regex.compile("[0-9]*x[0-9]")
if sprite_sheet_regex.search(sprite_sheet_size):
var sprite_sheet_array : Array = Array(sprite_sheet_size.split("x"))
sprite_sheet.x = int(sprite_sheet_array.pop_front())
sprite_sheet.y = int(sprite_sheet_array.pop_front())
var sheet_size : Rect2i = Rect2i(0,0,img.get_width() / sprite_sheet.x,img.get_height() / sprite_sheet.y)
var cell_size : Vector2 = Vector2(0,0)
if cell_size_array.size() == 1: # 32_うんたらかんたらの場合はVector2(32,32)で作成
cell_size = Vector2(1,1) * float(cell_size_array.front())
elif cell_size_array.size() == 2: # 32x48_うんたらかんたらの場合はVector2(32,48)で作成
cell_size = Vector2(float(cell_size_array.front()),float(cell_size_array.back()))
if cell_size != Vector2(0,0):
var sheet_id : int = 1
for sheet_y : int in sprite_sheet.y:
for sheet_x: int in sprite_sheet.x:
var sprite_frame : SpriteFrames = SpriteFrames.new()
sprite_frame.remove_animation("default")
#var sheet : AtlasTexture = ImageTexture.create_from_image( img.get_image().get_region(Rect2i(Vector2i(sheet_size.size.x * sheet_x,sheet_size.size.y * sheet_y),sheet_size.size)))
var sheet : AtlasTexture = AtlasTexture.new()
sheet.atlas = img
sheet.region = Rect2i(Vector2i(sheet_size.size.x * sheet_x,sheet_size.size.y * sheet_y),sheet_size.size)
await get_tree().process_frame
# 空シートは追加しない
if sheet.get_image().get_used_rect() != Rect2i(0,0,0,0):
for y : int in sheet.get_height() / cell_size.y:
var anim_name : String = str(y)
sprite_frame.add_animation(anim_name)
for x : int in img.get_width() / cell_size.x:
# 切り抜き
var atlas_texture : AtlasTexture = AtlasTexture.new()
atlas_texture.atlas = sheet
atlas_texture.region = Rect2(Vector2(cell_size.x * x,cell_size.y * y),cell_size)
#print(atlas_texture.get_size())
# 空セルは追加しない
if atlas_texture.get_image().get_used_rect() != Rect2i(0,0,0,0):
sprite_frame.add_frame(anim_name,atlas_texture,1.0,-1)
exp_dir.make_dir_recursive_absolute(exp_dir_path)
exp_dir.make_dir_recursive_absolute(after_dir_path)
var save_file_path : String = exp_dir_path + exp_file_name + "_" + str(sheet_id) + ".tres"
#すでに存在したら削除
if exp_dir.file_exists(save_file_path):
exp_dir.remove_absolute(save_file_path)
#sprite_frame.take_over_path(save_file_path)
ResourceSaver.save(sprite_frame,save_file_path)
await get_tree().process_frame
#not_use_spriteframe_remove(save_file_path)
sheet_id += 1
# 変換後はAfterフォルダに移動
if exp_dir.file_exists(after_dir_path + "/" + file_name + "." + ext):
exp_dir.remove_absolute(after_dir_path + "/" + file_name + "." + ext)
exp_dir.rename_absolute(path,after_dir_path + "/" + file_name + "." + ext)
# リロード
EditorInterface.get_resource_filesystem().scan_sources()
#Afterフォルダに画像がないリソースを削除します。pngだけにしてるけどwebp使うならそっちでも良いと思います。
func not_use_spriteframe_remove(path:String):
var file : FileAccess = FileAccess.open(path,FileAccess.READ)
if file.get_as_text().find("/Before/"):
var dir : DirAccess = DirAccess.open("res://")
var is_finished = false
var path_regex : RegEx = RegEx.new()
path_regex.compile('.*path="(.*\\.(png|webp))".*')
while is_finished == false:
var line : String = file.get_line()
if line.begins_with("[ext_resource"):
var img_path : String = path_regex.sub(line,"$1").replace("/Before/","/After/")
if dir.file_exists(img_path):
pass
else:
print("ないよ")
print(img_path)
file.close()
dir.remove_absolute(path)
await get_tree().process_frame
EditorInterface.get_resource_filesystem().scan_sources()
is_finished = true
if line.begins_with("[sub_resource"):
print("finished")
is_finished = true
if file.is_open():
var clean_txt : String = file.get_as_text().replace("/Before/","/After/")
file.close()
var clean_file : FileAccess = FileAccess.open(path,FileAccess.WRITE)
clean_file.store_string(clean_txt)
dir.remove_absolute(path)
if not EditorInterface.get_resource_filesystem().is_scanning():
EditorInterface.get_resource_filesystem().scan_sources()
気力が湧いたら記事にします。

Controlノードをドラッグアンドドロップで移動させるスクリプト
extends Control
var is_grab : bool = false
var offset : Vector2
func _on_gui_input(event: InputEvent) -> void:
if is_grab:
if event is InputEventMouseButton:
if event.pressed == false and event.button_index == 1:
is_grab = false
elif event is InputEventMouseMotion:
self.global_position = get_global_mouse_position() - offset
else :
if event is InputEventMouseButton:
if event.pressed and event.button_index == 1:
is_grab = true
elif event is InputEventMouseMotion:
offset = event.position