Image Generation
Generate images locally in Unity using AI models.
Overview
Local Image Generation allows agents to:
Generate images within Unity
Use local AI models
Create game assets without API calls
Generate textures and sprites
Produce procedural art
Basic Setup
Install Local Model
// Using Stable Diffusion in Unity
// Install via Package Manager or Unity ML-AgentsConfigure Agent
agent.AddLocalTool(LocalToolType.ImageGeneration);Generate Images
Simple Generation
await agent.SendAsync("Generate a fantasy sword");
await agent.SendAsync("Create a fire effect texture");With Parameters
var settings = new LocalImageSettings
{
Width = 512,
Height = 512,
Steps = 20,
Seed = 42,
GuidanceScale = 7.5f
};
await agent.GenerateImageLocallyAsync("A medieval castle", settings);Access Generated Images
Get Texture
agent.onLocalImageGenerated.AddListener((texture, prompt) =>
{
Debug.Log($"Generated: {texture.width}x{texture.height}");
// Display in UI
rawImage.texture = texture;
// Save to file
SaveTexture(texture, "generated.png");
});Save to Disk
void SaveTexture(Texture2D texture, string fileName)
{
byte[] bytes = texture.EncodeToPNG();
string path = Path.Combine(Application.persistentDataPath, fileName);
File.WriteAllBytes(path, bytes);
Debug.Log($"Saved: {path}");
}Integration with Unity
Generate Sprite
public async UniTask<Sprite> GenerateSprite(string description)
{
var texture = await agent.GenerateImageLocallyAsync(description);
Sprite sprite = Sprite.Create(
texture,
new Rect(0, 0, texture.width, texture.height),
new Vector2(0.5f, 0.5f)
);
return sprite;
}
// Usage
var weaponSprite = await GenerateSprite("A flaming sword icon");
itemImage.sprite = weaponSprite;Generate Material Texture
public async UniTask<Material> GenerateMaterial(string description)
{
var texture = await agent.GenerateImageLocallyAsync(description);
Material material = new Material(Shader.Find("Standard"));
material.mainTexture = texture;
return material;
}
// Usage
var stoneMaterial = await GenerateMaterial("Stone wall texture, seamless");
GetComponent<Renderer>().material = stoneMaterial;Model Configuration
Stable Diffusion Settings
agent.LocalImageSettings = new LocalImageSettings
{
ModelPath = "Models/stable-diffusion-v1-5",
Width = 512,
Height = 512,
Steps = 20, // Inference steps (higher = better quality)
Seed = -1, // -1 for random
GuidanceScale = 7.5f, // How closely to follow prompt
NegativePrompt = "blurry, low quality, distorted"
};Performance Options
// Fast generation (lower quality)
agent.LocalImageSettings.Steps = 10;
agent.LocalImageSettings.Width = 256;
agent.LocalImageSettings.Height = 256;
// High quality (slower)
agent.LocalImageSettings.Steps = 50;
agent.LocalImageSettings.Width = 1024;
agent.LocalImageSettings.Height = 1024;Game-Specific Use Cases
Character Portrait Generator
public class PortraitGenerator : MonoBehaviour
{
[SerializeField] private AgentBehaviour agent;
[SerializeField] private RawImage portraitDisplay;
public async void GenerateCharacterPortrait(CharacterData character)
{
string prompt = $@"
Portrait of {character.Name}:
{character.Race} {character.Class}
{character.Appearance}
Style: fantasy RPG character portrait
Quality: high detail, professional game art
";
var texture = await agent.GenerateImageLocallyAsync(prompt);
portraitDisplay.texture = texture;
// Save to character data
character.Portrait = texture;
}
}Procedural Terrain Textures
public class TerrainTextureGenerator : MonoBehaviour
{
[SerializeField] private AgentBehaviour agent;
[SerializeField] private Terrain terrain;
public async void GenerateTerrainTexture(string terrainType)
{
string prompt = $"{terrainType} ground texture, seamless, top-down view";
var texture = await agent.GenerateImageLocallyAsync(prompt);
// Apply to terrain
TerrainLayer layer = new TerrainLayer
{
diffuseTexture = texture,
tileSize = new Vector2(10, 10)
};
terrain.terrainData.terrainLayers = new[] { layer };
}
}
// Usage
GenerateTerrainTexture("grass");
GenerateTerrainTexture("rocky desert");Item Icon Generator
public async UniTask<Sprite> GenerateItemIcon(ItemData item)
{
string prompt = $@"
Game item icon: {item.Name}
{item.Description}
Style: clean icon, isometric view
Background: transparent or simple gradient
Quality: high detail, 512x512
";
var texture = await agent.GenerateImageLocallyAsync(prompt);
// Create sprite
return Sprite.Create(
texture,
new Rect(0, 0, texture.width, texture.height),
new Vector2(0.5f, 0.5f)
);
}Batch Generation
Generate Multiple Assets
public async void GenerateAssetPack(string[] descriptions)
{
Debug.Log($"Generating {descriptions.Length} assets...");
List<Texture2D> generatedAssets = new();
foreach (var description in descriptions)
{
var texture = await agent.GenerateImageLocallyAsync(description);
generatedAssets.Add(texture);
// Small delay to prevent overload
await UniTask.Delay(100);
}
Debug.Log($"✓ Generated {generatedAssets.Count} assets");
// Save pack
SaveAssetPack(generatedAssets);
}
void SaveAssetPack(List<Texture2D> textures)
{
string packPath = Path.Combine(Application.persistentDataPath, "AssetPack");
Directory.CreateDirectory(packPath);
for (int i = 0; i < textures.Count; i++)
{
byte[] bytes = textures[i].EncodeToPNG();
File.WriteAllBytes(Path.Combine(packPath, $"asset_{i}.png"), bytes);
}
}Style Control
Art Style Presets
public enum ArtStyle
{
Realistic,
Cartoon,
PixelArt,
Watercolor,
OilPainting
}
public string GetStylePrompt(ArtStyle style)
{
return style switch
{
ArtStyle.Realistic => "photorealistic, high detail, 3D render",
ArtStyle.Cartoon => "cartoon style, cel-shaded, bold outlines",
ArtStyle.PixelArt => "pixel art, 16-bit style, retro game graphics",
ArtStyle.Watercolor => "watercolor painting, soft colors, artistic",
ArtStyle.OilPainting => "oil painting, brush strokes, classical art",
_ => ""
};
}
public async UniTask<Texture2D> GenerateWithStyle(string subject, ArtStyle style)
{
string stylePrompt = GetStylePrompt(style);
string fullPrompt = $"{subject}, {stylePrompt}";
return await agent.GenerateImageLocallyAsync(fullPrompt);
}Image Variations
Generate Variations
public async UniTask<List<Texture2D>> GenerateVariations(string basePrompt, int count)
{
List<Texture2D> variations = new();
for (int i = 0; i < count; i++)
{
// Use different seed for each variation
agent.LocalImageSettings.Seed = Random.Range(0, 100000);
var texture = await agent.GenerateImageLocallyAsync(basePrompt);
variations.Add(texture);
}
return variations;
}
// Usage
var swordVariations = await GenerateVariations("Fantasy sword", 5);Image-to-Image
Modify Existing Image
public async UniTask<Texture2D> ModifyImage(Texture2D sourceImage, string modification)
{
// Convert to base64 or file
byte[] imageBytes = sourceImage.EncodeToPNG();
// Generate based on source
var result = await agent.GenerateImageToImageAsync(
sourceImage: imageBytes,
prompt: modification,
strength: 0.75f // How much to change (0-1)
);
return result;
}
// Usage
var modifiedTexture = await ModifyImage(
originalTexture,
"Add glowing magical effects"
);Performance Optimization
Async Loading
public class AsyncImageGenerator : MonoBehaviour
{
private Queue<ImageRequest> requestQueue = new();
private bool isGenerating;
public async void RequestImage(string prompt, System.Action<Texture2D> callback)
{
requestQueue.Enqueue(new ImageRequest { Prompt = prompt, Callback = callback });
if (!isGenerating)
{
await ProcessQueue();
}
}
async UniTask ProcessQueue()
{
isGenerating = true;
while (requestQueue.Count > 0)
{
var request = requestQueue.Dequeue();
var texture = await agent.GenerateImageLocallyAsync(request.Prompt);
request.Callback?.Invoke(texture);
await UniTask.Yield(); // Prevent frame drops
}
isGenerating = false;
}
struct ImageRequest
{
public string Prompt;
public System.Action<Texture2D> Callback;
}
}Texture Pooling
public class TexturePool : MonoBehaviour
{
private Dictionary<string, Texture2D> cache = new();
public async UniTask<Texture2D> GetOrGenerate(string prompt)
{
if (cache.TryGetValue(prompt, out var cached))
{
Debug.Log("Using cached texture");
return cached;
}
var texture = await agent.GenerateImageLocallyAsync(prompt);
cache[prompt] = texture;
return texture;
}
public void ClearCache()
{
foreach (var texture in cache.Values)
{
Destroy(texture);
}
cache.Clear();
}
}Error Handling
Handle Generation Errors
agent.onLocalToolError.AddListener((tool, error) =>
{
if (tool == LocalToolType.ImageGeneration)
{
Debug.LogError($"Image generation failed: {error}");
if (error.Contains("out of memory"))
{
// Reduce resolution
agent.LocalImageSettings.Width = 256;
agent.LocalImageSettings.Height = 256;
Debug.Log("Reduced resolution due to memory constraints");
}
else if (error.Contains("model not found"))
{
ShowMessage("AI model not installed. Please download model.");
}
}
});Complete Example
using UnityEngine;
using Glitch9.AIDevKit.Agents;
using Cysharp.Threading.Tasks;
public class LocalImageGenerator : MonoBehaviour
{
[SerializeField] private AgentBehaviour agent;
[SerializeField] private RawImage displayImage;
[Header("Settings")]
[SerializeField] private int imageWidth = 512;
[SerializeField] private int imageHeight = 512;
[SerializeField] private int inferenceSteps = 20;
private Dictionary<string, Texture2D> cache = new();
async void Start()
{
await SetupLocalImageGeneration();
}
async UniTask SetupLocalImageGeneration()
{
// Configure settings
agent.LocalImageSettings = new LocalImageSettings
{
Width = imageWidth,
Height = imageHeight,
Steps = inferenceSteps,
Seed = -1,
GuidanceScale = 7.5f,
NegativePrompt = "blurry, low quality, distorted, ugly"
};
// Add tool
agent.AddLocalTool(LocalToolType.ImageGeneration);
// Listen for results
agent.onLocalImageGenerated.AddListener(OnImageGenerated);
agent.onLocalToolError.AddListener(OnToolError);
Debug.Log("✓ Local image generation ready");
}
public async void GenerateImage(string prompt)
{
// Check cache
if (cache.TryGetValue(prompt, out var cached))
{
displayImage.texture = cached;
Debug.Log("Using cached image");
return;
}
Debug.Log($"🎨 Generating: {prompt}");
try
{
var texture = await agent.GenerateImageLocallyAsync(prompt);
// Display
displayImage.texture = texture;
// Cache
cache[prompt] = texture;
// Save
SaveTexture(texture, prompt);
}
catch (Exception ex)
{
Debug.LogError($"Generation failed: {ex.Message}");
}
}
void OnImageGenerated(Texture2D texture, string prompt)
{
Debug.Log($"✓ Generated: {texture.width}x{texture.height}");
displayImage.texture = texture;
}
void OnToolError(LocalToolType tool, string error)
{
if (tool == LocalToolType.ImageGeneration)
{
Debug.LogError($"Image generation error: {error}");
}
}
void SaveTexture(Texture2D texture, string prompt)
{
byte[] bytes = texture.EncodeToPNG();
string fileName = $"{prompt.Replace(" ", "_")}_{DateTime.Now:yyyyMMdd_HHmmss}.png";
string path = Path.Combine(Application.persistentDataPath, "Generated", fileName);
Directory.CreateDirectory(Path.GetDirectoryName(path));
File.WriteAllBytes(path, bytes);
Debug.Log($"💾 Saved: {path}");
}
public void ClearCache()
{
foreach (var texture in cache.Values)
{
Destroy(texture);
}
cache.Clear();
Debug.Log("✓ Cache cleared");
}
void OnDestroy()
{
ClearCache();
}
}Next Steps
Last updated