Hellos guys I am a beginner and have 2 classes. Class A has an Array that I need to access in Class B. I've searched through here for the past 36 hours trying different methods and this is what I ended up with any help?
This is the first Class with the Array
[System.Serializable]
public class Cli
{
public string name;
public int money;
};
public class BankManager : MonoBehaviour
{
private static BankManager _instance;
public static BankManager Instance
{
get
{
if (_instance == null)
{
GameObject go = new GameObject("BankManager");
go.AddComponent<BankManager>();
}
return _instance;
}
}
public GameObject player;
private string NaMe;
public Bank[] banks = new Bank[1];
public Cli[] clients = new Cli[1];
void Awake()
{
_instance = this;
}
void Start()
{
banks[0] = new Bank("In But Not Out", 10000);
player = GameObject.Find("Player");
clients[0].money = 1000;
}
void Update()
{
PlayerController pName = player.GetComponent<PlayerController>();
clients[0].name = pName.name;
}
}
The array I am trying to access is the clients[]
public class AccessAtm : MonoBehaviour
{
public Bank bank;
public int clams;
private void OnTriggerEnter2D(Collider2D other)
{
bank = BankManager.Instance.banks[0];
if (other.tag == "Player")
{
clams = Array.IndexOf(BankManager.Instance.clients,
other.GetComponent<PlayerController>().name);
Debug.Log(other.GetComponent<PlayerController>().name);
}
}
}
The debug.log properly tells me I'm getting the player.name I believe my error is in either getting that instance of the array or in the indexof command
The problem is IndexOf expects you to pass a instance of Bank for the 2nd parameter of
clams = Array.IndexOf(BankManager.Instance.clients,
other.GetComponent<PlayerController>().name);
you are passing it a string.
You need to use something else to search for it, the easist solution would be use a for loop.
var clients = BankManager.Instance.clients;
var otherName = other.GetComponent<PlayerController>().name;
int foundAt = -1;
for(int i = 0; i < clients.Length; i++)
{
if(clients[i].name == otherName)
{
foundAt = i;
break;
}
}
claims = foundAt;
Related
I am super new to C#, so apologies if this is a simple question or has already been answered. I've been looking at other SO questions, trying the exact methods they use, and am still having no luck.
In Unity, I have an Inventory Object class that I can use to create Inventories in my game:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "New Inventory", menuName = "Inventory System/Inventory")]
public class InventoryObject : ScriptableObject
{
public List<InventorySlot> Container = new List<InventorySlot>();
public void AddItem(ItemObject newItem, int itemAmount)
{
bool inventoryHasItem = false;
for (int i = 0; i < Container.Count; i++)
{
if (CurrentSlotHasItem(Container[i], newItem)) {
if (Container.FindAll((InventorySlot currentSlot) => CurrentSlotHasItem(currentSlot, newItem)).Count < newItem.maxStackSize)
{
Container[i].AddAmount(itemAmount);
inventoryHasItem = true;
break;
}
}
}
if (!inventoryHasItem)
{
Container.Add(new InventorySlot(newItem, itemAmount));
}
}
private bool CurrentSlotHasItem(InventorySlot currentSlot, ItemObject item)
{
return currentSlot.item == item;
}
}
[System.Serializable]
public class InventorySlot
{
public ItemObject item;
public int amount;
public InventorySlot(ItemObject _item, int _amount)
{
item = _item;
amount = _amount;
}
public void AddAmount(int value)
{
amount += value;
}
}
This works great, except for this line:
Container.FindAll((InventorySlot currentSlot) => CurrentSlotHasItem(currentSlot, newItem)).Count < newItem.maxStackSize
For some reason, no matter what I use for the findAll() predicate, I always get the same amount in my inspector - 1. Which means that .Count never goes above 1, and I can go way above my ItemObject.maxStackSize.
This is an example ItemObject class I have:
using UnityEngine;
[CreateAssetMenu(fileName = "New Food Object", menuName = "Inventory System/Items/Food")]
public class FoodObject : ItemObject
{
public int healthAmount;
private void Awake() {
type = ItemType.Food;
maxStackSize = 25;
}
}
This is also my Player script that adds the items to the inventory. It just adds them based off of OnTriggerEnter.
public class Player : MonoBehaviour
{
public InventoryObject inventory;
private void OnTriggerEnter(Collider other)
{
var item = other.GetComponent<Item>();
if (item)
{
inventory.AddItem(item.item, 1);
Destroy(other.gameObject);
}
}
}
Here's some screenshots of my Unity console/inspector, with these two lines added to my InventoryObject class. You can see that .Count never going above 1.
if (CurrentSlotHasItem(Container[i], newItem)) {
Debug.Log(Container.FindAll((InventorySlot currentSlot) => CurrentSlotHasItem(currentSlot, newItem)).Count);
Debug.Log(Container[i].amount);
// rest of class
I don't know what I was thinking here. All I needed to do to get maxStackSize to work was changing the logic inside of AddItem:
for (int i = 0; i < Container.Count; i++)
{
if (Container[i].item.id == newItem.id) {
if (Container[i].amount < newItem.maxStackSize)
{
Container[i].AddAmount(itemAmount);
inventoryHasItem = true;
break;
}
}
}
It just compares Container[i].amount to newItem.maxStackSize. If it's under, it will stack the item. Otherwise, it creates a new item in the inventory.
These two blocks of code I assume are involved with the following error:
Assets\Scripts\Weapons.cs(8,12): error CS7036: There is no argument given that corresponds to the required formal parameter 'name' of 'Item.Item(string, int)'
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Item : MonoBehaviour
{
private string name;
private int quantity;
public Item(string name, int quantity)
{
this.name = name;
this.quantity = quantity;
}
public string Name
{
get { return name; }
set { name = value; }
}
public int Quantity
{
get { return quantity; }
set { quantity = value; }
}
public virtual void UseItem()
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Weapons : Item
{
public GameObject bullet;
public GameObject shotgunSpawn;
public GameObject shotgunSpawn2;
//public bool useGun;
public Inventory iw;
GUI_2D m;
public float Speed;
GameObject patroller;
GameObject guard;
public bool pellet;
public bool shotGun;
public bool barGun;
public PlayerController player;
// Start is called before the first frame update
void Start()
{
Speed = 5f;
player = GameObject.FindGameObjectWithTag("Player");
patroller = GameObject.FindGameObjectWithTag("Patroller");
guard = GameObject.FindGameObjectWithTag("Guard");
guard = GameObject.FindGameObjectWithTag("Shotgun");
pellet = false;
shotGun = false;
barGun = false;
}
void DestroyEnemy()
{
if (patroller)
{
patroller.SetActive(false);
}
if (guard)
{
patroller.SetActive(false);
}
}
private void OnTriggerEnter(Collider other)
{
if (iw.invItems.Count < 12)
{
if (other.gameObject.CompareTag("Player"))
{
pellet = true;
}
}
}
public override void UseItem()
{
if (pellet)
{
player.pellet = true;
player.shotGun = false;
player.barGun = false;
}
if (shotGun)
{
player.pellet = false;
player.shotGun = true;
player.barGun = false;
}
if (barGun)
{
player.pellet = false;
player.shotGun = false;
player.barGun = true;
}
base.UseItem();
}
}
I am not trying to change the item class since doing so will affect another class that depends on the item classes constructor. Unless there is another way to resolve the error by changing the item class. The error given is also affecting another class in the same way. I am hoping to solve the issue in both the weapon class and the other class from an answer here. Thank you in advance.
You shouldn't add constructors to MonoBehaviours, as the Unity Engine is responsible for instantiating them and will never pass your constructors arguments (you shouldn't be creating instances of MonoBehaviours, either).
Just remove your Item constructor and assign the values manually whenever needed. Other construction code should be done in Awake() or Start().
Currently i'm developing a quiz kind of game. I've managed to develop the basic working of it. I was using the serialized field for updating the questions. Now i'm planning to use a CSV file for the questions.
CSVReader
using UnityEngine;
using System.Collections.Generic;
public class CSVReader : MonoBehaviour
{
public TextAsset csvFile;
[System.Serializable]
public class Row
{
public string Id;
public string Fact;
public string IsTrue;
}
public static List<Row> rowList = new List<Row>();
bool isLoaded = false;
void Start()
{
Load(csvFile);
}
public bool IsLoaded()
{
return isLoaded;
}
public List<Row> GetRowList()
{
return rowList;
}
public void Load(TextAsset csv)
{
rowList.Clear();
string[][] grid = CsvParser2.Parse(csv.text);
for (int i = 1; i < grid.Length; i++)
{
Row row = new Row();
row.Id = grid[i][0];
row.Fact = grid[i][1];
row.IsTrue = grid[i][2];
rowList.Add(row);
}
isLoaded = true;
}
public int NumRows()
{
return rowList.Count;
}
public static Row GetAt(int i)
{
if (rowList.Count <= i)
return null;
return rowList[i];
}
public static Row Find_Id(string find)
{
return rowList.Find(x => x.Id == find);
}
public List<Row> FindAll_Id(string find)
{
return rowList.FindAll(x => x.Id == find);
}
public static Row Find_Fact(string find)
{
return rowList.Find(x => x.Fact == find);
}
public List<Row> FindAll_Fact(string find)
{
return rowList.FindAll(x => x.Fact == find);
}
public static Row Find_IsTrue(string find)
{
return rowList.Find(x => x.IsTrue == find);
}
public List<Row> FindAll_IsTrue(string find)
{
return rowList.FindAll(x => x.IsTrue == find);
}
}
I'm trying to assign the values to this question class
using UnityEngine;
public class Question : MonoBehaviour
{
CSVReader csvr = new CSVReader();
public string Fact;
public bool IsTrue;
public void Start()
{
GameObject PlayCon = GameObject.FindWithTag("GameController");
if (PlayCon != null)
{
csvr = PlayCon.GetComponent<CSVReader>();
}
if (csvr == null)
{
Debug.Log("Cannot find 'GameController' script");
}
}
public void FixedUpdate()
{
for (int i = 1; i <= 2; i++)
{
Fact = CSVReader.Find_Id("i").Fact;
Debug.Log(Fact);
if (CSVReader.Find_Id("i").IsTrue == "true")
{
IsTrue = true;
}
else
IsTrue = false;
}
}
}
My game manager for generating questions
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour {
public Question[] facts;
private static List<Question> unansweredfacts;
private Question currentfacts;
[SerializeField]
private Text FactText;
[SerializeField]
private float TimeBetweenFacts = 1f;
[SerializeField]
private Text TrueAnswerText;
[SerializeField]
private Text FalseAnswerText;
[SerializeField]
private Animator animator;
[SerializeField]
public GameObject canvasquiz;
CSVReader csvr = new CSVReader();
void Start()
{
if (unansweredfacts == null || unansweredfacts.Count == 0)
{
unansweredfacts = facts.ToList<Question>();
}
SetCurrentfact();
Debug.Log(currentfacts.Fact + "is" + currentfacts.IsTrue);
}
void SetCurrentfact()
{
int RandomFactIndex = Random.Range(0, unansweredfacts.Count);
currentfacts = unansweredfacts[RandomFactIndex];
FactText.text = currentfacts.Fact;
if (currentfacts.IsTrue)
{
TrueAnswerText.text = "CORRECT !";
FalseAnswerText.text = "WRONG !";
}
else
{
TrueAnswerText.text = "WRONG !";
FalseAnswerText.text = "CORRECT !";
}
}
IEnumerator TransitionToNextFact()
{
unansweredfacts.Remove(currentfacts);
canvasquiz.SetActive(false);
yield return new WaitForSeconds(TimeBetweenFacts);
SetCurrentfact();
canvasquiz.SetActive(true);
}
public void UserSelected(bool isTrue)
{
animator.SetTrigger(isTrue.ToString());
//Debug.Log(isTrue.ToString);
if (currentfacts.IsTrue == isTrue)
{
Debug.Log("CORRECT !");
}
else
{
Debug.Log("WRONG !");
}
StartCoroutine(TransitionToNextFact());
}
Getting a NullReferenceException on SetCurrentFact() every time i try doing this. Even tried assigning the values directly on that method. Being stuck up on this for 2 days. Is there any way i can do this. I know i'm missing something. Sorry for my messed up code.
it appears facts field is not initialized before using.
but that would cause the error in Start method instead of SetCurrentfact
I have a generic base Interface IGameble (for the example move is generic) and Classes: Chess and Tic-Tac-Toe (and more board games) which derived from the generic Interface IGmable but are no longer generic.
I want to design class Agent (there might be several kind of these) that will play any kind of game (not in the same time), but the problem is that I cant
for example:
interface IGame<T_move,T_matrix>
{
T_matrix[,] game_state { get; }
void MakeMove(Move mov);
List<Move> ListAllMoves();
...// some more irrelevant code
}
class Tic_Tac_Toe : IGameble<Tuple<int,int>,int>
{
public int turn;
public int[,] game_state;
public List<Tuple<int,int>> ListAllMoves() {...}
public void MakeMove(Tuple<int,int> mov) {...}
}
public Chess : IGameble <some other kind> //...
// so on more classes which uses other kind in the generic.
interface IAgent<Move>
{
Move MakeMove();
}
public RandomAgent<T_Move,T_Matrix> : IAgent
{
public IGameble<T_move> game;
public D(IGameble game_) {game = game_}
public T_Move MakeMove() {//randomly select a move}
}
public UserAgent<T_Move,T_Matrix> : IAgent {get the move from the user}
The problems is that I want 1 instance of the random agent (or any other agent) to play all the games (1 game at a time) and using the generic enforce me to specifically choose the types of T_Move and T_Matrix that I want to use.
I might have it all wrong and used the generic poorly.
There was a suggestion to use IMovable and IBoardable instead of the generic. Is it the correct design? Would it solve all the problems?
I'm still a noobie in design pattern in C# :(
Also I will be appreciate it very much if some one can get a link to some design pattern that can help me here (if there is one..).
I think you can just use the is keyword:
public D(IA<T> a_)
{
if (a_ is B)
{
//do something
}
else if (a_ is C)
{
//do something else
}
}
Generics in C# aren't nearly as flexible as C++, so as some commenters pointed out, you may not want to do things this way. You may want to design an intermediate interface, say IMove, that your algorithm can use to enumerate the moves in a generic way.
I don't know is that what you want, but you can do it without generics.
Sample code:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main (string[] args)
{
var game = new TicTacToe (
(x) => new UserAgent (x, "John"),
(x) => new RandomAgent (x));
game.Play ();
Console.ReadKey ();
}
}
public interface IGame
{
IMove[] GetAvailableMoves ();
}
public interface IMove
{
void MakeMove ();
string Description { get; }
}
public interface IAgent
{
string Name { get; }
IMove SelectMove ();
}
public delegate IAgent AgentCreator (IGame game);
public class RandomAgent : IAgent
{
private readonly IGame game;
private readonly Random random = new Random ();
public RandomAgent (IGame game)
{
this.game = game;
}
public string Name => "Computer (random moves)";
public IMove SelectMove ()
{
var availableMoves = game.GetAvailableMoves ();
int moveIndex = random.Next (availableMoves.Length);
return availableMoves[moveIndex];
}
}
public class UserAgent : IAgent
{
private readonly IGame game;
public UserAgent (IGame game, string playerName)
{
this.game = game;
Name = playerName;
}
public string Name { get; }
public IMove SelectMove ()
{
var availableMoves = game.GetAvailableMoves ();
Console.WriteLine ("Choose your move:");
for (int i = 0; i < availableMoves.Length; i++)
{
Console.WriteLine (i + " " + availableMoves[i].Description);
}
int selectedIndex = int.Parse (Console.ReadLine ());
return availableMoves[selectedIndex];
}
}
public class TicTacToe : IGame
{
enum CellState { Empty = 0, Circle, Cross }
CellState[] board = new CellState[9]; // 3x3 board
int currentPlayer = 0;
private IAgent player1, player2;
public TicTacToe (AgentCreator createPlayer1, AgentCreator createPlayer2)
{
player1 = createPlayer1 (this);
player2 = createPlayer2 (this);
}
public void Play ()
{
PrintBoard ();
while (GetAvailableMoves ().Length > 0)
{
IAgent agent = currentPlayer == 0 ? player1 : player2;
Console.WriteLine ($"{agent.Name} is doing a move...");
var move = agent.SelectMove ();
Console.WriteLine ("Selected move: " + move.Description);
move.MakeMove (); // apply move
PrintBoard ();
if (IsGameOver ()) break;
currentPlayer = currentPlayer == 0 ? 1 : 0;
}
Console.Write ("Game over. Winner is = ..."); // some logic to determine winner
}
public bool IsGameOver ()
{
return false;
}
public IMove[] GetAvailableMoves ()
{
var result = new List<IMove> ();
for (int i = 0; i < 9; i++)
{
var cell = board[i];
if (cell != CellState.Empty) continue;
int index = i;
int xpos = (i % 3) + 1;
int ypos = (i / 3) + 1;
var move = new Move ($"Set {CurrentPlayerSign} on ({xpos},{ypos})", () =>
{
board[index] = currentPlayer == 0 ? CellState.Cross : CellState.Circle;
});
result.Add (move);
}
return result.ToArray ();
}
private char CurrentPlayerSign => currentPlayer == 0 ? 'X' : 'O';
public void PrintBoard ()
{
Console.WriteLine ("Current board state:");
var b = board.Select (x => x == CellState.Empty ? "." : x == CellState.Cross ? "X" : "O").ToArray ();
Console.WriteLine ($"{b[0]}{b[1]}{b[2]}\r\n{b[3]}{b[4]}{b[5]}\r\n{b[6]}{b[7]}{b[8]}");
}
}
public class Move : IMove // Generic move, you can also create more specified moves like ChessMove, TicTacToeMove etc. if required
{
private readonly Action moveLogic;
public Move (string moveDescription, Action moveLogic)
{
this.moveLogic = moveLogic;
Description = moveDescription;
}
public string Description { get; }
public void MakeMove () => moveLogic.Invoke ();
}
}
I'm hoping this might be something simple I'm missing.
I have a ScriptableObject script that looks like this:
using UnityEngine;
using System.Collections;
[CreateAssetMenu]
public class Item: ScriptableObject
{
public string iname;
public int iindex;
public Sprite sprite;
public GameObject itemObject;
public int value;
public string description;
public itemType itemtype;
public enum itemType
{
Consumable,
Equippable,
}
}
This works great in the editor, but if I publish to Android or Windows any script that references the ScriptableObject, it does not work. What am I missing?
For example the following block of code does not seem to execute at all:
for (int i = 0; i < 3; i++)
{
int lootnum = Random.Range(0, 4);
slot1 = itemdb[lootnum];
tlist[i] = itemdb[lootnum];
slotlist[i].transform.GetChild(0).GetComponent<Image>().sprite = itemdb[lootnum].sprite;
slotlist[i].transform.GetChild(0).GetComponent<Image>().enabled = true;
}
Those lists in the code are of the type Item defined in the above script. I'm not sure how to debug this as I get no errors or warnings in the editor.
Here is the script which populates the inventory. There's a bit of junk in there but it definitley works fine pressing play in the editor. Just not on build.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
public class Inventory : MonoBehaviour {
public int invSize;
public Item slot1;
public Item tslot1;
public Item tslot2;
public Item tslot3;
public GameObject t1;
public GameObject t2;
public GameObject t3;
public Sprite itemsprite;
public List<Item> itemdb = new List<Item>();
public List<Item> items = new List<Item>();
public List<Item> tlist = new List<Item>();
public Text stext;
public Text description;
public Item selectItem;
public GameObject selectSlot;
public Object test2;
public List<GameObject> slotlist = new List<GameObject>();
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public void addItem(Item itemToAdd)
{
//items.Add(itemdb[0]);
for (int i = 0; i < 5; i++)
{
if (items[i] == null)
{
items[i] = itemToAdd;
itemsprite = itemToAdd.sprite;
return;
}
}
}
public void GenTreasure()
{
for (int i = 0; i < 3; i++)
{
int lootnum = Random.Range(0, 4);
slot1 = itemdb[lootnum];
tlist[i] = itemdb[lootnum];
slotlist[i].transform.GetChild(0).GetComponent<Image>().sprite = itemdb[lootnum].sprite;
slotlist[i].transform.GetChild(0).GetComponent<Image>().enabled = true;
}
}
public void Uptext(int indexx)
{
stext.text = tlist[indexx].iname;
selectItem = tlist[indexx];
selectSlot = slotlist[indexx];
description.text = selectItem.description;
}
public void Take(int index)
{
//items.Add(selectItem);
for (int i = 0; i < invSize; i++)
{
if (items[i] == null)
{
items[i] = selectItem;
// itemsprite = itemToAdd.sprite;
selectItem = null;
// tlist[i] = null;
// slotlist[i].transform.GetChild(0).GetComponent<Image>().sprite = null;
selectSlot.transform.GetChild(0).GetComponent<Image>().enabled = false;
return;
}
}
}
}
If the script is not in the scene, how does it get loaded? Is it using an asset bundle? If that is the case, then it's possible that the class is being stripped from the build. You can include the class in your link.xml to make sure it's included. Another option is to simply reference the script in an included scene anywhere.