Forum rules - please read before posting.

Simulating circular room in 2D sidescrolling game

Hi

I am designing a 2D sidescrolling point and click game using AC (for the first time...!)
I have a scene that takes place in a circular room.
I am trying to create the following effect:

When the player moves to either side, the room (walls and furniture in from of it) will scroll to signify he is moving along the wall of the room.
If the player keeps walking he will eventually end up in the same place as he started - because the room is supposed to be circular.

How can I create this effect in AC?

I know how to make an endless repeating background, of course, but for this effect I need to move (or whatever) the whole thing - background, meshes, objects etc. and have them in their updated states (a drawer opened earlier should still be opened when the player has gone around the room and comes upon the drawer again).

Is this effect possible within AC framework - or am I looking at a nightmare of custom coding here? :-)

Comments

  • Welcome to the community, @jjs2018.

    This kind of behaviour is always going to be tricky, because it inevitably needs some smart magic to disguise the effect. Essentially you'd want to have either side extend just a little bit, and then teleport the player to the other end seamlessly when they go too far.

    Things get more complex still when you have interactive objects near those borders - otherwise, you'll get the Player clicking/pathfinding to one object, whereas you really want them to be moving to their duplicate on the other side of the scene.

    You'll make things a lot more manageable (and possible) if you keep anything interactive off-screen when the Player reaches the looping part of the background. Is this feasible in your scene's design?

    Just focusing on Player movement for the moment, this script should cause the Player to teleport to the left and right edges of the scene when he reaches the opposite side:

    using UnityEngine;
    using AC;
    
    public class LoopMovement : MonoBehaviour
    {
    
        public float leftEdge = -2f;
        public float rightEdge = 2f;
    
        private void Update ()
        {
            if (!KickStarter.stateHandler.IsInGameplay ())
            {
                return;
            }
    
            float playerX = KickStarter.player.transform.position.x;
            if (playerX < leftEdge)
            {
                OffsetPlayerPosition (rightEdge - playerX);
            }
            else if (playerX > rightEdge)
            {
                OffsetPlayerPosition (leftEdge - playerX);
            }
        }
    
    
        private void OffsetPlayerPosition (float offsetAmount)
        {
            Vector3 newPosition = KickStarter.player.transform.position + (Vector3.right * offsetAmount);
            KickStarter.player.Teleport (newPosition);
    
            if (KickStarter.player.IsPathfinding ())
            {
                KickStarter.player.GetPath ().nodes [KickStarter.player.GetPath ().nodes.Count-1] += Vector3.right * offsetAmount;
                KickStarter.player.RecalculateActivePathfind ();
            }
    
            if (KickStarter.mainCamera.attachedCamera != null)
            {
                KickStarter.mainCamera.attachedCamera.MoveCameraInstant ();
                KickStarter.mainCamera.SnapToAttached ();
            }
        }
    
    }
    

    Put that in your scene and configure its Inspector fields to match the edge position values. It won't account for interactive objects but see if you can get basic movement between the two edges working for now.

  • Hi
    Thanks for the quick reply and great script.
    It works perfectly - I now have completely smooth transition when the player is teleported and the room scrolls seamlessly.

    For other people watching - remember to set Follow Speed on your camera to 0. Otherwise you won't be able to get a seamless transition.

    So far so good :-)
    Did you have an idea about how to handle interactive stuff?
    It is probably not possible to completely remove interactive stuff from the area around the transition lines, unfortunately, but I can of course limit it.

  • Any such object would need to be duplicated and placed at both ends.

    You'd need to ensure that any change made to one Hotspot / interactive object is also made to its copy.

    However, you'd want the Player to only ever interact with the original's Hotspot - with the copy only being used for mouse-overs, and not interactions themselves. A further script would be necessary to cause clicking on the copy to instead interact with the other.

    What's your Settings Manager's Interaction method set to, and how many interactive things are we talking about? Some screens to illustrate would help me understand things a bit better.

  • edited July 2020

    My interaction method is set to "Choose hotspot then interaction".

    My artist has only just started working on content right now, so I only have a few sketches that I use for functionality testing. They are not a realistic representation of the layout or object density at all. But I am happy to share it with you anyway.

    About number of interactive objects - I would say not more than 5 objects would be inside the "problem areas". I can make it so there will never be any moving objects or NPCs in those areas.

  • edited July 2020


    Here's the image.
    Just a very, very raw sketch - don't know if you can use it for anything.

  • Thanks. It'll take a bit of thought to work out how best to approach the interactivity - leave it with me and I'll see if I can come up with something.

  • OK, replace the first script with this:

    using UnityEngine;
    using AC;

    public class LoopMovement : MonoBehaviour
    {

    public float leftEdge = -2f;
    public float rightEdge = 2f;
    
    
    private void Update ()
    {
        float playerX = KickStarter.player.transform.position.x;
        if (playerX < leftEdge)
        {
            OffsetPlayerPosition (rightEdge - playerX);
        }
        else if (playerX > rightEdge)
        {
            OffsetPlayerPosition (leftEdge - playerX);
        }
    }
    
    
    private void OffsetPlayerPosition (float offsetAmount)
    {
        Vector3 newPosition = KickStarter.player.transform.position + (Vector3.right * offsetAmount);
        KickStarter.player.Teleport (newPosition);
    
        if (KickStarter.player.IsPathfinding ())
        {
            KickStarter.player.GetPath ().nodes [KickStarter.player.GetPath ().nodes.Count-1] += Vector3.right * offsetAmount;
            KickStarter.player.RecalculateActivePathfind ();
        }
    
        if (KickStarter.mainCamera.attachedCamera != null)
        {
            KickStarter.mainCamera.attachedCamera.MoveCameraInstant ();
            KickStarter.mainCamera.SnapToAttached ();
        }
    }
    

    }

    Then, make a new script (DummyHotspot) with this:

    using UnityEngine;
    using AC;

    [RequireComponent (typeof (Hotspot))]
    public class DummyHotspot : MonoBehaviour
    {

    public Hotspot realHotspot;
    private Hotspot thisHotspot;
    
    
    private void OnEnable ()
    {
        thisHotspot = GetComponent <Hotspot>();
        EventManager.OnHotspotReach += OnHotspotReach;
        EventManager.OnHotspotTurnOn += OnHotspotTurnOn;
        EventManager.OnHotspotTurnOff += OnHotspotTurnOff;
        //EventManager.OnHotspotSetInteractionState += OnHotspotSetInteractionState;
    }
    
    private void OnDisable ()
    {
        EventManager.OnHotspotReach -= OnHotspotReach;
        EventManager.OnHotspotTurnOn -= OnHotspotTurnOn;
        EventManager.OnHotspotTurnOff -= OnHotspotTurnOff;
        //EventManager.OnHotspotSetInteractionState -= OnHotspotSetInteractionState;
    }
    
    
    private void OnHotspotReach (Hotspot hotspot, AC.Button button)
    {
        if (hotspot == thisHotspot)
        {
            switch (hotspot.GetButtonInteractionType (button))
            {
                case HotspotInteractionType.Use:
                    realHotspot.RunUseInteraction (button.iconID);
                    break;
    
                case HotspotInteractionType.Examine:
                    realHotspot.RunExamineInteraction ();
                    break;
    
                case HotspotInteractionType.Inventory:
                    realHotspot.RunInventoryInteraction (button.invID);
                    break;
    
                default:
                    break;
            }
        }
    }
    
    
    private void OnHotspotTurnOn (Hotspot hotspot)
    {
        if (hotspot == realHotspot)
        {
            thisHotspot.TurnOn ();
        }
    }
    
    
    private void OnHotspotTurnOff (Hotspot hotspot)
    {
        if (hotspot == realHotspot)
        {
            thisHotspot.TurnOff ();
        }
    }
    
    
    private void OnHotspotSetInteractionState (Hotspot hotspot, AC.Button button, bool isOn)
    {
        if (hotspot != realHotspot)
        {
            return;
        }
    
        AC.Button equivalentButton = null;
    
        switch (hotspot.GetButtonInteractionType (button))
        {
            case HotspotInteractionType.Use:
                {
                    int index = hotspot.useButtons.IndexOf (button);
                    equivalentButton = thisHotspot.useButtons[index];
                }
                break;
    
            case HotspotInteractionType.Examine:
                equivalentButton = thisHotspot.lookButton;
                break;
    
            case HotspotInteractionType.Inventory:
                {
                    int index = hotspot.invButtons.IndexOf (button);
                    equivalentButton = thisHotspot.invButtons[index];
                }
                break;
    
            default:
                break;
        }
    
        if (equivalentButton != null)
        {
            thisHotspot.SetButtonState (equivalentButton, isOn);
        }
    }
    

    }

    Then make "dummy" equivalents for any Hotspot that lies near the edge (i.e. that can be seen when on "the other side"). Define the same interaction types (i.e. Use, Inventory) as well as a Marker in the equivalent spot, but don't actually assign Interaction ActionLists (leave those fields blank).

    Then, add this new "DummyHotspot" component to the dummy, and assign it's "proper" equivalent in its Inspector. That should sync up the two, so that clicking on the dummy causes an interaction on the other.

    Notice I've also commented out a couple of lines near the top. When the next AC update is out, uncomment them and the dummy Hotspot's interactions should then sync up with the real one - so that using e.g. the Hotspot: Change interaction Action on the "real" Hotspot causes the "dummy" one to be similarly updated.

  • Thanks for giving this so much thought and effort!

    I get a error in line 107 in the DummyHotspot script:

    error CS1061: 'Hotspot' does not contain a definition for 'SetButtonState' and no accessible extension method 'SetButtonState' accepting a first argument of type 'Hotspot' could be found (are you missing a using directive or an assembly reference?)

    Should I just comment that line out for now also?

  • Hi again

    It looks like everything works now.
    I commented out the line mentioned above and my test-scene is now working.
    I made the ActionList for the real object also include changes for the dummy for the time being.

    Thanks a million for over-the-top support. I am heading straight to the Unity Store to leave a 5-star review.

    A comment for other people watching:

    • Remember to add markers for both the real and the dummy hotspot. The script won't work without them.
  • edited July 2020

    Sorry, yes - you'll need to comment out those lines for the moment. But once the next update it out, you can uncomment them and the interactions will sync up.

    A "Walk-to Marker" will also need to be defined, you're right.

    After the update, I'll post the code on the AC wiki.

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.