Home > Software design >  Object Reference Not Set... Arm Chain Generator Not Working
Object Reference Not Set... Arm Chain Generator Not Working

Time:02-17

So this one is a bit convoluted.

It started off with trying to learn kinematics for procedural animation. Then I went on a tangent about generating thicker lines. Now I'm trying to produce a program that generates a chain of inter-connected lines made out of DrawSpheres with variable length/angle/resolution.

I had the original single line functioning perfectly. But I can not get the program to generate more. Each line is generated at the end target of the previous line, but there seems to be no starting object to reference. What do I change?

The individual line code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;

public class Arm
{
    public GameObject joint;

    [Range(0, 10), HideInInspector]
    public float length;

    [Range(-360, 360), HideInInspector]
    public float angle;

    [HideInInspector]
    public Transform origin;

    [Range(0, 2560), HideInInspector]
    public int density;

    [Range(0, 256), HideInInspector]
    public float thickness;

    public Vector3 Target()
    {
        Vector3 target = new Vector3(0, 0, 0);

        target.x = origin.position.x   Mathf.Cos(this.angle) * this.length;
        target.y = origin.position.y   Mathf.Sin(this.angle) * this.length;

        return target;
    }

    public Arm(float length, float angle, Vector3 origin, int resolution, float thickness, Vector3 target)
    {
        this.length = length;
        this.angle = angle;
        this.origin.position = origin;
        this.density = resolution;
        this.thickness = thickness;

    }

    public void GenerateLine()
    {
        Vector3[] lineSegments = new Vector3[density];
        float segmentLength = length / lineSegments.Length;

        lineSegments[0] = (origin.position);

        for(int i = 1; i < lineSegments.Length; i  )
        {
            lineSegments[i].x = lineSegments[i - 1].x   Mathf.Cos(this.angle/57.3f) * segmentLength;
            lineSegments[i].y = lineSegments[i - 1].y   Mathf.Sin(this.angle/57.3f) * segmentLength;

            Gizmos.color = Color.black;
            Gizmos.DrawSphere(lineSegments[i], thickness / 1000);
        }

    }
}

The Chain manager code

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ArmChain : MonoBehaviour
{
    [Range(0, 5)]
    public int chainLength;

    [Range(0, 10)]
    public float length;

    [Range(-360, 360)]
    public float angle;

    public Transform origin;

    [Range(0, 2560)]
    public int resolution;

    [Range(0, 256)]
    public float thickness;

    public Vector3 Target()
    {
        Vector3 target = new Vector3(0, 0, 0);

        target.x = this.transform.position.x   Mathf.Cos(this.angle) * this.length;
        target.y = this.transform.position.y   Mathf.Sin(this.angle) * this.length;

        return target;
    }

    public Transform jointOrigin;

    [SerializeField, HideInInspector]
    public Arm[] arms;

    private void OnDrawGizmos()
    {
        Initialize();
        GenerateChain();
    }


    void Initialize()
    {
        Arm[] arms = new Arm[chainLength];

        for (int i = 1; i < chainLength; i  )
        {
            arms[i] = new Arm(arms[i - 1].length, arms[i].angle, arms[i - 1].Target(), arms[i - 1].density, arms[i - 1].thickness, arms[i].Target());
        }
    }

    public void GenerateChain()
    {
        foreach (Arm arm in arms)
        {
            arm.GenerateLine();
        }
    }


}

CodePudding user response:

you do

arms[i] = new Arm(arms[i - 1].length, arms[i].angle, arms[i - 1].Target(), arms[i - 1].density, arms[i - 1].thickness, arms[i].Target());
  • well what is arms[0] ..? You never create it so the very first arms[i - 1] already returns null.

  • and further note the last access to arms[i].Target()

    => if arms[i] has never been set yet (because you are just going to do this) then there won't be anything at arms[i].


Besides that you do

Arm[] arms = new Arm[chainLength];

which hides the class field arms

=> when you call GenerateChain the field arms was never initialized.

You rather would want

void Initialize()
{
    arms = new Arm[chainLength];

    ...
}

CodePudding user response:

With help from derHugo, and a C# youtube tutorial series by Caleb Curry I have found the solution. The issue came from lack of understanding of classes and the difference between static and non-static methods.

Here's the code, it's a fun little bit of code that makes a simple forward kinematic arm.

using UnityEngine;

public class Arm
{
    public float length { get; set; }

    public float angle { get; set; }

    public Vector3 origin { get; set; }

    public int resolution { get; set; }

    public float thickness { get; set; }

    public Vector3 target { get; set; }

    static public Vector3 Target(Vector3 origin, float angle, float length)
    {
        Vector3 target;

        target.x = origin.x   Mathf.Cos(angle) * length;
        target.y = origin.y   Mathf.Sin(angle) * length;
        target.z = 0;

        return target;
    }

    static public void GenerateLine(int resolution, float length, Vector3 origin, float angle, float thickness)
    {
        Vector3[] lineSegments = new Vector3[resolution];
        float segmentLength = length / lineSegments.Length;

        lineSegments[0] = origin;

        for(int i = 1; i < lineSegments.Length; i  )
        {
            lineSegments[i].x = lineSegments[i - 1].x   Mathf.Cos(angle) * segmentLength;
            lineSegments[i].y = lineSegments[i - 1].y   Mathf.Sin(angle) * segmentLength;

            Gizmos.color = Color.black;
            Gizmos.DrawSphere(lineSegments[i], thickness);
        }

    }
}

using UnityEngine;

public class ArmChain : MonoBehaviour
{
    [Range(0, 100)]
    public int chainLength;

    [Range(0, 10)]
    public float length;

    [Range(-.02f * 3.141592f, .02f * 3.141592f)]
    public float angle;

    [Range(0, 2560)]
    public int resolution;

    public Transform jointOrigin;

    [Range(0, 2560)]
    public float thickness;

    [SerializeField, HideInInspector]
    public Arm[] arms;

    private void OnDrawGizmos()
    {
        Initialize();
        GenerateChain();
    }


    void Initialize()
    {
        arms = new Arm[chainLength];

        arms[0] = new Arm();

        arms[0].length = length;
        arms[0].angle = angle;
        arms[0].origin = jointOrigin.position;
        arms[0].resolution = resolution;
        arms[0].thickness = thickness;
        arms[0].target = Arm.Target(jointOrigin.position, angle, length);

        for (int i = 1; i < chainLength; i  )
        {
            arms[i] = new Arm();
            arms[i].length = length;
            arms[i].angle = arms[i - 1].angle   angle;
            arms[i].origin = arms[i - 1].target;
            arms[i].resolution = resolution;
            arms[i].thickness = thickness;
            arms[i].target = Arm.Target(arms[i].origin, arms[i].angle, length);
        }
    }

    public void GenerateChain()
    {
       for(int i = 0; i < chainLength; i  )
        {
            Arm.GenerateLine(arms[i].resolution, arms[i].length, arms[i].origin, arms[i].angle, arms[i].thickness);
        }
    }
}

  • Related