using TMPro;
using UnityEditor;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityEngine.Video;
using InteractiveVideo.Examples;
namespace InteractiveVideo.Editor
{
///
/// GameObject > Interactive Video > Create Example Setup
/// Builds the whole example hierarchy in the open scene and wires every reference, so the only manual steps
/// are dropping a VideoClip on the VideoPlayer and (optionally) swapping the sample tracking JSON.
///
public static class InteractiveVideoSetupMenu
{
private const string Root = "Assets/InteractiveVideo/";
private const string HighlightMaterialPath = Root + "Materials/InteractiveVideoHighlight.mat";
private const string MaskMaterialPath = Root + "Materials/InteractiveVideoMask.mat";
private const string SampleJsonPath = Root + "Examples/tracking_sample.json";
[MenuItem("GameObject/Interactive Video/Create Example Setup", false, 10)]
public static void CreateExampleSetup()
{
var highlightMat = AssetDatabase.LoadAssetAtPath(HighlightMaterialPath);
var maskMat = AssetDatabase.LoadAssetAtPath(MaskMaterialPath);
var sampleJson = AssetDatabase.LoadAssetAtPath(SampleJsonPath);
if (highlightMat == null || maskMat == null)
Debug.LogWarning($"[InteractiveVideo] Materials not found under {Root}Materials. Assign them by hand on the Highlight Controller / Mask Renderer.");
// ---- EventSystem ----
if (Object.FindAnyObjectByType() == null)
{
var es = new GameObject("EventSystem", typeof(EventSystem));
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
var moduleType = System.Type.GetType("UnityEngine.InputSystem.UI.InputSystemUIInputModule, Unity.InputSystem");
if (moduleType != null) es.AddComponent(moduleType);
else es.AddComponent();
#else
es.AddComponent();
#endif
Undo.RegisterCreatedObjectUndo(es, "Create EventSystem");
}
// ---- Canvas ----
var canvasGo = new GameObject("InteractiveVideoCanvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
Undo.RegisterCreatedObjectUndo(canvasGo, "Create Interactive Video Setup");
var canvas = canvasGo.GetComponent();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
var scaler = canvasGo.GetComponent();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1920, 1080);
scaler.matchWidthOrHeight = 0.5f;
// ---- System object: VideoPlayer + our components ----
var systemGo = new GameObject("InteractiveVideo",
typeof(VideoPlayer), typeof(InteractiveVideoPlayer), typeof(VideoTrackingManager),
typeof(PolygonMaskRenderer), typeof(VideoHighlightController));
Undo.RegisterCreatedObjectUndo(systemGo, "Create Interactive Video Setup");
var videoPlayer = systemGo.GetComponent();
videoPlayer.playOnAwake = false;
videoPlayer.renderMode = VideoRenderMode.RenderTexture;
videoPlayer.isLooping = true;
videoPlayer.audioOutputMode = VideoAudioOutputMode.Direct;
var ivPlayer = systemGo.GetComponent();
var tracking = systemGo.GetComponent();
var mask = systemGo.GetComponent();
var highlight = systemGo.GetComponent();
// ---- Video RawImage (fills the canvas, letterboxing done by the shader) ----
var rawGo = new GameObject("VideoRawImage", typeof(RectTransform), typeof(CanvasRenderer), typeof(RawImage), typeof(VideoClickHandler));
rawGo.transform.SetParent(canvasGo.transform, false);
Stretch(rawGo.GetComponent());
var rawImage = rawGo.GetComponent();
rawImage.color = Color.white;
rawImage.raycastTarget = true;
var click = rawGo.GetComponent();
// ---- Debug overlay (child of the RawImage) ----
var debugGo = new GameObject("TrackingDebugOverlay", typeof(RectTransform), typeof(CanvasRenderer), typeof(TrackingDebugOverlay));
debugGo.transform.SetParent(rawGo.transform, false);
Stretch(debugGo.GetComponent());
var overlay = debugGo.GetComponent();
// ---- Info panel ----
var panelGo = new GameObject("InfoPanel", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter), typeof(VideoObjectInfoPanel));
panelGo.transform.SetParent(canvasGo.transform, false);
var panelRt = panelGo.GetComponent();
panelRt.anchorMin = panelRt.anchorMax = new Vector2(0f, 1f);
panelRt.pivot = new Vector2(0f, 1f);
panelRt.anchoredPosition = new Vector2(24f, -24f);
panelRt.sizeDelta = new Vector2(360f, 0f);
panelGo.GetComponent().color = new Color(0f, 0f, 0f, 0.6f);
panelGo.GetComponent().raycastTarget = false;
var layout = panelGo.GetComponent();
layout.padding = new RectOffset(16, 16, 12, 12);
layout.spacing = 4f;
layout.childForceExpandHeight = false;
layout.childControlHeight = true;
layout.childControlWidth = true;
panelGo.GetComponent().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
var nameText = MakeText(panelGo.transform, "NameText", "Father", 36, FontStyles.Bold);
var actionText = MakeText(panelGo.transform, "ActionText", "Drinking water", 26, FontStyles.Normal);
var idText = MakeText(panelGo.transform, "TrackIdText", "person_01", 18, FontStyles.Italic);
idText.color = new Color(1f, 1f, 1f, 0.6f);
var confText = MakeText(panelGo.transform, "ConfidenceText", "97 %", 18, FontStyles.Normal);
confText.color = new Color(1f, 1f, 1f, 0.6f);
var infoPanel = panelGo.GetComponent();
// ---- Demo controls (bottom bar) ----
var barGo = new GameObject("DemoControls", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(HorizontalLayoutGroup), typeof(InteractiveVideoDemoControls));
barGo.transform.SetParent(canvasGo.transform, false);
var barRt = barGo.GetComponent();
barRt.anchorMin = new Vector2(0f, 0f);
barRt.anchorMax = new Vector2(1f, 0f);
barRt.pivot = new Vector2(0.5f, 0f);
barRt.anchoredPosition = Vector2.zero;
barRt.sizeDelta = new Vector2(0f, 64f);
barGo.GetComponent().color = new Color(0f, 0f, 0f, 0.5f);
var hl = barGo.GetComponent();
hl.padding = new RectOffset(16, 16, 12, 12);
hl.spacing = 12f;
hl.childForceExpandWidth = false;
hl.childForceExpandHeight = true;
hl.childControlWidth = true;
hl.childControlHeight = true;
hl.childAlignment = TextAnchor.MiddleLeft;
var playBtn = MakeButton(barGo.transform, "PlayPauseButton", "Play", 110f, out var playLabel);
var slider = MakeSlider(barGo.transform, "Scrubber");
var frameLabel = MakeText(barGo.transform, "FrameLabel", "0 / 0", 20, FontStyles.Normal);
frameLabel.gameObject.AddComponent().preferredWidth = 140f;
frameLabel.alignment = TextAlignmentOptions.Center;
var modeBtn = MakeButton(barGo.transform, "ModeButton", "DimBackgroundAndOutline", 280f, out var modeLabel);
var toggle = MakeToggle(barGo.transform, "DebugToggle", "Debug");
var demo = barGo.GetComponent();
// ---- wire everything via SerializedObject (fields are private) ----
Set(ivPlayer, ("videoPlayer", videoPlayer), ("videoDisplay", rawImage), ("trackingManager", tracking), ("highlightController", highlight));
Set(tracking, ("trackingJson", sampleJson));
SetBool(tracking, "showTrackingDebug", true);
Set(mask, ("maskMaterial", maskMat));
Set(highlight, ("trackingManager", tracking), ("maskRenderer", mask), ("videoDisplay", rawImage), ("highlightMaterial", highlightMat));
Set(click, ("player", ivPlayer), ("trackingManager", tracking));
Set(overlay, ("player", ivPlayer), ("trackingManager", tracking));
Set(infoPanel, ("trackingManager", tracking), ("nameText", nameText), ("actionText", actionText), ("trackIdText", idText), ("confidenceText", confText));
SetBool(infoPanel, "showConfidence", true);
Set(demo, ("player", ivPlayer), ("trackingManager", tracking), ("highlightController", highlight),
("playPauseButton", playBtn), ("playPauseLabel", playLabel), ("scrubber", slider), ("frameLabel", frameLabel),
("modeButton", modeBtn), ("modeLabel", modeLabel), ("debugToggle", toggle));
Selection.activeGameObject = systemGo;
Debug.Log("[InteractiveVideo] Example setup created. Assign a VideoClip (or URL) on the VideoPlayer of 'InteractiveVideo' and press Play.");
}
// ------------------------------------------------------------------ helpers
private static void Stretch(RectTransform rt)
{
rt.anchorMin = Vector2.zero;
rt.anchorMax = Vector2.one;
rt.offsetMin = Vector2.zero;
rt.offsetMax = Vector2.zero;
}
private static TMP_Text MakeText(Transform parent, string name, string text, float size, FontStyles style)
{
var go = new GameObject(name, typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI));
go.transform.SetParent(parent, false);
var t = go.GetComponent();
t.text = text;
t.fontSize = size;
t.fontStyle = style;
t.color = Color.white;
t.raycastTarget = false;
t.textWrappingMode = TextWrappingModes.NoWrap;
t.overflowMode = TextOverflowModes.Ellipsis;
return t;
}
private static Button MakeButton(Transform parent, string name, string label, float width, out TMP_Text labelText)
{
var go = new GameObject(name, typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button), typeof(LayoutElement));
go.transform.SetParent(parent, false);
go.GetComponent().color = new Color(1f, 1f, 1f, 0.15f);
go.GetComponent().preferredWidth = width;
labelText = MakeText(go.transform, "Label", label, 20, FontStyles.Normal);
Stretch(labelText.rectTransform);
labelText.alignment = TextAlignmentOptions.Center;
return go.GetComponent();
}
private static Slider MakeSlider(Transform parent, string name)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Slider), typeof(LayoutElement));
go.transform.SetParent(parent, false);
go.GetComponent().flexibleWidth = 1f;
var bg = new GameObject("Background", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
bg.transform.SetParent(go.transform, false);
var bgRt = bg.GetComponent();
bgRt.anchorMin = new Vector2(0f, 0.35f); bgRt.anchorMax = new Vector2(1f, 0.65f);
bgRt.offsetMin = bgRt.offsetMax = Vector2.zero;
bg.GetComponent().color = new Color(1f, 1f, 1f, 0.2f);
var fillArea = new GameObject("Fill Area", typeof(RectTransform));
fillArea.transform.SetParent(go.transform, false);
var faRt = fillArea.GetComponent();
faRt.anchorMin = new Vector2(0f, 0.35f); faRt.anchorMax = new Vector2(1f, 0.65f);
faRt.offsetMin = new Vector2(8f, 0f); faRt.offsetMax = new Vector2(-8f, 0f);
var fill = new GameObject("Fill", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
fill.transform.SetParent(fillArea.transform, false);
Stretch(fill.GetComponent());
fill.GetComponent().color = new Color(1f, 0.85f, 0.1f, 0.9f);
var handleArea = new GameObject("Handle Slide Area", typeof(RectTransform));
handleArea.transform.SetParent(go.transform, false);
var haRt = handleArea.GetComponent();
Stretch(haRt);
haRt.offsetMin = new Vector2(8f, 0f); haRt.offsetMax = new Vector2(-8f, 0f);
var handle = new GameObject("Handle", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
handle.transform.SetParent(handleArea.transform, false);
handle.GetComponent().sizeDelta = new Vector2(16f, 0f);
handle.GetComponent().color = Color.white;
var slider = go.GetComponent();
slider.fillRect = fill.GetComponent();
slider.handleRect = handle.GetComponent();
slider.targetGraphic = handle.GetComponent();
slider.direction = Slider.Direction.LeftToRight;
slider.minValue = 0f;
slider.maxValue = 1f;
return slider;
}
private static Toggle MakeToggle(Transform parent, string name, string label)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Toggle), typeof(LayoutElement));
go.transform.SetParent(parent, false);
go.GetComponent().preferredWidth = 120f;
var box = new GameObject("Background", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
box.transform.SetParent(go.transform, false);
var boxRt = box.GetComponent();
boxRt.anchorMin = boxRt.anchorMax = new Vector2(0f, 0.5f);
boxRt.pivot = new Vector2(0f, 0.5f);
boxRt.anchoredPosition = Vector2.zero;
boxRt.sizeDelta = new Vector2(24f, 24f);
box.GetComponent().color = new Color(1f, 1f, 1f, 0.3f);
var check = new GameObject("Checkmark", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
check.transform.SetParent(box.transform, false);
var checkRt = check.GetComponent();
Stretch(checkRt);
checkRt.offsetMin = new Vector2(5f, 5f); checkRt.offsetMax = new Vector2(-5f, -5f);
check.GetComponent().color = new Color(1f, 0.85f, 0.1f, 1f);
var text = MakeText(go.transform, "Label", label, 20, FontStyles.Normal);
Stretch(text.rectTransform);
text.rectTransform.offsetMin = new Vector2(32f, 0f);
text.alignment = TextAlignmentOptions.MidlineLeft;
var toggle = go.GetComponent();
toggle.targetGraphic = box.GetComponent();
toggle.graphic = check.GetComponent();
toggle.isOn = true;
return toggle;
}
private static void Set(Object target, params (string property, Object value)[] refs)
{
var so = new SerializedObject(target);
foreach (var (property, value) in refs)
{
var p = so.FindProperty(property);
if (p == null) { Debug.LogWarning($"[InteractiveVideo] Setup: property '{property}' not found on {target.GetType().Name}."); continue; }
p.objectReferenceValue = value;
}
so.ApplyModifiedPropertiesWithoutUndo();
}
private static void SetBool(Object target, string property, bool value)
{
var so = new SerializedObject(target);
var p = so.FindProperty(property);
if (p != null) p.boolValue = value;
so.ApplyModifiedPropertiesWithoutUndo();
}
}
}