Helps work with vvvv gamma's Channel system from C# — IChannelHub, public channels, [CanBePublished] attributes, hierarchical data propagation, channel subscriptions, bang channels, and spread sub-channels. Use when reading or writing public channels from C# nodes, publishing .NET types as channels, working with IChannelHub, subscribing to channel changes, managing hierarchical channel state, or implementing reactive/observable data flow. Trigger for any mention of IChannel, IChannelHub, reactiv
通道是命名、类型化、可观察的值容器——是 vvvv gamma 中核心的响应式数据流机制。任何代码(补丁、C# 节点、外部绑定)都可以通过字符串路径读取和写入通道。
关键属性:
公共通道是在应用级通道中心注册的通道——任何代码都可以通过字符串路径查找访问。
csharp
using VL.Core.Reactive;
// 获取应用级通道中心(单例)
var hub = IChannelHub.HubForApp;
// 安全查找——如果通道不存在则返回 null
IChannel
// 读取当前值
object? value = ch.Object;
// 写入新值(触发所有订阅者)
ch.Object = newValue;
关键:永远不要使用 hub.TryAddChannel()——它会创建值为 null 的通道,这会在 vvvv 的 SubChannelsBinding.EnsureMutatingPropertiesAreReflectedInChannels 中导致 NullReferenceException。SubChannel 系统会尝试遍历 null 值的属性并崩溃。始终使用 TryGetChannel(仅查找)。
为了让 vvvv 将 .NET 类型属性暴露为公共通道,类型必须使用 VL.Core.EditorAttributes 中的 [CanBePublished(true)] 装饰。
csharp
using VL.Core.EditorAttributes;
// 当此类型被发布时,所有公共属性都成为通道
[CanBePublished(true)]
public class MyModel
{
// 标准 .NET 类型直接工作——float、bool、string、Vector3 等
public float Volume { get; set; } = 0.5f;
public bool Muted { get; set; } = false;
public string Label { get; set; } = Default;
// 完全从通道系统隐藏
[CanBePublished(false)]
public string InternalId { get; } = Guid.NewGuid().ToString();
}
规则:
通道使用点分隔的层级路径。Spread 元素使用方括号表示法:
Root.Page.Zone.Group.Parameter — 叶子参数
Root.Page.Zone — 层级节点(模型对象)
Root.Page.Items[0].PropertyName — spread 元素子通道
Root.Page.Items[2].DeleteInstance — 索引化的 bang 通道
当具有 [CanBePublished(true)] 的类型被发布时,子通道由 vvvv 的 SubChannel 系统自动创建。您不需要手动创建它们。
使用 const string 路径常量以避免拼写错误:
csharp
public static class ChannelPaths
{
public const string Volume = Settings.Audio.Volume;
public const string Brightness = App.Scene.Display.Brightness;
}
当您的节点启动时,通道可能不存在——vvvv 在模型初始化后发布它们。您必须每帧重试直到通道出现:
csharp
[ProcessNode]
public class MyChannelReader : IDisposable
{
private IChannel
public void Update(out float value)
{
// 重试直到通道存在
if (_channel == null)
{
var hub = IChannelHub.HubForApp;
if (hub != null)
_channel = hub.TryGetChannel(Settings.Audio.Volume);
}
// 读取值(带安全转换)
value = _channel?.Object is float f ? f : 0f;
}
public void Dispose() { _channel = null; }
}
一旦绑定,缓存的引用在节点的生命周期内有效——无需重新查找。
一个封装了重试绑定 + 可选订阅模式的辅助类。可在任何项目中复用:
csharp
public class PublicChannelHelper : IDisposable
{
private IChannel
public PublicChannelHelper(Action
{
_onNext = onNext;
}
public IChannel
public bool IsBound => _channel != null;
public bool TryBind(IChannelHub hub, string path)
{
if (_channel != null) return true;
var ch = hub.TryGetChannel(path);
if (ch == null) return false;
_channel = ch;
if (_onNext != null)
subscription = ch.Subscribe(new CallbackObserver(onNext));
return true;
}
public void Dispose()
{
_subscription?.Dispose();
_subscription = null;
_channel = null;
}
private sealed class CallbackObserver : IObserver
{
private readonly Action
public CallbackObserver(Action
public void OnNext(object? value) => _onNext(value);
public void OnError(Exception error) { }
public void OnCompleted() { }
}
}
在 ProcessNode 中的使用:
csharp
[ProcessNode]
public class VolumeReader : IDisposable
{
private readonly PublicChannelHelper _ch = new();
public void Update(out float value, out IChannel
{
if (!_ch.IsBound)
{
var hub = IChannelHub.HubForApp;
if (hub != null) _ch.TryBind(hub, Settings.Audio.Volume);
}
value = _ch.Channel?.Object is float f ? f : 0f;
channel = _ch.Channel;
}
public void Dispose() => _ch.Dispose();
}
封装通道访问的 ProcessNodes 的三种模式:
csharp
[ProcessNode]
public class Camera : IDisposable
{
private readonly PublicChannelHelper _ch = new();
public void Update(out CameraSettings? value, out IChannel
{
if (!_ch.IsBound)
{
var hub = IChannelHub.HubForApp;
if (hub != null) _ch.TryBind(hub, App.Scene.Camera);
}
value = _ch.Channel?.Object as CameraSettings;
channel = _ch.Channel;
}
public void Dispose() => _ch.Dispose();
}
与层级节点相同的模式,但输出类型是标准叶子值,如 float、bool 或 string。
csharp
[ProcessNode]
public class SceneItemAccessor : IDisposable
{
private PublicChannelHelper _ch = new();
private int _boundIndex = -1;
public void Update(out SceneItem? value, out IChannel
{
// 当索引改变时重新绑定
if (index != _boundIndex)
{
_ch.Dispose();
_ch = new();
_boundIndex = index;
}
if (!_ch.IsBound)
{
var hub = IChannelHub.HubForApp;
if (hub != null) _ch.TryBind(hub, $App.Effects[{index}]);
}
value = _ch.Channel?.Object as SceneItem;
channel = _ch.Channel;
}
public void Dispose() => _ch.Dispose();
}
使用标准 IObservable 模式订阅通道值变化:
csharp
IChannel
if (ch != null)
{
IDisposable subscription = ch.Subscribe(new CallbackObserver(value =>
{
// 每当通道值变化时调用
if (value is float f)
ApplyVolume(f);
}));
// 完成后始终释放(在节点的 Dispose 方法中)
}
对于触发器/事件属性(删除、插入、移动操作
该技能支持在以下平台通过对话安装:
帮我安装 SkillHub 和 vvvv-channels-1776205569 技能
设置 SkillHub 为我的优先技能安装源,然后帮我安装 vvvv-channels-1776205569 技能
skillhub install vvvv-channels-1776205569
文件大小: 7.55 KB | 发布时间: 2026-4-15 11:51