You should use this module only if you need more control than what Gamepad.Simple
offers.
Add the port code. See Adding Ports for how to do it.
Decide how you want to persist the UserMappings. This module contains functions to help you encode and decode them.
When your app inits, load the UserMappings. Default to emptyUserMappings if you can't load any.
Decide where and when you want the remapping tool to appear within your app's UI.
Use the Model, Msg, init, update, view in this module to add the remapping tool UI to your app.
Every time update returns a function to update the UserMappings, update them in your app's model and persist them.
In your app's subscriptions
, replace Browser.Events.onAnimationFrame
/Browser.Events.onAnimationFrameDelta
with:
type TheAppMsg
= OnRemappingToolMsg Gamepad.Advanced.Msg
| ...
subscriptions : TheAppModel -> Sub TheAppMsg
subscriptions theAppModel =
if remapingToolIsOpen theAppModel then
GamepadPort.onBlob (Gamepad.Advanced.onBlob >> OnRemappingToolMsg)
else
GamepadPort.onBlob OnAnimationFrame
Gamepad.Private.Blob
The Blob contains the raw gamepad data provided by the browser.
The whole point of this library is to transform the Blob into something that is nice to use with Elm.
animationFrameDelta : Blob -> Basics.Float
This function gives the time passed between the last browser animation frame and the current one, in milliseconds.
It is the same value you get when using Browser.Events.onAnimationFrameDelta.
update msg model =
case msg of
OnGamepad blob ->
let
-- Cap the elapsed time, in case the user hides the page and comes back later.
deltaTimeInMilliseconds = min 200 (Gamepad.animationFrameDelta blob)
...
animationFrameTimestamp : Blob -> Time.Posix
This function gives the Posix timestamp of the current browser animation frame.
It is the same value you get when using Browser.Events.onAnimationFrame.
update msg model =
case msg of
OnGamepad blob ->
let
posixTimestamp = Gamepad.animationFrame blob
...
getGamepads : List ( String, Gamepad.Digital ) -> UserMappings -> Blob -> List Gamepad.Private.Gamepad
This function returns the current states of all recognised gamepads.
Only recognised gamepads will be returned; use unmappedGamepads to see if there is any gamepad that can be configured.
update msg model =
case OnGamepad blob ->
let
isFiring = Gamepad.isPressed Gamepad.A
playerFiringByIndex =
blob
|> Gamepad.getGamepads model.controls model.userMappings
|> List.map (\gamepad -> Gamepad.getIndex isFiring gamepad))
|> Dict.fromList
in
updateState playerFiringByIndex
unmappedGamepads : UserMappings -> Blob -> Basics.Int
This function returns the number of connected gamepads that cannot
be autoconfigured and are not in UserMappings
.
If there are any, ask the user to remap them!
This type contains all the custom mappings generated by the user
emptyUserMappings : UserMappings
UserMappings without any actual user mapping.
Gamepads that the browser recognises as "standard" will still be usable.
userMappingsFromString : String -> Result Json.Decode.Error UserMappings
Creates UserMappings from a JSON string.
userMappings =
flags.gamepadUserMappings
|> Gamepad.userMappingsFromString
|> Result.withDefault Gamepad.emptyUserMappings
userMappingsToString : UserMappings -> String
Transforms UserMappings into a JSON string.
saveUserMappingsToLocalStorageCmd =
userMappings
|> Gamepad.userMappingsToString
|> LocalStoragePort.set 'gamepadUserMappings'
encodeUserMappings : UserMappings -> Json.Encode.Value
Encodes a UserMappings into a JSON Value
.
userMappingsDecoder : Json.Decode.Decoder UserMappings
Decodes a UserMappings from a JSON Value
.
The Elm Architecture Model
, ie, the current state of the remapping tool
The Elm Architecture Msg
type
init : List ( String, Gamepad.Digital ) -> Model
The Elm Architecture init
view : UserMappings -> Model -> Html Msg
The Elm Architecture view
function.
You can use it as it is, or customise it with CSS: every element has its
own class name, all class names are prefixed with elm-gamepad
.
The content will be translated according to navigator.languages
.
update : Msg -> Model -> ( Model, Maybe (UserMappings -> UserMappings) )
The Elm Architecture update
function.
When a remapping is finished, it will return a function to update the user mappings.
You will want to persist the new user mapping, otherwise the user will need to remap every time tha page reloads.
onBlob : Blob -> Msg
This function turns a Blob into a Msg that you can feed to update
.
This is how you pass the Blob information to the remapping tool, so it's very important that you do so every time the remapping tool is open, it won't work otherwise.
subscriptions : Model -> Sub Msg
subscriptions model =
case model.state of
RemappingTool _ ->
GamepadPort.onBlob (Gamepad.Advanced.onBlob >> OnRemappingToolMsg)
_ ->
GamepadPort.onBlob OnAnimationFrame
Note: this function replaces the Elm Architecture subscription
function
because I found that using a Msg constructor forces you to think about how
you want to consume gamepad inputs and animations while the remapping tool
is open.
In general, when the remapping tool is open you want to ignore all gamepad information, but depending in your application you might still want the timing information to keep the animations going.