GodotEngineでDMXの信号を受け取ってみた
GodotEngineを触ってみたい
前々から気になってたGodotEngineを触ってみようと思い初めて触ってみました。
概念の理解がちゃんとまだできてないので手探りですが一旦目標として、照明機器の操作などに用いられるDMX信号を受け取るところまでをやってみました。
今回動いたものは以下のリポジトリに入れてます。
環境
- GodotEngine: v4.1.1.stable.mono.official [bd6af8e0e]
- LXProtocols.ArtNet 3.0.4.2
- QLC+
以下から手順について書きますがQLCなどの基本的な使い方は省きます。
手順
GodotEngineでシーンを用意する
tscnファイルを作成してルートノードとして DMXReceiver
を配置しました。
このノードのライフサイクルを使って今回はDMXを受け取る処理を作成します。
QLC+の設定を行う
Universeは4つで 127.0.0.1
に対して出力するように設定しておきます。
また、機器は適当に設定して全チャンネルを一括で今回は操作できるようにしていました。
これでひとまずDMX信号を流す準備ができました。
LXProtocols.ArtNetを導入する
今回私の環境ではRiderを使っていたのでRider経由でNuGetを使用して導入しました。
Riderでは専用のタブがあるのでそこから検索で artnet
を入力することで探し出せます。そこからバージョンを指定して導入しています。
他のパッケージについて
Haukcode.ArtNet というものを当初導入しようとしたのですが今回QLC+で 127.0.0.1
のループバックで流そうとするとうまく受け取れませんでした。ローカルで流さずに他の機器からなら受け取れたのかもしれません。
DMXを受け取る部分を作成する
DMXRecieverがNodeに直接紐づけている部分です。
インスペクターからIPアドレスとDMXのUniverse設定を変更できるようにしています。
CaptureArtNetSignal.cs
という後で記載する直接DMX信号を受け取るクラスのハンドリングと、そこから信号を受けてログを流すように一旦しています。
本来であれば信号受け取ってCG照明などに反映させることもあると思うのですが今回は信号受け取るところまでの想定なのでログ出しだけです。
DMXReciever.cs
using System;
using System.Net;
using System.Text;
using Godot;
using GodotProject.scripts;
namespace GodotProject.scripts;
public partial class DMXReciever : Node
{
[Export]
private string ipAddressString = "127.0.0.1";
[Export]
private string subnetMaskAddressString = "255.0.0.0";
[Export]
private uint universe = 4;
private byte[] signals;
private StringBuilder stringBuilder = new();
private CaptureArtNetSignal captureArtNetSignal;
public override void _Ready()
{
var ipAddress = IPAddress.Parse(ipAddressString);
var subnetMaskAddress = IPAddress.Parse(subnetMaskAddressString);
captureArtNetSignal = new CaptureArtNetSignal(ipAddress, subnetMaskAddress, universe);
captureArtNetSignal.OnNotifySignals += OnNotifySignals;
}
private void OnNotifySignals(in byte[] signals)
{
this.signals = signals;
stringBuilder.Clear();
for (var i = 0; i < this.signals.Length; i++)
{
stringBuilder.AppendLine($"{i}: {signals[i]}");
}
GD.Print(stringBuilder.ToString());
}
public override void _ExitTree()
{
captureArtNetSignal.OnNotifySignals -= OnNotifySignals;
(captureArtNetSignal as IDisposable).Dispose();
}
}
CaptureArtNetSignalというクラスでLXProtocols.ArtNetの呼び出しなどを行っています。
CaptureArtNetSignal.cs
using System;
using System.Net;
using Godot;
using LXProtocols.Acn.Rdm;
using LXProtocols.Acn.Sockets;
using LXProtocols.ArtNet;
using LXProtocols.ArtNet.Packets;
using LXProtocols.ArtNet.Sockets;
namespace GodotProject.scripts;
public class CaptureArtNetSignal : IDisposable
{
private const uint DmxLengthPerUniverse = 512;
private readonly ArtNetSocket socket = new (UId.Empty);
private byte[] dmxData;
public delegate void NotifyNewPacket(in byte[] signals);
public event NotifyNewPacket OnNotifySignals;
public CaptureArtNetSignal(IPAddress ipAddress, IPAddress subnetMaskAddress, uint universe)
{
dmxData = new byte[DmxLengthPerUniverse * universe];
socket.NewPacket += SocketOnNewPacket;
socket.UnhandledException += SocketOnUnhandledException;
socket.Open(ipAddress, subnetMaskAddress);
}
private void SocketOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var exception = e.ExceptionObject as Exception;
GD.PrintErr(exception);
}
private void SocketOnNewPacket(object sender, NewPacketEventArgs<ArtNetPacket> e)
{
switch (e.Packet.OpCode) {
case ArtNetOpCodes.Dmx:
var dmxPacket = e.Packet as ArtNetDmxPacket;
Array.Copy(dmxPacket.DmxData, 0, dmxData, DmxLengthPerUniverse * dmxPacket.Universe, DmxLengthPerUniverse);
OnNotifySignals?.Invoke(dmxData);
break;
}
}
void IDisposable.Dispose()
{
socket.UnhandledException -= SocketOnUnhandledException;
socket.NewPacket -= SocketOnNewPacket;
socket.Close();
socket.Dispose();
}
}
信号を受け取ってみる
QLC+で信号を流してGodotでログを流した画像です。
設定した数値が反映されています。
また全体だけでなく特定のチャンネルを操作しても想定した数値の反映が確認できました。
最期に
Godotを初めて触りつつC#触れてみて思ったよりも難しくなく触れた印象でした。
パッケージ導入もNuGetでサクッと導入できたりインスペクターへの反映など使いやすかったです。
Engineとしての機能も少しずつ触ってみれたらと思います。
Discussion