NullReferenceException Can't figure out what's Null - c#

I get this error in Unity:
NullReferenceException: Object reference not set to an instance of an object
TowerSlot.OnGUI () (at Assets/TowerSlot.cs:26)
I'm relatively new to Unity and can't figure out what line this error is from(I'm assuming 26) and I don't know what is null. If someone could please help explain to me how to understand what the error is pointing at and what I need to do it would be much appreciated.
TowerSlot.cs:
using UnityEngine;
using System.Collections;
public class TowerSlot : MonoBehaviour {
public GUISkin skin = null;
bool gui = false;
// Tower prefab
public Tower towerPrefab = null;
void OnGUI() {
if (gui) {
GUI.skin = skin;
// get 3d position on screen
Vector3 v = Camera.main.WorldToScreenPoint(transform.position);
// convert to gui coordinates
v = new Vector2(v.x, Screen.height - v.y);
// creation menu for tower
int width = 200;
int height = 40;
Rect r = new Rect(v.x - width / 2, v.y - height / 2, width, height);
GUI.contentColor = (Player.gold >= towerPrefab.buildPrice ? Color.green : Color.red);
GUI.Box(r, "Build " + towerPrefab.name + "(" + towerPrefab.buildPrice + " gold)");
// mouse not down anymore and mouse over the box? then build the tower
if (Event.current.type == EventType.MouseUp &&
r.Contains(Event.current.mousePosition) &&
Player.gold >= towerPrefab.buildPrice) {
// decrease gold
Player.gold -= towerPrefab.buildPrice;
// instantiate
Instantiate(towerPrefab, transform.position, Quaternion.identity);
// disable gameobject
gameObject.SetActive(false);
}
}
}
public void OnMouseDown() {
gui = true;
}
public void OnMouseUp() {
gui = false;
}
}
Also I'm trying to follow this tutorial here http://makeagame.info/unity-tower-defense-game-step-4-scripting
Thanks!

You set towerPrefab to null early on, and then on line 26, you reference its buildPrice property or field before assigning it any non-null value. That will throw a null exception.
This line is the problem:
GUI.contentColor = (Player.gold >= towerPrefab.buildPrice ? Color.green : Color.red);

The screenshot in OP's reply to #Hatchet definitely shows the tower prefab has not been set in the editor.
Either it's supposed to be set by some other code (which is not working) or it should be set manually. You can do this by dragging a tower prefab onto the slot, or by clicking on the little circle to the right of 'None (Tower)': That'll pop up a picker dialog where you can choose a tower prefab. It shows up as 'Tower' because that's the kind of variable it wants.
BTW the GUIskin is also not set. That will probably cause a problem when you hit the line
GUI.skin = skin;
Docs on the Unity gui for assigning references like this are here: http://docs.unity3d.com/Documentation/Manual/EditingReferenceProperties.html

hatchet has pointed the problem. I think this may fix it:
public Tower towerPrefab = new Tower();
Because Tower class has the the property buildPrice which you are trying to access.

Related

Game objects wrong positioning

The objects are being set to out of the canvas
The objective of setting them is to show the user inventory with only the items that the user have
It's setting them active normally, and in the order that the user got it, but in wrong positioning
No error messages were sent
Before setting the positions array, i checked what were the positions in the scene to put it right on the array
But the objects are setting in the wrong place
I put this debug logs to try to see the problem
But it says the positions that i set
This is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class Filters
: MonoBehaviour
{
[Header("Items")]
public GameObject doubleSpeed;
public GameObject doubleJump;
public GameObject superForce;
public GameObject noGravity;
public GameObject noHit;
public GameObject apple;
private GameObject[] items;
public int index = 0;
public float[] position = new float[6] { -125.8f, -78.6f, -24.1f, 23.1f, 80.69913f, 36.3375f };
private float x;
// Update is called once per frame
void Update()
{
Player player = FindObjectOfType<Player>();
Filter(doubleJump, player.doubleJumps);
Filter(doubleSpeed, player.doubleSpeeds);
Filter(superForce, player.superForces);
Filter(noGravity, player.noGravitys);
Filter(noHit, player.noHits);
Filter(apple, player.apples);
items = GameObject.FindGameObjectsWithTag("Have");
if(items.Length != 0)
{
for(int i = 0; i < items.Length; i++)
{
items[i].transform.position = new Vector3(position[i], 52.8f , 1f);
items[i].SetActive(true);
Debug.Log("Changed the position and set active new position:" + items[i].transform.position);
}
}
}
void Filter(GameObject item, int quantity)
{
if(quantity < 1)
{
item.SetActive(false);
Debug.Log("less than 1");
}
else
{
item.SetActive(true);
Debug.Log("set active");
item.tag = "Have";
Debug.Log("set the tag");
}
}
}
In general there is a difference between the localPosition - the position relative to the parent object and usually the one displayed in the Unity Inspector - and the absolute world space position you are setting in
items[i].transform.position = new Vector3(position[i], 52.8f , 1f);
Further there is an even bigger difference when dealing with a RectTransform. What you see and edit in the Inspector is the RectTransform.anchoredPosition which is relative to the parents RectTransform and additionally takes the Anchors and Pivot settings into account.
So try this instead
items[i].GetComponent<RectTransform>().anchoredPosition3D = new Vector3(position[i], 52.8f , 1f);
anchoredPosition3D is basically the same as anchoredPosition but additionally allows to set the delta in the Z axis
In general for your usecase I would recommend to use a HorizontalLayoutGroup and rather let the UI handle the placement itself. You would then simply use SetActive to hide and show items.

how to check whether a not-rendered object is in camera

I've just started learning Unity and encountered some problems.
Here I have a prefab object ParticleAlly with a parent ParticleAllies. The parent object rotates itself, and ParticleAlly rotates too, like a planet and satellites.
Besides, I also want to regenerate each ParticleAlly as soon as it is out of the camera. Then they are in different orbits with only the same angular velocity but in different positions. When it gets into and then get out of the camera, do that again.
The problem is, some ParticleAlly appeared in the middle of the camera directly after they are regenerated. I tried to let them not rendered until they get into the camera from outside. But it seems that SpriteRenderer.isvisible is true only when SpriteRenderer.enabled is true, so I cannot get know when to render them again. Then I tried to judge by position, but I don't know why it doesn't work at all.
I'm totally confused and wasted a whole morning on that. Sorry for my inaccurate description and term using.
It would be appreciated if you could provide me a solution and tell me something about Update(), the rendering, the position/localposition in rotation etc.
// in ParticleAlly
void Update() {
//Debug.Log(trans.localPosition.y);
if(trans.position.y <= globalSettings.RotationLocationY) {
Debug.Log("Under");
if(!isRefreshed) {
refresh();
}
sRender.enabled = true;
}
else {
if(sRender.isVisible) {
isRefreshed = false;
}
}
}
/// <summary>
/// Regenerate the particle
/// </summary>
void refresh() {
isRefreshed = true;
float height = Random.value * 3;
trans.localPosition = (new Vector3(0f, globalSettings.RotationLocationY - height, 0f));
//trans.RotateAround(trans.parent.localPosition, new Vector3(0f, 0f, 1f), globalSettings.getDegree() * Random.value);
trans.localRotation = Quaternion.Euler(new Vector3(0f, 0f, globalSettings.getDegree() * Random.value));
//sRender.enabled = false;
}
// in ParticleAllies
void Update() {
trans.localRotation = Quaternion.Euler(new Vector3(0f, 0f, globalSettings.getDegree()));
}
It's my first time asking here, the tabs are somehow broken in the code?
The issue with Renderer.isVisible is that
As you already noted it only can be true if the renderer is enabled
It can stay true even if the object is not visible itself but has some visual influence on the rendering (such as e.g. shadows)
In case the position would be enough you could simply use e.g.
public static class Extensions
{
pulic static bool IsInFrustum(this Camera camera, Vector3 worldPoint)
{
var screenPos = camera.WoldToscreenPoint(worldPoint);
return screenPos.z >= camera.nearClipPlane
&& screenPos.z <= camera.farClipPlane
&& screenPos.x >= 0
&& screenPos.x <= Screen.width
&& screenPos.y >= 0
&& screenPos.y <= Screen.height;
}
public static bool sInFrustum(this Vector3 worldPoint, Camera camera)
{
return camera.IsInFrustum(worldPoint);
}
}
and would do e.g.
someRenderer.enabled = yourCamera.IsInFrustum(someRenderer.transform.position);
or also
someRenderer.enabled = someRenderer.transform.position.IsInFrustum(yourCamera);
If a single position is not enough, you can get the camera frustum planes using GeometryUtility.CalculateFrustumPlanes and then check if some Bounds e.g. the Renderer.bounds fall within those using GeometryUtility.TestPlanesAABB.
Bounds are not completely accurate always of course but are probably the best approximation you can get.
public static class Extensions
{
public static bool IsInFrustum(this Renderer renderer, Camera camera)
{
var planes = GeometryUtility.CalculateFrustumPlanes(camera);
return GeometryUtility.TestPlanesAABB(planes, renderer.bounds);
}
public static bool IsInFrustum(this Camera camera, Renderer renderer)
{
return renderer.IsVisibleFrom(camera);
}
}
You can now either use on your camera e.g.
someRenderer.enabled = yourCamera.IsInFrustum(someRenderer);
or also
someRenderer.enabled = someRenderer.IsInFrustum(yourCamera);

A script that makes anchor min max positioned at Gameobject ui corners?

I am a beginner in unity3d and i am struggling to make the Ui anchor min max same position as the Button position when moved, my goal is try to achieve like the pictures below.
as you can see when the button moves the anchors min max does not following with it, in what way should i solve this using C#? at the moment i move my button with the follow script:
public GameObject SaleButtonPrefab;
//To loop all the list
for(int i = 0; i < playerList.Count; i++)
{
//Instantiate the button prefab and make a parent
GameObject nu = Instantiate(SaleButtonPrefab) as GameObject;
nu.transform.SetParent(ParentButton.transform, false);
//To set the position
nu.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, (i*-301) - 0);
}
Thanks in advance!
I have figured it out, and to accomplish my goal i modify some of the scripts i have found useful in the internet. This is the code:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class GUITest : MonoBehaviour
{
private RectTransform t;
private RectTransform pt;
void Start()
{
t = gameObject.GetComponent<RectTransform>();
pt = gameObject.GetComponent<RectTransform>().parent as RectTransform;
if(t == null || pt == null) return;
Vector2 newAnchorsMin = new Vector2(t.anchorMin.x + t.offsetMin.x / pt.rect.width,
t.anchorMin.y + t.offsetMin.y / pt.rect.height);
Vector2 newAnchorsMax = new Vector2(t.anchorMax.x + t.offsetMax.x / pt.rect.width,
t.anchorMax.y + t.offsetMax.y / pt.rect.height);
t.anchorMin = newAnchorsMin;
t.anchorMax = newAnchorsMax;
t.offsetMin = t.offsetMax = new Vector2(0, 0);
}
}
To make this work i save this script as C#, after that i attach it to any UI object and when you run your unity game the UI anchor will automatically resize itself around the UI object. i find this useful when instantiating a prefab and looping it because you cant really change the anchors.
Source: http://answers.unity3d.com/questions/782478/unity-46-beta-anchor-snap-to-button-new-ui-system.html

How can i create an in-game system that lets me draw lines with the cursor that can act as walkable terrain

Hi im creating a 2D platformer, and i want to be able to draw for example lines ingame (playmode) with my cursor (like paint) that can act as walkable terrain. Im pretty new to coding and c# which im using, and im having a really hard time imagining how this can be achieved let alone if its possible? Would appreciate if you guys could give me some ideas and maybe could help me push it in the right direction. Thanks!
EDIT:
So i got got some code now which makes me being able to draw in playmode. The question now is how i can implement a type of collider to this? Maybe each dot can represent a little square or something? How can i go through with it? Heres some code. Thanks.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class DrawLine : MonoBehaviour
{
private LineRenderer line;
private bool isMousePressed;
private List<Vector3> pointsList;
private Vector3 mousePos;
// Structure for line points
struct myLine
{
public Vector3 StartPoint;
public Vector3 EndPoint;
};
// -----------------------------------
void Awake()
{
// Create line renderer component and set its property
line = gameObject.AddComponent<LineRenderer>();
line.material = new Material(Shader.Find("Particles/Additive"));
line.SetVertexCount(0);
line.SetWidth(0.1f,0.1f);
line.SetColors(Color.green, Color.green);
line.useWorldSpace = true;
isMousePressed = false;
pointsList = new List<Vector3>();
}
// -----------------------------------
void Update ()
{
// If mouse button down, remove old line and set its color to green
if(Input.GetMouseButtonDown(0))
{
isMousePressed = true;
line.SetVertexCount(0);
pointsList.RemoveRange(0,pointsList.Count);
line.SetColors(Color.green, Color.green);
}
else if(Input.GetMouseButtonUp(0))
{
isMousePressed = false;
}
// Drawing line when mouse is moving(presses)
if(isMousePressed)
{
mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mousePos.z=0;
if (!pointsList.Contains (mousePos))
{
pointsList.Add (mousePos);
line.SetVertexCount (pointsList.Count);
line.SetPosition (pointsList.Count - 1, (Vector3)pointsList [pointsList.Count - 1]);
}
}
}
}
Regarding means and tools:
Input.mousePosition
Input.GetKey, Input.GetKeyDown, Input.GetKeyUp
Line renderer manual
Line renderer scripting API
Regarding general idea:
Your script may use Input.GetKey to trigger the feature functionality (keyboard key, for example).
When the feature is activated the script awaits of mouse button to be clicked by the means of Input.GetKeyUp and when the event happens it must capture current mouse position using Input.mousePosition.
Only two points are necessary to build a segment of line, so when the script detects input of the second point it may create game object that will represent a piece of walkable terrain.
Visually such an object may be represented with line renderer. To enable iteraction with other game objects (like player character) it is usually enough to enhance object with a collider component (presumably, with a BoxCollider).
Regarding of how-to-add-a-collider:
GameObject CreateLineCollider(Vector3 point1, Vector3 point2, float width)
{
GameObject obj = new GameObject("LineCollider");
obj.transform.position = (point1+point2)/2;
obj.transform.right = (point2-point1).normalized;
BoxCollider boxCollider = obj.AddComponent<BoxCollider>();
boxCollider.size = new Vector3( (point2-point1).magnitude, width, width );
return obj;
}
You can add collider to object with line renderer but you still must orient it properly.
Example of integration in your code:
void Update ()
{
...
// Drawing line when mouse is moving(presses)
if(isMousePressed)
{
mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mousePos.z=0;
if (!pointsList.Contains (mousePos))
{
pointsList.Add (mousePos);
line.SetVertexCount (pointsList.Count);
line.SetPosition (pointsList.Count - 1, (Vector3)pointsList [pointsList.Count - 1]);
const float ColliderWidth = 0.1f;
Vector3 point1 = pointsList[pointsList.Count - 2];
Vector3 point2 = pointsList[pointsList.Count - 1];
GameObject obj = new GameObject("SubCollider");
obj.transform.position = (point1+point2)/2;
obj.transform.right = (point2-point1).normalized;
BoxCollider boxCollider = obj.AddComponent<BoxCollider>();
boxCollider.size = new Vector3( (point2-point1).magnitude, ColliderWidth , ColliderWidth );
obj.transform.parent = this.transform;
}
}
}

Unity C# camera zoom script

So before I explain my problem. I will first tell what I am really doing.
I am working on a click to move/zoom camera script. There are 3 planes in front of my Main Camera. Now what I am doing is, creating a script which says " The camera will zoom on the plane which gets clicked. I made several attempts to come up with a working script but it didn't worked well. Every time I come across new bugs , errors and what not. :|
I got frustrated and deleted the buggy script. Now I want to start from scratch. I am doing it in C#
Since I am not a professional, Can anyone explain me in detail to get it done?
I am confused how to deal with the planes I placed. I want to know what is missing in my script.
Here is a screenshot of how I placed those planes.
Edit. - I managed to work on it. Now I need advice, how to target the planes I placed in front of the camera.
using UnityEngine;
using System.Collections;
public class CameraZoom : MonoBehaviour
{
public int zoomLevelSelected = 0;
public float[] ZoomLevels = new float[] { 60, 40, 20 };
void Update()
{
int zoomChange = 0;
if (Input.GetMouseButtonDown(0)) { zoomChange = +1; } // back
else if (Input.GetMouseButtonDown(1)) { zoomChange = -1; } // forward
if (zoomChange != 0)
{
zoomLevelSelected = Mathf.Clamp(zoomLevelSelected + zoomChange, 0, ZoomLevels.Length - 1);
camera.fieldOfView = ZoomLevels[zoomLevelSelected];
}
}
}
Heck with it, here is one way to create a click zoom. The gist is that you create a ray from your camera in to the scene through the mouse cursor. When that ray intersects an object create a second ray from the point of intersection back out along the intersecting face's normal.
void Update () {
if(Input.GetMouseButtonDown(0)){
// get ray from camera in to scene at the mouse position
Ray ray = Camera.mainCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
// hardcoded "zoom" distance.
float zoomDist = 15.0f;
// Raycast from camera to mouse cursor, if object hit, zoom.
if (Physics.Raycast(ray,out hit,Mathf.Infinity)){
// Create a second ray from the hit object back out, zoom the camera along this ray.
Ray r = new Ray(hit.point,hit.normal);
Camera.mainCamera.transform.position = r.GetPoint(zoomDist);
}
}
}
Things to keep in mind:
Physics.Raycast, as written, will return true for any GameObject with a collider. Use layers if you only want to zoom when selecting specific GameObjects.
The camera won't directly center on the GameObject you click. I use the exact point of intersection to create the position where the camera will zoom to.
zoomDist is the distance away from the object.
This code only works with perspective cameras, if you use orthographic you'll need to modify the size value of the camera to zoom.
The problem with your script is that your var int zoomChange is getting set to zero every frame so move that variable to class level.
using UnityEngine;
using System.Collections;
public class CameraZoom : MonoBehaviour
{
public int zoomLevelSelected = 0;
public float[] ZoomLevels = new float[] { 60, 40, 20 };
int zoomChange = 0; //<<<<<<<<<<<<<
void Update()
{
if (Input.GetMouseButtonDown(0)) { zoomChange = +1; } // back
else if (Input.GetMouseButtonDown(1)) { zoomChange = -1; } // forward
if (zoomChange != 0)
{
zoomLevelSelected = Mathf.Clamp(zoomLevelSelected + zoomChange, 0, ZoomLevels.Length - 1);
camera.fieldOfView = ZoomLevels[zoomLevelSelected];
}
}
}

Categories