Hi Guys,
I know it’s an over talked subject sometimes, but I’m personally a little confused about my developing style. Basically I find many many instances where I’ll be inheriting for abstraction, rather than using interfaces. I know everyone seems to love interfaces over inheritance. So could someone take a look at my theory please?
Here’s my current example that led me to post a thread:
I have a class, which creates a random weather effect:
public enum WeatherCondition { Clear, Rain, Fog, Snow, Count }
using UnityEngine;
using System.Collections;
using CDSFramework.CDSEnvironment;
[RequireComponent(typeof(WindZone))]
public class EnvironmentManager : EnvironmentManagerFramework {
public WeatherCondition weatherCondition;
[SerializeField] protected bool randomWeather;
// Light Vars
[SerializeField] private Gradient nightDayClearColour, nightDayRainColour, nightDaySnowColour;
[SerializeField] private float minLightIntensity;
[SerializeField] private float maxLightIntensity;
[SerializeField] private float minAmbient;
[SerializeField] private float maxAmbient;
// Fog Vars
[SerializeField] private Gradient nightDayFogColour;
[SerializeField] private AnimationCurve fogDensityCurve;
[SerializeField] private float fogScale;
[SerializeField] private Gradient snowingNightDayFogCurve;
[SerializeField] private float adjustFogWeather;
// Atmos Vars
[SerializeField] private float minAtmosDensity;
[SerializeField] private float maxAtmosDensity;
// Temperature Vars
[SerializeField] private AnimationCurve nightDayTemperatureCurve;
[SerializeField] private float tempCurveAmplitude;
[SerializeField] private float baselineTemperature;
// Time vars
public float DayRotateSpeed { set { dayRotateSpeed = value; DebugPanel.DebugInstance.txtError.text = value.ToString() + " " + dayRotateSpeed.ToString(); } }
public float NightRotateSpeed { set { nightRotateSpeed = value; } }
[SerializeField] [Range(0.1f, 100)] private float dayRotateSpeed;
[SerializeField] [Range(0.1f, 100)] private float nightRotateSpeed;
[SerializeField] private Vector3 skySpeed;
[SerializeField] private float renderTimeStep;
[SerializeField] private GameObject rainEffect, fogEffect, snowFlakeEffect;
[SerializeField] private Material cloudyBox;
// Instance Vars
private float skyboxRotation = 0;
private bool rendering = false;
private Light directionalLight;
private Skybox sky;
private Material skyMat;
private WorldDataManager worldData;
private Gradient nightDayColour;
protected override bool Initialise()
{
if ((directionalLight = GetComponent<Light>()) && (skyMat = RenderSettings.skybox) && (worldData = transform.parent.GetComponentInChildren<WorldDataManager>()) )
{
rendering = true;
RandomConditionRequest(randomWeather);
StartCoroutine(TickRenderEnvironment());
return base.Initialise();
}
return false;
}
public void RandomConditionRequest(bool _randomise)
{
if(bInitialised)
{
rainEffect.GetComponent<IWeatherEffect>().DeactivateWeather();
fogEffect.GetComponent<IWeatherEffect>().DeactivateWeather();
snowFlakeEffect.GetComponent<IWeatherEffect>().DeactivateWeather();
}
if (_randomise)
{
int rand = Random.Range(0, (int)WeatherCondition.Count);
weatherCondition = (WeatherCondition)rand;
//float fRand = Random.Range(0, 360);
//transform.Rotate(fRand * skySpeed);
//skyboxRotation += fRand;
//RenderSettings.skybox.SetFloat("_Rotation", skyboxRotation);
}
SetConstantWeatherCondition();
}
public void SetConstantWeatherCondition()
{
switch (weatherCondition)
{
case WeatherCondition.Clear:
{
RenderSettings.skybox = skyMat;
nightDayColour = nightDayClearColour;
break;
}
case WeatherCondition.Fog:
{
nightDayColour = nightDayFogColour;
adjustFogWeather = 0.00175f;
RenderSettings.skybox = cloudyBox;
fogEffect.GetComponent<IWeatherEffect>().ActivateWeather();
break;
}
case WeatherCondition.Rain:
{
nightDayColour = nightDayRainColour;
adjustFogWeather = 0.00125f;
RenderSettings.skybox = cloudyBox;
rainEffect.GetComponent<IWeatherEffect>().ActivateWeather();
break;
}
case WeatherCondition.Snow:
{
nightDayColour = nightDaySnowColour;
adjustFogWeather = 0.00150f;
nightDayFogColour = snowingNightDayFogCurve;
RenderSettings.skybox = cloudyBox;
snowFlakeEffect.GetComponent<IWeatherEffect>().ActivateWeather();
break;
}
default:
break;
}
}
// Update World Time, rotating 'Sun & Skies'
void Update()
{
// World Time
float dot = Mathf.Clamp01(Vector3.Dot(directionalLight.transform.forward, Vector3.down));
Vector3 deltaRot = dot > 0 ? dayRotateSpeed * Time.deltaTime * skySpeed : nightRotateSpeed * Time.deltaTime * skySpeed;
transform.Rotate(deltaRot);
skyboxRotation += deltaRot.magnitude;
RenderSettings.skybox.SetFloat("_Rotation", skyboxRotation);
}
// Ticks Rendering
private IEnumerator TickRenderEnvironment()
{
while(rendering)
{
float dot = Mathf.Clamp01(Vector3.Dot(directionalLight.transform.forward, Vector3.down));
float i = ((maxLightIntensity - minLightIntensity) * Mathf.Clamp01(dot)) + minLightIntensity;
// Light Rendering
directionalLight.color = nightDayColour.Evaluate(dot);
RenderSettings.ambientLight = directionalLight.color;
RenderSettings.ambientIntensity = ((maxAmbient - minAmbient) * dot) + minAmbient;
directionalLight.intensity = i;
// Fog Rendering
RenderSettings.fogColor = nightDayFogColour.Evaluate(dot);
RenderSettings.fogDensity = (fogDensityCurve.Evaluate(dot) * fogScale) + adjustFogWeather;
// Temperature Adjusting
worldData.Temperature = baselineTemperature + (nightDayTemperatureCurve.Evaluate(dot) * tempCurveAmplitude);
// Atmospheric Density Rendering
i = ((maxAtmosDensity - minAtmosDensity) * dot) + minAtmosDensity;
RenderSettings.skybox.SetFloat("_Exposure", i);
// Performance Control
yield return new WaitForSeconds(renderTimeStep);
}
}
}
public interface IWeatherEffect
{
void ActivateWeather();
void DeactivateWeather();
}
using UnityEngine;
using System.Collections;
public class RainModule : CDSFramework.CDSEnvironment.BaseEnvironmentFramework, IWeatherEffect
{
[SerializeField] private ParticleSystem weatherEffect;
public void ActivateWeather()
{
weatherEffect.gameObject.SetActive(true);
}
public void DeactivateWeather()
{
// TODO::Add more effects! :smile:
weatherEffect.gameObject.SetActive(false);
}
}
You see I use an interface to control weather effects. However It’s a bit messy. My code here handles half of the changes of a weather effect, keeping a list of variables for each effect (like snowingNightDayFogCurve and nightDayRainColour for example), while the interface handles the player’s particle effects. It seems to me it’d be much cleaner to put all the variables into a single WeatherCondition type, which the EnvironmentManager just takes as the current, and uses it’s variables. So it knows to expect some sort of ‘Gradient nightDayColour’, but won’t know the details until it’s passed it from the current WeatherCondition. To me this screams inheritance, where I’d have a parent class called WeatherCondition, which’d hold the basics of activating and deactivating them, and each weatherConditions world variables like max/min temperature, all curves and gradients, and the EnvironmentManager would ask for a typeof WeatherCondition, and pull the child’s data (like RainyWeatherCondition, SnowyWeatherCondition).
However this is again me taking away an interface, and using inheritance instead. In this instance this might be okay, I’m not sure (please give me your input), but I notice I tend to use inheritance a lot more than interfaces, and I’m not sure if this is leaning toward a bad practice I’m not aware of.
Can someone give me their perspective on it please?
Thanks!