I have created a Scriptable Object with [CreateAssetMenu] option in the editor and it's called 'Assets/MyCard1.asset'. In there, I have specified some values like name, sprite, attack, health etc.
So my goal is to spawn a deck of 30 cards based on the same Prefab, but when I use 'Instantiate(gamobject)', it spawns a gameobject with default Prefab parameters. How do I assign 'Assets/MyCard[i].asset' data to EACH of newly spawned cards (with code)? I can do that with Inspector just fine by dragging Asset to the Prefab's script component.
[CreateAssetMenu(fileName = "New Card", menuName = "Card")]
public class CardScriptable : ScriptableObject
{
public new string name;
public string description;
public Sprite artwork;
public int manaCost;
public int attack;
public int health;
}
public class SpawnStuff : MonoBehaviour
{
public GameObject myPrefab;
GameObject[] tempKarta = new GameObject[30];
void Start()
{
for (int i = 0; i < 30; i++)
{
tempKarta[i] = Instantiate(myPrefab);
temp.Karta[i]. ?? DRAW_DATA_FROM "Assets/MyCard1.asset" (); // ??
}
}
}
Is my approach rational? If not, what is a better approach to this?
UPD:
Here's my presets, and prefab
also script that just displays stuff from CardScriptable
public class CardDisplay : MonoBehaviour
{
public CardScriptable card;
public Text nametext;
public Text descriptionText;
public Image artworkImage;
public Text manaText;
public Text attackText;
public Text healthText;
void Awake()
{
nametext.text = card.name;
descriptionText.text = card.description;
artworkImage.sprite = card.artwork;
}
}
You allready have your structure. All you would need is not do your stuff in Awake but rather have a method for it like
public class CardDisplay : MonoBehaviour
{
public Text nametext;
public Text descriptionText;
public Image artworkImage;
public Text manaText;
public Text attackText;
public Text healthText;
public void Initialize(CardScriptable card)
{
nametext.text = card.name;
descriptionText.text = card.description;
artworkImage.sprite = card.artwork;
}
}
Then have e.g. an array of CardScriptable[] in
public class SpawnStuff : MonoBehaviour
{
// Rather make this of the correct type!
public CardDisplay myPrefab;
private CardDisplay[] tempKarta = new CardDisplay[30];
// Adjust in Inspector
public CardScriptable[] availableCardConfigs;
void Start()
{
for (int i = 0; i < 30; i++)
{
var config = availableCardConfigs[Random.Range(0, availableCardConfigs.Length)];
// If you make the prefab of correct type this already returns a CardDisplay
tempKarta[i] = Instantiate(myPrefab);
tempKarta[i].Initialize(config);
}
}
}
After a few more details were added I think I have a handle on the issue.
You can create another ScriptableObject class which is used as a collection to avoid any external file issues which you may be having eg
Public class Deck : ScriptableObject {
Public CardScriptable[] cards;
}
ScriptableObjects can have methods of their own. The typical method I use in this situation is to make a method on your script able object to instantiate the cards and assign values there as opposed to instantiating the cards that way.
Edit: possible misinterpretation on my part, if the problem is you cannot easily change the text/sprites etc. I would make a handler class to put on your card prefab which holds references to where you need once instantiated.
That way you can use for eg GetComponent().sprite = newSprite;
Related
I want to create public Scene[] levels; and manually assign my levels to the array, then loop through it and generate level selection buttons, but it won't show up in the inspector.
Is there any workaround?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class True : MonoBehaviour
{
// Start is called before the first frame update
public static int money;
[SerializeField]
public SceneManager[] scenes;
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void nextscencefirst()
{
SceneManager.LoadScene("Level2");
money++;
}
}
SceneManager is a built-in static class to control scenes during runtime. We can go to any scene by calling LoadScene method from that class. Also SceneManager cannot be seen in Inspector since it cannot be serialized class.
A reminder: Your scenes should be listed on build settings for these to work.
There are three ways to do what you want to do:
Method 1. Create a string list that holds names of scenes
public List<string> sceneNameList = new List<string>;
Method 2. Create a list of indices of scenes that added in build settings.
public List<int> sceneBuildIndexList = new List<int>
If the names of scenes are long or somehow difficult or you want to use sceneBuildIndex value it would be good to create a class and reach scenes from it
[System.Serializable]
public class SceneData
{
public int sceneBuildIndex;
public string sceneKey;
//public void LoadScene() //Maybe
}
public List<SceneData> sceneDataList = new List<SceneData>();
public SceneData GetSceneData(string key)
{
for (int i = 0; i < sceneDataList.Count; i++)
{
if (sceneDataList[i].sceneKey == key)
{
return sceneDataList[i];
}
}
return null;
}
Method 3: In Unity most classes are created from UnityEngine.Object, so it may let you assign scene from the Inspector.
[System.Serializable]
public class SceneData
{
public UnityEngine.Object scene;
public string key;
public void LoadScene()
{
if (scene == null)
{
Debug.LogError("Scene is not assigned");
return;
}
string pathToScene = AssetDatabase.GetAssetPath(scene);
SceneManager.LoadScene(pathToScene); //If the scene is not set in BuildSettings it will throw an error.
}
}
public List<SceneData> sceneDataList = new List<SceneData>();
public SceneData GetSceneData(string key)
{
for (int i = 0; i < sceneDataList.Count; i++)
{
if (sceneDataList[i].key == key)
{
return sceneDataList[i];
}
}
return null;
}
private void Awake()
{
SceneData data = GetSceneData("Level1");
if (data != null)
{
data.LoadScene();
}
}
I need stats in a game that I'm making and I used code from Brackeys(https://www.youtube.com/watch?v=e8GmfoaOB4Y&t=136s), he can only set code in the inspector, but I want to make a database-like manager that assigns stats to every unit in the game in one single script.
public class Stat
{
[SerializeField]
//public float baseValue; // Starting value
public float baseValue;
}
public class CharacterStats : MonoBehaviour
{
public Stat armor;
void SetArmorToFive(){
armor = ???
}
}
What you probably want to do is keep a reference to every CharacterStats instance in the scene.
Than Create some kind of function that makes you able to set the variables of every CharacterStats instance via the Stat class.
example:
Global script that holds a reference to all the CharacterStats Instances
public class CharacterStatsHolder : MonoBehaviour
{
public CharacterStats[] characterStats;
private void Start()
{
characterStats = FindObjectsOfType<CharacterStats>();
foreach(CharacterStats stat in characterStats)
{
stat.SetArmorLevel(5);
}
}
}
The same script as in the question but with a parameter in the SetArmorLevel function
public class CharacterStats : MonoBehaviour
{
public Stat armor;
void SetArmorLevel(int level){
armor = level;
}
}
The same stat script
public class Stat
{
[SerializeField]
//public float baseValue; // Starting value
public float baseValue;
}
Here is a quick link if you're interested in learning how parameters work. https://www.youtube.com/watch?v=1MFiyP0RPW0
My variable in my parameter method is not being called in another class.
I tried telling it that we are getting the PlayerdmgAmount from the playerlivesdisplayed class. I get an error that says that PlayerLivesDisplay can not be converted to an int.
So I comment that out and wrote in the int value again. The code runs, but it is not doing what I want it to do.
public class PlayerLivesDisplay : MonoBehaviour
{
public void takeLives(int PlayerdmgAmount)
{
playerLives -= PlayerdmgAmount;
displayUpdate();
if (playerLives <= 0)
{
//TODO load mainMenu
}
}
}//playerLives
public class DamgePlayer : MonoBehaviour
{
private void OnTriggerEnter2D(Collider2D othercollision)
{
//PlayerLivesDisplay PlayerdmgAmount = GetComponent<PlayerLivesDisplay>()
int PlayerdmgAmount = 1;
FindObjectOfType<PlayerLivesDisplay>().takeLives(PlayerdmgAmount);
}
}
public class Attacker : MonoBehaviour
{
[Range(0f, 10f)] [SerializeField] float walkSpeed = 1f;
[SerializeField] int PlayerdmgAmount = 1;
GameObject currentTarget;
public void hurtplayer(int PlayerdmgAmount)
{
FindObjectOfType<PlayerLivesDisplay>().takeLives(PlayerdmgAmount);
}
}
What I am trying to achieve:
Have the attacker script have Player dmg amount on them.
Golem1 = take 5 lives away
Fox: takes 2 lives away
Pass these variables (when collided) to the players health damage (DamagePlayer script)
Then go to the player lives display class takeLives method and input the damage variables into the parameters that was initiated from the attackers script.
If your takeLives method takes an int variable as argument, you cannot pass your PlayerLivesDisplay object, you need to pass an int instead (that's what the error is about). PlayerLivesDisplay may contain the PlayerdmgAmount (so the int) but is not in itself the PlayerdmgAmount.
Depending on what you are trying to accomplish, you could do something like:
In your PlayerLivesDisplay add a property and use it to store the value that you need to get later on:
public class PlayerLivesDisplay : MonoBehaviour
{
...
public int PlayerdmgAmount { get; set; }
...
public void takeLives(int playerdmgAmount)
{
...
this.PlayerdmgAmount = playerdmgAmount;
}
}
Now you can access the value in other classes:
public class DamgePlayer : MonoBehaviour
{
private void OnTriggerEnter2D(Collider2D othercollision)
{
int playerdmgAmount = GetComponent<PlayerLivesDisplay>().PlayerdmgAmount;
...
}
}
Trying to make a Tile system in Unity. I have my tile class done but when I create a list in the TileManager class it gives me the option to change the size of the list but gives me an empty element with no changeable variables.
Tile Class:
public class Tile : MonoBehaviour
{
public Sprite tileSprite;
public string tileName;
public int tileID;
public Tile(Sprite newTileSprite, string newTileName, int newTileID)
{
tileSprite = newTileSprite;
tileName = newTileName;
tileID = newTileID;
}
void Start()
{
SpriteRenderer spriteRenderer = gameObject.GetComponent(typeof(SpriteRenderer)) as SpriteRenderer;
if (spriteRenderer != null) spriteRenderer.sprite = tileSprite;
}
TileManager Class:
public class TileManager : MonoBehaviour {
public List<Tile> Tiles;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update () {
}
}
Use the System.Serializable attribute on Tile class. Also be sure that the Tile class does not inherit from MonoBehaviour, otherwise it will still not appear in the inspector list.
[System.Serializable]
public class Tile
{
public Sprite tileSprite;
public string tileName;
public int tileID;
public Tile(Sprite newTileSprite, string newTileName, int newTileID)
{
tileSprite = newTileSprite;
tileName = newTileName;
tileID = newTileID;
}
}
I have a scene where I write down my player stats. In the next scene (basically in the next 2 scenes but it doesn't matter) I want to buy some weapons and change the variables.
The thing is I'm saving the object with "DontDestroyOnLoad" and when I go to the next scene I want to find out how can I change the variables.
First Scene:
The code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class statsTextsToDisplayAndChanges : MonoBehaviour
{
public static statsTextsToDisplayAndChanges _INstance;
buyWeapons bw;
public Text m_LevelText;
public Text m_AttackText;
public Text m_DefendText;
public Text m_MoneyText;
public int level = 0;
public int money = 500;
public int health = 400;
public int attack = 10;
public int def = 5;
string answer;
string url = "http://alex3685.dx.am/display.php";
public int sword1 = 200;
public void Start()
{
_INstance = this;
DontDestroyOnLoad(this.gameObject);
display();
}
public void display()
{
m_LevelText.text = level.ToString();
m_AttackText.text = attack.ToString();
m_DefendText.text = def.ToString();
m_MoneyText.text = money.ToString();
}
public void onPurchase()
{
if (money >= sword1)
{
Debug.Log("YOU BOUGHT IT");
money -= sword1;
//Destroy(GameObject.Find("playerStats"));
display();
}
}
}
Second scene:
When I press the button the debug.log from the purchase function works but in the text nothing changes (from 500 coins-200 coins of the sword=300 coins).
Any ideas?
Thanks in advance.
Hmmm....initial thoughs:
This is one of the times where you want a true manager object with a scrip attached that only knows how to store the relevant stat.
Then, in whatever scripts that handles the purchasing they have a private GameObject with a reference to the manager object.private GameObject _Manager;
But the object exists in another scene in my editor so I cant drag and drop the reference!
No problem,
Lets say that the object with the component that stores the stats are called "PlayerManager". In the script that handles the purchasing in the start() method add: _Manager = gameobject.Find("PlayerManager");
Now you can change all the variables to your hearts content, assuming of course that you have set the variables in the manager to be public, or if you are more advanced and concerned about robust code, you have the proper get;set; methods in place.
Hope this helped!
Use PlayerPrefs to save and retrieve data.
Source : https://docs.unity3d.com/ScriptReference/PlayerPrefs.html