跳到主要内容

数据绑定(Data Binding)

仅当 RiveWidget 上的 Data Binding Mode 设置为 Manual 时,才需要使用这些 API。否则,您可以直接在 Unity 检查器的 Data 部分配置视图模型绑定。

private void OnEnable()
{
riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;
}

private void OnDisable()
{
riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;
}

private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
File file = riveWidget.File;

// 按名称获取引用
ViewModel viewModel = file.GetViewModelByName("My View Model");

// 按索引获取引用
for (int i = 0; i < file.ViewModelCount; i++)
{
ViewModel indexedVM = file.GetViewModelAtIndex(i);
}

// 获取画板的默认视图模型引用
ViewModel defaultVM = riveWidget.Artboard.DefaultViewModel;
}
}

仅当 RiveWidget 上的 Data Binding Mode 设置为 Manual 时,才需要使用这些 API。否则,您可以直接在 Unity 检查器的 Data 部分配置视图模型绑定。

private void OnEnable()
{
riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;
}

private void OnDisable()
{
riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;
}

private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
ViewModel vm = riveWidget.File.GetViewModelByName("My View Model");

// 创建空白实例
ViewModelInstance vmiBlank = vm.CreateInstance();

// 创建默认实例
ViewModelInstance vmiDefault = vm.CreateDefaultInstance();

// 按索引创建
for (int i = 0; i < vm.InstanceCount; i++)
{
ViewModelInstance vmiIndexed = vm.CreateInstanceAt(i);
}

// 按名称创建
ViewModelInstance vmiNamed = vm.CreateInstanceByName("My Instance");
}
}
// 使用 Unity Inspector
// 1. 在 Inspector 中选择 RiveWidget
// 2. 在 "Data" 部分,设置 Data Binding Mode:
// - Auto Bind Default:自动绑定默认视图模型实例
// - Auto Bind Selected:使用下拉菜单中选中的特定实例
// - Manual:需要在代码中手动设置绑定

// 或者通过编程方式(设置为 Manual 或使用底层 API 时)
private void OnEnable()
{
riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;
}

private void OnDisable()
{
riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;
}

private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
ViewModel vm = riveWidget.Artboard.DefaultViewModel;
ViewModelInstance vmi = vm.CreateDefaultInstance();

// 应用到状态机将自动绑定到其画板
riveWidget.StateMachine.BindViewModelInstance(vmi);
}
}

📌 组件 API(推荐)

Rive Widget 提供了可视化和编程两种方式来配置自动绑定。在检查器中,您可以通过数据绑定模式下拉菜单轻松设置绑定:

Unity 检查器中的数据绑定模式下拉菜单

要以编程方式启用自动绑定,请使用以下 API:

// 在 Widget 加载之前设置:

// 选项 1:自动绑定默认实例
riveWidget.BindingMode = DataBindingMode.AutoBindDefault;

// 选项 2:按名称自动绑定特定实例
riveWidget.BindingMode = DataBindingMode.AutoBindSelected;
riveWidget.ViewModelInstanceName = "My Instance";

// 设置绑定模式后加载 Rive 文件
riveWidget.Load(riveFile, artboardName, stateMachineName);

...
// 访问已自动绑定的当前实例
ViewModelInstance boundInstance = riveWidget.StateMachine.ViewModelInstance;

📌 旧版 API

⚠️ 你正在使用旧版 API。建议升级到最新的组件 API。

如果您选择使用底层 API 来控制渲染循环,则需要在脚本中手动设置数据绑定。

作为参考,请查看此 RiveScreen 示例,它演示了如何在自定义渲染循环中实现自动绑定的一种方式。

使用底层 API 需要额外的实现工作以及对 Rive 运行时的理解。除非您有特殊需求需要设置自定义渲染循环,否则我们建议使用组件 API。

var vm = riveWidget.File.GetViewModelByName("My View Model");

// 属性列表
var properties = vm.Properties;
foreach (var prop in properties)
{
Debug.Log($"Property: {prop.Name}, Type: {prop.Type}");
}
private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
ViewModelInstance viewModelInstance = riveWidget.StateMachine.ViewModelInstance;

// 字符串属性
ViewModelInstanceStringProperty stringProperty = viewModelInstance.GetStringProperty("title");
Debug.Log($"String value: {stringProperty.Value}");
stringProperty.Value = "New Text";

// 数值属性
ViewModelInstanceNumberProperty numberProperty = viewModelInstance.GetNumberProperty("count");
Debug.Log($"Number value: {numberProperty.Value}");
numberProperty.Value = 42.5f;

// 布尔属性
ViewModelInstanceBooleanProperty boolProperty = viewModelInstance.GetBooleanProperty("isActive");
Debug.Log($"Boolean value: {boolProperty.Value}");
boolProperty.Value = true;

// 颜色属性
ViewModelInstanceColorProperty colorProperty = viewModelInstance.GetColorProperty("backgroundColor");
Color currentColor = colorProperty.Value;
colorProperty.Value = new UnityEngine.Color(1, 0, 0, 1); // 红色
Color32 currentColor32 = colorProperty.Value32;
colorProperty.Value32 = new Color32(0, 255, 0, 255); // 绿色

// 枚举属性
ViewModelInstanceEnumProperty enumProperty = viewModelInstance.GetEnumProperty("category");
Debug.Log($"Enum current value: {enumProperty.Value}");
Debug.Log($"Enum available values: {string.Join(", ", enumProperty.EnumValues)}");
enumProperty.Value = "option_name";

// 触发器属性
ViewModelInstanceTriggerProperty triggerProperty = viewModelInstance.GetTriggerProperty("onSubmit");
triggerProperty.Trigger(); // 触发
}
}
if (riveWidget.Status == WidgetStatus.Loaded)
{
var viewModelInstance = riveWidget.StateMachine.ViewModelInstance;

// 使用链式调用访问嵌套视图模型
var nestedNumberByChain = viewModelInstance
.GetViewModelInstanceProperty("My Nested View Model")
.GetViewModelInstanceProperty("My Second Nested VM")
.GetNumberProperty("My Nested Number");

// 使用路径表示法访问嵌套属性
var nestedNumberByPath = viewModelInstance
.GetNumberProperty("My Nested View Model/My Second Nested VM/My Nested Number");
}
private ViewModelInstanceNumberProperty numberProperty;
private ViewModelInstanceStringProperty stringProperty;
private ViewModelInstanceBooleanProperty boolProperty;
private ViewModelInstanceColorProperty colorProperty;
private ViewModelInstanceEnumProperty enumProperty;
private ViewModelInstanceTriggerProperty triggerProperty;

private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
ViewModelInstance viewModelInstance = riveWidget.StateMachine.ViewModelInstance;

// 为属性添加监听器
numberProperty = viewModelInstance.GetNumberProperty("count");
numberProperty.OnValueChanged += OnNumberPropertyChanged;

stringProperty = viewModelInstance.GetStringProperty("title");
stringProperty.OnValueChanged += OnStringPropertyChanged;

boolProperty = viewModelInstance.GetBooleanProperty("isActive");
boolProperty.OnValueChanged += OnBoolPropertyChanged;

colorProperty = viewModelInstance.GetColorProperty("backgroundColor");
colorProperty.OnValueChanged += OnColorPropertyChanged;

enumProperty = viewModelInstance.GetEnumProperty("category");
enumProperty.OnValueChanged += OnEnumPropertyChanged;

triggerProperty = viewModelInstance.GetTriggerProperty("onSubmit");
triggerProperty.OnTriggered += OnTriggerPropertyFired;
}
}

private void OnNumberPropertyChanged(float newValue) { Debug.Log($"Number changed to: {newValue}"); }
private void OnStringPropertyChanged(string newValue) { Debug.Log($"String changed to: {newValue}"); }
private void OnBoolPropertyChanged(bool newValue) { Debug.Log($"Boolean changed to: {newValue}"); }
private void OnColorPropertyChanged(UnityEngine.Color newValue) { Debug.Log($"Color changed to: {ColorUtility.ToHtmlStringRGBA(newValue)}"); }
private void OnEnumPropertyChanged(string newValue) { Debug.Log($"Enum changed to: {newValue}"); }
private void OnTriggerPropertyFired() { Debug.Log("Trigger fired!"); }

private void OnDestroy()
{
numberProperty.OnValueChanged -= OnNumberPropertyChanged;
stringProperty.OnValueChanged -= OnStringPropertyChanged;
boolProperty.OnValueChanged -= OnBoolPropertyChanged;
colorProperty.OnValueChanged -= OnColorPropertyChanged;
enumProperty.OnValueChanged -= OnEnumPropertyChanged;
triggerProperty.OnTriggered -= OnTriggerPropertyFired;
}
[SerializeField] private ImageOutOfBandAsset m_lightImageAsset;
[SerializeField] private ImageOutOfBandAsset m_darkImageAsset;

private ViewModelInstanceImageProperty imageProperty;
private bool isDarkMode = false;

private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
m_lightImageAsset.Load();
m_darkImageAsset.Load();
ViewModelInstance viewModelInstance = riveWidget.StateMachine.ViewModelInstance;

imageProperty = viewModelInstance.GetImageProperty("profileImage");
imageProperty.OnValueChanged += OnImageChanged;
imageProperty.Value = m_lightImageAsset; // 初始设置为亮色模式
}
}

public void ToggleTheme()
{
if (imageProperty != null)
{
isDarkMode = !isDarkMode;
imageProperty.Value = isDarkMode ? m_darkImageAsset : m_lightImageAsset;
}
}

public void ClearImage()
{
if (imageProperty != null) imageProperty.Value = null;
}

private void OnDestroy()
{
m_lightImageAsset.Unload();
m_darkImageAsset.Unload();
if (imageProperty != null) imageProperty.OnValueChanged -= OnImageChanged;
}

有关 Unity 中图像数据绑定的演示,请参阅 Rive Unity 示例仓库 中的 Image Data Binding 场景。

private ViewModelInstanceListProperty listProperty;

private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
ViewModelInstance viewModelInstance = riveWidget.StateMachine.ViewModelInstance;
listProperty = viewModelInstance.GetListProperty("todos");
listProperty.OnChanged += OnListChanged;

var todoItemVM = riveWidget.File.GetViewModelByName("TodoItem");
var newTodo = todoItemVM.CreateInstance();
newTodo.GetStringProperty("description").Value = "Buy groceries";
listProperty.Add(newTodo);

var anotherTodo = todoItemVM.CreateInstance();
listProperty.Insert(anotherTodo, 0); // 在开头插入

for (int i = 0; i < listProperty.Count; i++)
{
var item = listProperty.GetInstanceAt(i);
Debug.Log($"Item {i}: {item}");
}

listProperty.Remove(newTodo); // 移除特定实例
listProperty.RemoveAt(0); // 按索引移除
if (listProperty.Count > 1) listProperty.Swap(0, 1); // 交换位置
}
}

private void OnListChanged() { Debug.Log("List updated!"); }
private void OnDestroy() { if (listProperty != null) listProperty.OnChanged -= OnListChanged; }

画板属性使用 BindableArtboard 类,它与包中的常规 Artboard 类不同。BindableArtboard 是一个运行时包装器,用于通过数据绑定与画板进行交互。这些实例引用文件中现有的画板,因此无需在 Rive 编辑器中额外设置。

[SerializeField] private Asset m_externalRiveAsset;
private ViewModelInstanceArtboardProperty artboardProperty;
private File externalFile;

private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
ViewModelInstance viewModelInstance = riveWidget.StateMachine.ViewModelInstance;
artboardProperty = viewModelInstance.GetArtboardProperty("artboard_1");
artboardProperty.OnValueChanged += OnArtboardChanged;

var blueArtboard = riveWidget.File.BindableArtboard("ArtboardBlue");
artboardProperty.Value = blueArtboard;

if (m_externalRiveAsset != null) externalFile = File.Load(m_externalRiveAsset);
}
}

public void SwitchToRedArtboard()
{
if (artboardProperty != null)
artboardProperty.Value = riveWidget.File.BindableArtboard("ArtboardRed");
}

public void SwitchToExternalArtboard()
{
if (artboardProperty != null && externalFile != null)
artboardProperty.Value = externalFile.BindableArtboard("SomeArtboard");
}

private void OnDestroy()
{
externalFile?.Dispose();
if (artboardProperty != null) artboardProperty.OnValueChanged -= OnArtboardChanged;
}

使用带有可绑定画板的自定义视图模型实例

您可以将自定义的 ViewModelInstance 链接到可绑定画板,从而控制该画板使用的数据:

var file = riveWidget.File;
var viewModelInstance = file.GetViewModelByName("CharacterData").CreateInstance();
var bindableArtboard = file.BindableArtboard("FeaturedCharacterCard", viewModelInstance);

示例:特色内容槽 — 一个主屏幕有一个"特色"内容区域,可以动态显示不同类型的促销内容,每种使用不同的画板和独特的数据结构:

private ViewModelInstanceArtboardProperty featuredContentSlot;
private ViewModelInstance characterData, eventData, offerData;
private BindableArtboard featuredCharacter, limitedEvent, specialOffer;

private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
ViewModelInstance viewModelInstance = riveWidget.StateMachine.ViewModelInstance;
featuredContentSlot = viewModelInstance.GetArtboardProperty("featuredContentSlot");

// 特色角色 — 独特的数据结构
characterData = riveWidget.File.GetViewModelByName("CharacterData").CreateInstance();
characterData.GetStringProperty("name").Value = "Shadowblade";
characterData.GetStringProperty("class").Value = "Assassin";
characterData.GetNumberProperty("attackPower").Value = 92;
characterData.GetStringProperty("specialAbility").Value = "Phantom Strike";
characterData.GetBoolProperty("unlocked").Value = false;

// 限时活动 — 独特的数据结构
eventData = riveWidget.File.GetViewModelByName("EventData").CreateInstance();
eventData.GetStringProperty("title").Value = "Dragon Raid Weekend";
eventData.GetStringProperty("description").Value = "Team up to defeat the ancient dragon";
eventData.GetNumberProperty("hoursRemaining").Value = 36;
eventData.GetNumberProperty("participants").Value = 1247;
eventData.GetBoolProperty("active").Value = true;

// 特殊优惠 — 独特的数据结构
offerData = riveWidget.File.GetViewModelByName("OfferData").CreateInstance();
offerData.GetStringProperty("itemName").Value = "Legendary Weapon Pack";
offerData.GetNumberProperty("originalPrice").Value = 2999;
offerData.GetNumberProperty("discount").Value = 50;
offerData.GetStringProperty("currencyType").Value = "Gems";
offerData.GetNumberProperty("expiresInHours").Value = 12;

featuredCharacter = riveWidget.File.BindableArtboard("FeaturedCharacterCard", characterData);
limitedEvent = riveWidget.File.BindableArtboard("EventBanner", eventData);
specialOffer = riveWidget.File.BindableArtboard("OfferCard", offerData);
featuredContentSlot.Value = featuredCharacter;
}
}
var viewModelInstance = riveWidget.StateMachine.ViewModelInstance;

// 从文件中访问枚举
var enums = riveWidget.File.ViewModelEnums;
foreach (var enumType in enums)
{
Debug.Log($"Enum: {enumType.Name}");
foreach (var value in enumType.Values) Debug.Log($" - Value: {value}");
}

// 使用枚举属性
var enumProperty = viewModelInstance.GetEnumProperty("category");
Debug.Log($"Current value: {enumProperty.Value}");
Debug.Log($"Available values: {string.Join(", ", enumProperty.EnumValues)}");
enumProperty.Value = enumProperty.EnumValues[0];