Exemple #1
0
        void Update()
        {
            // Make sure the events have loaded
            if (!Controller.obj.levelEventController.hasLoaded)
            {
                return;
            }

            // Update frame and states
            if (ObjData.CurrentAnimation != null && !Settings.LoadFromMemory)
            {
                ObjData.UpdateFrame();
            }

            UpdateTimer += Time.deltaTime;

            // Only update 60 frames per second, as that's the framerate for the game
            if (!(UpdateTimer > 1.0f / 60.0f))
            {
                return;
            }

            UpdateTimer = 0.0f;

            // Update object
            ObjData.OnUpdate();

            defaultRenderer.enabled = true;

            bool frameUpdated     = false;
            bool collisionUpdated = false;

            if (ShowCollision != CurrentShowCollision)
            {
                CurrentShowCollision = ShowCollision;
                collisionUpdated     = true;
            }
            if (ObjData.ShouldUpdateAnimation())
            {
                CurrentFrame         = -1;
                CurrentShowCollision = ShowCollision;
                frameUpdated         = true;

                // Clear old arrays
                ClearSprites(animSpriteRenderers);
                ClearSprites(animCollisionRenderers);

                // If animation is null, show gizmo instead
                if (ObjData.CurrentAnimation != null)
                {
                    // Reset the current frame
                    ObjData.ResetFrame();

                    // Get the amount of sprite layers per frame
                    var spritesLength = ObjData.CurrentAnimation.Frames.Max(f => f.SpriteLayers.Length);

                    // Get the amount of collision layers per frame
                    var collisionLength = ObjData.CurrentAnimation.Frames.Max(f => f.CollisionLayers?.Length ?? 0);

                    // Create arrays
                    animSpriteRenderers    = new SpriteRenderer[spritesLength];
                    animCollisionRenderers = new SpriteRenderer[collisionLength];

                    bool is3D = ObjData is Unity_Object_3D && LevelEditorData.Level.IsometricData != null;

                    // Populate sprites
                    for (int i = 0; i < spritesLength; i++)
                    {
                        // Instantiate prefab
                        SpriteRenderer newRenderer = Instantiate(prefabSpritepart, transform).GetComponent <SpriteRenderer>();
                        newRenderer.sortingOrder     = is3D ? 0 : (Layer + i);
                        newRenderer.sortingLayerName = ObjData.MapLayer == 2 ? "Object Sprites Back" : "Object Sprites";
                        if (is3D)
                        {
                            newRenderer.gameObject.layer = LayerMask.NameToLayer("3D Object");
                        }

                        newRenderer.transform.localPosition = new Vector3(0, 0, is3D ? 0 : (spritesLength - i));
                        newRenderer.transform.localRotation = Quaternion.identity;
                        newRenderer.transform.localScale    = Vector3.one * ObjData.Scale;

                        // Add to list
                        animSpriteRenderers[i] = newRenderer;
                    }

                    // Populate collision boxes
                    for (int i = 0; i < collisionLength; i++)
                    {
                        // Instantiate prefab
                        var newRenderer = Instantiate(prefabBox, transform).GetComponent <SpriteRenderer>();
                        newRenderer.sortingOrder = is3D ? 0 : (Layer + spritesLength + i);
                        if (is3D)
                        {
                            newRenderer.gameObject.layer = LayerMask.NameToLayer("3D Object");
                        }

                        newRenderer.transform.localRotation = Quaternion.identity;

                        // Add to list
                        animCollisionRenderers[i] = newRenderer;
                    }
                }
            }

            // Get the current animation
            var anim = ObjData.CurrentAnimation;

            //
            bool defaultRendererEnabled = ShowGizmo;

            if (defaultRenderer.gameObject.activeSelf != defaultRendererEnabled)
            {
                defaultRenderer.gameObject.SetActive(defaultRendererEnabled);
            }

            UpdatePosition();

            // Don't move link cube if it's part of a link

            if (ObjData.EditorLinkGroup != 0)
            {
                linkCube.position = linkCubeLockPosition;
            }
            else
            {
                linkCubeLockPosition = linkCube.position;
            }

            var isVisible = IsVisible;

            // Update sprite parts in the animation
            if (anim != null)
            {
                // Get properties
                var frame = ObjData.AnimationFrame;
                if (CurrentFrame != frame)
                {
                    frameUpdated = true;
                    CurrentFrame = frame;
                }
                var sprites   = ObjData.Sprites;
                var pivot     = ObjData.Pivot;
                var mirroredX = ObjData.FlipHorizontally;
                var mirroredY = ObjData.FlipVertically;
                if (CurrentMirrorX != mirroredX || CurrentMirrorY != mirroredY)
                {
                    frameUpdated   = true;
                    CurrentMirrorX = mirroredX;
                    CurrentMirrorY = mirroredY;
                }

                if (frame >= anim.Frames.Length)
                {
                    throw new Exception($"Invalid frame index {frame} with length {anim.Frames.Length} for obj {Index}. {ObjData.PrimaryName}-{ObjData.SecondaryName}");
                }

                // Update sprites
                for (int i = 0; i < anim.Frames[frame].SpriteLayers.Length; i++)
                {
                    var layer = anim.Frames[frame].SpriteLayers[i];

                    // Get the sprite index
                    var spriteIndex = layer.ImageIndex;

                    // Change it if the event is multi-colored (Rayman 1 only)
                    if (ObjData is Unity_Object_R1 objr1 && objr1.EventData.Type.IsMultiColored())
                    {
                        spriteIndex += ((sprites.Count / 6) * objr1.EventData.HitPoints);
                    }

                    if (animSpriteRenderers.Length <= i)
                    {
                        continue;
                    }

                    if (frameUpdated)
                    {
                        // Set the sprite, skipping sprites which are out of bounds
                        if (spriteIndex >= sprites.Count && (LevelEditorData.CurrentSettings.EngineVersion != EngineVersion.R2_PS1 || spriteIndex < 0xFFF))
                        {
                            print($"Sprite index too high: {ObjData.PrimaryName}|{ObjData.SecondaryName}: {spriteIndex} >= {sprites.Count}");
                        }
                        animSpriteRenderers[i].sprite = spriteIndex >= sprites.Count ? null : sprites[spriteIndex];

                        var layerMirroredX = layer.IsFlippedHorizontally;
                        var layerMirroredY = layer.IsFlippedVertically;

                        // Indicate if the sprites should be flipped
                        animSpriteRenderers[i].flipX = (layerMirroredX ^ mirroredX);
                        animSpriteRenderers[i].flipY = (layerMirroredY ^ mirroredY);

                        // Get the dimensions
                        var w = animSpriteRenderers[i].sprite == null ? 0 : animSpriteRenderers[i].sprite.textureRect.width;
                        var h = animSpriteRenderers[i].sprite == null ? 0 : animSpriteRenderers[i].sprite.textureRect.height;

                        var xx = layer.XPosition + (layerMirroredX ? w : 0);

                        var yy = -(layer.YPosition + (layerMirroredY ? h : 0));

                        // scale
                        Vector2 pos = new Vector2(
                            ((xx - pivot.x) * (mirroredX ? -1f : 1f) * ObjData.Scale + pivot.x) / (float)LevelEditorData.Level.PixelsPerUnit,
                            ((yy - pivot.y) * (mirroredY ? -1f : 1f) * ObjData.Scale + pivot.y) / (float)LevelEditorData.Level.PixelsPerUnit);

                        animSpriteRenderers[i].transform.localPosition = new Vector3(pos.x, pos.y, animSpriteRenderers[i].transform.localPosition.z);
                        animSpriteRenderers[i].transform.localScale    = Vector3.one * ObjData.Scale;

                        animSpriteRenderers[i].transform.localRotation = Quaternion.Euler(0, 0, 0);
                        // First transform based on layer properties
                        if ((layer.Rotation.HasValue && layer.Rotation.Value != 0) || (layer.Scale.HasValue && layer.Scale.Value != Vector2.one))
                        {
                            Vector3 transformOrigin = new Vector3(
                                (((layer.TransformOriginX - pivot.x) * (mirroredX ? -1f : 1f) * ObjData.Scale + pivot.x) / (float)LevelEditorData.Level.PixelsPerUnit),
                                ((-layer.TransformOriginY - pivot.y) * (mirroredY ? -1f : 1f) * ObjData.Scale + pivot.y) / (float)LevelEditorData.Level.PixelsPerUnit,
                                animSpriteRenderers[i].transform.localPosition.z);

                            // Scale first
                            if (layer.Scale.HasValue && layer.Scale.Value != Vector2.one)
                            {
                                Vector3 scaleValue = new Vector3(layer.Scale.Value.x, layer.Scale.Value.y, 1f);
                                animSpriteRenderers[i].transform.localScale = Vector3.Scale(Vector3.one * ObjData.Scale, scaleValue);
                                Vector3 scaledPos = Vector3.Scale(animSpriteRenderers[i].transform.localPosition - transformOrigin, scaleValue);
                                animSpriteRenderers[i].transform.localPosition = transformOrigin + scaledPos;
                            }
                            // Then rotate
                            if (layer.Rotation.HasValue && layer.Rotation.Value != 0)
                            {
                                /*Quaternion rotation = Quaternion.Euler(0, 0, layer.Rotation * 180f);*/
                                //Vector3 rotationOrigin = Vector3.zero;

                                animSpriteRenderers[i].transform.RotateAround(transform.TransformPoint(transformOrigin), new Vector3(0, 0, 1), layer.Rotation.Value * ((mirroredX ^ mirroredY) ? -1f : 1f));

                                /*    Vector2 relativePos = pos - rotationOrigin;
                                 * Vector2 rotatedPos = rotation * relativePos;
                                 * prefabRenderers[i].transform.localRotation = rotation;
                                 * prefabRenderers[i].transform.localPosition = new Vector3(relativePos.x + rotatedPos.x, relativePos.y + rotatedPos.y, prefabRenderers[i].transform.localPosition.z);*/
                            }
                        }

                        // Then transform based on object properties
                        if ((ObjData.Rotation.HasValue && ObjData.Rotation.Value != 0))
                        {
                            Vector3 transformOrigin = new Vector3(
                                pivot.x / (float)LevelEditorData.Level.PixelsPerUnit,
                                pivot.y / (float)LevelEditorData.Level.PixelsPerUnit,
                                animSpriteRenderers[i].transform.localPosition.z);

                            // Then rotate
                            if (ObjData.Rotation.HasValue && ObjData.Rotation.Value != 0)
                            {
                                animSpriteRenderers[i].transform.RotateAround(transform.TransformPoint(transformOrigin), new Vector3(0, 0, 1), ObjData.Rotation.Value * ((mirroredX ^ mirroredY) ? -1f : 1f));
                            }
                        }
                    }

                    // Get visibility
                    animSpriteRenderers[i].enabled = isVisible;
                    animSpriteRenderers[i].color   = ObjData.IsDisabled ? new Color(1, 1, 1, 0.5f) : Color.white;
                }

                if (frameUpdated)
                {
                    // Remove unused sprites
                    for (int i = anim.Frames[frame].SpriteLayers.Length; i < animSpriteRenderers.Length; i++)
                    {
                        animSpriteRenderers[i].sprite  = null;
                        animSpriteRenderers[i].enabled = false;
                    }

                    // Remove unused collision layers
                    for (int i = anim.Frames[frame].CollisionLayers.Length; i < animCollisionRenderers.Length; i++)
                    {
                        animCollisionRenderers[i].sprite  = null;
                        animCollisionRenderers[i].enabled = false;
                    }
                }
                if (frameUpdated || collisionUpdated)
                {
                    // Update collision
                    for (int i = 0; i < anim.Frames[frame].CollisionLayers.Length; i++)
                    {
                        SetCollisionBox(anim.Frames[frame].CollisionLayers[i], animCollisionRenderers[i], pivot);
                        animCollisionRenderers[i].enabled = CurrentShowCollision;
                    }
                }
            }

            if (ShowGizmo)
            {
                UpdateGizmoPosition(ObjData.ObjCollision, ObjData.Pivot);
            }
            if (CurrentShowCollision && (frameUpdated || collisionUpdated))
            {
                // Update object collision
                var objCol = ObjData.ObjCollision;

                // Update new
                if ((objCollisionRenderers == null && objCol != null && objCol.Length > 0) || objCol?.Length != objCollisionRenderers?.Length)
                {
                    // Clear old object collision array
                    ClearSprites(objCollisionRenderers);

                    if (objCol != null && objCol.Length > 0)
                    {
                        if (CurrentShowCollision)
                        {
                            objCollisionRenderers = new SpriteRenderer[objCol.Length];
                            bool is3D = ObjData is Unity_Object_3D && LevelEditorData.Level.IsometricData != null;

                            // Instantiate prefabs
                            for (int i = 0; i < objCol.Length; i++)
                            {
                                objCollisionRenderers[i] = Instantiate(prefabBox, transform).GetComponent <SpriteRenderer>();
                                objCollisionRenderers[i].sortingOrder = is3D ? 0 : Layer;

                                objCollisionRenderers[i].transform.localPosition = new Vector3(0, 0, is3D ? 0.1f : (objCol.Length - i));
                                objCollisionRenderers[i].transform.localRotation = Quaternion.identity;
                                objCollisionRenderers[i].transform.localScale    = Vector3.one * ObjData.Scale;
                            }
                        }
                    }
                    else
                    {
                        objCollisionRenderers = null;
                    }
                }

                // Update object collision boxes
                if (objCollisionRenderers != null)
                {
                    for (var i = 0; i < objCollisionRenderers.Length; i++)
                    {
                        SetCollisionBox(objCol[i], objCollisionRenderers[i], ObjData.Pivot);
                        objCollisionRenderers[i].enabled = CurrentShowCollision;
                    }
                }
            }
            else if (!CurrentShowCollision)
            {
                if (objCollisionRenderers != null)
                {
                    for (var i = 0; i < objCollisionRenderers.Length; i++)
                    {
                        objCollisionRenderers[i].enabled = false;
                    }
                }
            }

            if (frameUpdated)
            {
                // Update the follow sprite line (Rayman 1 only)
                if (ObjData is Unity_Object_R1 r1 && anim != null)
                {
                    var animLayer = anim.Frames[r1.AnimationFrame].SpriteLayers.ElementAtOrDefault(r1.EventData.FollowSprite);
                    var imgDescr  = r1.ObjManager.DES.ElementAtOrDefault(r1.DESIndex)?.Data?.ImageDescriptors.ElementAtOrDefault(animLayer?.ImageIndex ?? -1);

                    followSpriteLine.localPosition = new Vector2(
                        ((animLayer?.XPosition ?? 0) + (imgDescr?.HitBoxOffsetX ?? 0)) / (float)LevelEditorData.Level.PixelsPerUnit,
                        -((animLayer?.YPosition ?? 0) + (imgDescr?.HitBoxOffsetY ?? 0)) / (float)LevelEditorData.Level.PixelsPerUnit - (r1.EventData.OffsetHY / (float)LevelEditorData.Level.PixelsPerUnit));

                    var w = (animSpriteRenderers.ElementAtOrDefault(r1.EventData.FollowSprite)?.sprite == null) ? 0 : imgDescr?.HitBoxWidth ?? 0;
                    followSpriteLine.localScale = new Vector2(w, 1f);
                }
            }

            // Update the collider size for when selecting the events
            if (frameUpdated || collisionUpdated)
            {
                if (anim != null || objCollisionRenderers?.Any() == true)
                {
                    var sprites = anim != null ? animSpriteRenderers : objCollisionRenderers;

                    // Set box collider size to be the combination of all parts
                    float leftX = 0, bottomY = 0, rightX = 0, topY = 0;
                    float w          = 1f;
                    float h          = 1f;
                    float centerX    = 0f;
                    float centerY    = 0f;
                    bool  first      = true;
                    bool  hasSprites = false;
                    foreach (SpriteRenderer part in sprites)
                    {
                        if (part.sprite == null)
                        {
                            continue;
                        }

                        hasSprites = true;

                        Bounds b = part.bounds;
                        if (boxCollider3D != null)
                        {
                            b = part.sprite?.bounds ?? b;
                            var scl = part.transform.localScale;
                            if (part.flipX)
                            {
                                scl.x = scl.x * -1;
                            }
                            if (part.flipY)
                            {
                                scl.y = scl.y * -1;
                            }
                            b = new Bounds((part.transform.localPosition + Vector3.Scale(b.center, scl)) * LevelEditorData.Level.PixelsPerUnit, Vector3.Scale(b.size, scl) * LevelEditorData.Level.PixelsPerUnit);
                        }
                        else
                        {
                            b = new Bounds(transform.InverseTransformPoint(b.center) * LevelEditorData.Level.PixelsPerUnit, transform.InverseTransformVector(b.size) * LevelEditorData.Level.PixelsPerUnit);
                        }
                        if (b.min.x < leftX || first)
                        {
                            leftX = b.min.x;
                        }
                        if (b.min.y < bottomY || first)
                        {
                            bottomY = b.min.y;
                        }
                        if (b.max.x > rightX || first)
                        {
                            rightX = b.max.x;
                        }
                        if (b.max.y > topY || first)
                        {
                            topY = b.max.y;
                        }

                        if (first)
                        {
                            first = false;
                        }
                    }

                    if (hasSprites)
                    {
                        w       = (rightX - leftX) / LevelEditorData.Level.PixelsPerUnit;
                        h       = (topY - bottomY) / LevelEditorData.Level.PixelsPerUnit;
                        centerX = leftX / LevelEditorData.Level.PixelsPerUnit + w / 2f;
                        centerY = topY / LevelEditorData.Level.PixelsPerUnit - h / 2f;
                        w       = Mathf.Abs(w);
                        h       = Mathf.Abs(h);
                        if (boxCollider != null)
                        {
                            boxCollider.size   = new Vector2(w, h);
                            boxCollider.offset = new Vector2(centerX, centerY);
                        }
                        else if (boxCollider3D != null)
                        {
                            boxCollider3D.size   = new Vector3(w, h, 0.1f);
                            boxCollider3D.center = new Vector2(centerX, centerY);
                        }
                    }
                }
                else
                {
                    SetGizmoBoxCollider();
                }
            }
            if (ShowGizmo && anim == null && !CurrentShowCollision)
            {
                SetGizmoBoxCollider();
            }

            // Update offset points
            if (anim != null)
            {
                var pivot = ObjData.Pivot;

                offsetCrossBX.localPosition = new Vector2(pivot.x / LevelEditorData.Level.PixelsPerUnit, (pivot.y / LevelEditorData.Level.PixelsPerUnit));

                if (ObjData is Unity_Object_R1 r1bj)
                {
                    int hy = -(r1bj.EventData.OffsetHY);

                    if (r1bj.EventData.GetFollowEnabled(LevelEditorData.CurrentSettings))
                    {
                        hy -= anim.Frames[r1bj.EventData.RuntimeCurrentAnimFrame].SpriteLayers.ElementAtOrDefault(r1bj.EventData.FollowSprite)?.YPosition ?? 0;
                    }

                    offsetCrossHY.localPosition = new Vector2(pivot.x / LevelEditorData.Level.PixelsPerUnit, hy / (float)LevelEditorData.Level.PixelsPerUnit);
                }
                else if (ObjData is Unity_Object_R2 r2bj)
                {
                    var hy = -(r2bj.EventData.CollisionData?.OffsetHY ?? 0);

                    offsetCrossHY.localPosition = new Vector2(pivot.x / LevelEditorData.Level.PixelsPerUnit, hy / (float)LevelEditorData.Level.PixelsPerUnit);
                }
            }

            if (PrevObjType != ObjData.Type)
            {
                PrevObjType = ObjData.Type;
                InitGizmo();
            }
            bool enableBoxCollider = EnableBoxCollider;

            if (boxCollider != null)
            {
                // Update visibility
                boxCollider.enabled = enableBoxCollider;

                // Set new midpoint
                midpoint = new Vector3(transform.position.x + boxCollider.offset.x, transform.position.y + boxCollider.offset.y, 0);
            }
            else if (boxCollider3D != null)
            {
                // Update visibility
                boxCollider3D.enabled = enableBoxCollider;

                // Set new midpoint
                midpoint = transform.TransformPoint(boxCollider3D.center);
            }
            // Set link line to cube
            lineRend.SetPosition(0, midpoint);
            lineRend.SetPosition(1, linkCube.position);

            // Update link colors
            if (ObjData.EditorLinkGroup == 0)
            {
                lineRend.startColor = Controller.obj.levelEventController.linkColorDeactive;
                lineRend.endColor   = Controller.obj.levelEventController.linkColorDeactive;
                linkCube.GetComponent <SpriteRenderer>().color = Controller.obj.levelEventController.linkColorDeactive;
            }
            else
            {
                lineRend.startColor = Controller.obj.levelEventController.linkColorActive;
                lineRend.endColor   = Controller.obj.levelEventController.linkColorActive;
                linkCube.GetComponent <SpriteRenderer>().color = Controller.obj.levelEventController.linkColorActive;
            }

            // Set link visibility
            var showLinks =
                // Make sure the obj is visible
                isVisible &&
                // Make sure links are set to show
                Settings.ShowLinks &&
                // Only show active links on web
                !(Settings.HideUnusedLinks && ObjData.EditorLinkGroup == 0) &&
                // Only show if available
                ObjData.CanBeLinkedToGroup;

            lineRend.enabled = showLinks;
            void SetGameObjectActive(GameObject gao, bool active)
            {
                if (gao.activeSelf != active)
                {
                    gao.SetActive(active);
                }
            }

            SetGameObjectActive(linkCube.gameObject, showLinks);

            // Change the offsets visibility
            bool showOffsets = ShowOffsets;

            SetGameObjectActive(offsetOrigin.gameObject, showOffsets);
            SetGameObjectActive(offsetCrossBX.gameObject, showOffsets && offsetCrossBX.transform.position != Vector3.zero);
            SetGameObjectActive(offsetCrossHY.gameObject, showOffsets && (ObjData is Unity_Object_R1 || ObjData is Unity_Object_R2) && offsetCrossHY.transform.position != Vector3.zero);

            var engineVersion = LevelEditorData.CurrentSettings.EngineVersion;

            followSpriteLine.gameObject.SetActive(
                ShowCollision &&
                ObjData is Unity_Object_R1 r1o &&
                r1o.EventData.GetFollowEnabled(LevelEditorData.CurrentSettings) &&
                !(engineVersion == EngineVersion.R1_PS1_JP || engineVersion == EngineVersion.R1_PS1_JPDemoVol3 || engineVersion == EngineVersion.R1_PS1_JPDemoVol6 || engineVersion == EngineVersion.R1_Saturn));

            // Update one-way link lines
            if (oneWayLinkLines != null)
            {
                for (var i = 0; i < oneWayLinkLines.Length; i++)
                {
                    oneWayLinkLines[i].enabled = (enableBoxCollider && Settings.ShowLinks && ObjData.CanBeLinked && connectedOneWayLinkLines[i]) || ForceShowOneWayLinks;
                }
            }

            HasInitialized = true;
        }
Exemple #2
0
        void Update()
        {
            // Make sure the events have loaded
            if (!Controller.obj.levelEventController.hasLoaded)
            {
                return;
            }

            // Update frame and states
            if (ObjData.CurrentAnimation != null && !Settings.LoadFromMemory)
            {
                ObjData.UpdateFrame();
            }

            UpdateTimer += Time.deltaTime;

            // Only update 60 frames per second, as that's the framerate for the game
            if (!(UpdateTimer > 1.0f / 60.0f))
            {
                return;
            }

            UpdateTimer = 0.0f;

            defautRenderer.enabled = true;

            bool frameUpdated     = false;
            bool collisionUpdated = false;

            if (ShowCollision != CurrentShowCollision)
            {
                CurrentShowCollision = ShowCollision;
                collisionUpdated     = true;
            }
            if (ObjData.ShouldUpdateAnimation())
            {
                CurrentFrame         = -1;
                CurrentShowCollision = ShowCollision;
                frameUpdated         = true;
                // If animation is null, use default renderer ("E")
                if (ObjData.CurrentAnimation == null)
                {
                    ClearSprites(prefabRenderers);
                    ClearSprites(prefabRenderersCollision);
                }
                else
                {
                    // Reset the current frame
                    ObjData.ResetFrame();

                    // Get the amount of sprite layers per frame
                    var spritesLength = ObjData.CurrentAnimation.Frames.Max(f => f.SpriteLayers.Length);

                    // Get the amount of collision layers per frame
                    var collisionLength = ObjData.CurrentAnimation.Frames.Max(f => f.CollisionLayers?.Length ?? 0);

                    // Clear old arrays
                    ClearSprites(prefabRenderers);
                    ClearSprites(prefabRenderersCollision);

                    // Create arrays
                    prefabRenderers          = new SpriteRenderer[spritesLength];
                    prefabRenderersCollision = new SpriteRenderer[collisionLength];

                    // Populate sprites
                    for (int i = 0; i < spritesLength; i++)
                    {
                        // Instantiate prefab
                        SpriteRenderer newRenderer = Instantiate(prefabSpritepart, transform).GetComponent <SpriteRenderer>();
                        newRenderer.sortingOrder = Layer + i;

                        newRenderer.transform.localPosition = new Vector3(0, 0, spritesLength - i);
                        newRenderer.transform.localRotation = Quaternion.identity;
                        newRenderer.transform.localScale    = Vector3.one * ObjData.Scale;

                        // Add to list
                        prefabRenderers[i] = newRenderer;
                    }

                    // Populate collision boxes
                    for (int i = 0; i < collisionLength; i++)
                    {
                        // Instantiate prefab
                        var newRenderer = Instantiate(prefabBox, transform).GetComponent <SpriteRenderer>();
                        newRenderer.sortingOrder = Layer + spritesLength + i;

                        newRenderer.transform.localRotation = Quaternion.identity;

                        // Add to list
                        prefabRenderersCollision[i] = newRenderer;
                    }
                }
            }

            // Get the current animation
            var anim = ObjData.CurrentAnimation;

            defautRenderer.enabled = ShowDefaultRenderer;

            UpdatePosition();

            // Don't move link cube if it's part of a link

            if (ObjData.EditorLinkGroup != 0)
            {
                linkCube.position = linkCubeLockPosition;
            }
            else
            {
                linkCubeLockPosition = linkCube.position;
            }

            var isVisible = IsVisible;

            // Update sprite parts in the animation
            if (anim != null)
            {
                // Get properties
                var frame = ObjData.AnimationFrame;
                if (CurrentFrame != frame)
                {
                    frameUpdated = true;
                    CurrentFrame = frame;
                }
                var sprites   = ObjData.Sprites;
                var pivot     = ObjData.Pivot;
                var mirroredX = ObjData.FlipHorizontally;
                var mirroredY = ObjData.FlipVertically;

                if (frame >= anim.Frames.Length)
                {
                    throw new Exception($"Invalid frame index {frame} with length {anim.Frames.Length} for obj {Index}. {ObjData.PrimaryName}-{ObjData.SecondaryName}");
                }

                // Update sprites
                for (int i = 0; i < anim.Frames[frame].SpriteLayers.Length; i++)
                {
                    var layer = anim.Frames[frame].SpriteLayers[i];

                    // Get the sprite index
                    var spriteIndex = layer.ImageIndex;

                    // Change it if the event is multi-colored (Rayman 1 only)
                    if (ObjData is Unity_Object_R1 objr1 && objr1.EventData.Type.IsMultiColored())
                    {
                        spriteIndex += ((sprites.Count / 6) * objr1.EventData.HitPoints);
                    }

                    if (prefabRenderers.Length <= i)
                    {
                        continue;
                    }

                    if (frameUpdated)
                    {
                        // Set the sprite, skipping sprites which are out of bounds
                        if (spriteIndex >= sprites.Count && (LevelEditorData.CurrentSettings.EngineVersion != EngineVersion.R2_PS1 || spriteIndex < 0xFFF))
                        {
                            print("Sprite index too high: " + ObjData.Name + ": " + spriteIndex + " >= " + sprites.Count);
                        }
                        prefabRenderers[i].sprite = spriteIndex >= sprites.Count ? null : sprites[spriteIndex];

                        var layerMirroredX = layer.IsFlippedHorizontally;
                        var layerMirroredY = layer.IsFlippedVertically;

                        // Indicate if the sprites should be flipped
                        prefabRenderers[i].flipX = (layerMirroredX ^ mirroredX);
                        prefabRenderers[i].flipY = (layerMirroredY ^ mirroredY);

                        // Get the dimensions
                        var w = prefabRenderers[i].sprite == null ? 0 : prefabRenderers[i].sprite.texture.width;
                        var h = prefabRenderers[i].sprite == null ? 0 : prefabRenderers[i].sprite.texture.height;

                        var xx = layer.XPosition + (layerMirroredX ? w : 0);

                        var yy = -(layer.YPosition + (layerMirroredY ? h : 0));

                        // scale
                        Vector2 pos = new Vector2(
                            ((xx - pivot.x) * (mirroredX ? -1f : 1f) * ObjData.Scale + pivot.x) / (float)LevelEditorData.Level.PixelsPerUnit,
                            ((yy - pivot.y) * (mirroredY ? -1f : 1f) * ObjData.Scale + pivot.y) / (float)LevelEditorData.Level.PixelsPerUnit);

                        prefabRenderers[i].transform.localPosition = new Vector3(pos.x, pos.y, prefabRenderers[i].transform.localPosition.z);
                        prefabRenderers[i].transform.localScale    = Vector3.one * ObjData.Scale;

                        prefabRenderers[i].transform.localRotation = Quaternion.Euler(0, 0, 0);
                        if ((layer.Rotation.HasValue && layer.Rotation.Value != 0) || (layer.Scale.HasValue && layer.Scale.Value != Vector2.one))
                        {
                            Vector3 transformOrigin = new Vector3(
                                (((layer.TransformOriginX - pivot.x) * (mirroredX ? -1f : 1f) * ObjData.Scale + pivot.x) / (float)LevelEditorData.Level.PixelsPerUnit),
                                ((-layer.TransformOriginY - pivot.y) * (mirroredY ? -1f : 1f) * ObjData.Scale + pivot.y) / (float)LevelEditorData.Level.PixelsPerUnit,
                                prefabRenderers[i].transform.localPosition.z);

                            // Scale first
                            if (layer.Scale.HasValue && layer.Scale.Value != Vector2.one)
                            {
                                Vector3 scaleValue = new Vector3(layer.Scale.Value.x, layer.Scale.Value.y, 1f);
                                prefabRenderers[i].transform.localScale = Vector3.Scale(Vector3.one * ObjData.Scale, scaleValue);
                                Vector3 scaledPos = Vector3.Scale(prefabRenderers[i].transform.localPosition - transformOrigin, scaleValue);
                                prefabRenderers[i].transform.localPosition = transformOrigin + scaledPos;
                            }
                            // Then rotate
                            if (layer.Rotation.HasValue && layer.Rotation.Value != 0)
                            {
                                /*Quaternion rotation = Quaternion.Euler(0, 0, layer.Rotation * 180f);*/
                                //Vector3 rotationOrigin = Vector3.zero;

                                prefabRenderers[i].transform.RotateAround(transform.TransformPoint(transformOrigin), new Vector3(0, 0, 1), layer.Rotation.Value * ((mirroredX ^ mirroredY) ? -1f : 1f));

                                /*    Vector2 relativePos = pos - rotationOrigin;
                                 * Vector2 rotatedPos = rotation * relativePos;
                                 * prefabRenderers[i].transform.localRotation = rotation;
                                 * prefabRenderers[i].transform.localPosition = new Vector3(relativePos.x + rotatedPos.x, relativePos.y + rotatedPos.y, prefabRenderers[i].transform.localPosition.z);*/
                            }
                        }
                    }

                    // Get visibility
                    prefabRenderers[i].enabled = isVisible;
                    prefabRenderers[i].color   = ObjData.IsDisabled ? new Color(1, 1, 1, 0.5f) : Color.white;
                }

                if (frameUpdated)
                {
                    // Remove unused sprites
                    for (int i = anim.Frames[frame].SpriteLayers.Length; i < prefabRenderers.Length; i++)
                    {
                        prefabRenderers[i].sprite  = null;
                        prefabRenderers[i].enabled = false;
                    }

                    // Remove unused collision layers
                    for (int i = anim.Frames[frame].CollisionLayers.Length; i < prefabRenderersCollision.Length; i++)
                    {
                        prefabRenderersCollision[i].sprite  = null;
                        prefabRenderersCollision[i].enabled = false;
                    }
                }
                if (frameUpdated || collisionUpdated)
                {
                    // Update collision
                    for (int i = 0; i < anim.Frames[frame].CollisionLayers.Length; i++)
                    {
                        SetCollisionBox(anim.Frames[frame].CollisionLayers[i], prefabRenderersCollision[i], pivot);
                        prefabRenderersCollision[i].enabled = CurrentShowCollision;
                    }
                }
            }

            if (frameUpdated || collisionUpdated)
            {
                var col = ObjData.ObjCollision;

                // Update object collision
                if (CurrentShowCollision && ((prefabRendersObjCollision == null && col != null && col.Length > 0) || col.Length != prefabRendersObjCollision?.Length))
                {
                    // Clear old sprites
                    ClearSprites(prefabRendersObjCollision);

                    if (col != null && col.Length > 0)
                    {
                        prefabRendersObjCollision = new SpriteRenderer[col.Length];

                        // Instantiate prefabs
                        for (int i = 0; i < col.Length; i++)
                        {
                            prefabRendersObjCollision[i] = Instantiate(prefabBox, transform).GetComponent <SpriteRenderer>();
                            prefabRendersObjCollision[i].sortingOrder = Layer;

                            prefabRendersObjCollision[i].transform.localPosition = new Vector3(0, 0, col.Length - i);
                            prefabRendersObjCollision[i].transform.localRotation = Quaternion.identity;
                            prefabRendersObjCollision[i].transform.localScale    = Vector3.one * ObjData.Scale;
                        }
                    }
                    else
                    {
                        prefabRendersObjCollision = null;
                    }
                }

                if (prefabRendersObjCollision != null)
                {
                    for (var i = 0; i < prefabRendersObjCollision.Length; i++)
                    {
                        SetCollisionBox(col[i], prefabRendersObjCollision[i], ObjData.Pivot);
                        prefabRendersObjCollision[i].enabled = CurrentShowCollision;
                    }
                }
            }

            if (frameUpdated)
            {
                // Update the follow sprite line (Rayman 1 only)
                if (ObjData is Unity_Object_R1 r1 && anim != null)
                {
                    var animLayer = anim.Frames[r1.AnimationFrame].SpriteLayers.ElementAtOrDefault(r1.EventData.FollowSprite);
                    var imgDescr  = r1.ObjManager.DES.ElementAtOrDefault(r1.DESIndex)?.Data?.ImageDescriptors.ElementAtOrDefault(animLayer?.ImageIndex ?? -1);

                    followSpriteLine.localPosition = new Vector2(
                        ((animLayer?.XPosition ?? 0) + (imgDescr?.HitBoxOffsetX ?? 0)) / (float)LevelEditorData.Level.PixelsPerUnit,
                        -((animLayer?.YPosition ?? 0) + (imgDescr?.HitBoxOffsetY ?? 0)) / (float)LevelEditorData.Level.PixelsPerUnit - (r1.EventData.OffsetHY / (float)LevelEditorData.Level.PixelsPerUnit));

                    var w = (prefabRenderers.ElementAtOrDefault(r1.EventData.FollowSprite)?.sprite == null) ? 0 : imgDescr?.HitBoxWidth ?? 0;
                    followSpriteLine.localScale = new Vector2(w, 1f);
                }
            }

            // Update the collider size for when selecting the events
            if (frameUpdated || collisionUpdated)
            {
                if (anim != null || prefabRendersObjCollision?.Any() == true)
                {
                    var sprites = anim != null ? prefabRenderers : prefabRendersObjCollision;

                    // Set box collider size to be the combination of all parts
                    float leftX = 0, bottomY = 0, rightX = 0, topY = 0;
                    bool  first = true;
                    foreach (SpriteRenderer part in sprites)
                    {
                        if (part.sprite == null)
                        {
                            continue;
                        }

                        Bounds b = part.bounds;
                        b = new Bounds(transform.InverseTransformPoint(b.center) * LevelEditorData.Level.PixelsPerUnit, transform.InverseTransformVector(b.size) * LevelEditorData.Level.PixelsPerUnit);

                        if (b.min.x < leftX || first)
                        {
                            leftX = b.min.x;
                        }
                        if (b.min.y < bottomY || first)
                        {
                            bottomY = b.min.y;
                        }
                        if (b.max.x > rightX || first)
                        {
                            rightX = b.max.x;
                        }
                        if (b.max.y > topY || first)
                        {
                            topY = b.max.y;
                        }

                        if (first)
                        {
                            first = false;
                        }
                    }

                    if (!first)
                    {
                        var w = (rightX - leftX) / LevelEditorData.Level.PixelsPerUnit;
                        var h = (topY - bottomY) / LevelEditorData.Level.PixelsPerUnit;
                        boxCollider.size   = new Vector2(w, h);
                        boxCollider.offset = new Vector2(leftX / LevelEditorData.Level.PixelsPerUnit + w / 2f, (topY / LevelEditorData.Level.PixelsPerUnit - h / 2f));
                    }
                }
                else
                {
                    boxCollider.size   = new Vector2(1, 1);
                    boxCollider.offset = new Vector2();
                }
            }

            // Update offset points
            if (anim != null)
            {
                var pivot = ObjData.Pivot;

                offsetCrossBX.localPosition = new Vector2(pivot.x / LevelEditorData.Level.PixelsPerUnit, (pivot.y / LevelEditorData.Level.PixelsPerUnit));

                if (ObjData is Unity_Object_R1 r1bj)
                {
                    int hy = -(r1bj.EventData.OffsetHY);

                    if (r1bj.EventData.GetFollowEnabled(LevelEditorData.CurrentSettings))
                    {
                        hy -= anim.Frames[r1bj.EventData.RuntimeCurrentAnimFrame].SpriteLayers.ElementAtOrDefault(r1bj.EventData.FollowSprite)?.YPosition ?? 0;
                    }

                    offsetCrossHY.localPosition = new Vector2(pivot.x / LevelEditorData.Level.PixelsPerUnit, hy / (float)LevelEditorData.Level.PixelsPerUnit);
                }
                else if (ObjData is Unity_Object_R2 r2bj)
                {
                    var hy = -(r2bj.EventData.CollisionData?.OffsetHY ?? 0);

                    offsetCrossHY.localPosition = new Vector2(pivot.x / LevelEditorData.Level.PixelsPerUnit, hy / (float)LevelEditorData.Level.PixelsPerUnit);
                }
            }

            // Update visibility
            boxCollider.enabled = EnableBoxCollider;

            // Set new midpoint
            midpoint = new Vector3(transform.position.x + boxCollider.offset.x, transform.position.y + boxCollider.offset.y, 0);

            // Set link line to cube
            lineRend.SetPosition(0, midpoint);
            lineRend.SetPosition(1, linkCube.position);

            // Update link colors
            if (ObjData.EditorLinkGroup == 0)
            {
                lineRend.startColor = Controller.obj.levelEventController.linkColorDeactive;
                lineRend.endColor   = Controller.obj.levelEventController.linkColorDeactive;
                linkCube.GetComponent <SpriteRenderer>().color = Controller.obj.levelEventController.linkColorDeactive;
            }
            else
            {
                lineRend.startColor = Controller.obj.levelEventController.linkColorActive;
                lineRend.endColor   = Controller.obj.levelEventController.linkColorActive;
                linkCube.GetComponent <SpriteRenderer>().color = Controller.obj.levelEventController.linkColorActive;
            }

            // Set link visibility
            var showLinks =
                // Make sure the obj is visible
                isVisible &&
                // Make sure links are set to show
                Settings.ShowLinks &&
                // Only show active links on web
                !(FileSystem.mode == FileSystem.Mode.Web && ObjData.EditorLinkGroup == 0) &&
                // Only show if available
                ObjData.CanBeLinkedToGroup;

            lineRend.enabled = showLinks;
            linkCube.gameObject.SetActive(showLinks);

            // Change the offsets visibility
            offsetOrigin.gameObject.SetActive(ShowOffsets);
            offsetCrossBX.gameObject.SetActive(ShowOffsets && offsetCrossBX.transform.position != Vector3.zero);
            offsetCrossHY.gameObject.SetActive(ShowOffsets && (ObjData is Unity_Object_R1 || ObjData is Unity_Object_R2) && offsetCrossHY.transform.position != Vector3.zero);

            var engineVersion = LevelEditorData.CurrentSettings.EngineVersion;

            followSpriteLine.gameObject.SetActive(
                ShowCollision &&
                ObjData is Unity_Object_R1 r1o &&
                r1o.EventData.GetFollowEnabled(LevelEditorData.CurrentSettings) &&
                !(engineVersion == EngineVersion.R1_PS1_JP || engineVersion == EngineVersion.R1_PS1_JPDemoVol3 || engineVersion == EngineVersion.R1_PS1_JPDemoVol6 || engineVersion == EngineVersion.R1_Saturn));

            // Update one-way link lines
            if (oneWayLinkLines != null)
            {
                foreach (var lr in oneWayLinkLines)
                {
                    lr.enabled = EnableBoxCollider && Settings.ShowLinks && ObjData.CanBeLinked;
                }
            }

            HasInitialized = true;
        }