Forum rules - please read before posting.

Water current/moving walkway/etc effect?

In a 2D game, I've created a river that is supposed to flow from left to right. The player is able to craft a raft which, when used on the river, changes the player sprite to include the raft. It also switches the navmesh so they can only "walk" on the river. If they stand still, they are dragged to the right by the current. If they move left, they move slowly because they are rowing against the current. If they click to move right, they move faster.

I was thinking about parenting the player to an invisible game object that slowly moves to the right, but I'm not entirely sure this would work (and I'm a bit nervous about a solution where an object could theoretically get too far away from origin and cause issues). Any suggestions that would play well with the AC movement system?

Comments

  • Best to manipulate the Player's root position directly.

    If it's a straight line, then you can just use a simple script to move the Player's Transform position by some amount if they're idle.

    If they're pathfinding, you can compare their current position and target position's x values to determine which direction they're moving, and update their movement speed accordingly.

    Here's a script that should do it. To enable / disable the effect, use the Object: Send message Action to pass the Turn On and Turn Off messages to it:

    using UnityEngine;
    using AC;
    
    public class BoatMovement : MonoBehaviour
    {
    
        public bool isOnBoat;
    
        public float dragSpeed = 1f;
        public float maxDragPosition = 5f;
    
        public float leftSpeed = 0.5f;
        public float rightSpeed = 2f;
    
        private float idleTimeBeforeDrag = 1f;
        private float idleTimer;
    
        void OnEnable ()
        {
            EventManager.OnCharacterEndPath += OnCharacterEndPath;
            EventManager.OnEnterGameState += OnEnterGameState;
        }
    
        void OnDisable ()
        {
            EventManager.OnCharacterEndPath -= OnCharacterEndPath;
            EventManager.OnEnterGameState -= OnEnterGameState;
        }
    
        public void TurnOn ()
        {
            isOnBoat = true;
        }
    
        public void TurnOff ()
        {
            isOnBoat = false;
        }
    
        void OnCharacterEndPath (Char character, Paths path)
        {
            if (character == KickStarter.player) idleTimer = 0f;
        }
    
        void OnEnterGameState (GameState gameState)
        {
            if (gameState == GameState.Normal) idleTimer = 0f;
        }
    
        void Update ()
        {
            if (!isOnBoat)
            {
                return;
            }
    
            if (KickStarter.player.charState == CharState.Idle)
            {
                idleTimer += Time.deltaTime;
                if (idleTimer >= idleTimeBeforeDrag && KickStarter.player.Transform.position.x < maxDragPosition)
                {
                    Vector3 positionOffset = Vector3.right * dragSpeed * Time.deltaTime;
                    KickStarter.player.Transform.position += positionOffset;
                }
                return;
            }
    
            idleTimer = 0f;
            if (KickStarter.player.GetTargetPosition ().x > KickStarter.player.Transform.position.x)
            {
                KickStarter.player.walkSpeedScale = rightSpeed;
            }
            else
            {
                KickStarter.player.walkSpeedScale = leftSpeed;
            }
        }
    
    }
    
  • Thank you, Chris! It wasn't originally working as intended (the character would be dragged, stop, be dragged again, etc), but if you make this change, it works perfectly:

    private float idleTimeBeforeDrag = 0f;

  • edited August 30

    Hey! This is working as intended, but it isn't very fun/responsive to move around like this. The main issue is that, since the character is paddling, the fixed walking speed doesn't look great.

    I tried attaching the BoatMovement script to the player sprite, so I could use the rowing (walking) animations to manipulate leftSpeed and rightSpeed. When the sprite hits the water and makes a paddling motion, I increase leftSpeed or rightSpeed, then slowly bring it down over the rest of the animation. It looks really cool!

    The problem is that the player sometimes reaches the destination right after the point where they paddle and gain speed, and they come to a very abrupt stop. Any advice on how to address this issue?

    I'm using a very thin horizontal navmesh, so I'm wondering if the pathfinding distance can be fixed to a specific distance? As in, if the player clicks somewhere 20 units away, the character will only pathfind to 10 units away; and if the player clicks somewhere very close, say 3 units away to their left, then the character will pathfind to that point + 7 units to the left. This way, I can time this to the rowing animation, and each player click will feel more like they are paddling (and they should only be able to click again once the pathfinding is over).

  • edited August 30

    Ahh, I think I almost got it!

    So, I'm still using the BoatMovement script to move the character to the right when idle. But when on the raft, I've decided to use the action Player > Constrain > Movement > Disabled.

    Instead of using regular movement, I've created an ActionList that (1) triggers the paddling animation, and (2) uses the following action: Object > Transform > Move Player > Active Player to translate the player's x position by a given amount (-20 or 30), using a custom curve to emulate the loss of speed after paddling. This works very well!

    Now, I just need to figure out how to trigger those actionlists. I know how to use AC's active inputs, but I'm not entirely sure what the best way is to capture whether a mouse click was done to the left or to the right of the player, so that I can use this direction to run the appropriate translate action.

  • This would best be incorporated into the script, where you can check the mouse position's X co-ordinate before running an ActionList.

    An ActionList can be run through script by invoking its Interact function:

    void Update ()
    {
        if (Input.GetMouseButtonDown (0))
        {
            if (Input.mousePosition.x < Screen.width / 2f)
            {
                leftActionList.Interact ();
            }
            else
            {
                rightActionList.Interact ();
            }
        }
    }
    
  • Thank you! For the benefit of anyone else looking to create a similar script, this is how I'm adapting the code to take the player position into account (if your player isn't perfectly centred):

            if (Input.GetMouseButtonDown(0))
            {
                Vector2 mousePosition = Input.mousePosition;
                Vector3 mouseWorldPosition = KickStarter.CameraMain.ScreenToWorldPoint(mousePosition);
                if (mouseWorldPosition.x < KickStarter.player.transform.position.x)
                {
                    leftActionList.Interact();
                }
                else
                {
                    rightActionList.Interact();
                }
            }
    
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.