Hi all,
The game I’m working on has a 2D environments with 3D character models. To manage both lighting sets, I’m trying to setup a CombinedLight class, that syncs both a Light2D and a Light (3D one) to match one another in color, intensity, shape, etc. So far the version I have seems to be working fairly well; however, there’s a few quality settings I want to always have set on my 2D lights and am hitting a wall.
The following fields of Light2D are apparently marked as read-only. The Light2D component is able to configure these in the Inspector, but I cannot seem to influence this from my CombinedLight class:
- useNormalMaps
- pointLightQualiy
- pointLightDistance
- falloffIntensity
I want to be able to create a fresh GameObject, add a CombinedLight class, and have the default Light2D have these fields configured. I don’t necessarily need to be able to change them at runtime if that is not possible. I can workaround this by using Prefabs, but ideally I want this to have the same level of configuration as a typical 2D light has. CombinedLight has a custom Editor, can I possibly leverage that to update these fields?
EDIT: I’ve also attempted to do this via reflection, but GetProperty returns an object without a Set method defined.
var flags = BindingFlags.Instance | BindingFlags.Public;
var normalMapField = typeof(Light2D).GetProperty("useNormalMap", flags);
normalMapField.SetValue(light2D, true);
EDIT2: SOLVED! After playing around with reflection more, I found that the readonly properties have internal fields that match with them (all of which appears to have a similar m_ prefix to them). So to change these fields - for example with normal maps, I just had to do:
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
var normalMapField = typeof(Light2D).GetField("m_UseNormalMap", flags);
normalMapField.SetValue(light2D, true);
The forms weren’t letting me add this to the original post so just going to post my code as a new comment.
[RequireComponent(typeof(Light2D))]
[RequireComponent(typeof(Light))]
public class CombinedLight : MonoBehaviour
{
public enum LightType
{
POINT, SPOT, GLOBAL
}
public LightType type = LightType.POINT;
public Color color = Color.white;
public float intensity = 1;
public bool syncInEditor = true;
public float radius = 1;
public float opacity = 0;
public bool useNormalMaps = true;
public float innerAngle, outerAngle = 180;
private Light2D light2D;
private Light light3D;
void Start()
{
CheckForLights();
InstantiateLights();
InitialLightConfiguration();
SyncLights();
}
void Update()
{
SyncLights();
}
void OnDestroy()
{
if (this == currentGlobalLight)
currentGlobalLight = null;
Destroy(this.gameObject);
}
public void SetupAndSyncExistingLights()
{
CheckForLights();
InitialLightConfiguration();
SyncLights();
}
public void CheckForLights()
{
if (light2D == null)
light2D = GetComponent<Light2D>();
if (light3D == null)
light3D = GetComponent<Light>();
}
private void InstantiateLights()
{
if (light2D == null)
light2D = gameObject.AddComponent<Light2D>();
if (light3D == null)
light3D = gameObject.AddComponent<Light>();
}
private void InitialLightConfiguration()
{
light3D.bounceIntensity = 0;
light3D.lightmapBakeType = LightmapBakeType.Realtime;
light3D.shadows = LightShadows.None;
}
private void SyncLights()
{
SyncCommonAttributes();
switch(type)
{
case LightType.POINT:
SyncPointLights();
break;
case LightType.SPOT:
SyncSpotLights();
break;
case LightType.GLOBAL:
SyncGlobalLights();
break;
}
}
private void SyncCommonAttributes()
{
light2D.color = color;
light3D.color = color;
light2D.intensity = intensity;
light3D.intensity = intensity;
//light2D.useNormalMap = useNormalMaps;
}
private void SyncPointLights()
{
light2D.lightType = Light2D.LightType.Point;
light3D.type = UnityEngine.LightType.Point;
light2D.pointLightInnerRadius = 0;
light2D.pointLightOuterRadius = radius;
light2D.pointLightInnerAngle = 360;
light2D.pointLightOuterAngle = 360;
light3D.range = radius;
}
private void SyncSpotLights()
{
light2D.lightType = Light2D.LightType.Point;
light3D.type = UnityEngine.LightType.Spot;
light2D.pointLightInnerRadius = 0;
light2D.pointLightOuterRadius = radius;
light2D.pointLightInnerAngle = innerAngle;
light2D.pointLightOuterAngle = outerAngle;
light3D.innerSpotAngle = innerAngle;
light3D.spotAngle = outerAngle;
}
private void SyncGlobalLights()
{
light2D.lightType = Light2D.LightType.Global;
light3D.type = UnityEngine.LightType.Directional;
}
}
Editor Script
[CustomEditor(typeof(CombinedLight))]
public class CombinedLightEditor : Editor
{
override public void OnInspectorGUI()
{
var light = (CombinedLight)target;
EditorGUI.BeginChangeCheck();
light.type = (CombinedLight.LightType)EditorGUILayout.EnumPopup("Type", light.type);
light.color = EditorGUILayout.ColorField("Color", light.color);
light.intensity = EditorGUILayout.FloatField("Intensity", light.intensity);
if (light.type == CombinedLight.LightType.POINT)
{
RadiusControls(light);
OpacityControls(light);
NormalMapControls(light);
}
else if (light.type == CombinedLight.LightType.SPOT)
{
RadiusControls(light);
OpacityControls(light);
SpotAngleControls(light);
NormalMapControls(light);
}
else if (light.type == CombinedLight.LightType.GLOBAL)
{
}
// Light syncing within the Editor
light.syncInEditor = EditorGUILayout.Toggle("Sync Lights in Editor?", light.syncInEditor);
if (EditorGUI.EndChangeCheck() && light.syncInEditor)
light.SetupAndSyncExistingLights();
}
private void RadiusControls(CombinedLight light)
{
light.radius = EditorGUILayout.FloatField("Radius", light.radius);
}
private void OpacityControls(CombinedLight light)
{
light.opacity = EditorGUILayout.Slider("Opacity", light.opacity, 0f, 1f);
}
private void NormalMapControls(CombinedLight light)
{
light.useNormalMaps = EditorGUILayout.Toggle("Use Normal Maps?", light.useNormalMaps);
}
private void SpotAngleControls(CombinedLight light)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Inner/Outer Angle");
light.innerAngle = EditorGUILayout.FloatField(light.innerAngle);
EditorGUILayout.MinMaxSlider(ref light.innerAngle, ref light.outerAngle, 0, 360);
light.outerAngle = EditorGUILayout.FloatField(light.outerAngle);
EditorGUILayout.EndHorizontal();
}
}
Hey there! Newbie here, I’m trying to effect the falloff strength of my 2D lights. Are you saying this is possible via scripting? Right now I’m running into the read-only issue.
What version are you on? Falloff Intensity has a setter, 2021.3+ and possibly earlier.
GetComponent<Light2D>().falloffIntensity = Mathf.PingPong(Time.time, 1f);
For other properties, if you go to your Light2D component and on the 3 little dots in the top right click it and then select Edit Script you should be able to see the backing fields to access via reflection.