I put together a simple script that creates a sprite-based pointer I can use in UI. It's not complete yet (still needs support for when the player pushes a button), but already running into problems.
I get a NullReferenceException: Object reference not set to an instance of an object
At line 52...
_ptrSpriteRenderer.sprite = newSprite;
...and 72
_ptr.transform.position = new Vector2(((float)_ptrPosX * _ptrPositionXDistance) + _ptrPositionTopLeft.x, ((float)_ptrPosY * _ptrPositionYDistance) + _ptrPositionTopLeft.x);
I am creating _ptr and _ptrSpriteRenderer in the Start, but for some reason at all my other functions these two GameObjects are null, and yet they aren't null during Start.
I'm sure it's something simple that I goofed up on, but I've spent hours comparing this class to other classes where I've created sprites and I can't see the issue.
using UnityEngine;
using System.Collections;
public class Pointer : MonoBehaviour {
private GameObject _ptr;
private Sprite _ptrSprite;
private SpriteRenderer _ptrSpriteRenderer;
private bool _ptrEnabled; // Is the pointer receiving input?
private int _ptrMovePerSecond; // Number of positions to move per second if input held down
private int _ptrXInput;
private int _ptrYInput;
private float _ptrTimeSinceLastInput = 0f;
private float _ptrTimePerInput = 0.25f;
private bool _ptrInputDelay = false;
private Vector2 _ptrPositionTopLeft; //The top left position the pointer can reach in the grid
private Vector2 _ptrPositionBottomRight; //The bottom right position the pointer can reach in the grid
private int _ptrPositionsX; //The number of grid positions the pointer can traverse in X
private int _ptrPositionsY; //The number of grid positions the pointer can traverse in Y
private float _ptrPositionXDistance; //The distance of each X position in the grid
private float _ptrPositionYDistance; //The distance of each Y position in the grid
private int _ptrPosX; //Current X position of pointer in the grid
private int _ptrPosY; //Current Y position of pointer in the grid
// Use this for initialization
void Start () {
_ptr = new GameObject();
_ptrSpriteRenderer = new SpriteRenderer();
_ptr.AddComponent<SpriteRenderer>();
_ptrSpriteRenderer = _ptr.GetComponent<SpriteRenderer>();
_ptrEnabled = true;
}
public void setSprite ( Sprite newSprite )
{
if (newSprite == null)
{
Debug.LogError("No sprite passed to setSprite in Pointer");
}
else
{
_ptrSpriteRenderer.sprite = newSprite;
}
}
public void setPositions (Vector2 positionTopLeft, Vector2 positionBottomRight, int numPositionsX, int numPositionsY)
{
_ptrPositionsX = numPositionsX;
_ptrPositionsY = numPositionsY;
_ptrPositionTopLeft = positionTopLeft;
_ptrPositionBottomRight = positionBottomRight;
_ptrPositionXDistance = Mathf.Abs((positionBottomRight.x - positionTopLeft.x) / numPositionsX);
_ptrPositionYDistance = Mathf.Abs((positionBottomRight.y - positionTopLeft.y) / numPositionsY);
}
public void setPosition (int x, int y)
{
_ptrPosX = x;
_ptrPosY = y;
_ptr.transform.position = new Vector2(((float)_ptrPosX * _ptrPositionXDistance) + _ptrPositionTopLeft.x, ((float)_ptrPosY * _ptrPositionYDistance) + _ptrPositionTopLeft.x);
}
// Update is called once per frame
void Update () {
//Is the pointer enabled?
if (_ptrEnabled)
{
if (_ptrInputDelay)
{
_ptrTimeSinceLastInput += Time.deltaTime;
if (_ptrTimeSinceLastInput >= _ptrTimePerInput)
{
_ptrInputDelay = false;
}
}
if (_ptrInputDelay == false)
{
_ptrXInput = (int)Input.GetAxis("Horizontal");
_ptrYInput = (int)Input.GetAxis("Vertical");
if (_ptrXInput != 0 || _ptrYInput != 0)
{
_ptrPosX += _ptrXInput;
_ptrPosY += _ptrYInput;
Debug.Log("WHEE");
if (_ptrPosX < 0) _ptrPosX = 0;
if (_ptrPosX > _ptrPositionsX) _ptrPosX = _ptrPositionsX;
if (_ptrPosY < 0) _ptrPosY = 0;
if (_ptrPosY > _ptrPositionsY) _ptrPosY = _ptrPositionsY;
_ptr.transform.position = new Vector2(((float)_ptrPosX * _ptrPositionXDistance) + _ptrPositionTopLeft.x, ((float)_ptrPosY * _ptrPositionYDistance) + _ptrPositionTopLeft.x );
_ptrInputDelay = true;
_ptrTimeSinceLastInput = 0f;
}
}
}
}
}
And the place where my Pointer class is being called is done like this:
GameObject newPointer = new GameObject();
newPointer.AddComponent<Pointer>();
Pointer newPointerScript = newPointer.GetComponent<Pointer>();
newPointerScript.setPositions(new Vector2(-1f, -1f), new Vector2(1f, 1f), 3, 3);
newPointerScript.setSprite(newWeapon);
newPointerScript.setPosition(1, 1);
These lines look wrong:
_ptrSpriteRenderer = new SpriteRenderer();
_ptr.AddComponent<SpriteRenderer>();
_ptrSpriteRenderer = _ptr.GetComponent<SpriteRenderer>();
You are creating a SpriteRenderer and then two lines later overwriting the value with what's in _ptr which is, in all probability, null.
Do you really need this line?
Also if you're adding a component shouldn't you be actually passing the component into the Add method?
So turns out everything works fine if I switch Start() with Awake(). That's all that was needed.
Related
I am trying to Instatiate Objects with the code attached but only the first Object has the Method awake called and doesent get any values out of orbitValues. The other Objects get instantiated but they also dont get any values. Can you help please?
if (listChecked == true & objectsCreated == false)
{
objectsCreated = true;
for (int i = 0; i < 5 + 1; i++)
{
Ellipse orbitValues = new Ellipse(float.Parse(orbitList[i].attributes.inc), float.Parse(orbitList[i].attributes.sma), float.Parse(orbitList[i].attributes.ecc), float.Parse(orbitList[i].attributes.aPer));
Debug.Log("i: " + i + " aPer: " + orbitValues.aPer);
var tempOrbiter = Instantiate(orbit, Vector3.zero, transform.rotation);
tempOrbiter.GetComponent<OrbitMotion>().orbitPath = orbitValues;
tempOrbiter.GetComponent<OrbitMotion>().id = i;
tempOrbiter.transform.rotation = Quaternion.Euler(0, 0, orbitValues.inc);
tempOrbiter.transform.parent = GameObject.FindGameObjectWithTag("OrbiterGroup").transform;
}
}
The class Ellipse:
public class Ellipse
{
public float inc;
public float sma;
public float ecc;
public float aPer;
public float c;
public float e;
public float xAxis;
public float zAxis;
float x;
float z;
public Ellipse(float inc, float sma, float ecc, float aPer)
{
this.inc = inc;
this.sma = sma;
this.ecc = ecc;
this.aPer = aPer;
} ...
and the script that should be with every instantiated object:
public class OrbitMotion : MonoBehaviour
{
public Transform orbitingObject;
public Ellipse orbitPath;
public int id;
[Range(0f,1f)]
public float orbitProgress = 0f;
public float orbitPeriod = 60f;
public bool orbitActive = false;
public float xOffset = 0;
public float zOffset = 0;
void Awake()
{
Debug.Log("id: " + id);
Debug.Log(orbitPath.aPer);
if (orbitingObject == null)
{
orbitActive = false;
return;
}
orbitingObject = transform.GetChild(0).transform;
transform.parent = GameObject.Find("OrbiterGroup").transform;
transform.localRotation = Quaternion.Euler(0, 0, orbitPath.inc);
transform.localRotation = Quaternion.Euler(0, orbitPath.aPer, 0);
xOffset = (Mathf.Sin(orbitPath.aPer) * (orbitPath.sma - orbitPath.e));
zOffset = (Mathf.Cos(orbitPath.aPer) * (orbitPath.sma - orbitPath.e));
Debug.Log(orbitPath.e);
Debug.Log(xOffset);
transform.position = new Vector3(xOffset, 0, zOffset) / 1000;
SetOrbitingObjectPosition();
StartCoroutine(AnimateOrbit());
gameObject.GetComponent<EllipseRenderer>().ellipse = orbitPath;
}...
You're instantiating them, then Awake() gets called, then you update the script values then, before the next Update() call, Start() is called.
Awake() is getting called on ALL of the instantiated objects, but it's getting called as soon as the prefab is instantiated, before you set any values. This means that the id for all of them is the same, and it's defaulting to 0, so it probably just looks like the first one is getting called (especially if you're collapsing the notifications in the console window).
Moving the work to Start() gets the functionality you want because Start() gets called later, before the first Update() call. Awake() is invoked immediately on construction/instantiation. You don't have time to update anything between instantiation and Awake() getting called.
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 2 years ago.
I'm trying to get a Gesture Recognizer to work on unity 3D. The script let's me choose if I'm recording a gesture or comparing to existing ones if not recording.
The problem is
On the first run, it works perfectly: It lets me draw and record gestures and then, after disabling recording, it compares to existing ones to look for a match on a list (templates.templates).
But then after I close the game and re-open it, it debugs "loading successful" and I can see the list of the templates in the Inspector, but it returns an error of NullRefferenceException for the templates or current gesture. I'm losing my mind here. Can you help me notice what behaves differently from the first run and second (after loading the templates from json file) and figure this thing out?
The script is as follows (I serialized every private thing just because...):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.IO;
//******************************************* Gesture Recognizer *********************************************//
//
// Author: Andres De La Fuente Duran
//
// Use: This script can simply be attached to a camera to function.
// It allows for recording of 2D 'gestures', in this case meaning a set of points
// representing a continuous motion of the mouse (or touch input).
// It also allows for testing 'gestures' against a collection of templates created
// with this very same script.
//
// Template gestures are saved in a JSON file and loaded into a list at start.
//
// The way the recognition works is that a list of points is recorded (relative to
// the location of the initial click as (0,0)), scaled to a square resolution of
// choice, reduced by interpolation to a set number of points, and then compared
// to gestures already processed by this script.
//
// This is built for maximum customizability, so you can change the number of points
// allowed when recording, the number of points once reduced, the rate of sampling,
// and the square ratio gestures are scaled to. Recording the gestures and testing
// can be done easily by swithching the 'recording' boolean variable.
//
// Some additional notes: Because the origin of each gesture is the initial
// point, and comparison follows points in order of
// recording, directionality is captured by this
// solution. The gestures do not have to be wildly
// different for the recognition to be reliable.
//
// However, you can turn on 'anomaliesTesting' to
// weight more heavily sudden differences in gestures
// than constant differences to allow for similar
// gestures with small modifications or flares.
//****************************************** Recognizer Class ****************************************************//
//
// Use: Stores all information for the current gesture being recorded, the existing gestures,
// the conditions selected by an editor user, and variables needed to perform recognition.
// This is the central class with most of the functionality in the script.
//
// Fields:
// Editor Controlled............................................................................
//
// recording: boolean to control whether to save a gesture or try to recognize it
//
// anomaliesTesting: boolean to control whether to weight sudden differences during
// comparison more than other differences
//
// pointsPerGesture: the size of the array of points stored for each gesture
//
// templateSaveName: the string name of the gesture to be saved when recording
//
// samplingRate: time interval between samples while recording
//
// maxPointsAllowed: the maximum number of points that will be recorded
//
// standardRatio: the size of one side of the square that points will be scaled to
//
// devTightness: the number of deviations from the average difference between the points
// of two gestures that are allowed before they are weighted more
//
// anomaliesFactor: how much extra to weight the differences that surpass the devTightness
//
// Control Flow................................................................................
//
// gestureStarted: boolean to execute code to start gesture and to avoid starting anew
//
// gestureComplete: boolean to execute recording of gesture until complete
//
// inputReady: boolean to prevent execution of anything until input is lifted
// so as not to start gestures immediately after one is complete
//
// Recording and Recognizing...................................................................
//
// gestureFileName: JSON file to load saved gestures from as templates for recognition
//
// startPoint: the initial point from which to calculate every other point
//
// currentPoint: the last point recorded
//
// currentGesture: the object containing the recorded gesture for current execution
//
// currentPointList: list of points as they are recorded
//
// reducedPoints: array of points for after scaling and mapping of currentPointList
//
// templates: object to store list of template gestures
//
// tempTime: time since last sample
//
// Methods: Documentation is above each significant function
public class GestureRecognizer : MonoBehaviour {
public bool recording = true;
public bool anomaliesTesting = false;
public string templateSaveName;
public int pointsPerGesture = 30;
public float samplingRate = 0.01f;
public bool limitSamples = false;
public int maxPointsAllowed = 100;
public float standardRatio = 100f;
public float devTightness = 1f;
public float anomaliesFactor = 5f;
[SerializeField]private bool gestureStarted;
[SerializeField] private bool gestureComplete;
[SerializeField] private bool inputReady;
[SerializeField] private string gestureFileName = "gestures.json";
[SerializeField] private TwoDPoint startPoint;
[SerializeField] private TwoDPoint currentPoint;
[SerializeField] private DrawnGesture currentGesture;
[SerializeField] private List<TwoDPoint> currentPointList;
[SerializeField] private TwoDPoint[] reducedPoints;
[SerializeField] private GestureTemplates templates;
[SerializeField] private float tempTime = 0f;
private void Awake()
{
}
void Start () {
LoadTemplates();
varInitialization();
}
#region variable initialization and reset
private void varInitialization()
{
currentPoint = new TwoDPoint(0, 0);
startPoint = new TwoDPoint(0, 0);
currentPointList = new List<TwoDPoint>();
currentPointList.Add(new TwoDPoint(0, 0));
reducedPoints = new TwoDPoint[pointsPerGesture];
for (int i = 0; i < pointsPerGesture; i++)
{
reducedPoints[i] = new TwoDPoint(0, 0);
}
gestureStarted = false;
gestureComplete = false;
inputReady = false;
currentGesture = new DrawnGesture("currentGesture", pointsPerGesture);
}
private void varReset()
{
for (int i = 0; i < pointsPerGesture; i++)
{
reducedPoints[i].SetX(0);
reducedPoints[i].SetY(0);
}
currentPointList.Clear();
currentPointList.Add(new TwoDPoint(0,0));
gestureStarted = false;
gestureComplete = false;
}
#endregion
void Update() {
tempTime += Time.deltaTime;
if (Input.GetMouseButton(0))
{
if (inputReady)
{
if (!gestureStarted)
{
gestureStarted = true;
StartGesture();
}
if ((!gestureComplete) && (tempTime > samplingRate))
{
tempTime = 0f;
ContinueGesture();
}
if (gestureComplete)
{
EndGesture();
}
}
} else
{
if (gestureStarted)
{
EndGesture();
}
inputReady = true;
}
}
//******************************************
// Save and Load Gestures
//
// SaveTemplates
// use: writes templates to json file
// LoadTemplates
// use: called on start to read json templates
// object from file if it's there
[SerializeField]
private void SaveTemplates()
{
string filePath = Application.dataPath + "/StreamingAssets/" + gestureFileName;
string saveData = JsonUtility.ToJson(templates);
File.WriteAllText(filePath, saveData);
Debug.Log("Template Saved!");
}
[SerializeField]
private void LoadTemplates()
{
templates = new GestureTemplates();
string filePath = Path.Combine(Application.streamingAssetsPath, gestureFileName);
if (File.Exists(filePath))
{
string data = File.ReadAllText(filePath);
templates = JsonUtility.FromJson<GestureTemplates>(data);
Debug.Log("Templates Loaded!");
}
}
//***************************************
// StartGesture
//
// use: Set up recording of gesture by
// setting the start point and control bool.
// Called when player first clicks.
[SerializeField]
private void StartGesture()
{
Debug.Log("gesture started");
startPoint.SetX(Input.mousePosition.x);
startPoint.SetY(Input.mousePosition.y);
gestureComplete = false;
}
//***************************************
// ContinueGesture
//
// use: Update min and max x and y values for
// the current gesture being recorded
// and add the new point to the list.
// Called while player holds input down.
[SerializeField]
private void ContinueGesture()
{
currentPoint.SetX(Input.mousePosition.x - startPoint.GetX());
currentPoint.SetY(Input.mousePosition.y - startPoint.GetY());
currentPointList.Add(new TwoDPoint(currentPoint.GetX(), currentPoint.GetY()));
if (currentPoint.GetX() > currentGesture.GetMaxX())
{
currentGesture.SetMaxX(currentPoint.GetX());
}
if (currentPoint.GetX() < currentGesture.GetMinX())
{
currentGesture.SetMinX(currentPoint.GetX());
}
if (currentPoint.GetY() > currentGesture.GetMaxY())
{
currentGesture.SetMaxY(currentPoint.GetY());
}
if (currentPoint.GetY() < currentGesture.GetMinY())
{
currentGesture.SetMinY(currentPoint.GetY());
}
if (limitSamples && currentPointList.Count >= maxPointsAllowed)
{
gestureComplete = true;
Debug.Log(message: "Gesture Complete!");
}
}
//***************************************
// EndGesture
//
// use: Resets control bools and other variables
// records gesture to the templates object
// or calls recognition.
// Called when max recording points reached.
[SerializeField]
private void EndGesture()
{
if (inputReady) inputReady = false;
gestureStarted = false;
gestureComplete = true;
Rescale(currentGesture);
MapPoints(currentGesture);
if (recording)
{
currentGesture.SetName(templateSaveName);
templates.templates.Add(new DrawnGesture(currentGesture.GetName(), pointsPerGesture, currentGesture.GetMaxX(), currentGesture.GetMaxY(),
currentGesture.GetMinX(), currentGesture.GetMinY(), currentGesture.GetPoints()));
} else
{
DrawnGesture m = FindMatch(currentGesture, templates);
Debug.Log(m.GetName());
}
varReset();
}
[SerializeField]
private void OnApplicationQuit()
{
SaveTemplates();
}
//***************************************
// Rescale
//
// use: scales recorded list of points to a square field
// of a chosen size by multiplication of the factor
// of the desired size it already is
// Called on every gesture after recording
[SerializeField]
private void Rescale(DrawnGesture gesture)
{
float scale = 1f;
float xrange = gesture.GetMaxX() - gesture.GetMinX();
float yrange = gesture.GetMaxY() - gesture.GetMinY();
if (xrange >= yrange)
{
scale = standardRatio / (gesture.GetMaxX() - gesture.GetMinX());
} else
{
scale = standardRatio / (gesture.GetMaxY() - gesture.GetMinY());
}
if (scale != 1)
{
foreach (TwoDPoint point in currentPointList)
{
point.SetX(point.GetX() * scale);
point.SetY(point.GetY() * scale);
}
}
}
//***************************************
// MapPoints
//
// use: maps the list of recorded points to a desired
// number of points by calculating an even distance
// between such a number of points and interpolating
// when that distance is reached upon traversal of the
// list
// Called after scaling on every gesture
//
// param: gesture: the object to store the new array
[SerializeField]
private void MapPoints(DrawnGesture gesture)
{
reducedPoints[0].SetX(currentPointList[0].GetX());
reducedPoints[0].SetY(currentPointList[0].GetY());
int newIndex = 1;
float totalDistance = TotalDistance();
float coveredDistance = 0;
float thisDistance = 0;
float idealInterval = totalDistance / pointsPerGesture;
for (int i = 0; i < currentPointList.Count - 1; i++)
{
thisDistance = PointDistance(currentPointList[i], currentPointList[i + 1]);
bool passedIdeal = (coveredDistance + thisDistance) >= idealInterval;
if (passedIdeal)
{
TwoDPoint reference = currentPointList[i];
while (passedIdeal && newIndex < reducedPoints.Length)
{
float percentNeeded = (idealInterval - coveredDistance) / thisDistance;
if (percentNeeded > 1f) percentNeeded = 1f;
if (percentNeeded < 0f) percentNeeded = 0f;
float new_x = (((1f - percentNeeded) * reference.GetX()) + (percentNeeded * currentPointList[i + 1].GetX()));
float new_y = (((1f - percentNeeded) * reference.GetY()) + (percentNeeded * currentPointList[i + 1].GetY()));
reducedPoints[newIndex] = new TwoDPoint(new_x, new_y);
reference = reducedPoints[newIndex];
newIndex++;
thisDistance = (coveredDistance + thisDistance) - idealInterval;
coveredDistance = 0;
passedIdeal = (coveredDistance + thisDistance) >= idealInterval;
}
coveredDistance = thisDistance;
} else
{
coveredDistance += thisDistance;
}
gesture.SetPoints(reducedPoints);
}
}
//***************************************
// FindMatch
//
// use: determines template gesture with the minimum
// average distance between points to the
// currently recorded gesture
// Called after finishing a gesture when not
// recording
//
// param: playerGesture: current gesture to be matched
// templates: object containting list of
// gestures to compare against
//
// return: returns gesture object of the minimum
// difference template
[SerializeField]
private DrawnGesture FindMatch(DrawnGesture playerGesture, GestureTemplates templates)
{
float minAvgDifference = float.MaxValue;
DrawnGesture match = new DrawnGesture("no match", pointsPerGesture);
foreach(DrawnGesture template in templates.templates)
{
Debug.Log(template.GetName());
float d = AverageDifference(playerGesture, template);
Debug.Log(d.ToString());
if (d < minAvgDifference)
{
minAvgDifference = d;
match = template;
}
}
return match;
}
//***************************************
// AverageDifference
//
// use: caluclates the average distance between
// the points of two gestures
//
// param: playerGesture: first to be compared
// template: gesture to be compared against
//
// return: returns float value of the average distance
// between points of two parameter gestures
[SerializeField]
private float AverageDifference(DrawnGesture playerGesture, DrawnGesture template)
{
int numPoints = playerGesture.GetNumPoints();
if (numPoints != template.GetNumPoints())
{
Debug.Log("Number of points differs from templates");
return -1f;
}
float totalDifference = 0;
for (int i = 0; i < numPoints; i++)
{
totalDifference += PointDistance(playerGesture.GetPoints()[i], template.GetPoints()[i]);
}
return (totalDifference / numPoints);
}
//***************************************
// AverageDistanceWithAnomalies
//
// use: calculates the average difference between
// the points of two gestures but weighing
// those which deviate significantly by
// multiplying them
// Both the tightness of this and the factor
// of multiplication are customizable
// above
//
// param: playerGesture: first to be compared
// template: gesture to be compared against
//
// return: returns float value of the average distance
// between points of two parameter gestures
// with weights
[SerializeField]
private float AverageDifferenceWithAnomalies(DrawnGesture playerGesture, DrawnGesture template)
{
int numPoints = playerGesture.GetNumPoints();
if (numPoints != template.GetNumPoints())
{
Debug.Log("Number of points differs from templates");
return -1f;
}
float totalDifference = 0;
float[] sampleDifferences = new float[numPoints];
float[] sampleDeviations = new float[numPoints];
float standardDev = 0;
for (int i = 0; i < numPoints; i++)
{
float thisDistance = PointDistance(playerGesture.GetPoints()[i], template.GetPoints()[i]);
sampleDifferences[i] = thisDistance;
totalDifference += thisDistance;
}
float average = totalDifference / numPoints;
for (int i = 0; i < numPoints; i++)
{
sampleDeviations[i] = Math.Abs(sampleDifferences[i] - average);
standardDev += sampleDifferences[i];
}
standardDev = standardDev / numPoints;
for (int i = 0; i < numPoints; i++)
{
if (Math.Abs(sampleDeviations[i]) > devTightness * standardDev)
{
totalDifference -= sampleDifferences[i];
totalDifference += anomaliesFactor * sampleDifferences[i];
}
}
average = totalDifference / numPoints;
return (average);
}
//***************************************
// TotalDistance
//
// use: calculates the total distance covered
// when traversing the current list of recorded
// points in order of recording
// Called when determining ideal intervals
// for mapping onto desired number of points
[SerializeField]
private float TotalDistance()
{
float totalDistance = 0;
for(int i = 0; i < currentPointList.Count - 1; i++)
{
totalDistance += PointDistance(currentPointList[i], currentPointList[i + 1]);
}
Debug.Log("total distance: " + totalDistance);
return totalDistance;
}
//***************************************
// PointDistance
//
// use: calculates the absolute value of the distance
// between two points using pythagorean theorem
[SerializeField]
private float PointDistance(TwoDPoint a, TwoDPoint b)
{
float xDif = a.GetX() - b.GetX();
float yDif = a.GetY() - b.GetY();
return Mathf.Sqrt((xDif * xDif) + (yDif * yDif));
}
}
//******************************************************* Templates ******************************************************//
//
// Use: Groups gestures to be used for comparison to a player's attempts
[Serializable]
public class GestureTemplates
{
public List<DrawnGesture> templates;
public GestureTemplates()
{
templates = new List<DrawnGesture>();
}
}
//******************************************************** Gestures ******************************************************//
//
// Use: Groups all information pertinent to a 'gesture'
// which is essentially a single stroke drawing represented by points
//
// Fields: points: list of points representing the gesture, only populated once a hand drawn gesture is
// reduced by the MapPoints method
//
// min/max: these are the minimum and maximum x and y values of the points (starting point
// is used as the origin)
//
// numPoints: the size of the points array (set to a variable of the GestureRecognizer class to
// keep control there)
//
// name: string that will be returned when matched with a non-recorded gesture
//
// Methods: Initializer(2 parameters): use when creating a new gesture for later use
//
// Initializer(7 parameters): use when copying data from another gesture
//
// Reset: for use in clearing the gesture used for each player gesture attempt
[Serializable]
public class DrawnGesture
{
[SerializeField]private TwoDPoint[] points;
[SerializeField] private string name;
[SerializeField] private float maxX;
[SerializeField] private float minX;
[SerializeField] private float maxY;
[SerializeField] private float minY;
[SerializeField] private int numPoints;
public DrawnGesture(string newName, int pointsPerGesture)
{
numPoints = pointsPerGesture;
points = new TwoDPoint[numPoints];
name = newName;
maxX = 0;
maxY = 0;
}
public DrawnGesture(string newName, int pointsPerGesture, float max_x, float max_y, float min_x, float min_y, TwoDPoint[] newPoints)
{
numPoints = pointsPerGesture;
points = new TwoDPoint[numPoints];
SetPoints(newPoints);
name = newName;
maxX = max_x;
minX = min_x;
maxY = max_y;
minY = min_y;
}
public void Reset()
{
maxX = 0;
minX = 0;
maxY = 0;
minY = 0;
name = "";
Array.Clear(points, 0, numPoints);
}
public TwoDPoint[] GetPoints()
{
return points;
}
public void SetPoints(TwoDPoint[] new_points)
{
for(int i = 0; i < numPoints; i++)
{
points[i] = new TwoDPoint(new_points[i].GetX(), new_points[i].GetY());
}
}
public string GetName()
{
return name;
}
public void SetName(string n)
{
name = n;
}
public float GetMaxX()
{
return maxX;
}
public void SetMaxX(float x)
{
maxX = x;
}
public float GetMaxY()
{
return maxY;
}
public void SetMaxY(float y)
{
maxY = y;
}
public float GetMinY()
{
return minY;
}
public void SetMinY(float y)
{
minY = y;
}
public float GetMinX()
{
return minX;
}
public void SetMinX(float x)
{
minX = x;
}
public int GetNumPoints()
{
return numPoints;
}
public void SetNumPoints(int n)
{
numPoints = n;
}
}
//******************************************************** Points ********************************************************//
//
// Use: This is a class to maintain 2D coordinates
//
// Fields: x: the x coordinate (relative to the first point when recorded)
// y: the y coordinate (also relative to first point)
public class TwoDPoint
{
private float x;
private float y;
public TwoDPoint(float startx, float starty)
{
x = startx;
y = starty;
}
public float GetX()
{
return x;
}
public void SetX(float new_x)
{
x = new_x;
}
public float GetY()
{
return y;
}
public void SetY(float new_y)
{
y = new_y;
}
}
The error is as follows:
NullReferenceException: Object reference not set to an instance of an object
GestureRecognizer.AverageDifference (DrawnGesture playerGesture, DrawnGesture template) (at Assets/Scripts/GestureRecognizer.cs:475)
GestureRecognizer.FindMatch (DrawnGesture playerGesture, GestureTemplates templates) (at Assets/Scripts/GestureRecognizer.cs:437)
GestureRecognizer.EndGesture () (at Assets/Scripts/GestureRecognizer.cs:319)
GestureRecognizer.Update () (at Assets/Scripts/GestureRecognizer.cs:200)
Important:
I did not develop the script, I'm just trying to make it work.
Thanks in advance!
In general: Start Debugging your code, go through it line by line so you see exactly what is null.
I just did that and the Problem is as follows:
The class TwoDPoint is not serializable! Thus the field DrawnGesture.points which is of type TwoDPoint[] is also not serializable.
So when you generate the JSON this field is skipped. There are two places where you could already have noted that:
you could have simply confirmed this in the saved file
{"templates":[{"name":"","maxX":245.0,"minX":-4.0,"maxY":263.0,"minY":0.0,"numPoints":30}]}
→ No field named points
you can see that this field points is actually not appearing in the Unity Inspector!
Since the Inspector uses the same Serialization Rules as JsonUtility this should be a hint that something is wrong.
So later for loading the file in LoadTemplates you simply do
templates = JsonUtility.FromJson<GestureTemplates>(data);
but since the field points does not exists in your file and the field is not serializable anyway it is skipped again it keeps the value null for all the loaded templates!
Solution:
Tag your class as [System.Serializable]
[System.Serializable]
public class TwoDPoint
{
...
}
this would almost fix it.
The next problem is that this class contains no serializable fields either! You can change this by again tagging them [SerializeField]:
[System.Serializable]
public class TwoDPoint
{
[SerializeField] private float x;
[SerializeField] private float y;
...
}
Actually the whole getter setter make not much sense in this case ... you could simply make your field public as well
[System.Serializable]
public class TwoDPoint
{
public float X;
public float Y;
public TwoDPoint(float x, float y)
{
X = x;
Y = y;
}
}
and adjust the rest of the code to directly read and write X and Y instead of using get and set methods. public fields of serializable types are automatically serialized.
The change you can already see in the Unity Inspector! As said: If a field doesn't appear here it won't be serialized via JsonUtility neither - and the other way round.
As you can see there now is a field called points in a template!
It is now also present in the JSON
{"templates":[{"points":[{"x":0.0,"y":0.0},{"x":-1.2672364711761475,"y":-4.435328006744385},{"x":-2.534141778945923,"y":-8.870744705200196},{"x":-3.6529128551483156,"y":-13.34582805633545},{"x":-4.771683692932129,"y":-17.820911407470704},{"x":-5.8904547691345219,"y":-22.295995712280275},{"x":-7.009225845336914,"y":-26.771080017089845},{"x":-8.127996444702149,"y":-31.246164321899415},{"x":-9.246767044067383,"y":-35.721248626708987},{"x":-10.365538597106934,"y":-40.19633102416992},{"x":-11.484309196472168,"y":-44.67141342163086},{"x":-12.603079795837403,"y":-49.1464958190918},{"x":-13.577103614807129,"y":-53.655174255371097},{"x":-14.543621063232422,"y":-58.165592193603519},{"x":-15.510139465332032,"y":-62.67601013183594},{"x":-16.438554763793947,"y":-67.18716430664063},{"x":-15.64819049835205,"y":-71.73175811767578},{"x":-14.857826232910157,"y":-76.27635192871094},{"x":-14.067461967468262,"y":-80.8209457397461},{"x":-13.277097702026368,"y":-85.36553955078125},{"x":-12.486733436584473,"y":-89.91014099121094},{"x":-11.696369171142579,"y":-94.4547348022461},{"x":-8.638381004333496,"y":-96.89103698730469},{"x":-4.163297653198242,"y":-98.00981140136719},{"x":0.31178566813468935,"y":-99.12857818603516},{"x":4.815909385681152,"y":-99.94639587402344},{"x":9.422344207763672,"y":-99.70394897460938},{"x":14.028779029846192,"y":-99.46150970458985},{"x":18.63521385192871,"y":-99.21906280517578},{"x":23.241649627685548,"y":-98.97662353515625}],"name":"","maxX":35.0,"minX":-13.0,"maxY":0.0,"minY":-79.0,"numPoints":30}]}
And it is of course also after loading filled with valid values
This is a follow-up question from How to make individual anchor points of bezier continuous or non-continuous. Please refer to it for the relevant code in the accepted answer (please note that I did this to keep this question clean since the related code is quite lengthy).
I am trying to achieve the following:
Make the bezier curve handles/control points selectable in such a way that the properties (for example continuity) for an individual handle are displayed in the inspector window when selected. Please note I'd like this to be done without making creating game objects for the handles/ control points
Retain a single method that handles the movement of each point instead of having separate methods for the movement of each point.
Am I late to the party?
Make the bezier curve handles/control points selectable in such a way that the properties (for example continuity) for an individual handle are displayed in the inspector window when selected. Please note I'd like this to be done without making creating game objects for the handles/ control points
I like #jour's solution in general, except 1 thing: with Handles.Button you have to click a point to select it and then you click and drag to move the control point.
I propose a different approach. Using the same Handles.FreeMoveHandle, but having a variable to remember the id of the last clicked handle, so I can identify it.
Usually, a built in Handle won't give you more info than what it is designed to do. FreeMoveHandle, for example, returns the delta of its translation and that's all. The problem is: You want to capture a simple click, but if you just clicked and didn't drag, the return value is Vector3.zero and it is just the same as if you didn't do a click at all.
Good news: among the overloads of any Handle, there are some calls with an argument named controlID - it is an identifier for each interactable GUI object. If you supress it, the Engine will choose one and you will never know what. But if you pass an int, that value will be the id of the handle. But if I pass an int and it happen to conflict with any of the other ids i don't see? Well, you can call GUIUtility.GetControlID to get a safe id.
Then, it is straightforward. If the id of a Handle is the same as EditorGUIUtility.hotControl (that is, the control that got clicked or have the keyboard focus), then I save the index of this point in selectedControlPointId and use it to display a custom property in Inspector.
Retain a single method that handles the movement of each point instead of having separate methods for the movement of each point.
Hmm... Here become the controversy. If I understood it correctly, you want a single code to draw both nodes and tangents. The thing is: this two things are different in nature. Of course, if you keep it plain and simple, they are bot movable points in scene. But, when you introduces things like constraints (the continuity, or smooth) and selection, they become different beasts, with different logic. Even if you want to make ControlPoint a struct (like i did now) and pass it over as a whole, you'd still need to point what component you are aiming to update, so the constraints will apply to the others - you will always need a "master" field to avoid circular updates (you change tangentBack, and that makes tangentFront to update, that trigger tangentBack to update again and so on).
That's why, even though I reorganized somehow the ControlPoint methods and made it a struct, I can't make a single logic to draw both nodes and tangents.
Here are some codes. I started over from the codes on my answer in the previous question.
ControlPoint.cs
using System;
using UnityEngine;
[Serializable]
public struct ControlPoint
{
public Vector2 position;
public Vector2 tangentBack;
public Vector2 tangentFront;
public bool smooth;
static public ControlPoint MovePosition(ControlPoint pt, Vector2 newPos)
{
var newPt = pt;
newPt.position = newPos;
return newPt;
}
static public ControlPoint MoveTangentBack(ControlPoint pt, Vector2 newTanBack)
{
var newPt = pt;
newPt.tangentBack = newTanBack;
if (pt.smooth) newPt.tangentFront = pt.tangentFront.magnitude * -newTanBack.normalized;
return newPt;
}
static public ControlPoint MoveTangentFront(ControlPoint pt, Vector2 newTanFront)
{
var newPt = pt;
newPt.tangentFront = newTanFront;
if (pt.smooth) newPt.tangentBack = pt.tangentBack.magnitude * -newTanFront.normalized;
return newPt;
}
static public ControlPoint WithSmooth(ControlPoint pt, bool smooth)
{
var newPt = pt;
if (smooth != pt.smooth) newPt.tangentBack = -pt.tangentFront;
return newPt;
}
public ControlPoint(Vector2 position, Vector2 tanBack, Vector2 tanFront, bool smooth = false)
{
this.position = position;
this.tangentBack = tanBack;
this.tangentFront = tanFront;
this.smooth = smooth;
}
}
I removed ControlPointDrawer, so the other propertis you added to it won't be hidden in inspector.
Path.cs
using System;
using UnityEngine;
using System.Collections.Generic;
[Serializable]
public class Path
{
[SerializeField] List<ControlPoint> _points;
[SerializeField] bool _loop = false;
public Path(Vector2 position)
{
_points = new List<ControlPoint>
{
new ControlPoint(position, -Vector2.one, Vector2.one),
new ControlPoint(position + Vector2.right, -Vector2.one, Vector2.one)
};
}
public bool loop { get { return _loop; } set { _loop = value; } }
public ControlPoint this[int i]
{
get { return _points[(_loop && i == _points.Count) ? 0 : i]; }
set { _points[(_loop && i == _points.Count) ? 0 : i] = value; }
}
public int NumPoints { get { return _points.Count; } }
public int NumSegments { get { return _points.Count - (_loop ? 0 : 1); } }
public ControlPoint InsertPoint(int i, Vector2 position)
{
_points.Insert(i, new ControlPoint(position, -Vector2.one, Vector2.one));
return this[i];
}
public ControlPoint RemovePoint(int i)
{
var item = this[i];
_points.RemoveAt(i);
return item;
}
public Vector2[] GetBezierPointsInSegment(int i)
{
var pointBack = this[i];
var pointFront = this[i + 1];
return new Vector2[4]
{
pointBack.position,
pointBack.position + pointBack.tangentFront,
pointFront.position + pointFront.tangentBack,
pointFront.position
};
}
public ControlPoint MovePoint(int i, Vector2 position)
{
this[i] = ControlPoint.MovePosition(this[i], position);
return this[i];
}
public ControlPoint MoveTangentBack(int i, Vector2 position)
{
this[i] = ControlPoint.MoveTangentBack(this[i], position);
return this[i];
}
public ControlPoint MoveTangentFront(int i, Vector2 position)
{
this[i] = ControlPoint.MoveTangentFront(this[i], position);
return this[i];
}
}
PathCreator.cs is the same
PathCreatorEditor.cs
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(PathCreator))]
public class PathCreatorEditor : Editor
{
PathCreator creator;
Path path;
SerializedProperty property;
static int selectedControlPointId = -1;
public override void OnInspectorGUI()
{
serializedObject.Update();
var loopProp = property.FindPropertyRelative("_loop");
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(loopProp);
var ptsProp = property.FindPropertyRelative("_points");
var msg = "Total points in path: " + ptsProp.arraySize + "\n";
if (selectedControlPointId >= 0 && ptsProp.arraySize > 0)
{
EditorGUILayout.HelpBox(msg + "Selected control point: " + selectedControlPointId, MessageType.Info);
EditorGUILayout.Separator();
EditorGUILayout.PropertyField(ptsProp.GetArrayElementAtIndex(selectedControlPointId), true);
}
else
{
EditorGUILayout.HelpBox(msg + "No control points selected", MessageType.Info);
}
if (EditorGUI.EndChangeCheck()) serializedObject.ApplyModifiedProperties();
}
void OnSceneGUI()
{
Input();
Draw();
}
void Input()
{
Event guiEvent = Event.current;
Vector2 mousePos = HandleUtility.GUIPointToWorldRay(guiEvent.mousePosition).origin;
mousePos = creator.transform.InverseTransformPoint(mousePos);
if (guiEvent.type == EventType.MouseDown && guiEvent.button == 0 && guiEvent.shift)
{
Undo.RecordObject(creator, "Insert point");
path.InsertPoint(path.NumPoints, mousePos);
}
else if (guiEvent.type == EventType.MouseDown && guiEvent.button == 0 && guiEvent.control)
{
for (int i = 0; i < path.NumPoints; i++)
{
if (Vector2.Distance(mousePos, path[i].position) <= .25f)
{
Undo.RecordObject(creator, "Remove point");
path.RemovePoint(i);
break;
}
}
}
}
void Draw()
{
Handles.matrix = creator.transform.localToWorldMatrix;
var rot = Quaternion.Inverse(creator.transform.rotation) * Tools.handleRotation;
var snap = Vector2.zero;
Handles.CapFunction cap = Handles.DotHandleCap;
for (int i = 0; i < path.NumPoints; i++)
{
float size;
var pos = path[i].position;
size = HandleUtility.GetHandleSize(pos) * .05f;
Handles.Label(pos, i.ToString());
Handles.color = i == selectedControlPointId ? Handles.selectedColor : Color.red;
int ctrlId = GUIUtility.GetControlID(FocusType.Passive);
Vector2 newPos = Handles.FreeMoveHandle(ctrlId, pos, rot, size, snap, cap);
if (ctrlId == EditorGUIUtility.hotControl) selectedControlPointId = i;
if (pos != newPos)
{
Undo.RecordObject(creator, "Move point position");
path.MovePoint(i, newPos);
}
pos = newPos;
Handles.color = Color.black;
if (path.loop || i != 0)
{
var tanBack = pos + path[i].tangentBack;
Handles.DrawLine(pos, tanBack);
size = HandleUtility.GetHandleSize(tanBack) * .03f;
Vector2 newTanBack = Handles.FreeMoveHandle(tanBack, rot, size, snap, cap);
if (tanBack != newTanBack)
{
Undo.RecordObject(creator, "Move point tangent");
path.MoveTangentBack(i, newTanBack - pos);
}
}
if (path.loop || i != path.NumPoints - 1)
{
var tanFront = pos + path[i].tangentFront;
Handles.DrawLine(pos, tanFront);
size = HandleUtility.GetHandleSize(tanFront) * .03f;
Vector2 newTanFront = Handles.FreeMoveHandle(tanFront, rot, size, snap, cap);
if (tanFront != newTanFront)
{
Undo.RecordObject(creator, "Move point tangent");
path.MoveTangentFront(i, newTanFront - pos);
}
}
}
Repaint();
}
[DrawGizmo(GizmoType.Selected | GizmoType.NonSelected | GizmoType.Pickable)]
static void DrawGizmo(PathCreator creator, GizmoType gizmoType)
{
Gizmos.matrix = creator.transform.localToWorldMatrix;
var path = creator.path;
for (int i = 0; i < path.NumSegments; i++)
{
Vector2[] points = path.GetBezierPointsInSegment(i);
var pts = Handles.MakeBezierPoints(points[0], points[3], points[1], points[2], 30);
Gizmos.color = Color.green;
for (int j = 0; j < pts.Length - 1; j++)
{
Gizmos.DrawLine(pts[j], pts[j + 1]);
}
}
}
void OnEnable()
{
creator = (PathCreator)target;
path = creator.path ?? creator.CreatePath();
property = serializedObject.FindProperty("path");
}
}
Note: I moved the drawing of the Bezier line from OnSceneGui to DrawGizmo, so the green line will be visible even when the object is not selected, and it will be pickable in the Scene Editor, for having it selected.
Lastly, I would suggest some further development of this scripts. It wouldn't be very hard to make multiple point selection possible. Maybe making the default handles (like position and rotation) to be applicable individually on points. Or changing the method for creating and deleting points to something move intuitive like double-clicking or dragging the path line. Or even a custom toolbar to smart point manipulation, like align, distribute, sculpt... Different constraints, like smooth, symmetric, cusp or straight...
Not entirely sure if I understood the question, but
Make the bezier curve handles/control points selectable in such a way
that the properties (for example continuity) for an individual handle
are displayed in the inspector window when selected. Please note I'd
like this to be done without making creating game objects for the
handles/ control points
You need to use OnSceneGUI option to be able to select the handles, and everytime you select a new point just store its value.
private void OnSceneGUI ()
{
spline = target as Path;
handleTransform = spline.transform;
handleRotation = Tools.pivotRotation == PivotRotation.Local
? handleTransform.rotation : Quaternion.identity;
Vector3 p0 = ShowPoint (0);
Color gg = Color.gray;
gg.a = 0.25f;
for (int i = 1; i < spline.ControlPointCount; i += 3)
{
Vector3 p1 = ShowPoint (i);
Vector3 p2 = ShowPoint (i + 1);
Vector3 p3 = ShowPoint (i + 2);
Handles.color = gg;
Handles.DrawLine (p0, p1);
Handles.DrawLine (p2, p3);
Handles.DrawBezier (p0, p3, p1, p2, Color.white, null, 2f);
p0 = p3;
}
ShowDirections ();
}
private Vector3 ShowPoint (int index)
{
Vector3 point = handleTransform.TransformPoint (spline.Points[index]);
float size = HandleUtility.GetHandleSize (point);
if (index == 0)
{
size *= 2f;
}
Handles.color = modeColors[(int) spline.GetControlPointMode (index)];
if (Handles.Button (point, handleRotation, size * handleSize, size * pickSize, Handles.DotCap))
{
selectedIndex = index;
Repaint ();
}
if (selectedIndex == index)
{
EditorGUI.BeginChangeCheck ();
point = Handles.DoPositionHandle (point, handleRotation);
if (EditorGUI.EndChangeCheck ())
{
Undo.RecordObject (spline, "Move Point");
EditorUtility.SetDirty (spline);
spline.SetControlPoint (handleTransform.InverseTransformPoint (point), index);
}
}
return point;
}
and for showing the point on inspector GUI
private void DrawSelectedPointInspector ()
{
GUILayout.Label ("Selected Point");
EditorGUI.BeginChangeCheck ();
Vector3 point = EditorGUILayout.Vector3Field ("Position", spline.Points[selectedIndex]);
if (EditorGUI.EndChangeCheck ())
{
Undo.RecordObject (spline, "Move Point");
EditorUtility.SetDirty (spline);
spline.SetControlPoint (point, selectedIndex);
}
EditorGUI.BeginChangeCheck ();
BezierControlPointModes mode = (BezierControlPointModes) EditorGUILayout.EnumPopup ("Mode", spline.GetControlPointMode (selectedIndex));
if (EditorGUI.EndChangeCheck ())
{
Undo.RecordObject (spline, "Change Point Mode");
spline.SetControlPointMode (selectedIndex, mode);
EditorUtility.SetDirty (spline);
}
}
and call this on the InspectorGUI
if (selectedIndex >= 0 && selectedIndex < spline.points.Count)
{
DrawSelectedPointInspector ();
}
Retain a single method that handles the movement of each point instead
of having separate methods for the movement of each point.
For this you just need to retain the
I dont know if I can call this algorithm. But I am working on a game in which player will move in a circular path.
As you can see in the picture player is suppose to orbit the circle. And obstacle shall be instantiated in the circle.I am trying to first create the obstacle in first half(left to the long cube) and then in the second half. But things are getting created in the next half too when code is not supposed to do that. Also, it is showing argument exception error. Please have a look at my code and tell me whether my method is wrong or my formulas are wrong or anything else.
public class ObjectInstantiater : MonoBehaviour {
DataHolder dataholder;
GameObject Obstacle;
LevelData leveldata;
private int currentlevel=0; // default level starts from 0
private List<GameObject> Inactivegameobject = new List<GameObject>(); // this object can be used
private List<GameObject> Activegameobject = new List<GameObject>();
private int totalgameobjects;
private int firsthalfgameobjects, secondhalfgameobjects;
public float outerradius;
public float innerradius;
private bool shallspawnouterradiues = true;
// Use this for initialization
void Awake () {
dataholder = (Object)GameObject.FindObjectOfType<DataHolder>() as DataHolder;
Obstacle = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
leveldata = dataholder.Leveldata[0];
}
void Start()
{
Updateleveldata();
FirstHalf();
}
public int Currentlevel
{
get { return currentlevel; }
set { currentlevel = value;
leveldata = dataholder.Leveldata[currentlevel];//sets the level data
}
}
private void Updateleveldata() // this function gets called after a round
{
totalgameobjects = Random.Range(leveldata.MinimumObstacle, leveldata.MaximumObstacle);
firsthalfgameobjects = Mathf.RoundToInt(totalgameobjects / 2);
secondhalfgameobjects = totalgameobjects - firsthalfgameobjects;
}
private void FirstHalf()
{
Debug.Log(firsthalfgameobjects);
Vector3 pos;
if (Inactivegameobject.Count < firsthalfgameobjects)
{
for (int x = 0; x <= (firsthalfgameobjects - Inactivegameobject.Count); x++)
{
GameObject obs = Instantiate(Obstacle) as GameObject;
obs.SetActive(false);
Inactivegameobject.Add(obs);
}
}
float spawnangledivision = 180 / firsthalfgameobjects;
float spawnangle = 180f;
for(int x = 0; x < firsthalfgameobjects; x++)
{
float proceduralRandomangle = spawnangle;
proceduralRandomangle = Random.Range(proceduralRandomangle , proceduralRandomangle + 2f);
if (shallspawnouterradiues)
{
pos = new Vector3(outerradius * Mathf.Cos(spawnangle), outerradius * Mathf.Sin(spawnangle), 0f);
shallspawnouterradiues = false;
}else
{
pos = new Vector3(innerradius * Mathf.Cos(spawnangle), innerradius * Mathf.Sin(spawnangle), 0f);
shallspawnouterradiues = true;
}
spawnangle += spawnangledivision;
Inactivegameobject[0].SetActive(true); // set it to 0
Inactivegameobject[0].transform.position = pos;
Activegameobject.Add(Inactivegameobject[0]);
Inactivegameobject.RemoveAt(0);
}
}
private void SecondHalf()// No need to check this
{
if (Inactivegameobject.Count < firsthalfgameobjects)
{
GameObject obs = Instantiate(Obstacle) as GameObject;
obs.SetActive(false);
Inactivegameobject.Add(obs);
}
}
}
I am trying to get two blocks to respawn vertically with a gap in-between. I have done this however, when I click play, one of the blocks width is too short. this results in having two gaps. how can I stop this from happening. I would only like the one gap which is between the two blocks. the code I am currently using:
public Transform block;
public Transform player;
private float objectSpawnedTo = 5.0f;
public static float distanceBetweenObjects = 9.5f;
private float nextCheck = 0.0f;
private ArrayList objects = new ArrayList();
void Start () {
maintenance(0.0f);
}
void Update () {
float playerX = player.position.y;
if(playerX > nextCheck)
{
maintenance(playerX);
}
}
private void maintenance(float playerX)
{
nextCheck = playerX + 30;
for (int i = objects.Count-1; i >= 0; i--)
{
Transform blck = (Transform)objects[i];
if(blck.position.y < (transform.position.y - 30))
{
Destroy(blck.gameObject);
objects.RemoveAt(i);
}
}
spawnObjects(5);
}
private void spawnObjects(int howMany)
{
float spawnX = objectSpawnedTo;
for(int i = 0; i<howMany; i++)
{
Vector3 pos = new Vector3(6.0f, spawnX, 0);
float firstRandom = Random.Range(1,8.6f);
Transform blck = (Transform)Instantiate(block, pos, Quaternion.identity);
blck.localScale+=new Vector3(firstRandom*2,0,0);
objects.Add(blck);
pos = new Vector3(-6.0f, spawnX, 0);
blck = (Transform)Instantiate(block, pos, Quaternion.identity);
blck.localScale +=new Vector3((8.6f-firstRandom)*2,0,0);
objects.Add(blck);
spawnX = spawnX + distanceBetweenObjects;
}
objectSpawnedTo = spawnX;
}
i have attached an image to show what i am trying to achieve. the red outline is the mobile screen. also i would like to be able to move the blocks either left or right using touch. how would i go about doing this.