Skip to main content

Global Component

Although portable components and user components can be used to handle some logic, they are scene-specific, i.e. they cannot run across multiple scenes.

We need a way to run components inside the core that could manipulate core runtime data (like the running scene). Global components are a general model defined as:

type alias GCCommonData userdata scenemsg =
MAbstractScene userdata scenemsg

type alias GCBaseData =
{ dead : Bool
, postProcessor : Renderable -> Renderable
}

type alias GCMsg =
Json.Decode.Value

type alias GCTarget =
String

type alias ConcreteGlobalComponent data userdata scenemsg =
{ init : GlobalComponentInit userdata scenemsg data
, update : GlobalComponentUpdate userdata scenemsg data
, updaterec : GlobalComponentUpdateRec userdata scenemsg data
, view : GlobalComponentView userdata scenemsg data
, id : GCTarget
}

type alias AbstractGlobalComponent userdata scenemsg =
MAbstractGeneralModel (GCCommonData userdata scenemsg) userdata GCTarget GCMsg GCBaseData scenemsg

Differing from user components and portable components, global components have fixed msg and target types, which are Value and String. The common data is the running scene, while the base data is a record of dead and postProcessor. Global components can communicate with each other via the limited Value message. However, you can use whatever data type you want to design the data of the global component and initialize it with any data.

You may load the global components either at the beginning of the game or during runtime via an SOMMsg. To load a global component at the beginning, edit GlobalComponents.elm:

allGlobalComponents : List (GlobalComponentStorage UserData SceneMsg)
allGlobalComponents =
[ FPS.genGC (FPS.InitOption 20) Nothing
]

To load, unload, or communicate with global components at runtime, users could use these two SOMMsgs:

type SceneOutputMsg scenemsg userdata
...
| SOMLoadGC (GlobalComponentStorage userdata scenemsg)
| SOMUnloadGC GCTarget
| SOMCallGC (List ( GCTarget, GCMsg ))

Global components will update before the scene and will render after the scene. They can remove themselves by changing dead to True in base data. Besides, global components may post-process the Renderable result generated by the scene by adding a PostProcessor: Renderable -> Renderable in the postProcessor field of the base data.

There are some global components in the messenger-extra library, including FPS and Transition. Users can read the source code of those two global components to understand how global components work.

FPS

This is a global component to show the Frames Per Second (FPS) value on the screen. It stores the last 10 values of delta every Tick event and calculates the average to determine the FPS. To use FPS, add it in GlobalComponents.elm.

Its generator is:

genGC : InitOption -> Maybe GCTarget -> GlobalComponentStorage userdata scenemsg

InitOption contains some rendering options. Maybe GCTarget is the name of the component. Leave Nothing to use the default fps name.

Transition

First, let's take a look at the concepts of transition.

Transition has "two stages":

  1. From the old scene to the transition scene
  2. From the transition scene to the new scene

It is defined by:

type alias Transition =
{ currentTransition : Int
, outT : Int
, inT : Int
, outTrans : SingleTrans
, inTrans : SingleTrans
}
  • outT is the time (in milliseconds) of the first stage
  • inT is the time of the second stage
  • outTrans: the SingleTrans of the first stage
  • inTrans: the SingleTrans of the second stage

The real transition effect is implemented in SingleTrans:

type alias SingleTrans =
InternalData -> Renderable -> Float -> Renderable

The Renderable argument is the view of the next scene. The Float argument is the current progress of the transition. It is a value from 0 to 1. 0 means the transition starts, and 1 means the transition ends.

To generate a full transition, use genTransition:

genTransition : ( SingleTrans, Duration ) -> ( SingleTrans, Duration ) -> Maybe TransitionOption -> Transition

TransitionOption is some option for transition. It is:

type alias TransitionOption =
{ mix : Bool
}

mix indicates using the mixed transition mode. There are two transition modes. Mixed mode means during transition, two scenes are running at the same time. Sequential mode means the second scene will run after the first scene finishes transition.

To actually use transition, you need to load the global component.

The genGC function requires an InitOption to initialize:

type alias InitOption scenemsg =
{ transition : Transition
, scene : ( String, Maybe scenemsg )
, filterSOM : Bool
}

The Transition can be generated by genTransition, scene is the scene you want to make the transition to, and filterSOM indicates whether some SOMMsgs should be blocked. If users choose to block SOMMsg, then SOMChangeScene and SOMGCLoad will not be filtered out.

Now let's take Fade transition as an example to explain how to use transition when changing the scene.

Black Screen Transition

Consider a common scenario when a scene A ends. The user wants it to first fade out to a black screen and then fade in the next scene B.

Then we emit the following SOMMsg in A's update:

SOMLoadGC (Transition.genGC (Transition.InitOption (genTransition ( fadeOutBlack, Duration.seconds 5 ) ( fadeInBlack, Duration.seconds 3 ) Nothing) ( "B", Nothing ) True) Nothing)

5 is the fade-out time and 3 is the fade-in time.

Direct Transition

Users may want to directly transition to the next scene without the black screen.

This is possible by using nullTransition and fadeInWithRenderable functions.

Since the second scene always exists behind the transition scene during the transition, if the original scene is transparent, the effect will be quite strange. To avoid this, add a white background to the scene (or layer):

view env data =
group []
[ coloredBackground Color.white env.globalData
...
]

Then emit this SOMMsg:

SOMLoadGC (Transition.genGC (Transition.InitOption (genTransition ( nullTransition, Duration.seconds 0 ) ( fadeInWithRenderable <| view env data, Duration.seconds 3 ) Nothing) ( "B", Nothing ) True) Nothing)

Mixed Transition

We can do transition in mixed mode:

SOMLoadGC (Transition.genGC (Transition.InitOption (genTransition ( fadeOutTransparent, Duration.seconds 1 ) ( fadeInTransparent, Duration.seconds 1 ) (Just <| TransitionOption True)) ( "B", Nothing ) True) Nothing)

The result is "A" is fading out while "B" is fading in. Two scenes are running at the same time. Scene "A" could still receive user events and send SOMMsgs.

The examples are here.

Transition Implementing

Let's see how fadeIn and fadeOut are implemented in the core. Users can follow this to design their own transitions.

fadeIn : Color -> SingleTrans ls
fadeIn color gd rd v =
group []
[ rd
, shapes [ fill color, alpha (1 - v) ]
[ rect gd ( 0, 0 ) ( gd.virtualWidth, gd.virtualHeight )
]
]

fadeOut : Color -> SingleTrans ls
fadeOut color gd rd v =
group []
[ rd
, shapes [ fill color, alpha v ]
[ rect gd ( 0, 0 ) ( gd.virtualWidth, gd.virtualHeight )
]
]

The common pattern is to put the next scene as the background and use an "alpha" value to control the transition scene.

tip

See demo.

Asset Loading

This GC is used to show an asset loading scene when there is a resource loading.

Currently no options are required.