


AI workflows, character systems, and ComfyUI experiments
// A-LINE · LAB
Screenshots
// 01 · character customization
Character Customization AI Repaint
A Unity face-creation pipeline. Player draws on the in-game canvas, ComfyUI runs a custom face_blindbox LoRA plus ControlNet Scribble, and the texture comes back in seconds.
LoRA training set — 12 hand-curated references












Pipeline overview + ComfyUI workflow

Before the LoRA — 50 prompt-only iterations


















































After the LoRA — 64 player-generated faces
































































Expression generator — V4 to V7 iteration wall
































Final expression set — base / happy / angry / sad / shocked





UV-mapped to 3D characters








Character animation — walking demo
// 02 · ai-generated ui
AI UI Workflow
Feed AI a stack of references, generate concept sheets and design principles, then curate and assemble the pieces in Figma into the final Unity interface.
Step 01 // References I fed in


Step 02 // AI-generated concept + component library




Step 03 // AI-generated design principles


Manual assembly — final Figma panels built in Unity



Final result

// 03 · ai-assisted 3d
AI Helps 3D Workflow
Two paths feeding the 3D pipeline: image-to-3D plus DCC validation, and manual modeling with AI texture passes before Unity integration.
Module 01 · Image-to-3D + DCC export
Image-to-3D — complete concept to AI-split modeling sheet


DCC asset check tool — custom Blender add-on


Asset check tool — source code
asset_health_check.pypython
bl_info = {
"name": "Asset Health Check",
"author": "Yvaine",
"version": (0, 5, 0),
"blender": (4, 0, 0),
"location": "View3D > Sidebar (N) > Asset Check",
"description": "Game-art mesh audit + one-click fixes.",
"category": "Object",
}
import bpy
import bmesh
from mathutils import Vector
from bpy.props import BoolProperty, EnumProperty, FloatProperty, IntProperty
from bpy.props import PointerProperty, StringProperty
from bpy.types import Operator, Panel, PropertyGroup
def _iter_target_meshes(context):
for obj in context.selected_objects:
if obj.type == 'MESH' and obj.data is not None:
yield obj
def _audit_mesh(obj):
me = obj.data
bm = bmesh.new()
bm.from_mesh(me)
bm.normal_update()
non_manifold_edges = sum(1 for e in bm.edges if not e.is_manifold)
boundary_edges = sum(1 for e in bm.edges if e.is_boundary)
loose_verts = sum(1 for v in bm.verts if not v.link_edges)
loose_edges = sum(1 for e in bm.edges if not e.link_faces)
zero_area_faces = sum(1 for f in bm.faces if f.calc_area() < 1e-9)
ngons = sum(1 for f in bm.faces if len(f.verts) > 4)
tris = sum(1 for f in bm.faces if len(f.verts) == 3)
flipped_estimate = 0
for f in bm.faces:
neigh = [nf for e in f.edges for nf in e.link_faces if nf is not f]
if not neigh:
continue
avg = neigh[0].normal.copy() * 0
for nf in neigh:
avg = avg + nf.normal
avg /= len(neigh)
if f.normal.dot(avg) < -0.5:
flipped_estimate += 1
bm.free()
s = obj.scale
scale_off = abs(s.x - 1) > 1e-4 or abs(s.y - 1) > 1e-4 or abs(s.z - 1) > 1e-4
return {
"name": obj.name,
"verts": len(me.vertices),
"tris": tris,
"faces": len(me.polygons),
"non_manifold_edges": non_manifold_edges,
"boundary_edges": boundary_edges,
"loose_verts": loose_verts,
"loose_edges": loose_edges,
"zero_area_faces": zero_area_faces,
"ngons": ngons,
"flipped_estimate": flipped_estimate,
"has_custom_normals": me.has_custom_normals,
"scale_off": scale_off,
"scale": (s.x, s.y, s.z),
"materials": len(obj.material_slots),
}
def _format_report(rows):
headers = ["name", "verts", "tris", "ngons", "nm_e", "loose_v",
"loose_e", "zero_area", "flip?", "scale!=1", "mats"]
lines = [" | ".join(headers)]
for r in rows:
lines.append(" | ".join(str(x) for x in [
r["name"], r["verts"], r["tris"], r["ngons"],
r["non_manifold_edges"], r["loose_verts"], r["loose_edges"],
r["zero_area_faces"], r["flipped_estimate"],
"Y" if r["scale_off"] else "-",
r["materials"],
]))
return "\n".join(lines)
class ASSETCHK_OT_Check(Operator):
bl_idname = "assetchk.check"
bl_label = "Check Selected"
bl_description = "Audit selected meshes; results shown in panel and console"
def execute(self, context):
targets = list(_iter_target_meshes(context))
if not targets:
self.report({'WARNING'}, "No mesh objects selected")
return {'CANCELLED'}
rows = [_audit_mesh(o) for o in targets]
report = _format_report(rows)
context.scene.assetchk.last_report = report
print("\n[Asset Health Check]\n" + report + "\n")
problems = sum(
r["non_manifold_edges"] + r["loose_verts"] + r["loose_edges"]
+ r["zero_area_faces"] + r["flipped_estimate"] + (1 if r["scale_off"] else 0)
for r in rows
)
if problems == 0:
self.report({'INFO'}, f"OK — {len(rows)} mesh(es), no issues found")
else:
self.report({'WARNING'}, f"{len(rows)} mesh(es), {problems} potential issue(s)")
return {'FINISHED'}
class ASSETCHK_OT_FixFaceOrientation(Operator):
bl_idname = "assetchk.fix_face_orientation"
bl_label = "Fix Flipped Normals"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
targets = list(_iter_target_meshes(context))
if not targets:
self.report({'WARNING'}, "No mesh objects selected")
return {'CANCELLED'}
prev_active = context.view_layer.objects.active
if context.mode != 'OBJECT':
bpy.ops.object.mode_set(mode='OBJECT')
for obj in targets:
context.view_layer.objects.active = obj
obj.select_set(True)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.normals_make_consistent(inside=False)
bpy.ops.object.mode_set(mode='OBJECT')
if prev_active is not None:
context.view_layer.objects.active = prev_active
self.report({'INFO'}, f"Recalculated normals on {len(targets)} mesh(es)")
return {'FINISHED'}
class ASSETCHK_OT_ExportUnity(Operator):
bl_idname = "assetchk.export_unity"
bl_label = "Export to Unity"
bl_description = "FBX export with Unity-friendly Y-up / -Z forward settings."
def execute(self, context):
# Full add-on continues with naming checks, transform fixes,
# one-file-per-object FBX export, UI panels, registration,
# and the last report display in the Blender sidebar.
return {'FINISHED'}Mesh inspection in Blender

Final — in Unity URP

Module 02 · Modeling + textures
Preview — manual modeling + AI textures

Unity mesh + UV to AI-generated label texture


// 04 · marketing & ip
Posters · stickers · merch
Blender pose to AI background to final poster, then extended into stickers, merch directions, and an in-game async photo feature.
Module 01 · Key visual iteration
v1 — initial key visuals


v2 — concepts that sell the game feel




v3 — final cast group photo + A3 posters



Module 02 · Poster pipeline + stickers
Yarn theme — Blender · AI background · final poster



Cassette theme — Blender · AI background · final poster



Check-in photos — Unity pose to AI transparent pose


Final — composited with custom A-LINE frame






Sticker packs — hand-drawn base, AI restyled per character





