// ENTITY BASE CLASS
public class Entity {
    private readonly List<Component> addedComponents = new List<Component>();
 
    public void AddComponent<TComponent>(TComponent component) where TComponent : Component {
        if (HasComponent<TComponent>()) throw new ComponentAlreadyAddedException();
 
        addedComponents.Add(component);
    }
 
    public bool HasComponent<TComponent>() where TComponent : Component {
        return addedComponents.OfType<TComponent>().Any();
    }
 
    public void RemoveComponent<TComponent>() where TComponent : Component {
        if (!HasComponent<TComponent>()) throw new ComponentNotAddedException();
 
        TComponent componentToRemove = GetComponent<TComponent>();
        addedComponents.Remove(componentToRemove);
    }
 
    public TComponent GetComponent<Component>() where TComponent : Component {
        if (!HasComponent<TComponent>()) throw new ComponentNotAddedException();
        return addedComponents.OfType<TComponent>().Single();
    }
}
 
// COMPONENTS
public abstract class Component { }
 
public abstract class TangibleComponent : Component {
    public Vector3 Position;
    public Vector3 Velocity;
}
 
public class ModelledComponent : TangibleComponent {
    public ModelReference Model;
    public float Scaling;
 
    public bool CameraCanSeeThis(Camera camera) {
        return !PhysicsSystem.RayTest(camera.Position, Position).IsObscured;
    }
}
 
public class AudibleComponent : TangibleComponent {
    public SoundReference Sound;
    public float SoundRadius;
 
    public bool CameraCanHearThis(Camera camera) {
        return SoundRadius > Vector3.Distance(camera.Position, Position);
    }
}
 
// ENTITIES
public class ArmourPowerupEntity : Entity {
    private const float DEFAULT_ARMOUR_SCALING = 1f;
    
    public ArmourPowerupEntity(Vector3 powerupPosition) 
        : this(powerupPosition, DEFAULT_ARMOUR_SCALING) { }
 
    public ArmourPowerupEntity(Vector3 powerupPosition, float customScaling) {
        ModelledComponent modelledComponent = new ModelledComponent();
        modelledComponent.Model = ModelDatabase.ArmourModel;
        modelledComponent.Position = powerupPosition;
        modelledComponent.Scaling = customScaling;
        AddComponent(modelledComponent);
    }
}
 
public class RocketProjectileEntity : Entity {
    public RocketProjectileEntity(Vector3 firingPoint, Vector3 firingVelocity, bool isBigRocket) {
        ModelledComponent modelledComponent = new ModelledComponent();
        AudibleComponent audibleComponent = new AudibleComponent();
        modelledComponent.Model = ModelDatabase.RocketModel;
        modelledComponent.Position = firingPoint;
        modelledComponent.Velocity = firingVelocity;
        modelledComponent.Scaling = isBigRocket ? 2f : 1f;
        audibleComponent.Sound = SoundDatabase.RocketSound;
        audibleComponent.Position = firingPoint;
        audibleComponent.Velocity = firingVelocity;
        audibleComponent.SoundRadius = 1000f;
        AddComponent(modelledComponent);
        AddComponent(audibleComponent);
    }
}