Forum Discussion

EchoExclusive's avatar
EchoExclusive
Start Partner
5 months ago

Restricting player to move side to side

Hi all!

I would like to restrict my player to move side to side only, not forward or back.  Is there a way to do that? 

If so, can I limit my player to how far side to side? For example, subway surfer, user can only go side to side on certain amount of lanes. The user isn't moving the player forward at all, it does it himself (or the world is coming to the player)

 

6 Replies

  • There is currently no way to constraint an object/player position in this manner. You will have to do this yourself via on update. However it may not prevent the move animations from playing. I have something similar to this in Dungeon Delvers. If this is what you are looking for I'll drop the code here https://horizon.meta.com/world/23885681981036356 

    • EchoExclusive's avatar
      EchoExclusive
      Start Partner

      Hey gausroth , thanks for the response. 

      It's not exactly what I'm imagining though I am still curious how that's done!

      My teammate and I looked deeper into other possible solutions. Do you think any of these alternative solutions could work? 

      1.Moving the world and keeping player static likely will work, but might be a heavy solution
      2.Somehow making player a child on an object unlikely
      3.Getting rid of a player’s avatar all together and give player control over an object. And utilize swiping somehow from here "Focused Interaction" document  https://developers.meta.com/horizon-worlds/learn/documentation/create-for-web-and-mobile/typescript-apis-for-mobile/focused-interaction similar to swipe gestures the game from "Stress Busters" https://horizon.meta.com/world/10236764669324867/. Maybe swipe the "world" so the lanes change. World as in when I swipe, the lane prefab will move side to side

  • Well, depending on the world I don't recommend number 1 and there will probably be some weird animations for that player. Number 1 isn't possible, at least for 3p creators and number 3 is definitely possible. SeeingBlue​ does something like this in his Cells game.

    • EchoExclusive's avatar
      EchoExclusive
      Start Partner

      Oh that's awesome! I started looking into how to implement swipe and remove the moving/jump default icons. According to the focused interaction document, it needs to be in local scripting mode. So I created a script called SwipeGesture.ts. Attached it to an empty gameobject in the hierarchy. Set SwipeGesture.ts to local. But it keeps thinking it's local. I had a few version, ended off with this one which is created by GenAI. I would get "Script is not running in local mode" on line 22. Any idea what I could be doing wrong? 

       

      import * as hz from 'horizon/core';
      
      class SwipeGesture extends hz.Component<typeof SwipeGesture> {
        static propsDefinition = {};
      
        private initialTouchPos!: hz.Vec3;
      
        start(): void {
          // Get the local player
          const localPlayer = this.world.getLocalPlayer();
          const serverPlayer = this.world.getServerPlayer();
      
          // Check if the entity is owned by the local player
          if (this.entity.owner.get().id !== localPlayer.id) {
            // Transfer ownership to the local player
            console.log("set to localplayer");
            this.entity.owner.set(localPlayer);
          }
      
          // Check if the script is running on the local client
          if (localPlayer.id === this.world.getServerPlayer().id) {
            console.log("Script is not running in local mode");
            return;
          }
      
          // Enable Focused Interaction mode
          localPlayer.enterFocusedInteractionMode();
      
          // Customize the visuals for swipe input
          localPlayer.focusedInteraction.setTrailOptions(true, {
            length: 0.5,
            startWidth: 0.1,
            endWidth: 0.01,
            startColor: hz.Color.white,
            endColor: hz.Color.white,
            startOpacity: 0.4,
            endOpacity: 0,
          });
      
          // Connect to the FocusedInteractionInputStarted event
          this.connectLocalBroadcastEvent(hz.PlayerControls.onFocusedInteractionInputStarted, (data: { interactionInfo: hz.InteractionInfo[] }) => {
            // Store the initial touch position
            this.initialTouchPos = data.interactionInfo[0].screenPosition;
          });
      
          // Connect to the FocusedInteractionInputEnded event
          this.connectLocalBroadcastEvent(hz.PlayerControls.onFocusedInteractionInputEnded, (data: { interactionInfo: hz.InteractionInfo[] }) => {
            // Calculate the swipe direction
            const swipeDirection = data.interactionInfo[0].screenPosition.x - this.initialTouchPos.x;
            console.log(swipeDirection);
          });
      
          // Disable the default on-screen controls
          hz.PlayerControls.disableSystemControls(true);
        }
      }
      
      hz.Component.register(SwipeGesture);

       

      • EchoExclusive's avatar
        EchoExclusive
        Start Partner

        ok so thanks to this video https://www.youtube.com/watch?app=desktop&v=IljIh9vpueQ

        I realized the part I was missing was setting the entity to the owner. Which now makes sense when I tried your game Dungeon Delvers, it always started off in normal mode before the camera videw changes. 

        So I attached my SwipeGesture to a trigger, when the player enters the trigger I do

        this.entity.owner.set(player). And when this is triggered, start() will run again

        So now I have some sort of swiping going and can move an object side to side but it's only moving after the swipe action is done. It doesn't move like subway surfer where the moment the user move their finger left or right the player would move. Any suggestions?

        import { CodeBlockEvents, Component, InteractionInfo, Player, PlayerControls, PropTypes, Vec3 } from 'horizon/core';
        import { Gestures, SwipeDirection } from 'horizon/mobile_gestures';
        
        class SwipeGesture extends Component<typeof SwipeGesture> {
          static propsDefinition = {
            character: { type: PropTypes.Entity },
          };
          private localPlayer: Player | null = null;
          gestures = new Gestures(this);
          start() {
        
        
            this.localPlayer = this.world.getLocalPlayer();
            if (!this.localPlayer) {
              console.error("SwipeGesture: Could not get the local player.");
              return;
            }
        
            // Check if the script is running on the server and exit if it is.
            if (this.world.getLocalPlayer().id === this.world.getServerPlayer().id) {
              // This log is for confirmation and can be removed if desired.
              console.log("SwipeGesture component is correctly ignored on the server.");
              this.connectCodeBlockEvent(this.entity, CodeBlockEvents.OnPlayerEnterTrigger, (player) => {
                this.entity.owner.set(player);
              })
              return;
            }
        
        
            this.localPlayer.enterFocusedInteractionMode({ disableFocusExitButton: true });
        
        
            
            
            this.gestures.onTap.connectLocalEvent(({ touches }) => {
              console.log('tap', touches[0].current.screenPosition);
            });
            this.gestures.onLongTap.connectLocalEvent(({ touches }) => {
              console.log('long tap', touches[0].current.screenPosition);
            });
            this.gestures.onSwipe.connectLocalEvent(({ swipeDirection }) => {
              console.log('swipe', swipeDirection);
              this.MovePlayer(swipeDirection);
            });
            this.gestures.onPinch.connectLocalEvent(({ scale, rotate }) => {
              console.log('pinch', scale, rotate);
            });
          }
        
          private MovePlayer(direction: SwipeDirection): void {
            // Get the current position of the entity
            const currentPosition = this.props.character?.position.get();
        
        
            if (direction === SwipeDirection.Left) {
              const newPosition = currentPosition!.add(new Vec3(-5, 0, 0));
              this.props.character?.position.set(newPosition);
            }
            if (direction === SwipeDirection.Right) {
              const newPosition = currentPosition!.add(new Vec3(5, 0, 0));
              this.props.character?.position.set(newPosition);
            }
          }
        
        
        }
        
        Component.register(SwipeGesture);