I'm creating a system where I have my cards in ScriptableObject, but each card has a unique function. What I thought of was creating a new class for each skill that inherits from "skills". So I put this script for each letter in my ScriptableObject, but now I'm trying to access it and I can't, because I couldn't use the AddComponent.
public class Cards : ScriptableObject
{
public Sprite cardSprite;
public int cardAttack;
public int cardHealth;
public int cardCost;
public Object cardAbility;
}
As you realized, you can't use AddComponent with ScriptableObjects, because it can only be used to attach components to GameObjects.
ScriptableObjects however do support a somewhat similar concept of main assets and sub assets.
You can use AssetDatabase.AddObjectToAsset to add new sub assets, and they'll appear as nested children below the main asset in the Project view.
public abstract class Skill : ScriptableObject
{
#if UNITY_EDITOR
protected static void AddToCard<TSkill>(MenuCommand command) where TSkill : Skill
{
Card card = (Card)command.context;
TSkill skill = CreateInstance<TSkill>();
skill.name = skill.GetType().Name;
card.skill = skill;
string path = AssetDatabase.GetAssetPath(card);
AssetDatabase.AddObjectToAsset(skill, path);
AssetDatabase.ImportAsset(path);
}
[MenuItem("CONTEXT/Card/Remove Skill")]
protected static void RemoveSkill(MenuCommand command)
{
Card card = (Card)command.context;
string path = AssetDatabase.GetAssetPath(card);
Skill skill = AssetDatabase.LoadAssetAtPath<Skill>(path);
AssetDatabase.RemoveObjectFromAsset(skill);
AssetDatabase.ImportAsset(path);
}
#endif
}
using UnityEditor;
using UnityEngine;
[CreateAssetMenu]
public class ExampleSkill : Skill
{
#if UNITY_EDITOR
[MenuItem("CONTEXT/Card/Add Skill/Example Skill")]
private static void AddToCard(MenuCommand command) => AddToCard<ExampleSkill>(command);
#endif
}
using UnityEngine;
[CreateAssetMenu]
public class Card : ScriptableObject
{
public Skill skill;
}
A scriptable object is a data container. A material or lighting settings are scriptable objects. You can save them as assets and use them as settings for other scripts, but not attach them to GameObjects. Read more here.
To create ScriptableObjects in the asset browser, you need to add the following before the start of your class.
[CreateAssetMenu(fileName = "DefaultFileName", menuName = "ScriptableObjects/NameOfYourObjects", order = 1)]
To add them to a script, you use public Cards cards; then you can drag them from your asset browser to the script in the inspector.
The only things you can add to a GameObject are MonoBehaviors
Related
I tried to create nested object in C# script in Unity and change the values in it.
Class of nested object
using UnityEngine;
public class Replica : ScriptableObject
{
public string Text;
public int Speed = 1;
public int PersonaID = 0;
}
Class with nested object
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/DialogContainer", order = 1)]
public class DialogContainer : ScriptableObject {
public Replica[] Replicas; // Here is nested object
public Sprite[] Avatars;
}
And when I created the ScriptableObject I saw that:
Photo of ScriptableObject interface
Here I can only put an instance of the class there, but I cannot configure it.
But i want to change values right in inspector without creating and inserting object of class "Replica" like in InputManager where I can create one more obj in axis, open it and change values in inspector like that.
Photo of interface I want to see
If u want configurate your class in inspector, that class must be not derived from MonoBehaviour or ScriptableObject and be with tag [System.Serializable] than u can serialize it in nested class without creating instance.
Replica:
[System.Serializable]
public class Replica
{
public string Text;
public int Speed = 1;
public int PersonaID = 0;
}
Dialog Container:
using System.Collections.Generic;
using UnityEngine;
public class DialogContainer : MonoBehaviour{
public List<Replica> Replicas;
public Sprite[] Avatars;
}
Than I saw that:Inspector screenshot
Unless you make a custom editor for DialogContainer, you can't do what you want to do. If you have a parent ScriptableObject that contains a child list of ScriptableObjects, you can't edit each child's fields when you have the parent selected in your Project View.
You will have to edit each child Replica by selecting the Replica instance in the Project View. Then the Inspector will let you edit that Replica.
I've been trying to perfectly structure this project I'm working on in different classes while maximizing the benefits of inheritance. So far however, it's given me more headaches than benefits.
Consider this:
public class SuperClass : MonoBehaviour
{
[SerializeField] protected Camera _camera;
}
and this
public class SubClass : SuperClass
{
}
Both scripts are attached to different game objects in the scene.
The Camera is to be assigned by dragging it in the inspector
I tried this, and unity seemed to tell me that I had to assign the camera to the SuperClass game object AND to the subclass game object, which makes no sense to me.
How can I assign a camera to SuperClass.cs, which is then used and shared by all of its subclasses?
Thanks in advance!
shared by all of its subclasses
Shared by classes could can only be achieved by using "static" (static variable or singleton).
A workaround could be
public class SubClass :SuperClass
{
[SerializeField] Camera camera;
void Awake()
{
if(camera!=null)
{
_camera=camera;
}
}
// Start is called before the first frame update
void Start()
{
camera=_camera;
}
}
To further extend the solution, you could write a editor script or just get the camera from the code.
You need to create public static Camera property somewhere and reference it in your code, using property:
public static class StaticValues
{
public static Camera Camera {get; set;}
}
public class SuperClass : MonoBehaviour
{
[SerializeField] protected Camera _camera
{
get
{
return StaticValues.Camera;
}
set
{
StaticValues.Camera = value;
}
}
}
public class SubClass : SuperClass
{
}
I have a problem when changing values and trying to play game these values disappear. For example, if I enable bool in editor and the start game bool disables itself. I use custom editor to change these values.
This is my settings script and [system.serializable] doesn't help:
using UnityEngine;
[System.Serializable]
public class Settings : MonoBehaviour
{
public static bool testMode;
public static string androidUnitID;
public static string iphoneUnitID;
}
And this is my editor script:
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(Settings))]
class TestEditor : Editor {
public override void OnInspectorGUI() {
GUILayout.Label("Settings", EditorStyles.boldLabel);
Settings.testMode = EditorGUILayout.Toggle("Test Mode", Settings.testMode);
Settings.androidUnitID = EditorGUILayout.TextField("Android UnitID", Settings.androidUnitID);
Settings.iphoneUnitID = EditorGUILayout.TextField("Android UnitID", Settings.iphoneUnitID);
}
First I'm changing values on editor:
Then when play mode is active all values disappear:
Is there any way to fix this problem?
Thank you for your help.
Unity doesn't support serialising static variables "out-of-the-box" (you can rewrite the serialisation to accommodate it, but effort vs. reward and all that). So, based on that, what you're essentially seeing is the values that you're setting being stored in the class during Edit, but when Unity goes into Play mode, all the class are re-initialised, deserialised and prepared for game-play. Your static values are lost.
It's the same reason why changed values during game play aren't kept when you go back the other way to Edit mode. Edit mode and Play mode are essentially two separate processes.
Your best bet here would be to create a ScriptableObject, with those fields you've marked as static in your Settings class, create a reference to the new ScriptableObject, and then, if you want, modify the values directly from the ScriptableObject.
Something along the lines of:
[CreateAssetMenu ( fileName = "Settings", menuName = "Settings/Create Settings SO", order = 1 )]
public class SettingsScriptableObject : ScriptableObject
{
public bool testMode;
public string androidUnitID;
public string iphoneUnitID;
//... Any further configuration/settings items you want should go here.
}
You would then modify your Settings class:
public class Settings : MonoBehaviour
{
public SettingsScriptableObject SettingsSO;
}
And your editor script:
[CustomEditor(typeof(Settings))]
class TestEditor : Editor
{
public override void OnInspectorGUI()
{
if ( target == null ) return;
var settings = target as Settings;
GUILayout.Label("Settings", EditorStyles.boldLabel);
settings.SettingsSO.testMode = EditorGUILayout.Toggle("Test Mode", settings.SettingsSO.testMode);
settings.SettingsSO.androidUnitID = EditorGUILayout.TextField("Android UnitID", settings.SettingsSO.androidUnitID);
settings.SettingsSO.iphoneUnitID = EditorGUILayout.TextField("Android UnitID", settings.SettingsSO.iphoneUnitID);
}
}
There's an excellent video at Unity about the Edit and Play modes, and how ScriptableObjects can be used to overcome the serialisation problem.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
public class Prefabs : MonoBehaviour
{
List<GameObject> prefabs = new List<GameObject>();
// Use this for initialization
void Start ()
{
var resourcesPath = Application.dataPath;
var absolutePaths = System.IO.Directory.GetFiles(resourcesPath, "*.prefab", System.IO.SearchOption.AllDirectories);
foreach (var absolutePath in absolutePaths)
{
var prefab = Path.GetFileName(absolutePath);
prefabs.Add(prefab);
}
}
// Update is called once per frame
void Update () {
}
}
The problem is that the variable prefab is a string and the list is type GameObject. What i want to get all the prefabs not as strings but as prefabs.
Update:
I moved all the Prefabs to the path Asstes/Resources
But i'm getting 0 prefabs.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
public class Prefabs : MonoBehaviour
{
// Use this for initialization
void Start ()
{
var pres = Resources.LoadAll<GameObject>("Assets/Resources/");
}
}
The variable pres is empty.
I want to get all the prefabs in the folder Resources and the subdirectories in Resources.
Assuming you are using Unity5, take a look at AssetDatabase.LoadAssetAtPath. https://docs.unity3d.com/ScriptReference/AssetDatabase.LoadAssetAtPath.html
...From the linked page:
static void ImportExample()
{
Texture2D t = (Texture2D)AssetDatabase.LoadAssetAtPath("Assets/Textures/texture.jpg", typeof(Texture2D));
}
There's a pretty good explanation on the page on how to use. Pay attention to the notes, as things like "only use forward slashes" is pretty important!
Also note that this is in the UnityEditor namespace (which isn't normally included in a build), so you will probably want an #if around it.
#if (UNITY_EDITOR)
... your class/code ...
#endif
You can create a new class, which derives from the GameObject class. And in your new class, you can add a string property.
For example:
public class MyGameObject : GameObject{
public MyGameObject() : base(){
Console.WriteLine("Calls constructor from base class and this!");
}
public string MyPath {get; set;}
}
And then you can use your class, like this:
List<MyGameObject> prefabs = new List<MyGameObject>();
foreach (var absolutePath in absolutePaths)
{
var prefab = Path.GetFileName(absolutePath);
prefabs.Add(new MyGameObject(){MyPath = prefab);
}
If you want to play with that constellation, look here: https://dotnetfiddle.net/Y1KPpU
I'm trying to extend a base class on my player object.
player has damage script that looks like this
using UnityEngine;
using System.Collections;
public class Damage : MonoBehaviour
{
public int health = 100;
public virtual void ReceiveDamage(int damageAmount)
{
Debug.Log ("Original");
}
}
And then the same player has another script like this :
using UnityEngine;
using System.Collections;
public class playerDamage : Damage
{
public override void ReceiveDamage(int damageAmount)
{
Debug.Log ("Extended");
}
}
But when I call the script from a 3rd scrip on another object like this:
var damageScript = collision.gameObject.GetComponent<Damage>();
if( damageScript)
{
damageScript.ReceiveDamage(damageAmount);
}
the only response to the log is "Original"
Shouldn't the child be called and "Extended" written to the log?
There are several ways to do this. The easiest one is to SendMessage.
collision.gameObject.SendMessage("ReceiveDamage", damageAmount);
Whatever implementation of ReceivedDamage that the collision GameObject has, that will be the one that is called. This is awesome because you don't need to specify the type yourself nor use GetComponent.
Important Extra Information
In any implementation that you choose the key step is to make sure that the right script is attached to the collision.gameObject. If you attach both scripts then you are playing with fire.
To avoid playing with fire please make Damage an abstract class.
public abstract class Damage : MonoBehaviour
{
public int health = 100;
public virtual void ReceiveDamage(int damageAmount)
{
Debug.Log ("Original");
}
}
Abstract will give you the same functionality you want, except that Unity3d won't let you attach Damage to the GameObjects, which is good to avoid mistakes. You will always have the option to have the original ReceiveDamage and the choice to override it on future classes that inherit from Damage, like this:
public class Example : Damage
{
// This one still has ReceiveDamage but it happens in the base class.
}
or
public class PlayerDamage : Damage
{
public override void ReceiveDamage(int damageAmount)
{
Debug.Log("Extended");
}
}
I think when you call this line:
var damageScript = collision.gameObject.GetComponent<Damage>();
It gets the component by name, which in this case would be the Damage script, not the playerDamage script.
In you script it should be:
var damageScript = collision.gameObject.GetComponent<playerDamage>();
if( damageScript)
{
damageScript.ReceiveDamage(damageAmount);
}