TestCourse
"TestCourse" is the name given to a test stage used in Sonic Unleashed and Sonic Generations, the only version we have of it comes from the SONIC X SHADOW GENERATIONS version of Sonic Generations, where it was bundled alongside the 1.0 release of the Nintendo Switch version. This stage has no geometry, but it has a lot of XML files and collision geometry.
In-game
The stage contains no geometry at all as it wasn't bundled with the stage, but there is a collision file, which means we can force the game to display the collision geometry instead and get an idea of what the level looks like. [1]
(The music in the 2nd video is not from the original stage)
File Structure
cmn_obj_jumppanel15S_HD.phy.hkx
cmn_obj_jumppanel30L_HD.phy.hkx
cmn_obj_jumppanel30M_HD.phy.hkx
cmn_obj_jumppanel30S_HD.phy.hkx
cmn_obj_jumppanel_2.phy.hkx
cmn_obj_jumppanel_4.phy.hkx
cmn_obj_pointmarkerLR_HD_col.phy.hkx
cmn_obj_pulleystand_HD.phy.hkx
Direct01.light
ForwardSet.fst.xml
ForwardSet02.fst.xml
ghz_col_tn1_woodswing3M_000.phy.hkx
ghz_col_tn1_woodswing4M_000.phy.hkx
ghz_col_tn1_woodswing5M_000.phy.hkx
ghz_col_tn1_woodswing7M_000.phy.hkx
Path.path.xml
SceneEffect.prm.xml
Set.set.xml
Stage.stg.xml
StageObject.sto.xml
Terrain.stg.xml
TestCourse.phy.hkx
FST Files
This stage contains 2 files with an extension of .fst.xml
. This extension is never used in any other stage or game using Hedgehog Engine, and the layout of the file resembles the Set Layer layout, which could possibly mean that this was either an earlier implementation of the Set object system, or it was an earlier way of creating MultiSet objects.
ForwardSet.fst.xml
<?xml version="1.0"?>
<XmlData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Define>
<SetName>ForwardSet.fst.xml</SetName>
<Interval>5</Interval>
<Line>
<Category>LineA0</Category>
<Index>2</Index>
<Height>0</Height>
</Line>
<Line>
<Category>LineA1</Category>
<Index>1</Index>
<Height>0</Height>
</Line>
<Line>
<Category>LineA2</Category>
<Index>0</Index>
<Height>0</Height>
</Line>
<Line>
<Category>LineB0</Category>
<Index>2</Index>
<Height>5</Height>
</Line>
<Line>
<Category>LineB1</Category>
<Index>1</Index>
<Height>5</Height>
</Line>
<Line>
<Category>LineB2</Category>
<Index>0</Index>
<Height>5</Height>
</Line>
</Define>
<SetData>
<SetArray>
<Index>0</Index>
<SetObj>
<ObjID>Ring</ObjID>
<Line>LineA0</Line>
<Param>
<ParamID>Count</ParamID>
<Value>1</Value>
</Param>
<Param>
<ParamID>Interval</ParamID>
<Value>0</Value>
</Param>
</SetObj>
<SetObj>
<ObjID>Ring</ObjID>
<Line>LineA1</Line>
<Param>
<ParamID>Count</ParamID>
<Value>1</Value>
</Param>
<Param>
<ParamID>Interval</ParamID>
<Value>0</Value>
</Param>
</SetObj>
<SetObj>
<ObjID>Ring</ObjID>
<Line>LineA2</Line>
<Param>
<ParamID>Count</ParamID>
<Value>1</Value>
</Param>
<Param>
<ParamID>Interval</ParamID>
<Value>0</Value>
</Param>
</SetObj>
</SetArray>
<SetArray>
<Index>7</Index>
<SetObj>
<ObjID>Ring</ObjID>
<Line>LineA0</Line>
<Param>
<ParamID>Count</ParamID>
<Value>6</Value>
</Param>
<Param>
<ParamID>Interval</ParamID>
<Value>1.5</Value>
</Param>
</SetObj>
<SetObj>
<ObjID>WoodBox</ObjID>
<Line>LineA1</Line>
</SetObj>
</SetArray>
<SetArray>
<Index>12</Index>
<SetObj>
<ObjID>WoodBox</ObjID>
<Line>LineA0</Line>
</SetObj>
<SetObj>
<ObjID>WoodBox</ObjID>
<Line>LineA2</Line>
</SetObj>
</SetArray>
<SetArray>
<Index>17</Index>
<SetObj>
<ObjID>WoodBox</ObjID>
<Line>LineA1</Line>
</SetObj>
<SetObj>
<ObjID>WoodBox</ObjID>
<Line>LineA2</Line>
</SetObj>
</SetArray>
<SetArray>
<Index>23</Index>
<SetObj>
<ObjID>WoodBox</ObjID>
<Line>LineA0</Line>
</SetObj>
</SetArray>
<SetArray>
<Index>24</Index>
<SetObj>
<ObjID>WoodBox</ObjID>
<Line>LineA1</Line>
</SetObj>
</SetArray>
<SetArray>
<Index>27</Index>
<SetObj>
<ObjID>WoodBox</ObjID>
<Line>LineA2</Line>
</SetObj>
</SetArray>
<SetArray>
<Index>52</Index>
<SetObj>
<ObjID>Wall</ObjID>
<Line>LineA1</Line>
<Param>
<ParamID>Offset</ParamID>
<Value>0.7</Value>
</Param>
</SetObj>
</SetArray>
</SetData>
</XmlData>
ForwardSet02.fst.xml
<?xml version="1.0"?>
<XmlData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Define>
<SetName>ForwardSet.fst.xml</SetName>
<Interval>5</Interval>
<Line>
<Category>LineA0</Category>
<Index>2</Index>
<Height>0</Height>
</Line>
<Line>
<Category>LineA1</Category>
<Index>1</Index>
<Height>0</Height>
</Line>
<Line>
<Category>LineA2</Category>
<Index>0</Index>
<Height>0</Height>
</Line>
<Line>
<Category>LineB0</Category>
<Index>2</Index>
<Height>5</Height>
</Line>
<Line>
<Category>LineB1</Category>
<Index>1</Index>
<Height>5</Height>
</Line>
<Line>
<Category>LineB2</Category>
<Index>0</Index>
<Height>5</Height>
</Line>
</Define>
<SetData>
<SetArray>
<Index>0</Index>
<SetObj>
<ObjID>Ring</ObjID>
<Line>LineA0</Line>
<Param>
<ParamID>Count</ParamID>
<Value>1</Value>
</Param>
<Param>
<ParamID>Interval</ParamID>
<Value>0</Value>
</Param>
</SetObj>
<SetObj>
<ObjID>Ring</ObjID>
<Line>LineA1</Line>
<Param>
<ParamID>Count</ParamID>
<Value>1</Value>
</Param>
<Param>
<ParamID>Interval</ParamID>
<Value>0</Value>
</Param>
</SetObj>
<SetObj>
<ObjID>Ring</ObjID>
<Line>LineA2</Line>
<Param>
<ParamID>Count</ParamID>
<Value>1</Value>
</Param>
<Param>
<ParamID>Interval</ParamID>
<Value>0</Value>
</Param>
</SetObj>
</SetArray>
<SetArray>
<Index>7</Index>
<SetObj>
<ObjID>Ring</ObjID>
<Line>LineA0</Line>
<Param>
<ParamID>Count</ParamID>
<Value>6</Value>
</Param>
<Param>
<ParamID>Interval</ParamID>
<Value>1.5</Value>
</Param>
</SetObj>
<SetObj>
<ObjID>WoodBox</ObjID>
<Line>LineA1</Line>
</SetObj>
</SetArray>
<SetArray>
<Index>12</Index>
<SetObj>
<ObjID>WoodBox</ObjID>
<Line>LineA0</Line>
</SetObj>
<SetObj>
<ObjID>WoodBox</ObjID>
<Line>LineA2</Line>
</SetObj>
</SetArray>
<SetArray>
<Index>17</Index>
<SetObj>
<ObjID>WoodBox</ObjID>
<Line>LineA1</Line>
</SetObj>
<SetObj>
<ObjID>WoodBox</ObjID>
<Line>LineA2</Line>
</SetObj>
</SetArray>
<SetArray>
<Index>23</Index>
<SetObj>
<ObjID>WoodBox</ObjID>
<Line>LineA0</Line>
</SetObj>
</SetArray>
<SetArray>
<Index>24</Index>
<SetObj>
<ObjID>WoodBox</ObjID>
<Line>LineA1</Line>
</SetObj>
</SetArray>
<SetArray>
<Index>27</Index>
<SetObj>
<ObjID>WoodBox</ObjID>
<Line>LineA2</Line>
</SetObj>
</SetArray>
<SetArray>
<Index>33</Index>
<SetObj>
<ObjID>Ledge</ObjID>
<Line>LineA1</Line>
<Param>
<ParamID>Offset</ParamID>
<Value>-9.8</Value>
</Param>
</SetObj>
</SetArray>
<SetArray>
<Index>52</Index>
<SetObj>
<ObjID>Wall</ObjID>
<Line>LineA1</Line>
<Param>
<ParamID>Offset</ParamID>
<Value>0.7</Value>
</Param>
</SetObj>
</SetArray>
<SetArray>
<Index>72</Index>
<SetObj>
<ObjID>Ledge</ObjID>
<Line>LineA1</Line>
<Param>
<ParamID>Offset</ParamID>
<Value>-9.8</Value>
</Param>
</SetObj>
</SetArray>
</SetData>
</XmlData>
Set Data
The stage also contains a regular Set Layer by the name of Set.set.xml
. However, it appears to be a different version of the same schema as it has a slightly different layout compared to Set layers in Sonic Unleashed or Sonic Generations.
UserIDGroup
The file contains a UserIDGroup node, which no other Set layer file uses in either of the 2 games. However, in both games there is a class called CUserIDGroupCategoryManager
, which might've replaced this node, or it could've been how the game read this node in the first place.
<UserIDGroup>
<Group>
<Category></Category>
<GroupID>0</GroupID>
<SetObjectID>0</SetObjectID>
<UserID>0</UserID>
</Group>
</UserIDGroup>
Different LayerDefine
In Sonic Unleashed and Sonic Generations, the LayerDefine node is used to offset a Set layer and to toggle between active and inactive, however in TestCourse it seems that all layers are combined into one, and LayerDefine defines which sub-layers are active or not.
<LayerDefine>
<Layer>
<Index>0</Index>
<IsVisible>true</IsVisible>
<Name>Layer0</Name>
</Layer>
<Layer>
<Index>1</Index>
<IsVisible>true</IsVisible>
<Name>Layer1</Name>
</Layer>
<Layer>
<Index>2</Index>
<IsVisible>true</IsVisible>
<Name>Layer2</Name>
</Layer>
<Layer>
<Index>3</Index>
<IsVisible>true</IsVisible>
<Name>Layer3</Name>
</Layer>
<Layer>
<Index>4</Index>
<IsVisible>true</IsVisible>
<Name>Layer4</Name>
</Layer>
<Layer>
<Index>5</Index>
<IsVisible>true</IsVisible>
<Name>Layer5</Name>
</Layer>
<Layer>
<Index>6</Index>
<IsVisible>true</IsVisible>
<Name>Layer6</Name>
</Layer>
<Layer>
<Index>7</Index>
<IsVisible>true</IsVisible>
<Name>Layer7</Name>
</Layer>
<Layer>
<Index>8</Index>
<IsVisible>true</IsVisible>
<Name>Layer8</Name>
</Layer>
<Layer>
<Index>9</Index>
<IsVisible>true</IsVisible>
<Name>Layer9</Name>
</Layer>
</LayerDefine>
Spring object
This level contains a single Spring object that launches Sonic into a wall, and as soon as he touches the wall, he starts infinitely running on it. This could've been used to test wall-running sections.
Purpose of the level
This level contains a huge amount of BendBox objects, which are objects that only exist in Sonic Unleashed. These objects allowed the developers to make collision geometry in-game by using parameters to generate primitive shapes and splines; it also contains equally as many TextDebug objects, which contain a float value called m_Value
. Considering that these TextDebug objects are placed near the BendBox objects, this level could've been for teaching the developers on how to create basic level blockouts using the object, with some examples of what all the parameters do.
Oddities
Despite being bundled in the 2024 version of Sonic Generations, the Havok files present in this stage use Havok 2010 instead of Havok 2022, meaning the stage possibly got bundled into the game by accident, and wasn't used to test anything in the re-release.
Notes
- ↑ Thanks to archxee and NextinHKRY for making these stages work in both games; and thanks to RadiantDerg for recording the stage in Generations.