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
Related
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.
I wanted to make a programme if it take a TextAsset from inspector(it is placed in 'ExampleEditor'), it make a custom data instance for use blendshape animation.
At first I made my custom data class not using [System.Serializable].
public class Matrix {
public int row;
public int col;
public double[,] mat;
}
My strategy was taking a TextAsset, parsing string from TextAsset and finally making a Matrix instance to a field variable in 'Example.cs'. I thought when it was initialized and assigned first, I was able to use that variable in Example instance. However, It did not work, It threw 'NullReferenceException'.
So I found solution using '[System.Serializable]'. It did work. (and I knew that Unity3D does not provide multi-dimensional array for their serialization method.)
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
[System.Serializable]
public class Matrix {
[System.Serializable]
public class mRow {
public List<float> aRow = new List<float>();
}
[HideInInspector]
[SerializeField]
public int row;
[HideInInspector]
[SerializeField]
public int col;
//[HideInInspector]
[SerializeField]
public List<mRow> mat = new List<mRow>();
}
Finally, I know the solution, But I don't know why this problem was happened. It is related to GC?
Your question isn't very clear, but I think to know what you are not understanding.
My strategy was taking a TextAsset, parsing string from TextAsset and
finally making a Matrix instance to a field variable in 'Example.cs'.
You didn't explain how you did that (perhaps call the parsing function from a custom inspector?).
However, despite on how you managed to initialize a field from the editor, if the field isn't serializable, it will be null when switching from editor to play mode.
That happens because when entering play mode, Unity3D serializes all C# classes populating the relative C++ classes of the engine. If a field isn't a public field of a type that Unity can serialize (or even a private field marked with a SerializeFieldAttribute, always of a type that Unity can serialize ), Unity3D won't serialize it.
So when you switch to play mode the reference will be null.
In other word, all fields that you want to initialize from the editor MUST be serializable in order to be used in play mode.
[SerializeField]
public List<mRow> mat = new List<mRow>();
The code above doesn't make much sense. If you plan to intialize a serializable field from the inspector don't initialize it by code, otherwise you could eventually lose the reference.
An example of such an error:
[SerializeField]
private List<int> aList;
public void Start()
{
aList = new List<int>();
}
The code above will cause troubles. If you had initialized aList from inspector, it will be correctly serialized but when you switch in play mode e the Start callback is called, aList will be reinitialized and you'll end up with a reference to an empty list.
Here's a nice blog post explaining details on how Unity3D handle serialization of objects. Read it!
So I would like to create an extension method for a Type that the api has otherwise sealed.
If you know about extension methods the following should look familiar.
private static List<Member> memberList = new List<Member>();
public static List<Member> GetMemberList(this GameObject go)
{
return memberList;
}
Notice that to declare an extension method, it needs to be static, and because it needs to be static, the List that Im accessing through the GameObject type has to be static to.
I would like each GameObject to have its own list of Members. However Im pretty sure since this is a static field every instanced GameObject will point to the same static memberList.
So would my assumptions be true? And if so, what might be an alternative? I would like to avoid putting the GameObject in a wrapper class that also holds the memberList because the api only allows GameObjects to be detected and manipulated at runtime. There are ways to reverse reference the wrapper class through the gameObject but that adds a lot more complexity to the code i would like to avoid.
Yes, if you want to keep something outside the game object and access it through the extension method, it would have to be static.
You can use a dictionary to map one member list to each game object:
private static Dictionary<GameObject, List<Member>> memberLists = new Dictionary<GameObject, List<Member>>();
public static List<Member> GetMemberList(this GameObject go) {
return memberLists[go];
}
It appears you are working with Unity3. There is a related answer on the UnityAnswers site that may help: http://answers.unity3d.com/questions/22493/unity-3-sealed-class-gameobject-.html
It appears that there should be ways to attach the behaviors you want inherent in the Unity framework using the builtin scripting system.
Yes, you are right. If you have a static method, all instances of that class share the same data. The call return memberList; is illegal. It is the same as return this.memberList; and this is not available in a static method. Instead you'd have to call the class: return GameObject.memberList;. But I understand that you are not looking for this solution.
Extension methods are meant to create additional behavior. If you want to create additional data, extending the GameObject class using inheritance is the right choice.
Alternatively you could attach the memberList by using a dictionary of the form Dictionary<GameObject, List<Member>>. But personally I'd favor composition as shown below:
public class myGameObject
{
public List<Member> memberList { get; set; }
public GameObject go { get; set; }
}
private static ConditionalWeakTable<GameObject, List<Member>> dict = new ConditionalWeakTable<GameObject, List<Member>>();
public static List<Member> GetMemberList(this GameObject go)
{
return dict.GetOrCreateValue(go);
}
ConditionalWeakTable manages the object lifetime, because it uses weak-references. Therefore it doesn't stop the GC from collecting the GameObject if it there are no other live references to it, and this will also allow the List<Member> to be collected.
It is threadsafe, but this assumes that you want your starting point to be an empty list (the default constructor is called in GetOrCreateValue if there isn't a current value). If you want a different starting point, your threading issues become more complicated.
Extension methods are simply just static methods that "appear" to look like instance methods.
They however, do not add any additional functionality a static method doesn't, it's just for ease-of-use, maintenance, and readability. Extension methods cannot access protected / private members either.
If GameObject is not actually sealed (ie it doesn't have the sealed keyword), then you can write a class that inherits GameObject to gain access to its protected methods/fields/properties. This will only work if you yourself are the one constructing these objects.
I've been trying for hours now, but I have no idea what is wrong.
Normally, when you define a public object/var in Unity C#, it shows up in the inspector or default references where you can drag something to it.
Here's my test code:
using UnityEngine;
using System;
using System.Collections;
[Serializable]
public class modGlobals : MonoBehaviour {
public static GUIStyle defaultMask;
public GUIStyle trollo;
}
Aaaaaand...
I tried several options, wrote the thing in MonoDev as also in Visual Studio, put all my files in an namespace, w and w/o Serializable attribe but.. What could be wrong?
Thanks for help!
Yes you do need to attach it to a gameObject in the scene in order to show the public members in the property inspector, since your script inherits from MonoBehaviour.
This is what I got.
Additionally, you cannot expose static members in the property inspector - even when marked as SerializableAttribute. System.SerializableAttribute is not valid on this declaration type. It is valid on `class, struct, enum, delegate' declarations only.
In relation to your comment about requiring a globally accessible script with objects, you have a couple options, such as
Singleton script attached to gameObject (example), set with DontDestroyOnLoad() so it persists between scene changes.
Static class
I'm wondering if there is anyway to use the "Content.Load<>" in a class that is not the game itself, say I want to load a texture from within the class, rather than sending the texture to it.
namespace ProjectGame1
{
public class Ship
{
public Texture2D texture;
public Ship()
{
this.texture = Content.Load<Texture2D>("ship");
}
}
}
That's an example of what I'm trying to achieve
You just need to pass your ContentManager to the Ship object:
public Ship(ContentManager content)
{
this.texture = content.Load<Texture2D>("ship");
}
From your game class you instantiate the Ship:
Ship ship = new Ship(this.Content);
First of all, I recommend not using DrawableGameComponent, my reasoning for this is outlined in this answer here.
Now, to make your code work as-is, you need to pass a ContentManager into the constructor you are creating (see JoDG's answer). But to do this you must only construct it after the ContentManager is ready. For Game's content manager, this is during and after LoadContent being called (so not in your game's contstructor or Initialize method).
Now, you could do something like using DrawableGameComponent, which is much nicer: Just give your Ship class a LoadContent method, and call that from your game's LoadContent (as you would do for Draw and Update).
If the texture that your ship uses is not part of your ship's state (ie: all ships use the same texture), you could even make it static, saving you from having to call LoadContent on every ship you create. I have an example of this is this answer here, which also has a list of other useful information about Content Manager.
If the class derive form DrawableGameComponent then you still can use
Game.Content.Load<Texture2D>("ship");
In order to get this to work, you'll need a Constructor that accepts a parameter of type Game. This will mean you'll need to add a reference to Microsoft.Xna.Framework.Game.
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
namespace ProjectGame1
{
public class Ship : DrawableGameComponent // Notice the class now inherits from
{ // DrawableGameComponent
public Texture2D texture;
public Ship(Game game)
: base(game) // <<---- Don't forget to pass Game to the base constructor
{
this.texture = game.Content.Load<Texture2D>("ship");
}
}
}
I think you'll need a reference to the Game object to get its Content member. This could either be passed in or you could make the game a Singleton.
Okay I solved it, I used a different method since yours didnt quite work as I'd expect.
What I did was make a "public static ContentManager asd;" and then assigned it to Game's ContentManager, and then it worked by redirecting it to "Game1.asd
(Variable names are examples)