The experiment config is by far the most elaborate of the configuration files. It sets a number of "world" parameters that will be constant across any given experiment. Many high-level parameters are implemented here, though most are optional.
Broad areas of control included in this config file include:
- Scene, field of view, and rendering parameters
- Timing for experimental states
- Universal player parameters like the move rate, HUD control, and walk simulation
- Information on the weapon to use
- Session setup and target movement
For a full description of fields see the descriptions below. Along with each subsection an example configuration is provided. In many cases the example values below are the defaults provided in these field values. Where this is not the case the default will be indicated in the comments.
The experimentconfig.Any
file is located in the data-files
directory at the root of the project. If no experimentconfig.Any
file is present at startup, a default experiment configuration is written to experimentconfig.Any
.
FPSci uses a multi-tiered experiment and configuration heirarcy to manage presentation of stimulus to users. Configuration is inherited by each new lower level of the heirarchy from its parents, with only some levels of the heirarcy supporting specification of general configuration.
This heirarchy is intended to let developers re-use global configuration while tapering configuration towards specific conditions they'd like to change on a per session or trial level.
The heirarchy of FPSci specification is as follows:
- Experiment: Provides base-level general config at the highest-level (plus a few specific fields mentioned below)
- Session: Supports different general config (plus a few specific fields mentioned below)
- Block: A repeat of all tasks/trials below this level, does not support configuration or questions
- Task: A grouping of trials with a count and specific affiliated questions, does not support configuration
- Trial: Lowest-level (general) configuration, affiliates a (set of) target(s) with a task to run
- Task: A grouping of trials with a count and specific affiliated questions, does not support configuration
- Block: A repeat of all tasks/trials below this level, does not support configuration or questions
- Session: Supports different general config (plus a few specific fields mentioned below)
Note: Tasks and blocks do not need to be specified in FPSci. If blocks are not specified, by default each session consists of only 1 block (no repeats of the tasks/trials below the session). If tasks are not specified the default behavior is that each trial becomes its own task, with a task count as specified in the trial. For more information on tasks see the description below.
The experment config supports inclusion of any of the configuration parameters documented in the general configuration parameter guide. In addition to these common parameters, there are a number of unique inputs to experiment config. The following is a description of what each one means, and how it is meant to be used.
description
allows the user to annotate this experiment's results with a custom stringcloseOnComplete
closes the application once all sessions from the sessions array (defined below) are complete
"description": "your description here", // Description of this file (default = "default")
"closeOnComplete": false, // Don't close automatically when all sessions are complete
Each session can specify any of the general configuration parameters used in the experiment config above to create experimental conditions. If both the experiment level and the session level specify a field supported by the general configuration, the session value has priority and will be used for that session. The experiment level configuration will be used for any session that doesn't specify that parameter.
In addition to these general parameters each session also has a few unique parameters documented below.
sessions
is a list of all sessions and their affiliated information:session id
is a short name for the sessiondescription
is used to indicate an additional mode for affiliated sessions (such asreal
vstraining
)closeOnComplete
signals to close the application whenever this session (in particular) is completedrandomizeTaskOrder
determines whether tasks are presented in the order they are listed or in a randomized order (if specified).randomizeTrialOrder
is treated asrandomizeTaskOrder
to preserve its historical behavior.weightByCount
determines whether tasks are randomized proportional to their remaining count (i.e. a task with 10 repeats remaining is 10x as likely to be shown next as a task with 1 repeat remaining)blockCount
is an integer number of (repeated) groups of trials within a session, with the block number printed to the screen between "blocks" (or a single "default" block if not provided)trials
is a list of trials referencing thetrials
table above:id
is an (optional) trial ID that (if specified) is used for logging purposes. If unspecified theid
defaults to the (integer) index of the trial in thetrials
array.targetIds
is a list of target names (id
fields from withintargets
array elements) to spawn in this trial, if multiple target ids are provided multiple targets are spawned simultaneously in each trialcount
provides the number of trials in this session (should always be an integer strictly greater than 0)
An example session configuration snippet is included below:
"sessions" : [
{
"id" : "test-session", // This is a short name for our session
"description" : "test", // This is an arbitrary string tag (for now)
"closeOnComplete": false, // Don't automatically close the application when the session completes
"randomizeTaskOrder": true, // Randomize order of tasks by default
"weightByCount": true, // Randomize based on the count of this task remaining
"frameRate" : 120, // Example of a generic parameter modified for this session
"blockCount" : 1, // Single block design
"trials" : [
{
// Single target example
"targetIds" : ["simple_target"],
"count": 20,
},
{
// Multi-target example
"targetIds" : ["simple_target", "world-space paramtetric", "example_target", "example_target"],
"count" : 5,
}
],
},
{
"id" : "minimal-session", // This session inherits all settings from the experiment above it
"trials" : [
{
"id": "example trial",
"targetIds" : ["simple_target", "destination-based"],
"count" : 10,
}
],
},
],
FPSci tasks are a way to affiliate trials into meaningful grouping with repeat logic automatically managed.
Tasks are configured using the following parameters:
id
an identifier for the task, used for loggingtrialOrders
an array of valid orderings of trials (referenced byid
) with each order consisting of:order
the set of trials (in order) to be presented in this ordercorrectAnswer
the correct answer to respond to (the final) multiple choice question in the task (see below)
questions
a question(s) asked after eachtrialOrder
is completedcount
a number of times to repeat each of thetrialOrders
specified in this task
Note that rather than being a direct Array<Array<String>>
the trialOrders
array needs to be an Array<Object>
in order to parse correctly in the Any format. To do this the trialOrders
array requires specification of an order
array within it to accomplish this nesting, as demonstrated in the example below.
For example a task may specify presenting 2 trials (in either order) then asking a question comparing them. An example of this configuration is provided below:
tasks = [
{
id = "comparison";
trialOrders = [
{order = ["trial 1", "trial 2"], correctAnswer = "first"}, // Show trial 1/2 in this order
{order = ["trial 2", "trial 1"], correctAnswer = "second"} // Show trial 1/2 in reverse order
];
// Present a question about which trial was preferred after any pairing of 2 trials
questions = [
{
prompt = "Which trial did you prefer?";
type = "MultipleChoice";
options = ["First", "Second"];
}
];
// Repeat each order 10 times (20 total sets of 2 trials)
count = 10;
},
]
FPSci supports minimal "correctness" checking of questions that are asked affiliated with each order
specified in the task configuration. In order to use this feature you must have at least one question with type
of "MultipleChoice"
specified in your task-level questions array. In this case you may choose to specify a correctAnswer
field with each element in the trialOrders
array. This answer must correspond to an option in the final multiple choice question of the task (if not an exception will be thrown).
When specified correctly FPSci will check if the correctAnswer
matches the response provided by a user during the task-level questions and if so display the taskSuccessFeedback
message. If the response does not match correctAnswer
the taskFailureFeedback
message is provided instead. It is up to the experiment designer to make sure the correctAnswer
field exactly matches the desired option from the final multiple choice question in a task.
By default if no correctAnswer
is specified or no questions
are asked in a task FPSci defaults to the declaring all tasks a success.
When no tasks
are specified in a session configuration, FPSci treats trials as tasks creating one task per trial type in the trial configuration.
As described above the randomizeTaskOrder
session-level configuration parameter allows the experiment designer to select tasks in either the order they are specified or a random order. To support legacy configuration randomizeTrialOrder
is treated as equivalent to randomizeTaskOrder
in FPSci, but is overwritten when randomizeTaskOrder
is defined in the config. Trial order randomization within order
s in tasks is current not supported in FPSci (all orders need to be specified explicitly).
Trials provide the lowest level of general configuration in FPSci. Trials are specified with the following parameters:
id
is a name to refer to this trial by in logging (and in tasks)targetIds
is an array of target ids to present within this trialcount
is a count that is applied to this trial (when it is treated as a task)
As mentioned above, if no tasks are specified, each trial's count
is used to generate a task with a single trial order with just this trial inside of it. This provides a fallback to pre-task FPSci configuration in which trials were run this way directly below blocks.
The targets
array specifies a list of targets each of which can contain any/all of the following parameters. The following sections provide a more detailed breakdown of target parameters by group.
The following configuration is universal to all target types.
id
a short string to refer to this target informationrespawnCount
is an integer providing the number of respawns to occur. For non-respawning items use0
or leave unspecified. A value of-1
creates a target that respawns infinitely (trial ends when ammo or task time runs out).visualSize
is a vector indicating the minimum ([0]) and maximum ([1]) visual size for the target (in deg)colors
is an array of 2 colors (max and min health) which are interpolated between based on target damage (note this setting overrides the experiment or session-leveltargetHealthColors
setting). If unspecified the experiment/session level settings are used.gloss
is aColor4
representing glossyness, the first 3 channels are RGB w/ alpha representing minimum reflection (F0). Set all channels to 0 or do not specify to disable glossy reflections (note this setting overrides the experiment or session-leveltargetGloss
setting). If unspecified the experiment/session level settings are used.emissive
is an array of 2 colors (max and min health) which are interpolated between based on the target damage (note this setting overrides the experiment or session-leveltargetEmissive
setting). If unspecified the experiment/session level settings are used.destSpace
the space for which the target is rendered (useful for non-destiantion based targets, "player" or "world")hitSound
is a filename for the sound to play when the target is hit but not destroyed (for no sound use an empty string).hitSoundVol
provides the volume (as a float) for the hit sound to be played at (default is1.0
).destroyedSound
is a filename for the sound to play when the target is both hit and destroyed (for no sound use an empty string).destroyedSoundVol
provides the volume (as a float) for the destroyed sound to be played at (default is1.0
).destroyDecal
the decal to show when destroyeddestroyDecalScale
a scale to apply the the destroy decal (may be decal dependent)destroyDecalDuration
is the duration tomodelSpec
is anAny
that constructs anArticulatedModel
similar to that used in the the weapon config readme. For now this spec needs to point to anobj
file with a model namedcore/icosahedron_default
.
The following configuration only applies to player-bound parametric targets.
speed
is a vector indictating the minimum ([0]) and maximum ([1]) speeds in angular velocity (in deg/s)distance
is the distance to this target (in meters)symmetricEccH/V
WhenTrue
the eccH/V are assumed symmetric about their respective axes and should always be >0. WhenFalse
the eccentricities can be signed, with positive eccentricities indicate values to the left (azim) and up (elev) of the central view direction.eccH/V
are controls for min ([0])/max([1]) horizontal/vertical eccentricity for target initial position (in deg)motionChangePeriod
is a vector indicating the minimum ([0]) and maximum ([1]) motion change period allowed (in s)upperHemisphereOnly
is a boolean flag indicating whether target flies only on the upper hemisphere of player-centric sphere. Only applicable toFlyingEntity
defined in the "player" space.logTargetTrajectory
is a boolean flag indicating whether or not this (individual) target's position should be logged for trials it is displayed forjumpEnabled
determines whether the target can "jump" or notjumpPeriod
is a vector indicating the minimum ([0]) and maximum ([1]) period to wait between jumps (in seconds)jumpSpeed
is a vector indicating the minimum ([0]) and maximum([1]) angular speed with which to jump (in deg/s)accelGravity
is the min ([0])/max ([1]) acceleration due to gravity during the jump (in m/s^2)
The following configuration parameters are specific to world-space targets:
destinations
is an array ofDestination
types each of which contains:t
the time (in seconds) for this point in the pathxyz
the position for this point in the path
spawnBounds
specifies an axis-aligned bounding box (G3D::AABox
) to specify the bounds for the target's spawn location in cases wheredestSpace="world"
and the target is not destination-based. For more information see the section below on serializing bounding boxes.moveBounds
specifies an axis-aligned bounding box (G3D::AABox
) to specify the bounds for target motion in cases wheredestSpace="world"
and the target is not destination-based. For more information see the section below on serializing bounding boxes.axisLocked
is a boolean array specifying which (if any) axes of motion are "locked" (i.e. disallowed) for this target's motion in [X,Y,Z] order. This only applies for world-space, parametric targets.
An example target configuration snippet is provided below:
targets = [
{
"id": "simple_target",
"destSpace" : "player", // This is a player-centered-spherical-space target
"visualSize" : [0.5, 0.5], // 0.5m size
"colors": [Color3(0,1,0), Color3(1,0,0)]; // Green at max health, red at min health
"respawnCount" : 0, // Don't respawn
"speed": [1.0, 3.0], // 1-3m/s speed
"symmetricEccH" : true; // Target will only spawn randomly on any quadrant within specified eccH (between (-)15 to (-)5 or 5 to 15)
"symmetricEccV" : true; // Target will only spawn randomly on any quadrant within specified eccH (between (-)5 to 0 or 0 to 5)
"eccH" : [5.0, 15.0], // 5-15° initial spawn location (horizontal)
"eccV" : [0.0, 5.0], // 0-5° intitial spawn location (vertical)
"hitSound" : "sound/fpsci_ding_100ms.wav", // Sound to play when target hit
"hitSoundVol" : 1.0, // Volume to play the hit sound at
"destroyedSound" : "sound/fpsci_destroy_150ms.wav", // Sound to play when target destroyed (explosion)
"destroyedSoundVol" : 1.0f, // Volume to play destroyed sound at
"destroyDecal" : "explosion_01.png", // Use default explosion decal
"destroyDecalScale" : 1.0, // Use default sizing
"destroyDecalDuration" : 0.1, // Show the decal for 0.1s (default)
},
{
"id": "world-space paramtetric",
"destSpace" : "world", // This is a world-space target
"bounds" : AABox {
Point3(-8.5, 0.5, -11.5), // It is important these are specified in "increasing order"
Point3(-6.5, 1.5, -7.5) // All x,y,z coordinates must be greater than those above
},
"axisLocked": [true, false, false],
"visualSize" : [0.3, 1.0], // Visual size between 0.3-1m
"respawnCount" : -1, // Respawn forever
},
{
"id" : "destination-based",
"destSpace" : "world", // Important this is specified here
"destinations" : {
{"t": 0.0, "xyz": Vector3(0.00, 0.00, 0.00)},
{"t": 0.1, "xyz": Vector3(0.00, 1.00, 0.00)},
...
{"t": 10.2, "xyz": Vector3(10.1, 1.01, -100.3)}
},
modelSpec = ArticulatedModel::Specification{
filename = "model/target/sphere.obj";
},
},
#include("example_target.Any"), // Example of including an external .Any file
],
Target models can get quite large in file size when they're highly detailed. As a result, we only include a handful of different model files with the FPSci repo and binary distributions. We have made a high quality sphere model available that you can download from google drive here. Once you get that file (named high_poly_sphere.obj
), you can place it in the data-files/model/target/
directory or the FPSci/model/target/
directory if using a binary distribution. We recommend scripting the download of this type of file if you are building an automated experiment build on FPSci. These files can then be used by targets as follows:
targets = (
{
id = "ico";
destSpace = "player";
speed = ( 0, 0 );
visualSize = ( 0.05, 0.05 );
modelSpec = ArticulatedModel::Specification{
filename = "model/target/target.obj";
};
},
{
id = "low";
destSpace = "player";
speed = ( 0, 0 );
visualSize = ( 0.05, 0.05 );
modelSpec = ArticulatedModel::Specification{
filename = "model/target/low_poly_sphere.obj";
};
},
{
id = "mid";
destSpace = "player";
speed = ( 0, 0 );
visualSize = ( 0.05, 0.05 );
modelSpec = ArticulatedModel::Specification{
filename = "model/target/mid_poly_sphere.obj";
};
},
{
// This one only works if you download the file from https://drive.google.com/file/d/1LvJaJUD3k7DR0taZYZ_9Y0PNVQDMdShM/view?usp=sharing
id = "high";
destSpace = "player";
speed = ( 0, 0 );
visualSize = ( 0.05, 0.05 );
modelSpec = ArticulatedModel::Specification{
filename = "model/target/high_poly_sphere.obj";
};
},
);
The above examples are borrowed from the Spheres experiment sample that is provided with FPSci. Feel free to try that out if you want to compare the target shapes in game.
The destinations
array within the target object overrides much of the default motion behavior in the target motion controls. Once a destinations array (including more than 2 destiantions) is specified all other motion parameters are considered unused. Once a destinations
array is specified only the following fields from the target configuration apply:
id
visualSize
respawnCount
modelSpec
When specifying a destinations
array there are several key assumptions worth noting:
- All interpolation between points is linear w/ time. This means that velocity can be controlled using either timing or point location, points do not need to be uniformly sampled (i.e. any two destinations may have arbitrary time between them)
- The default behavior is to "loop" paths once they are complete to avoid requiring paths to match trial times, this will include a discontinuity (jump) in the target motion if the path is not a closed loop. If you want to avoid this behavior we suggest creating closed loop paths and including a duplicate beginning/end sample to guarantee smooth motion
- Time values can be specified at any precision, but the
oneFrame()
loop rate (ideally the frame rate) sets the "resampling" rate for this path, destinations whose time values are spaced by less than a frame time are not recommended
Currently the destination time values are specified as an increasing time base (i.e. 0.0 on the first destination up to the total time); however, in the future we could move towards/also include time deltas to allow for faster editing of files.
The G3D::AABox
is a 3D, axis-aligned bounding box useful for specifying regions of the scene in which a player/target can move.
Like many G3D native objects the G3D::AABox
supports direct (de)serialization from .Any
using a simple definition. Any table value in the .Any
which starts with an AABox {}
specification will deserialize to a G3D::AABox
object.
Within the AABox{}
definition, only 2 points need be specified (a lower and upper corner, in that order) to produce a G3D:AABox
.
The G3D::AABox
implementation seems to work bets when then AABox
any specification lists the "lower" corner before the "upper". That is that all 3 coordinates of the first provided corner are less than or equal to the 3 coordinates of the second provided corner.
Thus we always recommend specifying the TargetConfig
's bounds
field as follows:
...
"bounds" : AABox {
Point3(x1, y1, z1),
Point3(x2, y2, z2)
}
...
Where x1 < x2
, y1 < y2
, and z1 < z2
. Negative coordinates are not treated any differently here (do not use the magnitude of the points, just their values).