internal void Construct(NsvAssetLoader assetLoader, ColorManager colorManager)
        {
            _colorManager = colorManager;
            _config       = PluginConfig.Instance;

            BuildNote(assetLoader);
        }
        private void BuildNote(NsvAssetLoader assetLoader)
        {
            /*
             * The hierarchy is the following:
             * (RootBlockGroup
             *   RoundRect
             *   Circle
             *   Arrow
             *	  (SliceGroup
             *		MissedArea
             *		Slice))
             */

            _blockTransform = gameObject.AddComponent <RectTransform>();
            gameObject.AddComponent <RectMask2D>();

            _blockTransform.localScale    = Vector3.one * _config.CubeScale;
            _blockTransform.localRotation = Quaternion.identity;
            _blockTransform.localPosition = Vector3.zero;

            /*
             * {
             *  var blockMaskGO = new GameObject("BlockMask");
             *  var maskTransform = blockMaskGO.AddComponent<RectTransform>();
             *  maskTransform.SetParent(blockTransform);
             *  maskTransform.localScale = Vector3.one;
             *  maskTransform.localRotation = Quaternion.identity;
             *  var halfWidth = Assets.RRect.rect.width / Assets.RRect.pixelsPerUnit / 2.0f;
             *  var halfHeight = Assets.RRect.rect.height / Assets.RRect.pixelsPerUnit / 2.0f;
             *  maskTransform.localPosition = new Vector3(-halfWidth, -halfHeight, 0f);
             *
             *  var blockMask = blockMaskGO.AddComponent<SpriteMask>();
             *  blockMask.sprite = Assets.RRect;
             *  blockMask.backSortingOrder = -4;
             *  blockMask.frontSortingOrder = 0;
             * }
             */
            //Log.Info(string.Format("transform.rotation: {0}", blockMaskGO.transform.localRotation));
            //Log.Info(string.Format("transform.position: {0}", blockMaskGO.transform.localPosition));

            {
                // Construct the note body
                var backgroundGO        = new GameObject("RoundRect");
                var background          = backgroundGO.AddComponent <SpriteRenderer>();
                var backgroundTransform = backgroundGO.AddComponent <RectTransform>();
                background.material = assetLoader.UINoGlowMaterial;
                // background.color = new Color(1.0f, 0.5f, 0.5f, 1.0f);
                // background.sortingLayerID = SortingLayerID;
                background.sortingOrder = -4;
                backgroundTransform.SetParent(_blockTransform);
                backgroundTransform.localScale    = Vector3.one;
                backgroundTransform.localRotation = Quaternion.identity;
                if (assetLoader.RRect != null)
                {
                    background.sprite = assetLoader.RRect;
                    var halfWidth  = assetLoader.RRect.rect.width / assetLoader.RRect.pixelsPerUnit / 2.0f;
                    var halfHeight = assetLoader.RRect.rect.height / assetLoader.RRect.pixelsPerUnit / 2.0f;
                    backgroundTransform.localPosition = new Vector3(-halfWidth, -halfHeight, 0f);
                }

                _background = background;
            }

            {
                // Construct the small circle in the center
                var circleGO        = new GameObject("Circle");
                var circle          = circleGO.AddComponent <SpriteRenderer>();
                var circleTransform = circleGO.AddComponent <RectTransform>();
                circle.material = assetLoader.UINoGlowMaterial;
                circle.color    = _config.CenterColor;
                // circle.sortingLayerID = SortingLayerID;
                circle.sortingOrder = -3;
                circleTransform.SetParent(_blockTransform);
                circleTransform.localScale    = Vector3.one * _config.CenterScale;
                circleTransform.localRotation = Quaternion.identity;
                if (assetLoader.Circle != null)
                {
                    var sprite = assetLoader.Circle;
                    circle.sprite = sprite;
                    var halfWidth    = _config.ArrowScale * sprite.rect.width / sprite.pixelsPerUnit / 2.0f;
                    var centerOffset = halfWidth - _config.ArrowScale * sprite.rect.height / sprite.pixelsPerUnit;
                    circleTransform.localPosition = new Vector3(-halfWidth, centerOffset, 0f);
                }

                _circle = circle;
            }

            {
                // Construct the directional arrow
                var arrowGO        = new GameObject("Arrow");
                var arrow          = arrowGO.AddComponent <SpriteRenderer>();
                var arrowTransform = arrowGO.AddComponent <RectTransform>();
                arrow.material = assetLoader.UINoGlowMaterial;
                arrow.color    = _config.ArrowColor;
                // arrow.sortingLayerID = SortingLayerID;
                arrow.sortingOrder = -2;
                arrowTransform.SetParent(_blockTransform);
                arrowTransform.localScale    = Vector3.one * _config.ArrowScale;
                arrowTransform.localRotation = Quaternion.identity;
                if (assetLoader.Arrow != null)
                {
                    var sprite = assetLoader.Arrow;
                    arrow.sprite = sprite;
                    var halfWidth    = _config.ArrowScale * sprite.rect.width / sprite.pixelsPerUnit / 2.0f;
                    var centerOffset = halfWidth - _config.ArrowScale * sprite.rect.height / sprite.pixelsPerUnit;
                    arrowTransform.localPosition = new Vector3(-halfWidth, centerOffset, 0f);
                }

                _arrow = arrow;
            }

            {
                // Construct the slice UI
                var sliceGroupGO        = new GameObject("SliceGroup");
                var sliceGroupTransform = sliceGroupGO.AddComponent <RectTransform>();
                sliceGroupTransform.SetParent(_blockTransform);
                sliceGroupTransform.localScale    = Vector3.one;
                sliceGroupTransform.localRotation = Quaternion.identity;
                sliceGroupTransform.localPosition = Vector3.zero;
                _sliceGroupTransform = sliceGroupTransform;

                {
                    // missed area background
                    var missedAreaGO        = new GameObject("MissedArea");
                    var missedArea          = missedAreaGO.AddComponent <SpriteRenderer>();
                    var missedAreaTransform = missedAreaGO.AddComponent <RectTransform>();
                    missedArea.material = assetLoader.UINoGlowMaterial;
                    missedArea.sprite   = assetLoader.White;
                    missedArea.color    = _config.MissedAreaColor;
                    // missedArea.sortingLayerID = SortingLayerID;
                    missedArea.sortingOrder = -1;
                    // missedArea.maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
                    missedAreaTransform.SetParent(sliceGroupTransform);
                    missedAreaTransform.localScale    = new Vector3(0f, 1f, 1f);
                    missedAreaTransform.localRotation = Quaternion.identity;
                    missedAreaTransform.localPosition = new Vector3(0f, -0.5f, 0f);
                    _missedAreaTransform = missedAreaTransform;
                    _missedArea          = missedArea;
                }

                {
                    // slice line
                    var sliceGO        = new GameObject("Slice");
                    var slice          = sliceGO.AddComponent <SpriteRenderer>();
                    var sliceTransform = sliceGO.AddComponent <RectTransform>();
                    slice.material = assetLoader.UINoGlowMaterial;
                    slice.sprite   = assetLoader.White;
                    slice.color    = _config.SliceColor;
                    // slice.sortingLayerID = SortingLayerID;
                    slice.sortingOrder = 0;
                    // slice.maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
                    sliceTransform.SetParent(sliceGroupTransform);
                    sliceTransform.localScale    = new Vector3(_config.SliceWidth, 1f, 1f);
                    sliceTransform.localRotation = Quaternion.identity;
                    sliceTransform.localPosition = new Vector3(-_config.SliceWidth / 2f, -0.5f, 0f);
                    _sliceTransform = sliceTransform;
                    _slice          = slice;
                }
            }

            SetActive(false);
        }