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;
}
}
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 am trying to make a vehicle changing game where there are 3 gameObject: car, tank and hover and if I press a button to change from car to a tank/hover i want them to be in a position where the car was.
the z axis is forward
I tried this code below but when I go backwards this will not work
UnityEngine;
public class Carswitcer : MonoBehaviour
{
public Transform car;
public Transform hover;
public Transform tank;
void Start()
{
car.gameObject.SetActive(true);
tank.gameObject.SetActive(false);
hover.gameObject.SetActive(false);
}
public void Car()
{
if (car.position.z < hover.position.z)
{
car.position = hover.position;
}
if (car.position.z < tank.position.z)
{
car.position = tank.position;
}
car.gameObject.SetActive(true);
tank.gameObject.SetActive(false);
hover.gameObject.SetActive(false);
}
public void Tank()
{
if (tank.position.z < hover.position.z)
{
tank.position = hover.position;
}
if (tank.position.z < car.position.z)
{
tank.position = car.position;
}
car.gameObject.SetActive(false);
tank.gameObject.SetActive(true);
hover.gameObject.SetActive(false);
}
public void Hover()
{
if (hover.position.z < car.position.z)
{
hover.position = car.position;
}
if (hover.position.z < tank.position.z)
{
hover.position = tank.position;
}
car.gameObject.SetActive(false);
tank.gameObject.SetActive(false);
hover.gameObject.SetActive(true);
}
Why compare the z at all? Simply always copy the position of the currently active vehicle:
public class Carswitcer : MonoBehaviour
{
public Transform car;
public Transform hover;
public Transform tank;
// Store the current vehicle
private Transform currentVehicle;
void Start()
{
tank.gameObject.SetActive(false);
hover.gameObject.SetActive(false);
SetActiveVehicle(car);
}
private void SetActiveVehicle(Transform newActiveVehicle)
{
// Is there a current vehicle?
if(currentVehicle)
{
// If so copy its position and set it inactive
newActiveVehicle.position = currentVehicle.position;
currentVehicle.gameObject.SetActive(false);
}
// Store the new active reference and set it active
currentVehicle = newActiveVehicle;
currentVehicle.gameObject.SetActive(true);
}
public void Car()
{
SetActiveVehicle(car);
}
public void Tank()
{
SetActiveVehicle(tank);
}
public void Hover()
{
SetActiveVehicle(hover);
}
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;
I am looking to do a specific task for a small unity game I am doing for class.
In it, I am trying to hash a class that contains variables and a method that is specific to each class created for the dictionary. The variables work fine as they do not need to be static and are not abstract, the method however I am struggling to work with.
Here is my entire script being used.
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
public class EnemyZ : MonoBehaviour
{
// Class used to hash names health assets and then move options
public class EnemySet
{
public string EnemyName { get; set; }
public float EnemyHealth { get; set; }
public void Moves()
{
}
}
//setting EnemySet with names health (assets and move options)
public static void Setup()
{
var cards = new Dictionary<string, EnemySet>()
{
{ "Slime", new EnemySet { EnemyName="Slime", EnemyHealth= 25} },
{ "Flaz", new EnemySet { EnemyName="Flaz", EnemyHealth= 34} },
{ "BandShee", new EnemySet { EnemyName="BandShee", EnemyHealth= 45} },
{"Fan-Natic", new EnemySet{EnemyName = "Fan-Natic", EnemyHealth = 20} }
};
}
void Update()
{
}
}
I am looking to have the Move function be overridden and callable.
How would I set this dictionary of class/method's up and how would I call it once it has been correctly added to the dictionary?
I think you are approaching your class structure and the approach to overwriting your abstract methods incorrectly which is causing the confusion around the dictionary.
I would create an interface for enemies that defines what your enemies need to contain and perform. You can then create an abstract base class the implements common functionality for enemies. Each enemy type should then inherit from your base class.
See below as an example:
// all enemy types must implement the following
public interface IEnemy {
string EnemyName { get; }
float EnemyHealth { get; }
void Move ();
}
// abstract base class for common functionality
public abstract class Enemy : IEnemy {
protected float speed = 0.1f;
public string EnemyName { get; protected set; }
public float EnemyHealth { get; protected set; }
public virtual void Move () {
Debug.Log ($"{EnemyName} moving at {speed}");
}
}
public class Slime : Enemy {
public Slime () {
speed = 0.1f;
EnemyName = "Slimer";
EnemyHealth = 100f;
}
public override void Move () {
Debug.Log ($"{EnemyName} moving at {speed}");
}
}
public class Flaz : Enemy {
public Flaz () {
speed = 0.5f;
EnemyName = "Flaz";
EnemyHealth = 50f;
}
public override void Move () {
Debug.Log ($"{EnemyName} moving at {speed}");
}
}
public class Test : MonoBehaviour {
readonly List<IEnemy> Enemies = new List<IEnemy> ();
void Start () {
var slimer = new Slime ();
Debug.Log ($"{slimer.EnemyName} initialized with health of {slimer.EnemyHealth}");
slimer.Move ();
var flaz = new Flaz ();
Debug.Log ($"{flaz.EnemyName} initialized with health of {flaz.EnemyHealth}");
flaz.Move ();
Enemies.Add (slimer);
Enemies.Add (flaz);
Debug.Log ($"Added {Enemies.Count} enemies");
}
}
I'd say that having a better understanding of classes and interfaces would be good if you wanted to store your enemies in code (the easy way if you're good at object oriented code).
However to answer the question directly I'm going to assume that you're loading enemies from a database or a file or some other non-code format.
In that case I'd go with something like
// Class used to hash names health assets and then move options
public class EnemySet
{
public string EnemyName { get; set; }
public float EnemyHealth { get; set; }
public Action Moves { get; set; }
}
Then when ever you are reading from your datasource to set up the dictionary of enemies I'd have something like
public class EnemyLoader
{
public void MovePawn() => console.WriteLine("I'm a chess piece");
public void MoveZombie() => console.WriteLine("Brains");
public void MoveSlime() => console.WriteLine("Wobbo");
public Action PickMovement(string moveType)
{
switch(moveType)
{
case "pawn": return MovePawn;
case "pawn": return MoveZombie;
default: return MoveSlime;
}
}
public Dictionary<string, EnemySet> LoadEnemies()
{
var dataSource = ReadDataFromSomewhere();
return dataSource.ToDictionary(
k => k.EnemyName,
v => new EnemySet
{
EnemyName = v.EnemyName,
EnemySpeed = v.EnemySpeed,
Move = PickMovement(v.MoveType)
});
}
}
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;
...
}
}