Forum rules - please read before posting.

[Puzzle] Implementing a simple clock (mobile: rotating hands via touch) with 12 snaps+combination

edited August 2019 in Technical Q&A

Hi there!

I'm wondering if anyone has ever created a simple puzzle with a closeup of an old clock.
Sounds like a pretty common scenario in AC, but search results on the forum didn't provide the clues I was looking for (pretty a sure few of the answers are already in the manual, which I'm studying step by step)

I'm deciding whether to implement it with AC or falling back to the uberbloated Puzzle Creator

Required features:
1: be able to rotate one hand at a time, dragging around the finger/pointer on the screen
2: on release, the hand should snap in one of the 12 possible snap positions
3: I need to be able to assign each clock a "correct" position, and make a check in some way
4: I have at least 3 different clocks in the same scene (maybe more in other scenes), so I'm more inclined to use prefabs, parameters, and Component Variables

Simplification: clicking on a hand increases its angle by 1 step

I'm struggling a bit with CompVars. Tendentially, I'd use a CompVar integer to track the hand positions (0-11), and would check their value against other CompVar holding the correct solutions

I watched a video tutorial about using movables, but I don't know how to hook the "released" event of the hands.

On the paper, it doesn't sound to be too complicated, but I'd love to hear your experience, possible pitfalls I may be ignoring, and suggestion about how to implement it.

What's your experience with this?
Could you please suggest a possible (reusable as a drag'n'drop prefab) solution?

I'm in a 3D game, Resident-Evil like, so I also am going to implement something like "Look at the clock and enter puzzle mode", and locking a few systems, also showing a "back" button exactly like the one in the AC videotutorials.

I'm wondering if all this could be a custom action with 3 exits: Puzzle_unsolved, Puzzle_solved, Puzzle_already_solved, or if it could be better to use another approach.

I'm not sure if disabling some of the subsystems could hamper the dragging behaviour of the hands.

I already have a prototype of a clock (to improve for usability and aesthetics), with properly positioned pivots for the hands.
My biggest question mark is how to hook a callback from the release of each hand, and how to store the position. (also for saving/restoring it in a savegame)

Comments

  • Dealing with draggable objects and position checks etc gives this a fair bit of complexity. I would recommend getting a local version of the puzzle working in a scene before attempting to prefab anything.

    how to hook a callback from the release of each hand

    The callback you're looking for is OnDropMoveable, which is a custom event. Here's a sample code snippet that you'd insert into a C# MonoBehaviour script and attach to the Drag object to have it run an ActionList at this time:

    public ActionList actionListOnDrop;
    
    private void OnEnable ()
    {
        EventManager.OnDropMoveable += DropMoveable;
    }
    
    private void OnDisable ()
    {
        EventManager.OnDropMoveable -= DropMoveable;
    }
    
    private void DropMoveable (DragBase dragBase)
    {
        if (dragBase == GetComponent <DragBase>())
        {
            actionListOnDrop.Interact ();
        }
    }
    

    (Replace ActionList with ActionListAsset to use an asset file).

    However, I can see there is room for improvement with the current Drag inspector. I'll see about exposing an ActionList field for this, as well as auto-setting parameters and an option to use asset files.

    In your "On Drop" ActionList, you can use the Object: Check track position and Object: Set track position Actions to "snap" the hand to the correct place. For 12 points on a clock, though, this may quickly fill up - so you could instead incorporate a custom Action so snap it to the nearest 1/12th. The above pair of Actions are effectively just calling:

    myDragObject.GetPositionAlong ();
    myDragObject.AutoMoveAlongTrack (positionAlong, speed, removePlayerControl);

    A custom Action could combine these into one.

    how to store the position. (also for saving/restoring it in a savegame)

    Attach the Remember Moveable component to a drag object to save it's state.

  • Thanks, that is some really valuable info!

    However, I can see there is room for improvement with the current Drag inspector

    May I suggest (when dealing with angles) adding a "snap rotation on release", to let the object (not necessarily a clock, could be a door), snap on the nearest snapping angle? (eg: full open, full closed)?

    Sometimes the player just wants to mimic the action dragging in the right direction, but could release it earlier, resulting in the object being left in some awkward position (think of a door, when the drag stops right when the clickable area is just the profile/thickness of the door).

    Instead of using a flag, you could have a list of snap angles (local or world space),
    which could also be empty, or having 1, 2, etc. snap angles (euler, Vector3).

    The moveable would automatically snap to the closest of one of those, if present.

    Stacking on the whishlist, it could be "Immediate", or tweening in a certain amount of time (but, I guess, you are letting users choose their preferred easing engine), or just setting an internal variable to the closest snap position.

    A method (or even an action: moveable-Check Rotation Snap Index) to check which is the closest snap position would be nice.

    Same could be done for position.

    That alone would be of great help (a real boon, actually!) when doing things like puzzles or similar, as it wouldn't require any coding, and I believe it's pretty intuitive to use.

    Also, if you're not using snapping, you can just leave those lists (snap pos, snap rot) empty and alone. To avoid clutter, the inspector could have "use snapping" to hide/show those fields.

    Extra idea: highlight the snapping positions/rotations with a transparent placeholder object with a highlight component.

    Being able to check the snap index either via code or via action would allow for easier checking a movable state in position/rotation critical situations.

    I know it's a lot, I'm just brainstorming :)

    Attach the Remember Moveable component to a drag object to save it's state.

    Yes, yet I'd need to track a number and position the hands accordingly (or both), I don't feel safe in saving a puzzle/game state only with a rotation/position :P

    Just curious: do I also need a Remember Transform if I put a Remember Movable?

  • Food for thought. Please leave those suggestions with me.

    Yes, yet I'd need to track a number and position the hands accordingly (or both), I don't feel safe in saving a puzzle/game state only with a rotation/position :P

    Remember Moveable does not save transform data - it saves the position/rotation along the track. It is, however, possible to script custom Remember scripts.

    Just curious: do I also need a Remember Transform if I put a Remember Movable?

    No, for the reason stated above.

  • edited August 2019

    Remember Moveable does not save transform data - it saves the position/rotation along the track. It is, however, possible to script custom Remember scripts.

    Thanks!

    I already wrote my first Remember script a while ago when trying to implement Puzzle Creator (that's where I added to the wishlist to be able to hook into the "autoadd" Remember Components list, to also add custom Remembers as set by custom actions (a custom action could require more than one Remember component, for instance, auto-adding a Remember Transform and a Remember Hotspot)

    Moveables, it's another chapter I should test/read/something I should experiment with.
    I already watched the video, but this is more leaning to scripting.

    AC is a framework that really packs lots of features, but mastering all of them and to have a complete overview of what's going on takes some time. I should have started a long way ago, I'm still scratching the surface.

    And about Moveables, I'm going to create my very first one right in the above scenario.

    Thanks for your patience and understanding 😅

  • a custom action could require more than one Remember component, for instance, auto-adding a Remember Transform and a Remember Hotspot

    A custom Action can instruct the addition of Remember components when running the "Auto-add" operation by overriding the AssignConstantIDs function - see the ActionRename.cs script as an example.

  • edited August 2019

    A custom Action can instruct the addition of Remember components when running the "Auto-add" operation by overriding the AssignConstantIDs function - see the ActionRename.cs script as an example.

    Sounds cool!

    I was wondering if future versions could split up the AssignConstantIDs:
    `
    override public void AssignConstantIDs (bool saveScriptsToo, bool fromAssetFile)
    {
    if (saveScriptsToo)
    {
    AddSaveScript (hotspot);
    }

            AssignConstantID <Hotspot> (hotspot, constantID, parameterID);
        }`
    

    to something like:
    `
    override public void AssignConstantIDs (bool saveScriptsToo, bool fromAssetFile)
    {
    AddRememberScripts(saveScriptsToo, etc.);

            AssignConstantID <Hotspot> (hotspot, constantID, parameterID);
        }
    
        protected override void AddRememberScripts(bool saveScriptsToo, etc.)
        {
            // Add logic for remembering components here
        }`
    

    To just override what's needed, in a specific and dedicated virtual method

  • Here's a video link showing snapping, rotating, parallel scripts triggering inside the same action, to keep the construction of each puzzle as simple as possible :)

    Quite happy since everything is a prefab now (lost some time to figure out I had to check "allow multiple instance to run at the same time"... event if they were not running at the same time. For some reason I also had to force a Nonserialised to a few protected fields, as they were keeping their value between Unity sessions! Maybe it's an Odin Inspector serialization issue? I will try switching it off.)

    https://drive.google.com/file/d/1oHqszwRSc3xWBGh0G99Qnl2sBGVzw6YR/view?usp=sharing

    Also, the "puzzle" action got a lot simpler :)

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.