This post documents how to modify and replace the SimplePlanes sky through modding. This guide assumes basic knowledge and literacy of Unity and C#.
Example GitHub repository with SPMOD. View the full scripts and download the SPMOD from there. Feel free to use the scripts shown here.
The repository contains:
Examples.cs
script demonstrating editing TOD parameters with commands. Attach to persistent object.Examples2.cs
script demonstrating automatic application of a custom skybox. Attach to a child of custom map or map plugin.- Example Unity scene.
SkyModExamples.spmod
.
The SPMOD contains:
ChangeSkyBrightness
command that can be run in the main menu or sandbox.- "Skybox Replace Test" sandbox map. This map uses the "Cold Sunset" skybox in AllSky Free.
[ Open SPMODing ] This guide and my mod source code repositories are openly available and intended to help those that may have an interest in programming.
Contents
- Basics
- Sky References
- Sky Modification
- Sky Replacement
1. Basics
This section provides some basic technical information.
The SimplePlanes sky is controlled by the Time of Day (TOD) asset, of which documentation exists online.
Since TOD contains its own classes that aren't part of C# or Unity, it's not possible to access them without bundling them with the mod. Of course, this is not practical due to its price.
Luckily, C# has a feature called reflection, which allows us to use TOD types and classes without having to physically include them in the mod. The online documentation can be consulted to find out which parameters are available for editing.
While we are using reflection here, we do not need to use the SP Reflection Tools package.
2. Sky References
This section demonstrates how to get the main TOD objects for processing in Sections 3 and 4.
The first step is to get a reference to the TOD_Sky
component, which is the master component and responsible for controlling others. At the same time, get the reference to its host GameObject. This will facilitate getting the other components.
Here are the first three fields we need to populate. Notice how we store the Type of TOD_Sky
in addition to the reference to the actual instance of TOD_Sky
.
// [Examples.cs ~ Lines 7 to 14]
// Sky Dome GameObject.
private GameObject SkyDome;
// Sky Dome "TOD_Sky" component type
private Type SkyComponentType;
// A reference to the actual component of type "TOD_Sky"
private object SkyComponent;
We can compare the Type name to any string, in this case the known TOD_Sky
Component Type. This module finds one component of any Type, but should only be used if only one component of that Type exists in the scene.
Note: Even though we know the names of the Sky Dome GameObjects in the main menu and sandbox, we should rely on GameObject names as little as possible. Even though there are no more updates planned for the game, mods can introduce new objects with the same names.
// [Examples.cs ~ Lines 34 to 45]
// Get all components, and looks through them to find a TOD_Sky component
Component[] AllComponents = FindObjectsOfType<Component>();
foreach (Component c in AllComponents)
{
if (c.GetType().Name == "TOD_Sky")
{
// Saves Sky Dome information
SkyDome = c.gameObject;
SkyComponentType = c.GetType();
SkyComponent = c;
}
}
If a different TOD component is required, it can be obtained by changing the compared Type. To get more than one component from this foreach
block, add else if
block(s). If you like switch
statements, it works too.
3. Sky Modification
This section demonstrates getting Fields in TOD components.
Before starting, make a note of the location of each Field you want to modify in your mod. In this example, we will choose the Brightness
parameter, found in the Type TOD_AtmosphereParameters
. Like before, we store both the instance of the object, and its Type.
Note that "Attributes" as listed in the documentation are actually called Fields in C#, and Attributes are something different.
// [Examples.cs ~ Lines 16 to 23]
// "TOD_AtmosphereParameters" type found in "TOD_Sky"
private Type AtmosphereType;
// A reference to the object of type "TOD_AtmosphereParameters" in SkyComponent
private object Atmosphere;
// The brightness field defined in the "TOD_AtmosphereParameters" type
private FieldInfo BrightnessField;
Get the TOD_AtmosphereParameters Atmosphere
Field in the TOD_Sky
component, and hence get the Brightness
Field.
// [Examples.cs ~ Lines 54 to 73]
// Get all fields in the TOD_Sky type,
// and looks through them to find the Atmosphere field
FieldInfo[] fields = SkyComponentType.GetFields();
foreach (FieldInfo field in fields)
{
// also try (field.FieldType.Name == "TOD_AtmosphereParameters")
if (field.Name == "Atmosphere")
{
AtmosphereType = field.FieldType;
Atmosphere = field.GetValue(SkyComponent);
// Get all fields in the TOD_AtmosphereParameters type,
// and looks through them to find the Brightness field
FieldInfo[] atmoFields = AtmosphereType.GetFields();
foreach (FieldInfo atmoField in atmoFields)
{
if (atmoField.Name == "Brightness")
BrightnessField = atmoField;
}
}
}
Once we have the Brightness
Field and the object with that Field we want to modify, we can use SetValue()
to write a value to it.
private void ChangeSkyBrightness(float brightness)
{
BrightnessField.SetValue(Atmosphere, brightness);
}
Download the example SPMOD in the GitHub repository, and run the command ChangeSkyBrightness
. Watch the sky's brightness change according to your entry, keeping in mind that 1.5
is the default value.
4. Sky Replacement
This section demonstrates how to remove the TOD system and add a custom skybox.
We have previously learnt that TOD_Sky
is the main Component used in the TOD sky asset. However, to replace the sky, we must get the TOD_Components
Component instead. This Component holds references to all GameObjects used by TOD to generate the sky at runtime.
Try to convert the Component-finding example code in Section 2! It should look like this.
// [Examples2.cs ~ Lines 19 to 29]
// Similar Component finder to the first example
Component[] AllComponents = FindObjectsOfType<Component>();
foreach (Component c in AllComponents)
{
if (c.GetType().Name == "TOD_Components")
{
SkyDome = c.gameObject;
SkyComponentHolderType = c.GetType();
SkyComponentHolder = c;
}
}
To remove the default TOD sky, all its GameObjects must be destroyed. Find all GameObject fields in the TOD_Components
Component and destroy them. After that, destroy the Sky Dome itself to remove the TOD scripts. (While not strictly necessary, it will prevent NullReferenceException
s from appearing in the dev console.)
// [Examples2.cs ~ Lines 36 to 51]
// Similar Field finder to the first example
FieldInfo[] fields = SkyComponentHolderType.GetFields();
foreach (FieldInfo field in fields)
{
// Get all GameObjects referenced by TOD and destroy them
if (field.FieldType.Name == "GameObject")
{
GameObject RefGameObject = (GameObject)field.GetValue(SkyComponentHolder);
Debug.Log("Destroyed GameObject " + RefGameObject.name);
Destroy(RefGameObject);
}
}
// Finally destroy the Sky Dome itself to prevent errors from appearing in the dev console
Debug.Log("Destroyed GameObject " + SkyDome.name);
Destroy(SkyDome);
Change the skybox with your own, and set up the cameras to use it. From running tests in the dev console, Camera (MainCamera)
has the lowest depth
value, and so it is the one to be modified. However, due to the existence of multiple cameras tagged MainCamera
in the sandbox, we cannot use Camera.main
to get the proper camera.
In this example, the lowest Camera is selected by name, but it is also possible to use the depth
value to find the Camera. The depth
value of Camera (MainCamera)
is 1
.
// [Examples2.cs ~ Lines 53 to 64 (modified)]
// Replace the skybox
RenderSettings.skybox = SkyboxMaterial;
// Set up the Main Camera to use the skybox
MainCamera = GameObject.Find("MainCamera").GetComponent<Camera>();
MainCamera.clearFlags = CameraClearFlags.Skybox;
Debug.Log("Applied settings on Camera " + MainCamera.name);
When making your map, keep in mind that the Directional Light controlled by TOD is also destroyed by the skybox replacement script. You must add your own Directional Light.
@hpgbproductions Okay, I'll try to find it again
@GhostTeam2 i don't have TOD, i have only changed the options of TOD in SP
@hpgbproductions So can you share it with me or is there any other way to get it?
@GhostTeam2 you can read and change the values that the devs use, but you cannot download TOD with this way
@hpgbproductions I mean, TOD assets can't be downloaded. I need to optimize some details according to the time in TOD assets But he seems to have been taken off the shelf.
@GhostTeam2 see the code block [Examples.cs ~ Lines 34 to 45]
1. get all components and store the result
2. iterate through the list/array until you find the components with the correct type names
3. store the type, and the object (you will need both)
I can't get the TOD component now. Can you teach me how to get it? This is very helpful for my new map lighting rendering optimization.
thanks mate, i was struggling to change the sky params because they kept resetting
OCD kicks in
I absolutely need to be the tenth upvote and the 20th comment-
@Suqingqing set sea level to null, it's one of the things in service provider
@hpgbproductions Can the ocean be hidden? To see the underground world of SP, without the floating physics of the ocean, if it can be achieved, that would be great!!
But unfortunately I can't C#......
: (
@Suqingqing you can put the script in the map plugin, but it cannot be loaded on android
I have been studying SP Sky according to Time of Day for a long Time
Can custom skyboxes be used in the map plugin?
@hpgbproductions ok.
@Suqingqing not sure what you mean, do you mean remove water?
ServiceProvider.Instance.GameWorld.SeaLevel = null;
.
(Look through the autofill results to see what else you can do stock with serviceprovider)
@hpgbproductions Thank you very much!I did it!
But I have a question:
Since you can change the Skybox, then can you eliminate the sea water energy?
@Suqingqing your .net api version is 2.0 when it should be 4.x
Edit > Project settings > Player > API compatibility level
.
(don't worry, everyone forgets to change the setting at some point)
@hpgbproductions Bug?
The script 'Examples' could not be instantiated!FileNotFoundException: Could not load file or assembly 'netstandard, Version-2.0.0.0, Culture=neutral, PublickeyToken=cc7b13fcd2dd51' or one of its dependencies.Unable to position aircraf. Location 'Starting Line 2' was not found
@hpgbproductions
The script is perfectly usable
The sky is constant
The plane didn't disappear.
@1342791782 need the following info to debug for you
- is examples2 is in a child object of the map component? (it should)
- is the file or class name changed? (try making sure both match, not sure if it's a must)
- is the script running partially or not at all? (see below)
- which debug messages appear? (it will print to the console when TOD gameobjects are destroyed, and when the camera is edited)
- is the sky unchanged or black?
- does the ground/ aircraft disappear?
@hpgbproductions I've always used custom maps.
@1342791782 i've designed it for custom map or map plugin, not persistent object. Is it on a gameobject under a custom map or map plugin?
Edited post to show usage of each script
@hpgbproductions I used your Examples2 to put a skybox texture ball in it. When I saved it in the game and opened the game, the sky did not appear.
@1342791782 a skybox is only one Material, read the Unity skybox reference
Can I see your setup?