I have this simple code to change the sprite of an image everytime I click a button.
using UnityEngine;
using UnityEngine.UI;
public class SampleChange : MonoBehaviour {
public Sprite sampleSprite;
public Image sampleImage;
public void Start()
{
sampleImage = GetComponent<Image>();
}
public void changeColor()
{
sampleImage.gameObject.GetComponent<Image>();
sampleImage.sprite = sampleSprite;
}
}
I attached this script to an EmptyGameObject and Loaded the function on the Button that is parented on a Canvas alongside the Image. I already also placed the Image and Sprite objects in the inspector:
Inspector Settings
When I run the game and click the Button, it gives me this error:
NullReferenceException: Object reference not set to an instance of an object
SampleChange.changeColor () (at Assets/Scripts/SampleChange.cs:18)
The cs:18 is the sampleImage.sprite = sampleSprite;. I really don't know why it's not working.
OK simple,
public Image sampleImage;
that means
you will set "sampleImage" variable in the inspector, in the editor, before you hit Play
But this one ..
sampleImage = GetComponent<Image>();
means
you will set "sampleImage" variable in code when the scene is running.
You have to sort it out and do it "one way or the other".
Suggest you use the first method while U learning.
(If you do use the second method, the "Image" must actually be on the game object which is holding the script in question. If you struggle with that, I would urge you to ask a separate question, or just study up on the basics using Unity tutorials.)
Cheers
It appears that you have no constuctor defined for your class and that you are just trying to call the changeColor() method as if it were a static function of the class. You need to construct objects of your class and then call the methods you defined on those objects, not on the class itself.
Related
So I was looking into implementing the new Unity input system into my game by reworking my code in my PlayerController script and got stuck pretty early on.
Here is the code that causes issues:
private PlayerInput playerInputController;
private void Awake()
{
playerInputController = new PlayerInput();
}
A green scribbly line appears under the 'Awake()' bit.
The code itself actually works, but it causes a lof of other issues, which makes a bunch of error messages appear saying:
NullReferenceException: Object reference not set to an instance of an object
The error messages all lead to places where I've used lines of code like this one in other scripts, to get variables and methods from my PlayerController script:
if (PlayerController.instance.isGroundedPhys)
Lastly, here's my singleton script, since it's likely relevant, as it's connected to my PlayerController script and allows it to be accessible from other scripts:
using UnityEngine;
public class Singleton<Instance> : MonoBehaviour where Instance : Singleton<Instance>
{
public static Instance instance;
public virtual void Awake()
{
if (!instance)
{
instance = this as Instance;
}
else
{
Destroy(gameObject);
}
}
}
This bit is at the top of my PlayerController script to make it accessible from other scripts:
public class PlayerController : Singleton<PlayerController>
{
I've been looking around and can't find anybody talking about similar issues. I think it's related to my singleton script, but I can't figure it out. :[
[Edit]:
I just found out it also gives this warning in the Unity console:
warning CS0114: 'PlayerController.Awake()' hides inherited member 'Singleton<PlayerController>.Awake()'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
PlayerInput is a MonoBehaviour!
No seriously, this is "Not Allowed" and you should actually give you an according warning in the console.
A MonoBehaviour "can"/should only exist attached to an according GameObject and always be created by the underlying c++ framework. It's a c# thing that Unity can't fully prevent you from using new to create one.
The only valid way of creating an instance of a MonoBehaviour are
Instantiate a prefab that has the component attached
use AddComponent on an existing GameObject
use the constructor of GameObject and pass in the according component type(s)
Or you probably rather want to use e.g. GetComponent or FindObjectOfType in order to get a reference to an already existing instance of PlayerInput.
And then the other warning means you implemented
public void Awake()
{
// ...
}
or similar in your PlayerController class. The warning already tells you exactly what to rather do:
// Use "override" in order to not hide the already existing implementation
// of "Awake" in the base class
public override void Awake()
{
// Make sure the base class behavior is executed
// this will initialize the Singleton thing
base.Awake();
// ... your additional behavior here
}
This question already has answers here:
Access variables/functions from another Component
(4 answers)
Closed 5 years ago.
Good afternoon! I've made a main menu in which you create your RPG character, in this menu I can take values from input fields/buttons that add/substract values from attributes, etc. Everything is stored within a class called CustomCharacterSheet, now this object has a DontDestroyOnLoad script that allows it to be moved into the next scene which is the first level.
In my playerbehavior class I have a method that takes a CustomCharacterSheet object and reads its values and then creates a character with the appropiate values, the problem is that when I want to use the method to generate the character on the Awake() within the playerbehavior, the method requires an object of type CustomCharacterSheet in order to be executed, but how do I tell this method that the CustomCharacterSheet that came from the main menu is the one that has to be read? I tried GameObject.Find(); but it will tell me it cant cast a GameObject to a CustomCharacterSheet class.
Here is the dummy code:
CustomCharacterSheetClass {
//Values, this was created into an object in the main menu with a DontDestroyOnLoad() script to be moved into the 1st Scene
}
PlayerBehavior Class {
private void generatePlayer(CustomCharacterSheet cs){
//Do Stuff, this method requires the CCS in order to pull data from it and generate the player
}
void Awake(){
generatePlayer();//This is inside the playerBehavior class and needs to reach the CustomCharacterClass created in the main menu that was later moved to the 1st Scene
}
}
GameObject.Find is made to find a certain gameObject by name not for finding a script.
Since you said that script was attached to a gameObject, what you need to do is call GameObject.Find("ObjectName") with the name of that object and then you can access the script CustomCharacterSheet with GetComponent
public class GetComponentGenericExample : MonoBehaviour
{
void Start()
{
GameObject gObject = GameObject.Find("ObjectName")
CustomCharacterSheet ccSheet = gObject.GetComponent<CustomCharacterSheet>();
}
}
GetComponent allows you to access any component of a GameObject.
I am making a game in Unity C# where the character will have different characterstics and movement functions in each scene. So I am trying to have different scripts for a player in different scenes, while all of them inheriting from the same base class. The definition will be something like below
public class PlayerScript1 : PlayerBase { /* for scene 1*/
public void Move(){
/* my movement code*/
}
}
Similarly, I have a separate class for Scene2 as
public class PlayerScript2 : PlayerBase { /* for scene 2*/
public void Move(){
/* my movement code*/
}
}
Now the problem is, my other scripts, like HealthScript,ScoreScript etc they do not change with scene. But they do access PlayerScript1 script. And thats why, I have the PlayerScript1 declaration in them. Like below:
public class HealthScript : MonoBehaviour {
PlayerScript1 playerScript;
void Start(){
/*accessing the script attached to game object*/
}
}
So how can I have my Health Script access different instances of my PlayerScript based on the scene? I know I could use delegates to call different methods in runtime, but how can I do the same with classes?
So how can I have my Health Script access different instances of my PlayerScript based on the scene?
Well first, you'll want to declare that object as of Type PlayerBase as you will be unable to assign an instance of PlayerScript2 to a variable of type PlayerScript1: those classes might inherit from the same parent, but they are not the same and you cannot convert from one to the other.
After that you will need to search for the player object in the scene, something like...
void Start(){
playerScript = GameObject.Find("Player").GetComponent<PlayerBase>();
}
Assuming, of course, that PlayerBase extends MonoBehaviour. If it doesn't you can't get a reference this way (as it won't exist in the scene at all). Additionally if you want this health object to persist from scene to scene, you need to call DontDestroyOnLoad() for it (as well as remembering that if you don't start testing from Scene 1 where this object is, it won't exist at all, or if you have a copy in every scene, you'll have duplication problems).
Someone answered to my question on the Unity forum which cleared all my doubts:
The key was using the below line in my HealthScript :
PlayerBase player = (PlayerBase)FindObjectOfType(typeof(PlayerBase));
http://answers.unity3d.com/answers/1348311/view.html
we've been banging our heads with this one, in theory, having a public static object to hold the prefab which is to be spawned should have solved this, but the script gets a static variable when the static keyword is not there, but still the objects get individual variables, while when I add the static keyword, the script's variable disappears. Are we doing something wrong? This is the exact definition:
public static Object prefab;
I too am still new with Unity, but will offer some personal research / findings. I too hate to explicitly have some object with public properties exposed so you can "stick" an object prefab in a scene. If trying to build a system / game components dynamically, knowing what / when to add would be your discretion. I too liked the approach of having a STATIC available so you can just go to it at any time you needed another "thing".
I found that using "Resources" could allow you to load a prefab via code without the need of having a scene object to drag controls to, such as for a master list.
In you default "Assets" folder, Create a folder "Resources" (not sure if case-sensitive). Anyhow, I put all my other stuff in respective folders under that... materials, prefabs, sprites, textures, whatever... So now, in my "Prefab" folder, I could have a prefab for a "Hero", "Laser", "Shield", whatever.
Now, how to get these pieces into your STATIC class. You could create a special method that you call to just load them via the resources class of Unity, then, create function that will return a CLONE of the item in question. This way, you leave the original alone, and have your own to work with in your game as needed.
Something like...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.Collections;
namespace Assets.Scripts
{
public static class MyGameObjManager
{
private static bool alreadyLoaded;
public static GameObject myHero { get; private set; }
public static GameObject myWeapon {get; private set;}
public static void LoadObjects()
{
if (alreadyLoaded)
return;
// Load the resource which is RELATIVE to the "Resources" path...
myHero = (GameObject)Resources.Load("Prefab/Hero");
// This OTHER appears to load at a SPECIFIC Path allowing it to go to the root level ASSETS
myWeapon = (GameObject)Resources.LoadAssetAtPath("Assets/Resources/Prefab/Weapon", typeof(GameObject));
alreadyLoaded = true;
}
}
public class GameMgr2 : MonoBehaviour
{
public static GameObject GetHero()
{
MyGameObjManager.LoadObjects();
return (GameObject)Instantiate(MyGameObjManager.myHero, new Vector3(0, 0, 0), new Quaternion());
}
public static GameObject GetWeapon()
{
MyGameObjManager.LoadObjects();
return (GameObject)Instantiate(MyGameObjManager.myWeapon, new Vector3(0, 0, 0), new Quaternion());
}
}
}
So, the STATIC class CAN NOT derive from MonoBehavior which allows the "Instantiate" call to create the clone of the object and return it. So, I created a SECONDARY non-static class that IS derived from MonoBehavior and put static methods that call the static game object manager to get the prefabs I need.
AGAIN, I too am still learning, but hope this helps you some with your design considerations... As for the calling to get the hero or weapon, you could obviously parameterize it by passing in its Vector3 coordinate, or Quaternion value, but you can run with it.
STATIC vs NOT
Per your code and making it a STATIC means a single instance no matter how many of the class are put anywhere within the system. So if you have 5 prefabs on a screen and try to put each one to a different "thing", it will overwrite with whatever one was assigned last. Having the property as just "public" will allow you to have 5 prefabs on a screen and then drag/drop different "things" on each respectively and each will retain their own "thing" instance.
Sorry I can't advise more without knowing what you are trying to implement.
non-static should help within the editor by drag/drop. However, STATIC
First post so hi! (please correct me if I use some term wrong)
I've got a class Screen in which I create an instance of another class named Player (let's call this instance Player). Now, in Screen I have a method called ResetHUD which I would like to call from Player.
How would I do that? The only code inside ResetHUD is 1 line which sets a variable to another variable. As far as I know I can use EventHandler (but since ResetHUD doesn't need any arguments I'm thinking that I'm using that wrong) or delegates (which I have no idea how to use).
Help is appreciated!
It sounds like you have a containment relationship... the Screen object contains one (or more?) Player objects.
When Screen creates Player, it can set a property on Player indicating the parent (creating) Screen object.
class Player
{
private Screen parentScreen;
public Player(Screen parentScreen) { this.parentScreen = parentScreen; }
public MyMethodThatHasToCallScreensMethod()
{
parentScreen.ResetHUD();
}
}
class Screen
{
public Player CreatePlayer()
{
return new Player(this);
}
}
You can also use events (events by the way can have parameters, or not, depending on what you need).
This is my favorite tutorial on events and delegates in C#. Starts simple and builds on each step.
http://www.akadia.com/services/dotnet_delegates_and_events.html
Put a MyScreen property on the Player class. When you create the Player class, set that property to your screen class. You now have a reference to screen that you can call from inside player.
Well, something in Player needs to have a reference to the Screen instance. Options:
When you create the Player, pass this to the constructor so that it can remember the Screen it belongs to
Create an event on Player and subscribe to it from Screen
Pass the Screen along as parameters along the method call chain involved (this may not be practical in your situation)