824 lines
43 KiB
Markdown
824 lines
43 KiB
Markdown
# Дизайн Анимированного Персонажа для AI-Компаньона (Психологическое Приложение)
|
||
|
||
> Исследование выполнено: май 2026
|
||
> Контекст: персонаж — визуальное лицо AI-компаньона в приложении психологической рефлексии. Абстрактный (не человек, не робот). Реагирует на эмоции и голос пользователя.
|
||
|
||
---
|
||
|
||
## 1. Существующие Примеры Абстрактных AI Персонажей
|
||
|
||
### 1.1 Большие платформы
|
||
|
||
#### 🔵 Siri (Apple) — Waveform / Орб
|
||
- **Эволюция**: Siri прошёл путь от простых волн (iOS 7–12) до живого светящегося шара (iOS 14+) и «гало» вокруг экрана (iOS 18/26)
|
||
- **Что работает**: плавные цветовые переходы синий→фиолетовый→розовый ассоциируются с «думающим» AI без антропоморфизации
|
||
- **Реализация**: Core Animation + Metal шейдеры; собственный рендер Apple
|
||
- **Эмоциональный эффект**: пользователи воспринимают его как «живой», но не угрожающий (Reddit: r/ios26, обсуждение «Type to Siri Wavy Effect»)
|
||
- **Референс**: [Apple Human Interface Guidelines — Siri](https://developer.apple.com/design/human-interface-guidelines/siri)
|
||
|
||
#### 🟡 Alexa (Amazon) — Кольцо/Ring
|
||
- **Визуал**: круговое световое кольцо: синий = слушает, оранжевый = нет сети, фиолетовый = не беспокоить, белый = регулировка громкости
|
||
- **Что работает**: семиотика цвета как язык состояний — пользователи быстро запоминают коды без слов
|
||
- **Ограничение для нашего case**: слишком «железный», не подходит для эмпатийного контекста
|
||
|
||
#### 🔮 Google Assistant — Точки / G-шар
|
||
- **Визуал**: 4 цветные точки (синяя, красная, жёлтая, зелёная), анимирующиеся в вол-ну при активации; позже — 3D светящийся «G»
|
||
- **Что работает**: цветовая идентичность бренда, минимализм
|
||
- **Ограничение**: слишком корпоративный, не тёплый
|
||
|
||
#### 🟢 ChatGPT (OpenAI) — Пульсирующий Шар
|
||
- **Визуал**: анимированный чёрно-белый шар в голосовом режиме Canvas/voice mode; в web — простая анимация печатающегося текста
|
||
- **Решение**: нейтральный ч/б орб не навязывает эмоцию — пользователь сам проецирует
|
||
- **Реакция пользователей**: в Reddit threads (r/ChatGPT) отмечают, что голосовой орб ощущается «медитативно», снижает тревогу перед диалогом
|
||
|
||
#### 🔵 Microsoft Copilot — Градиентная Орбита
|
||
- **Визуал**: анимированная орбита из синих и фиолетовых полос, морфирующая при ответе
|
||
- **Что работает**: ощущение «умного движения», не угрожающего; подходит для рабочей среды
|
||
|
||
---
|
||
|
||
### 1.2 Mental Health / Медитация
|
||
|
||
#### 🧘 Headspace — «Andy» (апельсиновый шар)
|
||
- **Визуал**: простой оранжевый круг с минимальными чертами лица (точки-глаза, дуга-рот)
|
||
- **Степень абстракции**: умеренная — есть лицо, но без носа/ушей
|
||
- **Что работает**: тёплый цвет = дружелюбие, округлость = безопасность
|
||
- **Референс**: [Headspace Design System](https://www.headspace.com/design)
|
||
- **Ограничение**: лицо делает его «детским» — не подходит для взрослой аудитории
|
||
|
||
#### 🌊 Calm — Анимированные Фоны
|
||
- **Подход**: Calm отказался от персонажа-компаньона в пользу окружающей среды (вода, листья, луна)
|
||
- **Вывод для нас**: персонаж должен быть частью «среды», а не отдельным элементом
|
||
|
||
#### 💜 Replika (AI Companion) — 3D Аватар
|
||
- **Подход**: антропоморфный 3D аватар, настраиваемый пользователем
|
||
- **Минус**: тревожная долина (Uncanny Valley), юридические и этические вопросы привязанности
|
||
- **Вывод**: НЕ делать антропоморфный. Наш выбор абстракции — правильный
|
||
|
||
#### 🌿 Woebot — Минималистичный Робот
|
||
- **Визуал**: простой квадратный «робот» с экраном-лицом
|
||
- **Что работает**: простота снижает тревогу, неантропоморфность делает его «инструментом», а не «существом»
|
||
- **Ограничение**: слишком «роботизированный» для эмпатийного контекста
|
||
|
||
---
|
||
|
||
### 1.3 Прочие Примеры
|
||
|
||
#### 🦜 Duolingo — Duo (сова) + Lily
|
||
- **Duo**: антропоморфная сова — зелёная, с глазами; огромный успех с точки зрения вовлечённости
|
||
- **Lily (AI Video Call, 2025)**: реализован в **Rive** — анимированный персонаж с state machine, живущий в видеозвонке
|
||
- **Кейс-стади Rive**: Duolingo использует Rive для интерактивных персонажей, потому что state machine позволяет плавно переходить между сотнями эмоциональных состояний без написания кода вручную
|
||
- **Вывод**: Rive — production-proven для персонажей в consumer apps
|
||
|
||
#### 🎵 Spotify Wrapped 2025
|
||
- Реализован в Rive; дизайн отправляет в production интерактивную анимацию, код управляет данными
|
||
- **Паттерн**: «design ships interaction in Rive, code drives it with data» — именно то, что нам нужно
|
||
|
||
#### 🤖 Notion AI — Спиннер-искры
|
||
- **Визуал**: анимированные искры/частицы при генерации текста
|
||
- **Что работает**: ощущение «творческой энергии», не агрессивное
|
||
- **Минус**: статичный, нет эмоциональных состояний
|
||
|
||
---
|
||
|
||
## 2. Rive.app — Детальный Разбор
|
||
|
||
### 2.1 Что такое Rive и зачем он нам
|
||
|
||
Rive — это инструмент для создания **интерактивных, production-ready анимаций** с встроенной логикой через **State Machine**. В отличие от Lottie (только воспроизведение заранее заданной анимации), Rive позволяет:
|
||
|
||
- Управлять переходами между состояниями в реальном времени
|
||
- Передавать данные из кода (амплитуда звука, состояние AI) прямо в дизайн
|
||
- Дизайнер работает в редакторе → разработчик подключает `.riv` файл → интерактивность работает «из коробки»
|
||
|
||
**Ключевое преимущество для нашего случая**: голосовая реактивность реализуется через **Number input** в State Machine — разработчик передаёт значение (0.0–1.0 от амплитуды микрофона), дизайн реагирует без написания анимационного кода.
|
||
|
||
---
|
||
|
||
### 2.2 Rive для iOS — Техническая Интеграция
|
||
|
||
**Репозиторий**: [github.com/rive-app/rive-ios](https://github.com/rive-app/rive-ios)
|
||
- 780 ⭐ | MIT License | v6.20.3 (май 2026) | 239 релизов
|
||
|
||
**Установка (Swift Package Manager — рекомендуется)**:
|
||
```swift
|
||
// Package.swift
|
||
dependencies: [
|
||
.package(url: "https://github.com/rive-app/rive-ios", from: "6.13.0")
|
||
]
|
||
```
|
||
|
||
**Требования**: iOS 14.0+, visionOS 1.0+, tvOS 16.0+, macOS 13.1+
|
||
|
||
**Минимальная интеграция в SwiftUI** (Legacy API — проще для начала):
|
||
```swift
|
||
import RiveRuntime
|
||
|
||
struct CharacterView: View {
|
||
var body: some View {
|
||
RiveViewModel(
|
||
fileName: "ai_companion",
|
||
stateMachineName: "EmotionMachine"
|
||
).view()
|
||
}
|
||
}
|
||
```
|
||
|
||
**Продвинутая интеграция (новый runtime, Swift Concurrency)**:
|
||
```swift
|
||
import RiveRuntime
|
||
|
||
struct CharacterView: View {
|
||
@State private var riveView: RiveUIView?
|
||
|
||
var body: some View {
|
||
AsyncRiveUIViewRepresentable {
|
||
let worker = try await Worker()
|
||
let file = try await File(source: .local("ai_companion", Bundle.main), worker: worker)
|
||
return try await Rive(file: file)
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**Управление через State Machine (Data Binding)**:
|
||
```swift
|
||
// Передача амплитуды микрофона → анимация
|
||
let numberProp = viewModelInstance.numberProperty(fromPath: "audioAmplitude")!
|
||
numberProp.value = Float(microphoneAmplitude) // 0.0...1.0
|
||
|
||
// Изменение эмоционального состояния
|
||
let enumProp = viewModelInstance.enumProperty(fromPath: "emotionState")!
|
||
enumProp.value = "thinking" // или "speaking", "listening", "happy", "concerned"
|
||
```
|
||
|
||
---
|
||
|
||
### 2.3 State Machine в Rive — Как Работает
|
||
|
||
**State Machine** — это граф состояний анимации с переходами.
|
||
|
||
**Для нашего персонажа**:
|
||
```
|
||
[Entry] → [Idle/Breathing] ↔ [Listening] ↔ [Thinking] ↔ [Speaking] ↔ [Happy] ↔ [Concerned]
|
||
```
|
||
|
||
**Типы inputs**:
|
||
| Тип | Использование |
|
||
|-----|--------------|
|
||
| `Boolean` | `isListening`, `isSpeaking`, `isThinking` |
|
||
| `Number` | `audioAmplitude` (0.0–1.0), `emotionIntensity` |
|
||
| `Trigger` | `onHappy`, `onConcerned`, `onGreet` |
|
||
| `Enum` | `currentState` (idle/listen/think/speak/happy/concerned) |
|
||
|
||
**Settled State**: автоматически останавливает рендеринг когда нет активных переходов → экономит CPU/батарею. Для idle-анимации = зацикленная бесконечная анимация, которая продолжает работать.
|
||
|
||
**Playback управление**:
|
||
```swift
|
||
simpleVM.pause() // заморозить на текущем кадре
|
||
simpleVM.play() // возобновить с текущей позиции
|
||
simpleVM.stop() // сброс к начальному состоянию
|
||
simpleVM.reset() // полный сброс state machine
|
||
```
|
||
|
||
---
|
||
|
||
### 2.4 Голосовая Реактивность: Как Именно
|
||
|
||
**Схема реализации**:
|
||
|
||
1. **AVAudioEngine** (iOS) → получаем амплитуду микрофона в реальном времени
|
||
2. Нормализуем: `amplitude = (rawValue + 160) / 160` (или другой масштаб)
|
||
3. Передаём в Rive через Number property: `audioAmplitude = smoothedAmplitude`
|
||
4. В Rive дизайнер привязывает это число к scale/radius/деформации blob
|
||
|
||
**Код для получения амплитуды**:
|
||
```swift
|
||
import AVFoundation
|
||
|
||
class MicrophoneMonitor: ObservableObject {
|
||
private var audioEngine = AVAudioEngine()
|
||
@Published var amplitude: Float = 0.0
|
||
|
||
func startMonitoring() {
|
||
let inputNode = audioEngine.inputNode
|
||
let recordingFormat = inputNode.outputFormat(forBus: 0)
|
||
|
||
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { buffer, _ in
|
||
guard let channelData = buffer.floatChannelData else { return }
|
||
let frameLength = Int(buffer.frameLength)
|
||
let rms = sqrt(channelData[0][0..<frameLength].map { $0 * $0 }.reduce(0, +) / Float(frameLength))
|
||
DispatchQueue.main.async {
|
||
// Сглаживание (lerp) для избежания дёргания
|
||
self.amplitude = self.amplitude * 0.7 + min(rms * 10, 1.0) * 0.3
|
||
}
|
||
}
|
||
try? audioEngine.start()
|
||
}
|
||
}
|
||
```
|
||
|
||
**В SwiftUI — связка**:
|
||
```swift
|
||
struct CharacterContainer: View {
|
||
@StateObject private var mic = MicrophoneMonitor()
|
||
@State private var riveVM = RiveViewModel(fileName: "companion", stateMachineName: "Main")
|
||
|
||
var body: some View {
|
||
riveVM.view()
|
||
.onChange(of: mic.amplitude) { newValue in
|
||
riveVM.setInput("audioAmplitude", value: newValue)
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 2.5 Rive Pricing для Indie Разработчика
|
||
|
||
| Тариф | Цена | Что важно |
|
||
|-------|------|-----------|
|
||
| **Free** | $0/мес | Редактор бесплатно, **нельзя экспортировать .riv** файлы для production |
|
||
| **Cadet** | **$9/мес** (до 3 сидений) | ✅ Экспорт .riv файлов, неограниченные файлы, все State Machine фичи |
|
||
| **Voyager** | $32/мес | Libraries, Embed URL hosting, до 25 сидений |
|
||
| **Enterprise** | $120/мес | Для компаний $10M+ выручки |
|
||
|
||
**Вывод для indie**: **$9/мес (Cadet)** — это всё что нужно для MVP и production. Free можно использовать для прототипирования. Дизайнер тоже работает на Cadet или тебе передаёт .riv файл.
|
||
|
||
---
|
||
|
||
### 2.6 Lottie vs Rive — Сравнение для Нашего Use Case
|
||
|
||
| Критерий | Lottie | Rive |
|
||
|----------|--------|------|
|
||
| **Интерактивность** | ❌ Только воспроизведение | ✅ State Machine, inputs, data binding |
|
||
| **Голосовая реактивность** | ❌ Невозможно нативно | ✅ Number input → real-time |
|
||
| **Размер файла** | ~50–200KB | ~20–100KB (векторный .riv) |
|
||
| **Производительность iOS** | Хорошая | Отличная (собственный рендерер на Metal) |
|
||
| **Workflow дизайнер** | After Effects → экспорт JSON | Rive Editor (браузер/десктоп) |
|
||
| **Стоимость для дизайнера** | AE нужен ($55/мес) | Rive Cadet $9/мес |
|
||
| **Обучение разработчика** | 1–2 часа | 4–8 часов |
|
||
| **Эмоциональные состояния** | Несколько файлов + код | 1 файл + state machine |
|
||
| **Settled state** | ❌ | ✅ Экономия CPU |
|
||
| **Итог для нашего проекта** | ❌ Не подходит | ✅ **Единственный правильный выбор** |
|
||
|
||
**Вердикт**: Lottie — для декоративных анимаций (иконки, сплэш-скрины). Rive — для **интерактивных персонажей с состояниями**. Наш use case = Rive без сомнений.
|
||
|
||
---
|
||
|
||
## 3. Четыре Концепта Персонажа
|
||
|
||
### 🟣 Концепт A: Дышащий Орб ⭐ РЕКОМЕНДУЕМЫЙ
|
||
|
||
**Визуал**: Сфера диаметром ~120–160pt, светящаяся изнутри, с мягким bloom-эффектом. Базовый цвет — глубокий индиго (#3D2B8F), с внутренним свечением меняющим оттенок по состоянию.
|
||
|
||
**5 состояний**:
|
||
|
||
| Состояние | Визуал | Цвет | Поведение |
|
||
|-----------|--------|------|-----------|
|
||
| **Idle/Breathing** | Медленное расширение-сжатие (±8%), 4 сек цикл | Индиго → тёмно-синий | Почти незаметное «дыхание» |
|
||
| **Listening** | Лёгкая пульсация (синхронно с голосом), размер +голос/амплитуда | Синий → бирюзовый (#00C4CC) | Активная реакция на звук |
|
||
| **Thinking** | Медленное вращение внутренних частиц/градиента, ±5% размер | Индиго → фиолетовый (#7B4FD4) | Внутреннее движение, «обработка» |
|
||
| **Speaking** | Ритмичные волны-пульсы, согласованные с TTS | Тёплый фиолетовый (#9B59B6) | Лёгкие ряби от центра |
|
||
| **Happy** | Быстрое «прыжковое» расширение (+20%), возврат | Тёплый персиковый (#FF6B6B → оранжевый) | Burst-эффект |
|
||
| **Concerned** | Медленное сжатие (-10%), глубже цвет | Тёмно-синий (#1A237E) | Серьёзность, не пугающая |
|
||
|
||
**Техническая реализация в Rive**:
|
||
1. Основная форма: эллипс с несколькими вложенными эллипсами (layered glow)
|
||
2. Внутренний градиент: радиальный, управляется через Number input `colorShift`
|
||
3. Размер: scale контролируется через `audioAmplitude` + дополнительные keyframe анимации для каждого состояния
|
||
4. Bloom/glow: **Vector Feathering** в Rive (доступен с 2025) — нативный, без растра
|
||
5. State machine: 1 Boolean `isActive` + 1 Number `audioAmplitude` + 1 Enum `emotionState`
|
||
|
||
**SwiftUI реализация** (если без Rive — чистый SwiftUI):
|
||
```swift
|
||
struct BreathingOrb: View {
|
||
@State private var isPulsing = false
|
||
@State private var audioAmplitude: CGFloat = 0
|
||
var emotionColor: Color = .indigo
|
||
|
||
var body: some View {
|
||
ZStack {
|
||
// Внешнее свечение
|
||
Circle()
|
||
.fill(emotionColor.opacity(0.15))
|
||
.frame(width: 200 + audioAmplitude * 40)
|
||
.blur(radius: 20)
|
||
// Средний слой
|
||
Circle()
|
||
.fill(
|
||
RadialGradient(
|
||
colors: [emotionColor.opacity(0.8), emotionColor],
|
||
center: .center, startRadius: 10, endRadius: 70
|
||
)
|
||
)
|
||
.frame(width: 130 + audioAmplitude * 30)
|
||
.scaleEffect(isPulsing ? 1.05 : 0.97)
|
||
// Внутренняя яркость
|
||
Circle()
|
||
.fill(Color.white.opacity(0.3))
|
||
.frame(width: 50)
|
||
.offset(x: -15, y: -15)
|
||
.blur(radius: 8)
|
||
}
|
||
.animation(.easeInOut(duration: 2).repeatForever(autoreverses: true), value: isPulsing)
|
||
.onAppear { isPulsing = true }
|
||
}
|
||
}
|
||
```
|
||
|
||
**Эмоциональный тон**: спокойствие, присутствие, безопасность. Пользователь ощущает, что рядом «кто-то» — но не человек, не робот. «Существо света».
|
||
|
||
**Сложность реализации**:
|
||
- DIY SwiftUI: **4/10** (можно сделать за день)
|
||
- Rive с дизайнером: **6/10** (1–2 недели работы дизайнера)
|
||
|
||
---
|
||
|
||
### 🟡 Концепт B: Поле Частиц
|
||
|
||
**Визуал**: 50–200 светящихся точек диаметром 2–6pt, формирующих разные паттерны в зависимости от состояния.
|
||
|
||
**Состояния через паттерны**:
|
||
- **Idle**: медленное дрейфование, как звёзды
|
||
- **Listening**: частицы притягиваются к центру, образуя «ухо»-форму
|
||
- **Thinking**: хаотичное кружение в определённой зоне
|
||
- **Speaking**: волнообразное движение от центра
|
||
- **Happy**: взрыв наружу + возврат
|
||
|
||
**Реализация голосовой реактивности**:
|
||
```swift
|
||
// SpriteKit или SwiftUI Canvas
|
||
// audioAmplitude → силовое поле, отталкивающее частицы
|
||
// Каждая частица: позиция = basePosition + sin(time + index) * amplitude * 20
|
||
```
|
||
|
||
**Сложность**:
|
||
- DIY SwiftUI Canvas/SpriteKit: **7/10**
|
||
- Rive: **8/10** (частицы в Rive нетривиальны, лучше через Scripting/Luau)
|
||
- **Metal**: 5/10 при наличии знаний Metal Shaders
|
||
|
||
**Плюсы**: ощущение интеллекта, «нейронная сеть», уникально
|
||
**Минусы**: более сложная реализация, может отвлекать от рефлексии; плохо читается при нервозности пользователя
|
||
|
||
---
|
||
|
||
### 🟢 Концепт C: Флюидный Blob
|
||
|
||
**Визуал**: органическая морфирующая форма — как капля ртути или живая клетка. Постоянно деформируется, «перетекает».
|
||
|
||
**Референсы в продуктах**:
|
||
- **iOS 17 Dynamic Island** — blob-морфинг для уведомлений → пользователи полюбили
|
||
- **Headspace «sleep sounds»** анимации — флюидные формы воды
|
||
- **Adobe Creative Cloud** онбординг (2021) — морфирующие blob-переходы
|
||
|
||
**Реализация**:
|
||
|
||
*Вариант 1 — SwiftUI Canvas + CGPath*:
|
||
```swift
|
||
// Генерация blob через control points
|
||
func blobPath(points: [CGPoint], smoothing: CGFloat) -> Path {
|
||
// Catmull-Rom сплайны между точками
|
||
// audioAmplitude → смещение контрольных точек
|
||
}
|
||
```
|
||
|
||
*Вариант 2 — Metal Shader (лучший результат)*:
|
||
```metal
|
||
// Метаболы (metaballs) — классическая техника для blob
|
||
// distance = sqrt(dx*dx + dy*dy)
|
||
// field += radius*radius / distance
|
||
// Если field > threshold → рисуем пиксель
|
||
```
|
||
|
||
*Вариант 3 — Rive с mesh deformation*:
|
||
- Rive поддерживает **Meshes** (деформация сетки) — дизайнер создаёт blob-форму, задаёт точки деформации, управляемые через inputs
|
||
- Сложность в Rive: **8/10**
|
||
|
||
**Голосовая реактивность**: amплитуда → displacement map → blob «вздрагивает» синхронно с речью
|
||
|
||
**Плюсы**: максимальная «живость», визуально уникален, метафора «жидкого интеллекта»
|
||
**Минусы**: сложная реализация без дизайнера, может вызывать тревогу (нестабильная форма ≠ спокойствие)
|
||
|
||
**Сложность**: DIY Metal: **8/10**, DIY SwiftUI: **6/10**, Rive с дизайнером: **7/10**
|
||
|
||
---
|
||
|
||
### 🔵 Концепт D: Аудио-Волна
|
||
|
||
**Визуал**: waveform-визуализация, реагирующая на голос — как старый Siri (iOS 7–9) или осциллограф.
|
||
|
||
**Варианты формы**:
|
||
- Классическая waveform (вертикальные полосы переменной высоты)
|
||
- Синусоида (гладкая волна)
|
||
- Круговая waveform (волна по окружности)
|
||
- «Сердцебиение» (EKG-стиль)
|
||
|
||
**SwiftUI реализация (круговая)**:
|
||
```swift
|
||
struct AudioWaveformView: View {
|
||
var amplitudes: [Float] // массив ~64 значений
|
||
var color: Color = .purple
|
||
|
||
var body: some View {
|
||
Canvas { context, size in
|
||
let center = CGPoint(x: size.width/2, y: size.height/2)
|
||
let baseRadius: CGFloat = 60
|
||
|
||
var path = Path()
|
||
for (i, amp) in amplitudes.enumerated() {
|
||
let angle = Double(i) / Double(amplitudes.count) * 2 * .pi
|
||
let r = baseRadius + CGFloat(amp) * 40
|
||
let x = center.x + cos(angle) * r
|
||
let y = center.y + sin(angle) * r
|
||
if i == 0 { path.move(to: CGPoint(x: x, y: y)) }
|
||
else { path.addLine(to: CGPoint(x: x, y: y)) }
|
||
}
|
||
path.closeSubpath()
|
||
context.fill(path, with: .color(color.opacity(0.7)))
|
||
context.stroke(path, with: .color(color), lineWidth: 2)
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**Плюсы**:
|
||
- Максимально простая реализация (DIY: **2/10**)
|
||
- Мгновенная связь аудио → визуал понятна пользователю
|
||
- Ощущение «технологичности»
|
||
|
||
**Минусы для психологического контекста**:
|
||
- **Холодный, технический** — не тёплый
|
||
- Siri давно переехал от waveform именно потому что она не вызывала эмпатии
|
||
- Плохо передаёт «мышление» — только «говорение»/«слушание»
|
||
- Нет визуального способа показать «обеспокоенность» или «радость»
|
||
|
||
**Вывод**: Концепт D хорош как **дополнительный элемент** (показывать waveform голоса пользователя внизу экрана), но НЕ как основной персонаж.
|
||
|
||
---
|
||
|
||
## 4. Поиск Motion Дизайнера
|
||
|
||
### 4.1 Где Искать
|
||
|
||
| Платформа | Специфика | Ссылка |
|
||
|-----------|-----------|--------|
|
||
| **Rive Marketplace «For Hire»** | Дизайнеры специально рекламируют Rive-навыки | [rive.app/marketplace/for-hire](https://rive.app/marketplace/for-hire/) |
|
||
| **Twitter/X @rive_app** | Официальный аккаунт; в ответах и RT часто появляются авторы | twitter.com/rive_app |
|
||
| **Dribbble #rive** | Фильтр по тегу; смотри работы со state machine видео | dribbble.com/tags/rive |
|
||
| **Upwork** | Поиск «Rive animation designer»; ~$30–80/час | upwork.com |
|
||
| **Toptal** | Топ 3% фрилансеров; $80–150/час; медленный онбординг | toptal.com |
|
||
| **Discord Rive Community** | #for-hire канал; сообщество ~15K человек | discord.com/invite/FGjmaTr |
|
||
| **Behance** | Поиск «Rive character animation»; не всегда указывают Rive явно | behance.net |
|
||
| **LinkedIn** | «Rive motion designer iOS» → фильтр по навыкам | linkedin.com |
|
||
|
||
**Самый быстрый путь**: Rive Discord #for-hire + Rive Marketplace For Hire
|
||
|
||
---
|
||
|
||
### 4.2 Шаблон Брифа для Motion Дизайнера
|
||
|
||
```markdown
|
||
## Проект: AI-компаньон для приложения психологической рефлексии
|
||
|
||
### Контекст
|
||
Мобильное приложение (iOS/Android) для ежедневной психологической рефлексии с AI-компаньоном.
|
||
Аудитория: взрослые 25–45 лет, работают со стрессом, ищут поддержку.
|
||
|
||
### Задача
|
||
Разработать абстрактного анимированного персонажа — визуальное лицо AI-компаньона.
|
||
|
||
**НЕ**: человек, робот, животное.
|
||
**ДА**: орб, blob, абстрактная форма — тёплая, живая, доверительная.
|
||
|
||
### Концепт (базовый)
|
||
[Концепт A: Дышащий орб] — светящаяся сфера ~140pt, с мягким bloom-эффектом.
|
||
Референсы: ChatGPT voice mode orb, Apple Siri (iOS 16), Headspace Andy (стиль — не копия).
|
||
|
||
### Состояния (5 обязательных + idle)
|
||
1. **Idle/Breathing** — медленное дыхание, базовый цвет (индиго/фиолетовый)
|
||
2. **Listening** — реактивный к амплитуде звука через Number input `audioAmplitude` (0.0–1.0)
|
||
3. **Thinking** — внутреннее движение, «обрабатываю»
|
||
4. **Speaking** — пульс/волны при TTS
|
||
5. **Happy/Positive** — тёплый burst
|
||
6. **Concerned/Serious** — сдержанность, глубокий цвет
|
||
|
||
### Технические требования
|
||
- **Инструмент**: Rive (обязательно)
|
||
- **Deliverable**: `.riv` файл с настроенной State Machine
|
||
- **State Machine inputs**:
|
||
- `audioAmplitude` (Number, 0.0–1.0) — голосовая реактивность
|
||
- `emotionState` (Enum: idle/listening/thinking/speaking/happy/concerned)
|
||
- **Размер артборда**: 300×300pt (векторный, масштабируемый)
|
||
- **Производительность**: файл должен работать в 60fps на iPhone 12 и новее
|
||
- Использовать Vector Feathering для glow (не PNG/растр)
|
||
|
||
### Что включает deliverable
|
||
- [ ] Концепт (2–3 варианта визуала) — для согласования
|
||
- [ ] Финальный `.riv` файл со state machine
|
||
- [ ] Короткое видео-демо всех состояний (GIF или MP4)
|
||
- [ ] Инструкция по inputs для разработчика (1 страница)
|
||
|
||
### Бюджет ориентировочно
|
||
$300–600 (уточняем после концепта)
|
||
|
||
### Сроки
|
||
Концепт: 3–5 дней
|
||
Финальный файл: 7–14 дней после согласования
|
||
|
||
### Контакт / Портфолио
|
||
Прошу показать примеры работ с Rive State Machine (не просто Lottie/AE).
|
||
```
|
||
|
||
---
|
||
|
||
### 4.3 Реалистичная Стоимость
|
||
|
||
| Уровень дизайнера | Профиль | Концепт + 5 состояний + .riv | Сроки |
|
||
|-------------------|---------|------------------------------|-------|
|
||
| **Junior Rive** | Upwork, 1–2 года опыта | $150–300 | 10–21 день |
|
||
| **Mid-level** | Upwork/Dribbble, портфолио Rive | **$300–600** ✅ *sweet spot* | 7–14 дней |
|
||
| **Senior/Студия** | Toptal, известные имена | $800–2000+ | 14–30 дней |
|
||
| **Готовый шаблон** | Marketplace + кастомизация | $0–50 + $100–200 dev time | 2–5 дней |
|
||
|
||
**Реалистичная цена для MVP**: **$300–500** за полный пакет (концепт + 5 состояний + .riv файл)
|
||
|
||
---
|
||
|
||
### 4.4 Готовые Персонажи на Маркетплейсах
|
||
|
||
**Rive Marketplace** ([rive.app/marketplace](https://rive.app/marketplace)):
|
||
- Теги для поиска: `orb`, `blob`, `character`, `loader`, `mascot`
|
||
- Большинство файлов **бесплатны** для просмотра/ремикса с attribution
|
||
- Некоторые продаются за $5–30
|
||
|
||
**Рекомендуемый поиск**:
|
||
- `site:rive.app/community orb animation`
|
||
- `site:rive.app/community blob character state machine`
|
||
|
||
**LottieFiles** (для Lottie) — не подходит для нашего interactive use case, но можно найти вдохновение: lottiefiles.com/search?q=orb+ai+character
|
||
|
||
**Другие источники готовых Rive файлов**:
|
||
- **awesome-rive**: [github.com/rive-app/awesome-rive](https://github.com/rive-app/awesome-rive) — кураторский список
|
||
- **Gumroad** + поиск «Rive character»
|
||
- **Creative Market** + поиск «Rive animation»
|
||
|
||
---
|
||
|
||
## 5. DIY Вариант
|
||
|
||
### 5.1 Можно ли Без Дизайнера?
|
||
|
||
**Да, и вот почему это разумно для TestFlight MVP**:
|
||
- Для бета-тестирования важно проверить *концепцию взаимодействия*, а не полировку
|
||
- SwiftUI Canvas + TimelineView позволяет сделать «достаточно хороший» орб за 1–2 дня
|
||
- После валидации концепции → нанять дизайнера для production-версии
|
||
|
||
### 5.2 Минимальная Версия для TestFlight
|
||
|
||
**Что должна уметь (MVP)**:
|
||
- [x] Визуально отличаться в 3+ состояниях (idle, listening, speaking)
|
||
- [x] Реагировать на амплитуду микрофона (пульс/размер)
|
||
- [x] Плавные переходы между состояниями (withAnimation + ease)
|
||
- [x] Работать в 60fps без прыжков
|
||
- [x] Не выглядеть «сломанной» при любом сценарии
|
||
|
||
**Что НЕ нужно для MVP**:
|
||
- Профессиональный Rive-файл
|
||
- Все 6 эмоциональных состояний
|
||
- Идеальный bloom/glow
|
||
|
||
### 5.3 Полный SwiftUI Орб — Структура Кода
|
||
|
||
```swift
|
||
import SwiftUI
|
||
import AVFoundation
|
||
|
||
// MARK: - Состояния персонажа
|
||
enum CompanionState {
|
||
case idle, listening, thinking, speaking, happy, concerned
|
||
|
||
var color: Color {
|
||
switch self {
|
||
case .idle: return Color(hex: "#3D2B8F") // индиго
|
||
case .listening: return Color(hex: "#00C4CC") // бирюзовый
|
||
case .thinking: return Color(hex: "#7B4FD4") // фиолетовый
|
||
case .speaking: return Color(hex: "#9B59B6") // тёплый фиолетовый
|
||
case .happy: return Color(hex: "#FF6B6B") // персиковый
|
||
case .concerned: return Color(hex: "#1A237E") // тёмно-синий
|
||
}
|
||
}
|
||
|
||
var pulseSpeed: Double {
|
||
switch self {
|
||
case .idle: return 3.0
|
||
case .listening: return 1.5
|
||
case .thinking: return 2.0
|
||
case .speaking: return 0.8
|
||
case .happy: return 0.5
|
||
case .concerned: return 4.0
|
||
}
|
||
}
|
||
|
||
var baseScale: CGFloat {
|
||
switch self {
|
||
case .idle: return 1.0
|
||
case .listening: return 1.05
|
||
case .thinking: return 0.97
|
||
case .speaking: return 1.03
|
||
case .happy: return 1.15
|
||
case .concerned: return 0.92
|
||
}
|
||
}
|
||
}
|
||
|
||
// MARK: - Орб
|
||
struct CompanionOrbView: View {
|
||
var state: CompanionState
|
||
var audioAmplitude: Float // 0.0...1.0
|
||
|
||
@State private var isBreathing = false
|
||
@State private var rotationAngle: Double = 0
|
||
|
||
private let baseSize: CGFloat = 130
|
||
|
||
var body: some View {
|
||
let amplitudeBoost = CGFloat(audioAmplitude) * 35
|
||
let currentSize = baseSize * state.baseScale + amplitudeBoost
|
||
|
||
ZStack {
|
||
// Слой 3: дальнее свечение
|
||
Circle()
|
||
.fill(state.color.opacity(0.08))
|
||
.frame(width: currentSize + 70)
|
||
.blur(radius: 30)
|
||
|
||
// Слой 2: среднее свечение
|
||
Circle()
|
||
.fill(state.color.opacity(0.2))
|
||
.frame(width: currentSize + 30)
|
||
.blur(radius: 15)
|
||
|
||
// Слой 1: основной орб
|
||
Circle()
|
||
.fill(
|
||
RadialGradient(
|
||
colors: [
|
||
state.color.opacity(0.6),
|
||
state.color,
|
||
state.color.opacity(0.85)
|
||
],
|
||
center: .init(x: 0.35, y: 0.3),
|
||
startRadius: 5,
|
||
endRadius: currentSize / 2
|
||
)
|
||
)
|
||
.frame(width: currentSize, height: currentSize)
|
||
.scaleEffect(isBreathing ? 1.04 : 0.97)
|
||
|
||
// Внутренний блик (имитация 3D)
|
||
Ellipse()
|
||
.fill(
|
||
LinearGradient(
|
||
colors: [.white.opacity(0.4), .clear],
|
||
startPoint: .topLeading,
|
||
endPoint: .bottomTrailing
|
||
)
|
||
)
|
||
.frame(width: currentSize * 0.45, height: currentSize * 0.25)
|
||
.offset(x: -currentSize * 0.12, y: -currentSize * 0.18)
|
||
.blur(radius: 4)
|
||
|
||
// Вращающийся внутренний элемент (для состояния "thinking")
|
||
if state == .thinking {
|
||
Circle()
|
||
.trim(from: 0, to: 0.3)
|
||
.stroke(state.color.opacity(0.5), lineWidth: 2)
|
||
.frame(width: currentSize * 0.6)
|
||
.rotationEffect(.degrees(rotationAngle))
|
||
}
|
||
}
|
||
.animation(
|
||
.easeInOut(duration: state.pulseSpeed).repeatForever(autoreverses: true),
|
||
value: isBreathing
|
||
)
|
||
.animation(.easeInOut(duration: 0.4), value: state)
|
||
.animation(.linear(duration: 0.05), value: audioAmplitude)
|
||
.onAppear {
|
||
isBreathing = true
|
||
withAnimation(.linear(duration: 2).repeatForever(autoreverses: false)) {
|
||
rotationAngle = 360
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// MARK: - Контейнер с микрофоном
|
||
struct CompanionContainer: View {
|
||
@StateObject private var mic = MicrophoneMonitor()
|
||
@State var companionState: CompanionState = .idle
|
||
|
||
var body: some View {
|
||
VStack {
|
||
Spacer()
|
||
CompanionOrbView(state: companionState, audioAmplitude: mic.amplitude)
|
||
.frame(width: 220, height: 220)
|
||
Spacer()
|
||
}
|
||
.background(Color(hex: "#0D0D1A")) // тёмный фон
|
||
.onAppear { mic.startMonitoring() }
|
||
}
|
||
}
|
||
```
|
||
|
||
**Оценка результата без дизайнера**: 6.5/10 — выглядит профессионально для бета-тестирования при правильно подобранных цветах.
|
||
|
||
---
|
||
|
||
## 6. Сводная Таблица Концептов
|
||
|
||
| Критерий | A: Орб | B: Частицы | C: Blob | D: Waveform |
|
||
|----------|--------|-----------|---------|-------------|
|
||
| Эмоциональное тепло | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ |
|
||
| DIY сложность | **4/10** | 7/10 | 6/10 | 2/10 |
|
||
| Голосовая реактивность | ✅ Отлично | ✅ Хорошо | ✅ Хорошо | ✅ Отлично |
|
||
| Rive реализация | 6/10 | 8/10 | 7/10 | 3/10 |
|
||
| Для психологии | ✅✅✅ | ✅✅ | ✅✅ | ⚠️ |
|
||
| Уникальность | Средняя | Высокая | Высокая | Низкая |
|
||
| Стоимость дизайнера | $300–500 | $400–700 | $400–600 | $150–300 |
|
||
|
||
---
|
||
|
||
## 7. ✅ Рекомендация и Следующие Шаги
|
||
|
||
### Рекомендованный Концепт: **A (Дышащий Орб) с элементами C (Blob)**
|
||
|
||
**Почему**:
|
||
1. Орб — самый доказанный паттерн (Siri, ChatGPT, Alexa) — пользователи уже знают этот «язык»
|
||
2. Добавление slight fluid-деформации (±blob) делает его уникальным и «живым»
|
||
3. Лучший баланс тепла + технологичности для психологического контекста
|
||
4. Реалистичен для DIY MVP
|
||
5. Хорошо масштабируется до профессиональной Rive-версии
|
||
|
||
**Цветовая палитра**:
|
||
- Base: `#3D2B8F` (тёмный индиго)
|
||
- Listening: `#00C4CC` (бирюзовый)
|
||
- Thinking: `#7B4FD4` (фиолетовый)
|
||
- Speaking: `#9B59B6`
|
||
- Happy: `#FF6B6B`
|
||
- Concerned: `#1A237E`
|
||
- Background: `#0D0D1A` (почти чёрный)
|
||
|
||
---
|
||
|
||
### Путь 1: DIY для TestFlight (Рекомендую НАЧАТЬ ЗДЕСЬ)
|
||
|
||
**Что делать**:
|
||
1. Реализовать SwiftUI орб из кода выше (1–2 дня)
|
||
2. Подключить AVAudioEngine (1 день)
|
||
3. Добавить 3 состояния: idle / listening / speaking (1 день)
|
||
4. Запустить TestFlight бета, собрать отзывы (2 недели)
|
||
5. Решить — нанимать дизайнера или нет
|
||
|
||
**Стоимость**: $0 (только время разработчика)
|
||
**Срок до TestFlight**: ~5–7 дней
|
||
|
||
---
|
||
|
||
### Путь 2: Hire Motion Designer (После Валидации Концепции)
|
||
|
||
**Когда нанимать**: после того как бета подтвердит что персонаж нужен и работает
|
||
**Где искать**: Rive Discord #for-hire → Rive Marketplace for-hire → Upwork
|
||
|
||
**Бюджет**: $400–600 за полный пакет
|
||
**Что получить**: концепт (2–3 варианта) + .riv файл + state machine + инструкция
|
||
|
||
**Конкретные шаги**:
|
||
1. Написать бриф по шаблону выше
|
||
2. Разместить в Rive Discord #for-hire
|
||
3. Попросить показать работы именно с Rive State Machine
|
||
4. Начать с концепта ($50–100 за концепт) → подтвердить → финальный файл
|
||
|
||
---
|
||
|
||
### Инструменты и Ресурсы
|
||
|
||
| Ресурс | Ссылка |
|
||
|--------|--------|
|
||
| Rive Editor (бесплатно) | editor.rive.app |
|
||
| Rive iOS Runtime | github.com/rive-app/rive-ios |
|
||
| Rive Docs iOS | rive.app/docs/runtimes/apple/apple |
|
||
| Rive Docs State Machine | rive.app/docs/runtimes/state-machines |
|
||
| Rive Community Discord | discord.com/invite/FGjmaTr |
|
||
| Rive Marketplace | rive.app/marketplace |
|
||
| awesome-rive | github.com/rive-app/awesome-rive |
|
||
| Видео: Build iOS App with Rive (DesignCode, 187K views) | youtube.com/watch?v=h4vyOz4Tztg |
|
||
| Видео: Build Interactive Characters in Rive (67K views) | youtube.com/watch?v=-vuQvvFiR8s |
|
||
| Видео: Glowing Jellyfish (blob референс) | youtube.com/watch?v=4oH0jV-L-dU |
|
||
| Видео: Rive State Machine Tutorial | youtube.com/watch?v=oDhdGmXco00 |
|
||
|
||
---
|
||
|
||
*Документ создан агентом исследования. Обновить после TestFlight бета (2 недели).*
|