Goal
I am designing a boid system using Unity. I deal with the awareness radius by adding a boid to the "Swarm" list when it enters a collider. In order to find the force for each boid, I need to cycle through the swarm list, access the "Boid" class, and retrieve velocity and position.
Problem
The Boid classes from each swarm entity are added to a new list, and passed to the physics controller. However, a NullReferenceException is thrown at line 96, and I don't understand why that variable would be null. As far as I know, accessing a populated Enumerable<Boid> using foreach should have variables within.
NullReferenceException: Object reference not set to an instance of an object Boid.Alignment (System.Collections.Generic.IEnumerable`1[T] boids) (at Assets/Scripts/Boid.cs:96) Boid.Update () (at Assets/Scripts/Boid.cs:42)
After testing, it seems it is thrown when accessing any part of the new list of Boids.
Why does my new list contain no data? Is there a better way to handle a 2D implementation of boids in a 3D space? Is there a resource I can use to better understand Linq?
P.S. I am very new to using Linq systems, and most of this code is taken from this video and this Unity project script
Code
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class Boid : MonoBehaviour
{
// Global Variables
public Boid_Settings settings;
// Local Variables
public Rigidbody body;
public Vector2 acceleration;
public Vector2 velocity
{
    get
    { return new Vector2(body.velocity.x, body.velocity.z); }
    set
    { body.velocity = new Vector3(value.x, body.velocity.y, value.y); }
}
public Vector2 position
{
    get
    { return new Vector2(transform.position.x, transform.position.z); }
}
public List<GameObject> swarm = new List<GameObject>();
public List<GameObject> targets = new List<GameObject>();
// Functions
private void Start()
{
    float angle = Random.Range(0, 2 * Mathf.PI);
    transform.rotation = Quaternion.Euler(new Vector3(0, 0, angle));
    velocity = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
}
private void Update()
{
    IEnumerable<Boid> boids = swarm.Select(o => o.GetComponent<Boid>()).ToList(); //Line 40
    Vector2 alignment = Alignment(boids); //LINE 42
    Vector2 separation = Separation(boids);
    Vector2 cohesion = Cohesion(boids);
    acceleration = settings.alignmentWeight * alignment + settings.cohesionWeight * cohesion + settings.seperationWeight * separation;
    UpdatePhysics();
}
// Entity Awareness Assignment
private void OnTriggerEnter(Collider collider)
{
    if (collider.CompareTag("Zombie"))
    { swarm.Add(collider.gameObject); }
    else if (collider.CompareTag("Player") || collider.CompareTag("Lure"))
    { targets.Add(collider.gameObject); }
}
private void OnTriggerExit(Collider collider)
{
    if (collider.CompareTag("Zombie"))
    { swarm.Remove(collider.gameObject); }
    else if (collider.CompareTag("Player") || collider.CompareTag("Lure"))
    {
        targets.Remove(collider.gameObject);
        StartCoroutine(LingerTarget(collider.gameObject));
    }
}
IEnumerator LingerTarget(GameObject target)
{
    targets.Add(target);
    yield return new WaitForSeconds(settings.lingerTime);
    targets.Remove(target);
}
// Core Boid Logic
public void UpdatePhysics()
{
    // Apply the acceleration, and then limit the speed to the maximum.
    Vector2 UncappedVelocity = velocity + acceleration;
    velocity = ApplyLimit(UncappedVelocity, settings.maxSpeed);
    float angle = Mathf.Atan2(velocity.y, velocity.x) * Mathf.Rad2Deg;
    body.transform.rotation = Quaternion.Euler(new Vector3(0, 0, angle));
}
private Vector2 Alignment(IEnumerable<Boid> boids)
{
    Vector2 velocity = Vector2.zero;
    if (!boids.Any()) return velocity;
    foreach (Boid boid in boids)
    { velocity += boid.velocity; } //LINE 96
    velocity /= boids.Count();
    Vector2 steer = Steer(velocity.normalized * settings.maxSpeed);
    return steer;
}
private Vector2 Cohesion(IEnumerable<Boid> boids)
{
    if (!boids.Any()) return Vector2.zero;
    Vector2 sumPositions = Vector2.zero;
    foreach (Boid boid in boids)
    { sumPositions += boid.position; }
    Vector2 average = sumPositions / boids.Count();
    Vector2 direction = average - position;
    Vector2 steer = Steer(direction.normalized * settings.maxSpeed);
    return steer;
}
private Vector2 Separation(IEnumerable<Boid> boids)
{
    Vector2 direction = Vector2.zero;
    boids = boids.Where(o => Vector3.Distance(o.transform.position, position) <= settings.avoidanceRadius);
    if (!boids.Any()) return direction;
    foreach (Boid boid in boids)
    {
        Vector2 difference = position - boid.position;
        direction += difference.normalized / difference.magnitude;
    }
    direction /= boids.Count();
    Vector2 steer = Steer(direction.normalized * settings.maxSpeed);
    return steer;
}
private Vector2 Steer(Vector2 desired)
{
    Vector2 steer = desired - velocity;
    steer = ApplyLimit(steer, settings.maxSteerForce);
    return steer;
}
// Calculation Helpers
private Vector2 ApplyLimit(Vector2 baseVector, float limit)
{
    if (baseVector.sqrMagnitude > limit * limit)
    { baseVector = baseVector.normalized * limit; }
    return baseVector;
}
}
The Boid_Settings Module:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CreateAssetMenu]
public class Boid_Settings : ScriptableObject
{
// Boid
public float maxSpeed = 5;
public float avoidanceRadius = 1;
public float maxSteerForce = 3;
public float lingerTime = 2.5f;
public float alignmentWeight = 1;
public float cohesionWeight = 1;
public float seperationWeight = 1;
public float targetWeight = 1;
// Spawner
public float awarenessRadius = 2.5f;
}
Context Pictures
Proof the boid classes have data to be read
 Unity Enviroment & Boid.cs Attachment
Unity Enviroment & Boid.cs Attachment
 Unity Boid Boid.body Component
Unity Boid Boid.body Component
 Each Boid finds two swarm mates when the game is run
Each Boid finds two swarm mates when the game is run

 
    