FPS ENGINE DOCUMENTATION
  • Welcome to FPS Engine!
  • BEFORE WE START
    • Early Considerations
    • List of Tutorials
    • List of Add-Ons & Compatible Packages
  • GETTING STARTED
    • 01. Basic set-up
    • 02. Roadmap
  • CONTENT
    • 03. Content
      • 03.1 CAMERA
        • MoveCamera
        • CameraFOVManager
      • 03.2 MOVEMENT
        • PlayerMovement
      • 03.3 PLAYER
        • PlayerStats
        • PlayerControl
        • PlayerMultipliers
        • PLAYER STATES
          • PlayerStates
          • PlayerBaseState
          • PlayerStateFactory
        • WEAPON STATES
          • WeaponStates
          • WeaponBaseState
          • WeaponStateFactory
        • Player Debugging
      • 03.4 WEAPONS
        • Weapon_SO
        • WeaponIdentification
        • WeaponController
        • Bullet
        • ATTACHMENTS
          • Attachment.cs
            • Barrel.cs
            • Flashlight.cs
            • Magazine.cs
            • Scope.cs
            • Stock.cs
            • Laser.cs
            • Grip.cs
          • AttachmentIdentifier_SO.cs
      • 03.5 PICK UP SYSTEM
        • InteractManager
        • Interactable
        • Item_SO
        • Identifiable
        • Pickeable
          • WeaponPickeable
          • BulletsPickeable
          • AttachmentPickeable
      • 03.6 ENEMIES
        • IDamageable
        • EnemyHealth
        • TrainingTarget
        • CIrcularTargetEnemy
        • Turret
        • TurretProjectile
      • 03.7 EFFECTS
        • CameraEffects
        • CamShake
        • CrouchTilt
        • JumpMotion
        • ProceduralShot
        • ProceduralShot_SO
        • WeaponEffects
        • WeaponSway
      • 03.8 UI
        • CowsinsButton
        • Crosshair
        • CrosshairShape
        • Hitmarker
        • UIController
        • UIEvents
        • RebindUI
      • 03.8 EXTRA
        • Checkpoint
        • Coin
        • Compass
        • CompassElement
        • Destructible
        • Crate
        • ExplosiveBarrel
        • PowerUp
        • DamageMultiplier
        • HealMultiplierPowerUp
        • Healthpack
        • DoorInteractable
        • Experience
        • GetGameInformation
        • HurtTrigger
        • JumpPad
        • Lootbox
        • PauseMenu
        • PointCapture
        • Trigger
        • DraggableButtonInSceneView
        • UTILITIES
          • DestroyMe
          • IgnoreCollision
          • LookAt
          • CowsinsUtilities
      • 03.09 MANAGERS
        • CoinManager
        • ExperienceManager
        • GameSettingsManager
        • InputManager
        • SoundManager
        • PoolManager
  • HOW TO USE
    • Getting Started: First Steps
    • Importing FPS Engine into URP / HDRP
    • Add a Player Controller
    • Adding New Weapons
    • Saving & Loading Presets
    • Changing Keybindings
    • Creating New Pickeables
    • Controllable & Not Controllable Player
    • Adding New Surfaces ( Grounds )
    • Working with Attachments
    • Modify Weapon and Camera Effects
    • Adding Custom Key Rebinds
    • Add breakable (Destructible) objects
    • Custom Shot Weapons
    • Switch FPS Engine´s Controller
    • Adding Enemies
    • Stairs & Slopes
  • FAQ
    • FAQ
  • SUPPORT
    • Support
Powered by GitBook
On this page
Export as PDF
  1. HOW TO USE

Switch FPS Engine´s Controller

This guide is available for 1.3.8 and above

FPS Engine includes a fully configured, professional controller packed with advanced movement mechanics and a wide range of customizable features.

However, there may be cases where you want to replace FPS Engine's built-in controller with your own, for example, if you want your player to fly or swim.

To keep things simple, let’s start with a basic example: a simple custom controller that lets us move a Rigidbody using the WASD keys.

using System;
using UnityEngine;

public class PlayerTest : MonoBehaviour
{
    private Rigidbody rb;

    private void Awake()
    {
        rb = GetComponent<Rigidbody>();
    }

    private void FixedUpdate()
    {
        float moveX = InputManager.x;
        float moveZ = InputManager.y;

        Vector3 move = new Vector3(moveX, 0f, moveZ) * CurrentSpeed;
        rb.velocity = new Vector3(move.x, rb.velocity.y, move.z);
    }
}

At the end of this section there is a table listing various scripts. Since we're focusing on player movement, we'll need to integrate the player switch logic into our existing PlayerTest script.

Let’s go through the changes step by step:

public class PlayerTest : MonoBehaviour, 
IPlayerMovementActionsProvider, 
IPlayerMovementEventsProvider, 
IPlayerMovementStateProvider
{

Your new Movement script must implement the following three interfaces: IPlayerMovementActionsProvider, IPlayerMovementEventsProvider, and IPlayerMovementStateProvider.

When you compile the script after adding these interfaces, you'll likely encounter several errors. That’s expected, interfaces define a set of required properties and methods that any implementing class must provide. Since our PlayerTest script doesn’t implement these members yet, it will throw errors.

Below is a list of all the required properties specified by the interfaces.

    private PlayerOrientation orientation;
    public PlayerOrientation Orientation => orientation;
    public float CurrentSpeed => WalkSpeed;
    public float RunSpeed => 10;
    public float WalkSpeed => 5;
    public float CrouchSpeed => 3;
    public bool Grounded => false;
    public bool IsCrouching => false;
    public bool IsClimbing => false;
    public bool WallRunning => false;
    public bool Dashing => false;
    public bool CanShootWhileDashing => false;
    public bool DamageProtectionWhileDashing => false;
    public float NormalFOV => 90;
    public float FadeFOVAmount => 1;
    public float WallRunningFOV => 100;
    public bool AlternateSprint => false;
    public bool AlternateCrouch => false;

    // Call these events to keep CameraEffects & WeaponEffects working. (Ex.: OnJump?.Invoke();)
    public event Action OnJump;
    public event Action OnLand;
    public event Action OnCrouch;
    public event Action OnUncrouch;
    public void AddJumpListener(Action listener) => OnJump += listener;
    public void RemoveJumpListener(Action listener) => OnJump -= listener;
    public void AddLandListener(Action listener) => OnLand += listener;
    public void RemoveLandListener(Action listener) => OnLand -= listener;
    public void AddCrouchListener(Action listener) => OnCrouch += listener;
    public void RemoveCrouchListener(Action listener) => OnCrouch -= listener;
    public void AddUncrouchListener(Action listener) => OnUncrouch += listener;
    public void RemoveUncrouchListener(Action listener) => OnUncrouch -= listener;
    public void TeleportPlayer(Vector3 position, Quaternion rotation, bool resetStamina, bool resetDashes) { }

Some of these variables are currently hard-coded, such as CurrentSpeed, RunSpeed, WalkSpeed, and others. However, you're free to connect them to your own custom variables.

For example, if you already have a variable that defines your walking speed, you can simply hook into it like this:

    public float WalkSpeed => myCustomWalkSpeed;

This approach allows you to integrate your existing logic while still satisfying the interface requirements.

Recommendations & Considerations

PLAYER ORIENTATION

PlayerOrientation defines the direction the player is currently facing, so the following piece of code is essential and must be included:

    private PlayerOrientation orientation;
    public PlayerOrientation Orientation => orientation;

Now, we need a way to initialize PlayerOrientation when the game starts.

private void Awake()
{
    orientation = new PlayerOrientation(transform.position + Vector3.up * 2, Quaternion.identity);
}

We also need a way to update PlayerOrientation each frame to reflect the player's current view direction.

private void Update()
{
    // Adapt yaw to your Look or Camera Rotation system
    float yaw = transform.eulerAngles.y;
    orientation.UpdateOrientation(transform.position, yaw);
}

CURRENT SPEED

It’s strongly recommended to link CurrentSpeed to your player’s actual movement speed, if you’re tracking it. For example, whether the player is walking, running, or moving at variable speeds, make sure CurrentSpeed reflects those changes. The same principle applies to WalkSpeed, RunSpeed, and CrouchSpeed, if your setup includes them.

GROUNDED LOGIC

The Grounded property should be tied to your custom ground detection logic. Likewise, ensure that states such as IsCrouching, Wallrunning, and IsClimbing are correctly linked to your own gameplay systems.

FIELD OF VIEW

NormalFOV controls your camera’s field of view only if you’re still using the FPS Engine’s camera system along with the CameraFOVManager.

TELEPORT PLAYER

You can implement your own custom teleportation logic inside the TeleportPlayer() method, giving you full control over how and where the player is moved.


Below, you´ll find the full reference scripts for switching different systems in FPS Engine

You´ll need to remove both PlayerMovement & PlayerStates from the Player.

using System;
using cowsins;
using UnityEngine;

public class PlayerTest : MonoBehaviour, IPlayerMovementActionsProvider, IPlayerMovementEventsProvider, IPlayerMovementStateProvider
{
    private PlayerOrientation orientation;
    public PlayerOrientation Orientation => orientation;
    public float CurrentSpeed => WalkSpeed;
    public float RunSpeed => 10;
    public float WalkSpeed => 5;
    public float CrouchSpeed => 3;
    public bool Grounded => false;
    public bool IsCrouching => false;
    public bool IsClimbing => false;
    public bool WallRunning => false;
    public bool Dashing => false;
    public bool CanShootWhileDashing => false;
    public bool DamageProtectionWhileDashing => false;
    public float NormalFOV => 90;
    public float FadeFOVAmount => 1;
    public float WallRunningFOV => 100;
    public bool AlternateSprint => false;
    public bool AlternateCrouch => false;

    // Call these events to keep CameraEffects & WeaponEffects working. (Ex.: OnJump?.Invoke();)
    public event Action OnJump;
    public event Action OnLand;
    public event Action OnCrouch;
    public event Action OnUncrouch;
    public void AddJumpListener(Action listener) => OnJump += listener;
    public void RemoveJumpListener(Action listener) => OnJump -= listener;
    public void AddLandListener(Action listener) => OnLand += listener;
    public void RemoveLandListener(Action listener) => OnLand -= listener;
    public void AddCrouchListener(Action listener) => OnCrouch += listener;
    public void RemoveCrouchListener(Action listener) => OnCrouch -= listener;
    public void AddUncrouchListener(Action listener) => OnUncrouch += listener;
    public void RemoveUncrouchListener(Action listener) => OnUncrouch -= listener;
    public void TeleportPlayer(Vector3 position, Quaternion rotation, bool resetStamina, bool resetDashes) { }

    private Rigidbody rb;

    private void Awake()
    {
        orientation = new PlayerOrientation(transform.position + Vector3.up * 2, Quaternion.identity);
        rb = GetComponent<Rigidbody>();
    }

    private void Update()
    {
        // Adapt yaw to your Look or Camera Rotation system
        float yaw = transform.eulerAngles.y;
        orientation.UpdateOrientation(transform.position, yaw);
    }

    private void FixedUpdate()
    {
        float moveX = InputManager.x;
        float moveZ = InputManager.y;

        Vector3 move = new Vector3(moveX, 0f, moveZ) * CurrentSpeed;
        rb.velocity = new Vector3(move.x, rb.velocity.y, move.z);
    }
}

You´ll need to remove both WeaponController & WeaponStates from the Player.

using cowsins;
using UnityEngine;

public class WeaponTest : MonoBehaviour, IWeaponReferenceProvider, IWeaponBehaviourProvider, IWeaponRecoilProvider
{
    [SerializeField] private Camera mainCamera;
    private Weapon_SO weapon;
    private WeaponIdentification weaponIdentification;
    public Camera MainCamera => mainCamera;
    private int currentWeaponIndex;
    private WeaponIdentification[] weaponsInventory;

    private bool isShooting, isMeleeAvailable, reloading;
    public Weapon_SO Weapon
    { 
        get { return weapon; } 
        set { weapon = value; }
    }
    public WeaponIdentification Id
    {
        get { return weaponIdentification; }
        set { weaponIdentification = value; }
    }
    public int CurrentWeaponIndex
    {
        get { return currentWeaponIndex; }
        set { currentWeaponIndex = value; }
    }
    public WeaponIdentification[] Inventory
    {
        get { return weaponsInventory; }
        set { weaponsInventory = value; }
    }
    public int InventorySize => 2;

    public bool IsAiming => false;
    public bool Reloading
    {
        get { return reloading; }
        set { reloading = value; }
    }
    public bool CanShoot => true;
    public bool IsShooting
    {
        get { return isShooting; }
        set { isShooting = value; }
    }
    public bool RemoveCrosshairOnAiming => true;
    public bool AlternateAiming => false;
    public bool IsMeleeAvailable
    {
        get { return isMeleeAvailable; }
        set { isMeleeAvailable = value; }
    }

    // Recoil
    public float RecoilPitchOffset => 0;
    public float RecoilYawOffset => 0;

    public void ReleaseCurrentWeapon()
    {
        weapon = null;
        weaponIdentification = null;
    }
}
using cowsins;
using UnityEngine;

public class PlayerStatsTest : MonoBehaviour, IDamageable, IPlayerStatsProvider, IFallHeightProvider
{
    [SerializeField] private float maxHealth, maxShield;

    private float health, shield;
    private bool isDead = false;
    private float? currentFallHeight;
    public float Health => health;
    public float MaxHealth => maxHealth;
    public float Shield => shield;
    public float MaxShield => maxShield;
    public bool IsDead => isDead;
    public float? CurrentFallHeight { get; }

    private void Awake()
    {
        health = maxHealth;
        shield = maxShield;
    }

    public void Heal(float amount)
    {
        health += amount;
    }
    public bool IsFullyHealed()
    {
        return shield >= maxShield && health >= maxHealth;
    }
    public void Damage(float damage, bool isHeadshot)
    {
        health -= damage;
    }
    public void SetFallHeight(float newFallHeight)
    {
        currentFallHeight = newFallHeight;
    }
}

PreviousCustom Shot WeaponsNextAdding Enemies

Last updated 1 day ago