ParticleMove

This controls all of the various movement types for particles shooting at Frothy Bird. The physics engine is not generally used in this game, as everything needed to be precisely moved and timed. Each particle is assigned a movement type that is selected from the switch statement, and that particle has all of the required parameters to make the particle move as planned with the script below.


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

public class particle_move : MonoBehaviour
{

    public float particleSpeed; //the speed of particles (will have a mutliplier applied later)

    public MovementType.choose myMovementType;

    //Terms used for various movement types
    public Vector3 originVector;
    public Vector3 finalVector;
    [HideInInspector] public float linearSlope;
    [HideInInspector] public float linearIntercept;
    public Vector3 parabolaVertex; //The (h,k) in x = a(y - k)^2 + h. Entered in the Unity Editor.
    [HideInInspector] float parabolaFactor; // The a in x = a(y - k)^2 + h
    public float sineAmplitude; //Will be adjusted in inspector as needed.
    public float sinePeriodFactor; //Will be adjusted in inspector as needed.
    [HideInInspector] public float startTime; //Time when class started running.
    [HideInInspector] public float distanceAlongCurve; //The calculated distance along a curve. Always assume z is constant.
    [HideInInspector] public Vector3 nextLocation; //The location the object will move to during this update cycle.
    [HideInInspector] public float distanceThisCycle; //The distance to travel in one update cycle.
    [HideInInspector] public float nextX;
    [HideInInspector] public float nextY;
    [HideInInspector] public float nextZ;
    [HideInInspector] public float deltaX; //The change in X for this one update cycle.
    [HideInInspector] public float deltaY; //The change in Y for this one update cycle.
    [HideInInspector] public int xSign; //The direction x unity of the direction we will be travelling in the x-direction
    [HideInInspector] public int ySign; //Same as above, but for y
    [HideInInspector] public bool xIsDone; //x-coordinate has been achieved;
    [HideInInspector] public bool yIsDone; //y-coordinate has been achieved;
    private Vector3 hoverPoint;
    public bool particleHover;
    private bool doneHovering;
    private float hoverTime;
    private float elapsedHoverTime;
    private GameObject blobCore;
    private List<Vector3> blobDeltas = new List<Vector3>();
    private bool linkedMovement; //Indicates that this particle's movement is tied to another particle and not independent at the time.
    [HideInInspector] public bool ballBounced; //This particle has bounced. Useful to find when the core ball of the blob has bounced as well.
    [HideInInspector] public bool particleSettled; //Particle is settled on the ground.
    public float settleTime; //How long the particle will remain settled (on the ground)
    public float timeSettledSoFar;
    public bool doneSettling; 
    private float figure8focus;
    private float figure8vertShift;
    private float theta;
    private float radius;
    private bool orbitStarted;
    private bool orbitComplete;
    private bool onePassComplete; //Have completed one full pass of an orbit, so ready to launch at Frothy
    private GameObject leadParticle; //If a movement type (e.g. linear double) needs to follow a lead particle, this is its lead.

    private float freezeTime;
    private float freezeGap;
    private float elapsedFreezeTime;
    private float elapsedGapTime;
    private bool frozen;

    private Vector3 bigStartingSize;
    private Vector3 finalShrinkScale; //What size the particle will be after shrinking --- when it hits frothy's mouth.

    private List<Vector3> zigZagPositions = new List<Vector3> (); //Contains all positions to be used for zig zag
    public static float biggestZigDistance;  //Note that biggestZigDistance is reset to zero at each level transition. This is the biggest zig 1 and 2 combo yet in the level.
    private int zigCounter; //Counts which segment of the zig we are on (index of zigZagPositions)

    //For sigmoid. f(x) = L/(1+e^(-k*(x-x0)))  
    private float sigXmid; //x-value of the midpoint -- x0 above
    private float sigHeight; //L-value above - the max height from the starting zero point (will subtract to find)
    private float sigSteepness; //The k value above. The curve steepness.

    //For spiralTop, starting as sigmoid
    private Vector3 spiralCenter;
    private Vector3 startSpiralVec;
    private float spiralA;
    private float spiralB;
    private float spiralThetaSpeed; //Angular speed
    private float beakShift; //Amount from beak point that the center of spiral is located.
    private bool spiralStarted;
    private bool movingLinearly;
    private bool movingSigmoidally;

    private FrothyParticle thisFrothyParticle;

    /*[HideInInspector]*/ public bool movementInitialized = false;

    void Awake()
    {
        thisFrothyParticle = gameObject.GetComponent<FrothyParticle>();
    }

    void Start () 
    {

    }

    public void InitializeMovement(float speed, MovementType.choose particleMovementType, float timeBetweenFreezes, float timeSpentFrozen)
    {

        originVector = transform.position;
        finalVector = GameObject.Find ("Beak Point").transform.position; //Beak Point is placed in Frothy's mouth at the optimum location for particle impact (hopefully).
        particleHover = false;
        doneHovering = false;
        linkedMovement = false;
        ballBounced = false;

        freezeGap = timeBetweenFreezes;
        freezeTime = timeSpentFrozen;
        elapsedGapTime = 0.0f;
        elapsedFreezeTime = 0.0f;
        frozen = false;

        myMovementType = particleMovementType;
        particleSpeed = speed;

        GetSign();

        startTime = Time.time;

        switch (myMovementType)
        {
        case MovementType.choose.linear:
            LinearInitialize ();
            break;
        case MovementType.choose.parabolic:
            if (ySign == -1)
                parabolaVertex = new Vector3 ((5.0f),6.2f,0.0f); //I guess and checked to get a vertex that would work. Math failed me.
            else
                parabolaVertex = new Vector3 ((5.0f),0.8f,0.0f);
            //Determine parabolaFactor based on parabolaVertex and originVector and finalVector
            parabolaFactor = (originVector.x - parabolaVertex.x)/(Mathf.Pow ((originVector.y - parabolaVertex.y),2));
            break;
        case MovementType.choose.sinusoidal:
            //No initializations needed ?
            break;
        case MovementType.choose.ufoTop:
            hoverPoint = new Vector3(7.2f,4.5f ,0.0f);
            nextY = transform.position.y;
            hoverTime = 1.0f;
            finalVector = hoverPoint;
            break;
        case MovementType.choose.ufoMid:
            hoverPoint = new Vector3(7.2f,1.5f ,0.0f);
            nextY = transform.position.y;
            hoverTime = 2.5f;
            finalVector = hoverPoint;
            break;
        case MovementType.choose.ufoBot:
            hoverPoint = new Vector3(7.2f,-1.5f ,0.0f);
            nextY = transform.position.y;
            hoverTime = 4.0f;
            finalVector = hoverPoint;
            break;
        case MovementType.choose.bounce:
            finalVector = new Vector3(0.0f, -10.0f, 0.0f);
            GetSign ();
            LinearInitialize();
            break;
        case MovementType.choose.bounce2: //Same as bounce, but with bounce point changed.
            finalVector = new Vector3(0.0f, -6.0f, 0.0f);
            //Need to get center of blob if it's not this particle to base movement off of.
            blobCore = null;
            if (gameObject.name != "Blob Core")
            {
                blobCore = GameObject.Find ("Blob Core");
                linkedMovement = true;
            }
            //Populate the blob deltas so we know how far apart to move the core-surrounding particles
            blobDeltas.Add (new Vector3(1.115f, 0.655f, 0.0f));
            blobDeltas.Add (new Vector3(0.0f, 1.331f, 0.0f));
            blobDeltas.Add (new Vector3(-1.115f, 0.655f, 0.0f));
            blobDeltas.Add (new Vector3(-1.115f, -0.655f, 0.0f));
            blobDeltas.Add (new Vector3(0.0f, -1.331f, 0.0f));
            blobDeltas.Add (new Vector3(1.115f, -0.655f, 0.0f));
            GetSign ();
            LinearInitialize();
            break;
        case MovementType.choose.bounce3: //Same as bounce2, but after blob hits, things are different.
            finalVector = new Vector3(0.0f, -6.0f, 0.0f);
            //Need to get center of blob if it's not this particle to base movement off of.
            blobCore = null;
            if (gameObject.name != "Blob Core")
            {
                blobCore = GameObject.Find ("Blob Core");
                linkedMovement = true;
                string trailingNumberString = gameObject.name.Substring(5);
                int index = int.Parse(trailingNumberString)-1;
                settleTime = index + 1.0f; //This is an integer. Forcing it to convert.
            }
            //Populate the blob deltas so we know how far apart to move the core-surrounding particles
            blobDeltas.Add (new Vector3(1.115f, 0.655f, 0.0f));
            blobDeltas.Add (new Vector3(0.0f, 1.331f, 0.0f));
            blobDeltas.Add (new Vector3(-1.115f, 0.655f, 0.0f));
            blobDeltas.Add (new Vector3(-1.115f, -0.655f, 0.0f));
            blobDeltas.Add (new Vector3(0.0f, -1.331f, 0.0f));
            blobDeltas.Add (new Vector3(1.115f, -0.655f, 0.0f));
            GetSign ();
            LinearInitialize();
            particleSettled = false;
            settleTime += 1.5f; //Add a standard amount to each particle settle time.
            timeSettledSoFar = 0.0f;
            doneSettling = false;
            break;
        case MovementType.choose.figure8: 
            //Figure 8 equation in polar coordinates: r^2 = 2a^2*cos(2*theta) 
            //ref: http://jwilson.coe.uga.edu/EMAT6680Su06/Byrd/Assignment%20Eleven/RBAssignmentEleven.html
            figure8focus = GameObject.Find ("frothy_bird").transform.position.x;
            figure8vertShift = GameObject.Find ("Beak Point").transform.position.y;
            //finalVector will be the first spot on the lemniscate that the particle will travel to linearly
            //before starting to orbit in a figure 8.
            theta = 0;
            finalVector = new Vector3(Mathf.Sqrt (2*Mathf.Pow ((figure8focus),2)*Mathf.Cos (theta)), figure8vertShift, 0.0f);
            orbitStarted = false;
            orbitComplete = false;
            onePassComplete = false;
            GetSign ();
            LinearInitialize();
            break;
        case MovementType.choose.linearDouble:
            finalVector = GameObject.Find ("Beak Point 2").transform.position; //Only difference between this and regular linear.
            LinearInitialize ();
            break;
        case MovementType.choose.linearToDoubled: //Same as linear, but goes to second frothy
            finalVector = GameObject.Find ("Beak Point 2").transform.position; //Only difference between this and regular linear.
            LinearInitialize ();
            break;
        case MovementType.choose.figure8Double: 
            //Figure 8 equation in polar coordinates: r^2 = 2a^2*cos(2*theta) 
            figure8focus = GameObject.Find ("frothy_bird duplicate").transform.position.x;
            figure8vertShift = GameObject.Find ("Beak Point 2").transform.position.y;
            //finalVector will be the first spot on the lemniscate that the particle will travel to linearly
            //before starting to orbit in a figure 8.
            theta = 0;
            finalVector = new Vector3(Mathf.Sqrt (2*Mathf.Pow ((figure8focus),2)*Mathf.Cos (theta)), figure8vertShift, 0.0f);
            orbitStarted = false;
            orbitComplete = false;
            onePassComplete = false;
            GetSign ();
            LinearInitialize();
            break;
        case MovementType.choose.shrink:  //Same as linear, but particle starts huge and shrinks to normal.
            bigStartingSize = new Vector3(3.0f,3.0f,1.0f); //Starting size of the particle.
            thisFrothyParticle.transform.localScale = bigStartingSize;
            finalShrinkScale = new Vector3 (1.0f, 1.0f, 1.0f);
            LinearInitialize ();
            break;
        case MovementType.choose.shrink2:  //Same as linear, but particle starts regular and shrinks to small.
            bigStartingSize = new Vector3(1.0f,1.0f,1.0f); //Starting size of the particle.
            thisFrothyParticle.transform.localScale = bigStartingSize;
            finalShrinkScale = new Vector3 (0.11111f, 0.11111f, 1.0f);
            LinearInitialize ();
            break;
        case MovementType.choose.grow:  //Same as linear, but particle starts small and gets normal size
            bigStartingSize = new Vector3(0.11111f,0.11111f,1.0f); //Starting size of the particle. 
            thisFrothyParticle.transform.localScale = bigStartingSize;
            finalShrinkScale = new Vector3 (1.0f, 1.0f, 1.0f); 
            LinearInitialize ();
            break;
        case MovementType.choose.shrinkbounce:
            finalVector = new Vector3(-10.0f, -10.0f, 0.0f);
            bigStartingSize = new Vector3(1.0f,1.0f,1.0f); //Starting size of the particle.
            thisFrothyParticle.transform.localScale = bigStartingSize;
            finalShrinkScale = new Vector3 (0.11111f, 0.11111f, 1.0f);
            GetSign ();
            LinearInitialize();
            break;
        case MovementType.choose.zigzag: //Will go to two random locations within a zone and then to frothy's mouth. Need to ensure the same distance is traveled w/ each particle to prevent doubling up.
            zigCounter = 0;
            ZigZagInitialize ();
            LinearInitialize ();
            break;
        case MovementType.choose.sigmoid:
            GetSign ();
            sigHeight = Mathf.Abs (finalVector.y - originVector.y);
            sigXmid = originVector.x + xSign*Mathf.Abs (finalVector.x - originVector.x)/2;
            sigSteepness = 0.5f;
            break;
        case MovementType.choose.spiralTop: //Starts as sigmoid to get to top point, and then switches to spiral once it gets to the top point.
            spiralA = 3.5f;
            spiralB = 0.4f;
            beakShift = 1.0f;
            spiralThetaSpeed = particleSpeed/20.0f;
            theta = 1.0f * Mathf.PI;

            spiralCenter = new Vector3 (finalVector.x, (finalVector.y - beakShift),finalVector.z); //Used if base movement on radius

            float startY = (spiralA*Mathf.Exp (spiralB*theta))-(beakShift) + finalVector.y;
            startSpiralVec = new Vector3(finalVector.x, startY, finalVector.z);
            //temporarily set final vector to intermediate point for GetSign()
            finalVector = startSpiralVec;

            GetSign ();
            sigHeight = Mathf.Abs (startSpiralVec.y - originVector.y);
            sigSteepness = 0.5f;
            spiralStarted = false;
            xIsDone = false;

            //Now going to add a linear piece before sigmoidal piece. Just shoot straight forward.
            float linearXFwd = 5.0f;
            finalVector = new Vector3(originVector.x + linearXFwd, originVector.y, originVector.z); //This is hardcoded bs. If want to change direction of boss, will have to make it more robust.
            GetSign ();
            LinearInitialize();
            movingLinearly = true;
            movingSigmoidally = false;

            sigXmid = finalVector.x + xSign*Mathf.Abs (startSpiralVec.x - originVector.x)/2; //finalVector here is where the linear bit stops

            break;
        default:
            break;
        }
        movementInitialized = true;
    }
    
    void Update () 
    {
        if(movementInitialized)
        {
            MovementUpdate();
        }

    }

    void MovementUpdate()
    {

        FrozenCheck();
        if (!frozen)
        {
            distanceThisCycle = particleSpeed * Time.deltaTime;
        }
        else
        {
            distanceThisCycle = 0.0f;
            elapsedFreezeTime += Time.deltaTime;
            if(elapsedFreezeTime >= freezeTime)
            {
                frozen = false;
                elapsedFreezeTime = 0.0f;
            }
        }

        switch (myMovementType)
        {
            
        case MovementType.choose.linear:
            LinearMovement ();
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        case MovementType.choose.parabolic:
            float parabolicSlopeDelta = 2*parabolaFactor*distanceThisCycle; //The slope at the current point, using the total distance as if it were x
            deltaY = Mathf.Sqrt ((Mathf.Pow (distanceThisCycle,2))/(1+(Mathf.Pow (parabolicSlopeDelta,2))));
            nextY = transform.position.y + (ySign * deltaY);
            nextX = parabolaFactor*Mathf.Pow ((nextY - parabolaVertex.y),2) + parabolaVertex.x;
            nextZ = transform.position.z;
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        case MovementType.choose.sinusoidal:
            deltaX = distanceThisCycle * xSign;
            nextX = transform.position.x + deltaX;
            nextY = sineAmplitude*Mathf.Sin (sinePeriodFactor*(transform.position.x + nextX) + originVector.x) + originVector.y + sineAmplitude/1.5f; //The extra bit at the end (+ sineAmplitude/1.5f) is to line things up better with Frothy's mouth.
            nextZ = transform.position.z;
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        case MovementType.choose.ufoTop:
            UFOMove();
            break;
        case MovementType.choose.ufoMid:
            UFOMove();
            break;
        case MovementType.choose.ufoBot:
            UFOMove();
            break;
        case MovementType.choose.bounce:
            LinearMovement ();
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        case MovementType.choose.bounce2:
            if (blobCore != null)
            {
                if(blobCore.GetComponent<particle_move>().ballBounced) //This will switch balls from following core to following their own path. Necessary b/c once middle bounces, top ones still followed.
                {
                    linkedMovement = false;
                    if(ballBounced) //Checking for this particular particle having bounced
                    {
                        originVector = blobCore.GetComponent<particle_move>().originVector; //Will make all particles travel in a line.
                    }
                }
            }
            if (!linkedMovement)
            {
                LinearMovement ();
            }
            else
            {
                string trailingNumberString = gameObject.name.Substring(5);
                int index = int.Parse(trailingNumberString)-1;
                nextX = blobCore.transform.position.x + blobDeltas[index].x;
                nextY = blobCore.transform.position.y + blobDeltas[index].y;
                nextZ = 0.0f;
            }
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        case MovementType.choose.bounce3:
            if (blobCore != null)
            {
                if(blobCore.GetComponent<particle_move>().ballBounced || ballBounced) //If core or this particle itself has bounced
                { 
                    linkedMovement = false;
                    if(ballBounced) //Checking for this particular particle having bounced
                    {
                        originVector = transform.position;
                        particleSettled = true;
                    }
                }
            }
            else //this is the blob core
            {
                if(ballBounced) //Checking for this particular particle having bounced
                {
                    originVector = transform.position;
                    particleSettled = true;
                }
            }

            if (!linkedMovement) //Ball core may have bounced, but since particle not settled, going to continue linearly until we have settled.
            {
                if (!particleSettled)
                {
                    LinearMovement ();
                }
                else //Ball has settled.
                {
                    nextX = transform.position.x; //Not moving.
                    nextY = transform.position.y;
                    nextZ = transform.position.z;
                    timeSettledSoFar += Time.deltaTime;
                    if (timeSettledSoFar >= settleTime && !doneSettling)
                    {
                        doneSettling = true;
                        //Re-initialize for final approach.
                        originVector = transform.position;
                        //Speed up a bit
                        particleSpeed = particleSpeed * 1.5f;
                        GetSign ();
                        LinearInitialize();
                    }
                }
            }
            else  //Movement is linked, so follow the core.
            {
                string trailingNumberString = gameObject.name.Substring(5);
                int index = int.Parse(trailingNumberString)-1;
                nextX = blobCore.transform.position.x + blobDeltas[index].x;
                nextY = blobCore.transform.position.y + blobDeltas[index].y;
                nextZ = 0.0f;
            }

            if (doneSettling)  //Settling complete and now attackFrothy!
            {
                LinearMovement ();
            }

            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        case MovementType.choose.figure8:
            if (!orbitStarted)
            {
                LinearMovement ();
                if(xIsDone && yIsDone)
                {
                    orbitStarted = true;
                }
            }
            else
            {
                if (!orbitComplete)
                {
                    Figure8Movement ();
                }
                else
                {
                    LinearMovement ();
                }
            }
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        case MovementType.choose.linearDouble:
            LinearMovement ();
            //Note special case within LinearMovment for linearDouble case.
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        case MovementType.choose.linearToDoubled:  //Same as regular, but final point is Beak Point 2
            LinearMovement ();
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        case MovementType.choose.figure8Double:  //Right now, exactly the same as figure8... could consider just using a fall through to prevent duplicate code.
            if (!orbitStarted)
            {
                LinearMovement ();
                if(xIsDone && yIsDone)
                {
                    orbitStarted = true;
                }
            }
            else
            {
                if (!orbitComplete)
                {
                    Figure8Movement ();
                }
                else
                {
                    LinearMovement ();
                }
            }
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        case MovementType.choose.shrink:
            ResizeParticle(MovementType.choose.shrink);
            LinearMovement ();
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        case MovementType.choose.shrink2:
            ResizeParticle(MovementType.choose.shrink2);
            LinearMovement ();
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        case MovementType.choose.grow:
            ResizeParticle(MovementType.choose.grow);
            LinearMovement ();
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        case MovementType.choose.shrinkbounce:
            ResizeParticle(MovementType.choose.shrink2);
            LinearMovement ();
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        case MovementType.choose.zigzag: //Will go to two random locations within a zone and then to frothy's mouth. Need to ensure the same distance is traveled w/ each particle to prevent doubling up.
            if (xIsDone && yIsDone)
            {
                zigCounter++;
                if (zigCounter < zigZagPositions.Count)
                {
                    xIsDone = false;
                    yIsDone = false;
                    originVector = zigZagPositions[zigCounter - 1];
                    finalVector = zigZagPositions[zigCounter];
                    GetSign ();
                    LinearInitialize ();
                }
            }
            LinearMovement ();
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        case MovementType.choose.sigmoid:
            SigmoidMovement();
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        case MovementType.choose.spiralTop:

            if (movingLinearly)
            {
                LinearMovement ();
                if (movingLinearly && xIsDone && yIsDone)
                {
                    originVector = finalVector; //We are now at the current finalVector, need to reset to the origin to get the sign.
                    finalVector = startSpiralVec; //Change to new final vector
                    GetSign ();
                    xIsDone = false;
                    yIsDone = false;
                    movingSigmoidally = true;
                    movingLinearly = false;
                }
            }

            if (movingSigmoidally && !xIsDone)
            {
                SigmoidMovement();
                if (xIsDone)
                {
                    spiralStarted = true;
                    finalVector = GameObject.Find ("Beak Point").transform.position;
                    GetSign ();
                }
            }


            if (spiralStarted)
            {
                SpiralMovement();
            }
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            break;
        default:
            //choice for all eternity?
            break;
        }

        if (!particleHover)
        {
            transform.position = nextLocation;
        }

    }

    void FrozenCheck()
    {

        if (freezeGap != 0.0f)
        {
            if (!frozen) //Don't want to add to gap time if particle is already frozen
            {
                elapsedGapTime += Time.deltaTime;
            }
            if (elapsedGapTime >= freezeGap)
            {
                frozen = true;
                elapsedGapTime = 0.0f;
            }
        }
    }

    void UFOMove()
    {
        if(!particleHover)
        {

            if (nextY <= hoverPoint.y)
            {
                nextY = hoverPoint.y;
                particleHover = true; //Have achieved the y-coordinate, so start to hover!
                elapsedHoverTime = 0.0f;
            }
            else
            {
                nextX = hoverPoint.x;
                nextZ = hoverPoint.z; 
                nextY = transform.position.y - distanceThisCycle;
            }
            nextLocation = new Vector3 (nextX,nextY,nextZ);
        }

        if (particleHover && !doneHovering)
        {
            if (elapsedHoverTime >= hoverTime)
            {
                doneHovering = true;
                //Ready to move from hoverPoint to beak now.
                originVector = hoverPoint;
                finalVector = GameObject.Find ("Beak Point").transform.position;
                GetSign ();
                LinearInitialize ();
            }
            else
            {
                elapsedHoverTime += Time.deltaTime;
            }
        }

        if (doneHovering)
        {
            LinearMovement ();
            nextLocation = new Vector3 (nextX,nextY,nextZ);
            transform.position = nextLocation;
        }
    }

    void GetSign()
    {
        if ((originVector.x > finalVector.x))
            xSign = -1;
        else
            xSign = 1;
        if ((originVector.y > finalVector.y))
            ySign = -1;
        else
            ySign = 1;
    }

    void LinearInitialize()
    {
        distanceAlongCurve = Mathf.Sqrt(Mathf.Pow ((finalVector.x - originVector.x),2)+Mathf.Pow ((finalVector.y - originVector.y),2));
        linearSlope = ((finalVector.y - originVector.y)/(finalVector.x - originVector.x));
        linearIntercept = originVector.y - (linearSlope * originVector.x);

        
        //Special case for linearDouble and odd numbers.
        if (myMovementType == MovementType.choose.linearDouble)
        {
            string nameNumberString = gameObject.name.Substring (gameObject.name.Length-1);
            int nameNumber = int.Parse (nameNumberString);
            if (nameNumber % 2 != 0) 
            {
                string leadParticleName = "particle_" + (nameNumber-1);
                leadParticle = GameObject.Find (leadParticleName);
            }
        }

    }

    void LinearMovement()
    {
        if (myMovementType == MovementType.choose.linearDouble && leadParticle != null)
        {
            deltaX = Mathf.Abs (transform.position.x - leadParticle.transform.position.x); //Move same amount in x-direction as lead particle.
        }
        deltaX = Mathf.Sqrt ((Mathf.Pow (distanceThisCycle,2))/(1+(Mathf.Pow (linearSlope,2))));
        nextX = transform.position.x + (xSign * deltaX);
        nextY = (linearSlope * nextX + linearIntercept);
        nextZ = transform.position.z;


        if ((nextX >= finalVector.x && xSign == 1) || (nextX <= finalVector.x && xSign == -1))
        {
            nextX = finalVector.x;
            xIsDone = true;
        }
        if ((nextY >= finalVector.y && ySign == 1) || (nextY <= finalVector.y && ySign == -1))
        {
            nextY = finalVector.y;
            yIsDone = true;
        }
    }

    void SigmoidMovement()
    {
        deltaX = particleSpeed * Time.deltaTime;
        nextX = transform.position.x + (xSign * deltaX);
        if (xSign == 1)
        {
            nextY = (originVector.y + ySign*(sigHeight / ((1 + Mathf.Exp (-1 * sigSteepness * (nextX - sigXmid))))));
        }
        else
        {
            nextY = (originVector.y + ySign*(sigHeight / ((1 + Mathf.Exp (-1 * sigSteepness * (sigXmid - nextX))))));
        }
        nextZ = transform.position.z;
        Debug.Log ("originVector = " + originVector);
        Debug.Log ("nextX = " + nextX);
        Debug.Log ("nextY = " + nextY);
        Debug.Log ("nextZ = " + nextZ);
        if (xSign == 1) //positive x-movment
        {
            if (nextX >= finalVector.x)
                xIsDone = true;
        }
        else
        {
            if (nextX <= finalVector.x)
                xIsDone = true;
        }
    }

    void SpiralMovement()
    {
        float deltaTheta = spiralThetaSpeed * Time.deltaTime;
        theta -= deltaTheta;
        float sinTheta; //Sin result at current theta
        float cosTheta; //cos result at current theta
        float spiralRadius; 
    
        spiralRadius = spiralA * Mathf.Exp (spiralB * theta);

        nextX = spiralRadius * Mathf.Sin (theta) + spiralCenter.x;
        nextY = -1*(spiralRadius * Mathf.Cos (theta)) + spiralCenter.y;
        nextZ = transform.position.z;

        //increase theta speed for next time through
        spiralThetaSpeed = spiralThetaSpeed * 1.01f;
    }

    void Figure8Movement()
    {
        //Use particle speed as an angular speed in equation. r^2 = 2a^2*cos(2*theta)
        float deltaTheta = .5f * Time.deltaTime; //Possibly very very large.
        theta += deltaTheta;
        float thetaZero = 0.0f; //Stores which theta is being used when the curve crosses the origin

        if (theta < (Mathf.PI/4.0f))
        {
            radius = Mathf.Sqrt (2 * Mathf.Pow ((figure8focus), 2) * Mathf.Abs(Mathf.Cos (2*theta)));
            nextX = radius * Mathf.Cos (theta);
            nextY = radius * Mathf.Sin (theta) + figure8vertShift;
        }
        if (theta >= (Mathf.PI / 4.0f) && theta < (Mathf.PI / 2.0f)) 
        {
            radius = Mathf.Sqrt (2 * Mathf.Pow ((figure8focus), 2) * Mathf.Abs(Mathf.Cos (2*theta)));
            nextX = -1*(radius * Mathf.Sin (theta));
            nextY = -1*(radius * Mathf.Cos (theta)) + figure8vertShift;
            thetaZero = Mathf.PI/4.0f;
        }
        if (theta >= (Mathf.PI / 2.0f) && theta < (Mathf.PI*(3.0f/4.0f))) 
        {
            radius = Mathf.Sqrt (2 * Mathf.Pow ((figure8focus), 2) * Mathf.Abs(Mathf.Cos (2*theta)));
            nextX = -1*(radius * Mathf.Sin (theta));
            nextY = -1*(radius * Mathf.Cos (theta)) + figure8vertShift;
            thetaZero = Mathf.PI/2.0f;
        }
        if (theta >= (Mathf.PI*(3.0f/4.0f)) && theta < (Mathf.PI)) 
        {
            radius = Mathf.Sqrt (2 * Mathf.Pow ((figure8focus), 2) * Mathf.Abs(Mathf.Cos (2*theta)));
            nextX = -1*(radius * Mathf.Cos (theta));
            nextY = -1*(radius * Mathf.Sin (theta)) + figure8vertShift;
            thetaZero = Mathf.PI*(3.0f/4.0f);
        }
        if (theta >= (Mathf.PI)) 
        {
            theta = 0.0f;
            onePassComplete = true;
        }

        if ((theta >= (Mathf.PI / 6.0f)) && onePassComplete) 
        {
            orbitComplete = true;
            originVector = new Vector3(nextX, nextY, 0.0f);
            finalVector = GameObject.Find ("Beak Point").transform.position;
            if (myMovementType == MovementType.choose.figure8Double) //particles going to duplicate frothy need to... go to duplicate frothy.
            {
                finalVector = GameObject.Find ("Beak Point 2").transform.position;
            }
            xIsDone = false;
            yIsDone = false;
            particleSpeed = particleSpeed * 1.5f; //Speed these up for a final blast!
            GetSign ();
            LinearInitialize ();
        }
    }

    void OnTriggerEnter2D(Collider2D other)
    {
        if (other.GetComponent<GroundBlock> () != null && ballBounced == false) //The object we are colliding with is a ground block, so make it 'bounce'
        {
            if (myMovementType == MovementType.choose.bounce2 || myMovementType == MovementType.choose.bounce3)
            {
                linkedMovement = false;
                ballBounced = true;
                GetRestingPosition(other);
            }
            finalVector = GameObject.Find ("Beak Point").transform.position;
            originVector = transform.position;
            GetSign ();
            LinearInitialize ();
            //Still calling LinearMovement() in the Update function, so movement should just shift to the new trajectory.
        }

    }

    void GetRestingPosition(Collider2D other)
    {
            
        //Assign an x-position relative to the core
        if (blobCore == null) //This is the blob core.
        {
            nextX = transform.position.x; //Sets x-position for all balls
        }
        else //Not the blob core, so will be placed based on the core.
        {
            string trailingNumberString = gameObject.name.Substring(5);
            int index = int.Parse(trailingNumberString);
            nextX = blobCore.transform.position.x + blobDeltas[index-1].x;
            if (index == 1 || index == 4)
            {
                nextX = blobCore.transform.position.x + blobDeltas[0].x*Mathf.Sign (blobDeltas[index].y)/2.0f; //complicated way to use half of the x-dimension and put one on pos and one on neg
            }
        }


        nextZ = transform.position.z; // ditto
        nextY = -4.58f; // Manually placed a particle at top of ground and found this number. Could do fancier means.
        transform.position = new Vector3(nextX, nextY, nextZ);
    }

    void ResizeParticle(MovementType.choose type)
    {

        Vector3 currentScale = thisFrothyParticle.transform.localScale;
        float newScaleX;
        float newScaleY;
        float shrinkSpeed = (bigStartingSize.x - finalShrinkScale.x) / (distanceAlongCurve / particleSpeed);  //Calculates shrinking speed with other variables.

 

        switch (type)
        {
        case MovementType.choose.shrink:
            newScaleX = thisFrothyParticle.transform.localScale.x -(Time.deltaTime * shrinkSpeed);
            newScaleY = thisFrothyParticle.transform.localScale.y -(Time.deltaTime * shrinkSpeed);
            thisFrothyParticle.transform.localScale = new Vector3 (newScaleX, newScaleY, 1.0f);
            break;
        case MovementType.choose.shrink2:
            newScaleX = thisFrothyParticle.transform.localScale.x -(Time.deltaTime * shrinkSpeed);
            newScaleY = thisFrothyParticle.transform.localScale.y -(Time.deltaTime * shrinkSpeed);
            if (newScaleX < finalShrinkScale.x && myMovementType == MovementType.choose.shrinkbounce) //So particle doesn't get smaller and then bigger. 
            {
                newScaleX = finalShrinkScale.x;
                newScaleY = finalShrinkScale.y;
            }
            thisFrothyParticle.transform.localScale = new Vector3 (newScaleX, newScaleY, 1.0f);
            break;
        case MovementType.choose.grow:
            newScaleX = thisFrothyParticle.transform.localScale.x -(Time.deltaTime * shrinkSpeed);
            newScaleY = thisFrothyParticle.transform.localScale.y -(Time.deltaTime * shrinkSpeed);
            thisFrothyParticle.transform.localScale = new Vector3 (newScaleX, newScaleY, 1.0f);
            break;
        default:
            break;
        }

    }

    void ZigZagInitialize()
    {
        //Using origin (spawner location) and final location (beak location) (both by default)
        //we will determine a good sized "box" for the particle inflection points to be within.
        float firstMaxDeltaX = 15.0f; //The max pos OR negative from frothy beak x that the particle can have an inflection point.
        float firstMinDeltaX = 8.0f; //The min distance from beak that a point can be picked.
        float secondMaxDeltaX = 7.0f;
        float secondMinDeltaX = 2.0f;
        float maxDeltaY = 5.0f; //The max pos AND negative from frothy beak y that the particle can have an inflection point.
        float minDeltaY = -5.0f;
        float zigX;
        float zigY;
        float distanceOfZig1;
        float distanceOfZig2;
        float particleWidth = 0.7875f * 2.0f; //Width in unity units of each particle. Value used is the cirlce collider radius x 2

        //Note that biggestZigDistance is reset to zero at each level transition;

        zigZagPositions.Add (originVector); //Add the origin as position zero.
        //Pick first random point.
        zigX = -1*xSign*Random.Range (firstMinDeltaX, firstMaxDeltaX);
        zigY = Random.Range (minDeltaY, maxDeltaY);
        zigZagPositions.Add (new Vector3(zigX,zigY,0.0f));
        distanceOfZig1 = Mathf.Sqrt(Mathf.Pow ((zigZagPositions[1].x - originVector.x),2)+Mathf.Pow ((zigZagPositions[1].y - originVector.y),2));
        zigX = -1*xSign*Random.Range (secondMinDeltaX, secondMaxDeltaX);
        zigY = -1*ySign*Random.Range (minDeltaY, maxDeltaY);
        zigZagPositions.Add (new Vector3 (zigX, zigY, 0.0f));
        distanceOfZig2 = Mathf.Sqrt(Mathf.Pow ((zigZagPositions[2].x - zigZagPositions[1].x),2)+Mathf.Pow ((zigZagPositions[2].y - zigZagPositions[1].y),2));
        while ((distanceOfZig1 + distanceOfZig2 + 8*particleWidth) <= biggestZigDistance) //ensure total distance is always increasing from one particle to the next. Will prevent overlaps.
        {
//            zigX = zigX + (biggestZigDistance - distanceOfZig1 - distanceOfZig2); //Picked a random way to increase the values here.
//            zigY = zigY + (biggestZigDistance - distanceOfZig1 - distanceOfZig2);
            zigX = zigX*1.1f;
            zigY = zigY*1.1f;
            //need to make sure still within bounding box max and min
            if (Mathf.Abs (zigX) > secondMaxDeltaX)
            {
                zigX = secondMaxDeltaX * Mathf.Sign (zigX); //Will be the pos or neg of the limit as determined by the random zig
            }
            if (Mathf.Abs (zigY) > maxDeltaY)
            {
                zigY = maxDeltaY * Mathf.Sign (zigY); //Will be the pos or neg of the limit as determined by the random zig
            }
            zigZagPositions[2] = new Vector3(zigX,zigY,0.0f);
            distanceOfZig2 = Mathf.Sqrt(Mathf.Pow ((zigZagPositions[2].x - zigZagPositions[1].x),2)+Mathf.Pow ((zigZagPositions[2].y - zigZagPositions[1].y),2));
        }
        if ((distanceOfZig1 + distanceOfZig2) > biggestZigDistance)
        {
            biggestZigDistance = distanceOfZig1 + distanceOfZig2;
        }
        zigZagPositions.Add (finalVector); //Add the final vector (beak point) as the last item in the list.
        finalVector = zigZagPositions [1]; //Set the "finalVector" to the end of the first zigzag.

    }

}