https://youtu.be/MFcwE1qkb9A?si=6dyvk6lau2Db51Mc
p.120 ~ 142
https://www.youtube.com/watch?v=IhdobKXv_vY
https://dev.epicgames.com/documentation/ko-kr/unreal-engine/unreal-engine-terminology
https://www.youtube.com/watch?v=IJSq2lCwCQg
UINTERFACE(Blueprintable)
class UInteractable : public UInterface // 타입 체크할 때 사용
{
GENERATED_BODY()
};
class IInteractable // 실제 구현 및 호출할 때 사용
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintImplementableEvent)
void OnInteract();
};
void AClassA::TriggerInteraction(AActor* InteractionTarget)
{
// 이 Actor가 UInteractable 인터페이스(네이티브 아니고 그냥 예시)를 구현했는지 확인
if (InteractionTarget->Implements<UInteractable>())
{
// C++에서 OnInteract() 함수가 구현돼 있다면 호출
Cast<IInteractable>(InteractionTarget)->OnInteract();
// 블루프린트에서 OnInteract 이벤트가 구현돼 있다면 실행
IInteractable::Execute_OnBlueprintImplementableEventInteract(InteractionTarget);
// 보통 c++ 블루프린트 둘 중 하나만 사용
}
}
싱글톤들
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
public class ApplyTpsheetTool : EditorWindow
{
[MenuItem("Tools/Apply TPSheet to Selected PNG")]
public static void ApplyTPSheetToSelected()
{
Object selected = Selection.activeObject;
if (selected == null)
{
UnityEngine.Debug.LogWarning("No PNG file selected.");
return;
}
string pngPath = AssetDatabase.GetAssetPath(selected);
if (!pngPath.EndsWith(".png"))
{
UnityEngine.Debug.LogWarning("Selected file is not a PNG.");
return;
}
string fullPngPath = Path.GetFullPath(pngPath);
string tpsheetPath = Path.ChangeExtension(fullPngPath, ".tpsheet");
if (!File.Exists(tpsheetPath))
{
UnityEngine.Debug.LogError($"TPSheet not found for {Path.GetFileName(pngPath)}");
return;
}
// 파싱 시작
string[] lines = File.ReadAllLines(tpsheetPath);
int imageHeight = 0;
List<SpriteMetaData> spriteMetaDataList = new List<SpriteMetaData>();
foreach (var line in lines)
{
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#"))
continue;
if (line.StartsWith(":size="))
{
var sizeParts = line.Replace(":size=", "").Split('x');
if (sizeParts.Length == 2)
int.TryParse(sizeParts[1], out imageHeight);
}
else if (line.Contains(";"))
{
var parts = line.Split(';');
if (parts.Length < 7)
continue;
string name = Path.GetFileNameWithoutExtension(parts[0]);
int.TryParse(parts[1], out int x);
int.TryParse(parts[2], out int y);
int.TryParse(parts[3], out int w);
int.TryParse(parts[4], out int h);
float.TryParse(parts[5], out float pivotX);
float.TryParse(parts[6], out float pivotY);
SpriteMetaData smd = new SpriteMetaData
{
name = name,
rect = new Rect(x, y, w, h),
pivot = new Vector2(pivotX, pivotY),
alignment = (int)SpriteAlignment.Custom
};
spriteMetaDataList.Add(smd);
}
}
if (spriteMetaDataList.Count == 0 || imageHeight == 0)
{
UnityEngine.Debug.LogWarning("TPSheet parsed but contains no valid sprite data.");
return;
}
// 텍스처 임포터에 메타데이터 적용
TextureImporter importer = AssetImporter.GetAtPath(pngPath) as TextureImporter;
if (importer == null)
{
UnityEngine.Debug.LogError("TextureImporter not found.");
return;
}
#pragma warning disable 0618
importer.textureType = TextureImporterType.Sprite;
importer.spriteImportMode = SpriteImportMode.Multiple;
importer.spritesheet = spriteMetaDataList.ToArray();
#pragma warning restore 0618
EditorUtility.SetDirty(importer);
AssetDatabase.ImportAsset(pngPath, ImportAssetOptions.ForceUpdate);
UnityEngine.Debug.Log($"✅ Applied {spriteMetaDataList.Count} sprites to {Path.GetFileName(pngPath)}");
}
}
https://free-tex-packer.com/ 이 프로그램으로 texture packing한 결과물 unity에 적용하는 코드

inkscape의 문제점 확인. 해결해보려고 하였으나 검색도 gpt도 실험도 도움이 안됨.
돈 써야할듯.

affinity designer free trial 써봤는데, 이미 결제해야할 것 같다는 생각이 든다.
문제도 검색하니까 바로 해결됐고, 가격도 싸고...
sprite shape도 적용해봤는데, 생각해보니 각지게 만들려면 9slice 한 평범한 sprite로 하는게 나을듯.

