Unity C# Problems - c#

I am getting this error:
An object reference is required to access non-static member
`Inventory.ItemID'
I want to change ItemID in Inventory to 1.
The class ItemSwordUneq:
using UnityEngine;
using System.Collections;
public class ItemSwordUneq : MonoBehaviour
{
public bool canPickup = false;
void Update ()
{
if(canPickup == true && Input.GetKeyDown(KeyCode.F))
{
GameObject player = GameObject.Find("Player");
Inventory inventory = player.GetComponent<Inventory>();
Inventory.ItemID = 1;
Destroy(gameObject);
}
}
void OnTriggerEnter(Collider other)
{
canPickup = true;
}
void OnTriggerExit(Collider other)
{
canPickup = false;
}
void OnGUI()
{
if(canPickup == true)
{
GUI.Label(new Rect(250, 250, 300, 20), "Press 'F' To Pick Up Sword");
}
}
}
The class Inventory:
using UnityEngine;
using System.Collections;
public class Inventory : MonoBehaviour
{
public bool hasItem = false;
public int ItemID;
}
What am I doing wrong? Any answers will be appreciated.

You probably want to write inventory.ItemID = 1; instead of Inventory.ItemID = 1;.
That is if you want to set the ItemId of the result of player.GetComponent<Inventory>(); to 1.
You are trying to call a method on the class Inventory and not on the object inventory, and that does not give any sense if the method is not static.

Related

GetKeyDown sends multiple outputs

specifically it sends as many outputs as there are objects with the code in them that allows them to be interacted with, even if I press E when not looking at anything. I wanted to make an inventory system, but this causes all objects that have this code to be interacted with. I included all the scripts that im using for this system if that can help. I genuinely don't know what I did wrong
the interactor code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using System;
public class Interactable : MonoBehaviour
{
public LayerMask interactableLayerMask;
//ITEM VARIABLE
public Item item;
//PICK UP RADIUS
public float radius = 4f;
public void Interact()
{
Debug.Log("Interacted with " + transform.name);
PickUp();
}
//PICK UP INTERACTION
void PickUp()
{
Debug.Log("Picking up " + item.name);
bool wasPickedUp = Inventory.instance.Add(item);
if (wasPickedUp)
Destroy(gameObject);
}
//INTERACTION
void Update()
{
if (Input.GetKeyDown(KeyCode.E))
{
Debug.Log("Added item -----------------------------------------");
RaycastHit hit;
if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hit, radius))
{
Interactable interactable = hit.collider.GetComponent<Interactable>();
if (interactable != null)
{
Interact();
}
} else
{
Debug.Log("Nothing");
}
}
}
}
the Inventory code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour
{
#region Singleton
public static Inventory instance;
void Awake()
{
if (instance != null)
{
Debug.LogWarning("More than one instance of Inventory found!");
return;
}
instance = this;
}
#endregion
public delegate void OnItemChanged();
public OnItemChanged onItemChangedCallback;
public int space = 20;
public List<Item> items = new List<Item>();
public bool Add (Item item)
{
if (!item.isDefaultItem)
{
if (items.Count >= space)
{
Debug.Log("Note enough space in inventory");
return false;
}
else
{
items.Add(item);
if (onItemChangedCallback != null)
onItemChangedCallback.Invoke();
}
}
return true;
}
public void Remove(Item item)
{
items.Remove(item);
if (onItemChangedCallback != null)
onItemChangedCallback.Invoke();
}
}
Item code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "New Item", menuName = "Inventory/Item")]
public class Item : ScriptableObject
{
new public string name = "New Item";
public Sprite icon = null;
public bool isDefaultItem = false;
}
To my understanding, your code goes into the Input.GetKeyDown(KeyCode.E) for every object in the scene, but it does not add it to the inventory.
That is because you use the Update function in the Interactable class, which are your objects. Instead, try moving the exact same block of code into your Inventory class. Make sure you make your Interact method public, so you can call it.

I can't Save Multiple PlayerPrefs on my Main Menu

Hi I'm a student in game development, making a game where players can grab certain amounts of coins on each maps, every maps has different type of coins data, like a highscore. But it only saves 1 playerprefs only. Why is that happening?
This is my Scene Management Script;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using TMPro;
public class changeScene : MonoBehaviour
{
public TextMeshProUGUI desertCoinAmount;
public TextMeshProUGUI plainsCoinAmount;
void Update()
{
desertCoinAmount.text = PlayerPrefs.GetInt("DesertCoins").ToString();
plainsCoinAmount.text = PlayerPrefs.GetInt("PlainsCoins").ToString();
}
public void mainMenu()
{
SceneManager.LoadScene("mainMenu");
PlayerPrefs.Save();
}
public void desertLevel()
{
SceneManager.LoadScene("ancientDesertLEVEL");
Time.timeScale = 1f;
PlayerPrefs.Save();
}
public void plainsLevel()
{
SceneManager.LoadScene("Plain Biome");
Time.timeScale = 1f;
PlayerPrefs.Save();
}
public void jungleLevel()
{
SceneManager.LoadScene("Jungle Biome");
Time.timeScale = 1f;
}
}
And This is my PlayerController;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class PlayerController : MonoBehaviour
{
public GameObject winPopup, losePopup;
public GameObject heart1, heart2, heart3;
public float gravityScale = 10f;
private Rigidbody rb;
public TextMeshProUGUI coinText;
public AudioSource coinSound;
int coin_sumDesert;
int coin_sumPlains;
int life_sum = 3;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
GetComponent<Rigidbody>().AddForce(Physics.gravity * gravityScale, ForceMode.Force);
}
void Update()
{
PlayerPrefs.SetInt("DesertCoins", coin_sumDesert);
PlayerPrefs.SetInt("PlainsCoins", coin_sumPlains);
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Coins")
{
coin_sumDesert++;
coinText.text = coin_sumDesert.ToString();
Destroy(other.gameObject);
coinSound.Play();
}
if (other.gameObject.tag == "PlainsCoins")
{
coin_sumPlains++;
coinText.text = coin_sumPlains.ToString();
Destroy(other.gameObject);
coinSound.Play();
}
if (other.gameObject.tag == "finishLine")
{
winPopup.SetActive(true);
}
if (other.gameObject.tag == "obstacles")
{
Debug.Log("Collide Detected");
life_sum--;
if (life_sum == 2)
{
heart1.SetActive(false);
}
else if (life_sum == 1)
{
heart2.SetActive(false);
}
else if (life_sum == 0)
{
heart3.SetActive(false);
losePopup.SetActive(true);
Time.timeScale = 0.0f;
}
}
}
}
I would appreciate the reply (this is my first time using StackOverflow xD)
If you have multiple PlayerController then obviously they write to the same PlayerPrefs keys.
Whenever you save a player's data, make sure you differentiate them e.g. Score1, Score2, etc.
This is a way among many others to achieve it:
The player, very simple, append index to player pref to differentiate among many:
public sealed class Player : MonoBehaviour
{
private const string ChocolateBarsKey = "ChocolateBars";
[SerializeField]
[HideInInspector]
private int Index;
private int ChocolateBars
{
get => GetInt(ChocolateBarsKey);
set => SetInt(ChocolateBarsKey, value);
}
private int GetInt([NotNull] string key, int defaultValue = default)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
return PlayerPrefs.GetInt($"{key}{Index}", defaultValue);
}
private void SetInt([NotNull] string key, int value)
{
PlayerPrefs.SetInt($"{key}{Index}", value);
}
[NotNull]
internal static Player Create([NotNull] GameObject parent, int index)
{
if (parent == null)
throw new ArgumentNullException(nameof(parent));
var controller = parent.AddComponent<Player>();
controller.name = $"{nameof(Player)} {index}";
controller.Index = index;
return controller;
}
}
The factory, scriptable singleton won't lose state on assembly reload, whereas if you'd used a static int for player count, it would reset itself to zero at assembly reload because static fields are not serialized by Unity.
public sealed class PlayerFactory : ScriptableSingleton<PlayerFactory>
{
[SerializeField]
private int PlayerCount;
[NotNull]
public Player Create(GameObject parent)
{
return Player.Create(parent, ++PlayerCount);
}
}
Now if you don't want to store score data within Player, it'll be another pattern. I leave that to you as an exercise.

Why when adding a script to a selected gameobject through another script it's adding the script to each object in the list twice?

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class GenerateAutomaticGuid : Editor
{
[MenuItem("GameObject/Generate Guid", false, 11)]
private static void GenerateGuid()
{
foreach (GameObject o in Selection.objects)
{
o.AddComponent<GenerateGuid>();
o.GetComponent<GenerateGuid>().GenerateGuidNum();
o.tag = "My Unique ID";
}
}
}
For example I'm selecting two gameobjects in the hierarchy right click and GenerateGuid I use a break point and it's making a loop on each object in the list Selection.objects twice so each object in the list have the script GenerateGuid twice. And I want it to add the script to each object once only.
This is the GenerateGuid script :
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GenerateGuid : MonoBehaviour, IStateQuery
{
public string uniqueGuidID;
public SaveLoad saveLoad;
public GameObject naviParent;
private Guid guidID;
private bool isNaviChildOfKid = false;
public void GenerateGuidNum()
{
guidID = Guid.NewGuid();
uniqueGuidID = guidID.ToString();
}
private State m_state = new State();
public Guid UniqueId => Guid.Parse("E0B03C9C-9680-4E02-B06B-E227831CB33F");
private class State
{
public bool naviInHand;
}
public string GetState()
{
return JsonUtility.ToJson(m_state);
}
public void SetState(string jsonString)
{
m_state = JsonUtility.FromJson<State>(jsonString);
if (m_state.naviInHand == true)
{
transform.GetComponent<InteractableItem>().distance = 0;
transform.parent = GameObject.Find("Navi Parent").transform;//rig_f_middle;
transform.localPosition = GameObject.Find("Navi Parent").transform.localPosition;
transform.localRotation = Quaternion.identity;
transform.localScale = new Vector3(0.001f, 0.001f, 0.001f);
}
}
private void Update()
{
if(transform.IsChildOf(naviParent.transform) == false && isNaviChildOfKid == false)
{
m_state.naviInHand = true;
saveLoad.Save();
isNaviChildOfKid = true;
}
}
}
A screenshot of all the places I'm calling the GenerateGuid :
Maybe you executing GenerateGuid twice or by amount of gameobjects?
Can you show all places where you executing GenerateGuid
You can add a null check to your foreach loop to see if your object already has a GenerateGuid component and skip the process if it does:
foreach (GameObject o in Selection.objects)
{
if(!o.GetComponent<GenerateGuid>()){
o.AddComponent<GenerateGuid>();
o.GetComponent<GenerateGuid>().GenerateGuidNum();
o.tag = "My Unique ID";
}
}
It won't prevent your function from being called multiple times but it can prevent duplicate GenerateGuid components.

When I used portal in My Game why Destroyed "Camera Fallow"?

I made a portal to the 2D game. Normally the camera needs to follow the character. But after the portal scripts I wrote, "CameraFallowScript" does not work. The character is passing through the portal. but after passing "CameraFallowScript" disappears. I'm a little new and my English is bad.
thanks for helping.
Camera Fallow Script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraFallow : MonoBehaviour
{
public GameObject target;
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.position = new Vector3(target.transform.position.x, target.transform.position.y, transform.position.z);
}
}
Portal Script here :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Portal : MonoBehaviour
{
private Rigidbody2D enteredRigidbody;
private float enterVelocity, exitVelocity;
private void OnTriggerEnter2D(Collider2D collision)
{
enteredRigidbody = collision.gameObject.GetComponent<Rigidbody2D>();
enterVelocity = enteredRigidbody.velocity.x;
if (gameObject.name == "BluePortal")
{
PortalControl.portalControlInstance.DisableCollider("orange");
PortalControl.portalControlInstance.CreateClone("atOrange");
}
else if (gameObject.name == "OrangePortal")
{
{
PortalControl.portalControlInstance.DisableCollider("blue");
PortalControl.portalControlInstance.CreateClone("atBlue");
}
}
}
private void OnTriggerExit2D(Collider2D collision)
{
exitVelocity = enteredRigidbody.velocity.x;
if (enterVelocity != exitVelocity)
{
Destroy(GameObject.Find("Clone"));
}
Destroy(collision.gameObject);
PortalControl.portalControlInstance.EnableColliders();
GameObject.Find("Clone").name = "Character";
CameraFallow.DontDestroyOnLoad(transform.gameObject);
}
}
PortalControl Script Here :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PortalControl : MonoBehaviour
{
public static PortalControl portalControlInstance;
[SerializeField]
private GameObject bluePortal, orangePortal;
[SerializeField]
private Transform bluePortalSpawnPoint, orangePortalSpawnPoint;
private Collider2D bluePortalCollider, orangePortalCollider;
[SerializeField]
private GameObject clone;
void Start()
{
portalControlInstance = this;
bluePortalCollider = bluePortal.GetComponent<Collider2D>();
orangePortalCollider = orangePortal.GetComponent<Collider2D>();
}
// Update is called once per frame
public void CreateClone(string whereToCreate)
{
if (whereToCreate == "atBlue")
{
var instantiatedClone = Instantiate(clone, bluePortalSpawnPoint.position, Quaternion.identity);
instantiatedClone.gameObject.name = "clone";
}
if (whereToCreate == "atOrange")
{
var instantiatedClone = Instantiate(clone, orangePortalSpawnPoint.position, Quaternion.identity);
instantiatedClone.gameObject.name = "clone";
}
}
public void DisableCollider(string ColliderToDisable)
{
if (ColliderToDisable == "orange")
{
orangePortalCollider.enabled = false;
}
else if (ColliderToDisable == "blue")
{
bluePortalCollider.enabled = false;
}
}
public void EnableColliders()
{
orangePortalCollider.enabled = true;
bluePortalCollider.enabled = true;
}
}
In general I wouldn't create clone I guess ... why instantiate a new player? Why not simply move it to the new position?
What happens here is that after Destroy(collision.gameobject) the FollowCamera loses the reference to its target so you would need to reassign it after the cloning e.g. in
private void OnTriggerExit2D(Collider2D collision)
{
exitVelocity = enteredRigidbody.velocity.x;
if (enterVelocity != exitVelocity)
{
Destroy(GameObject.Find("Clone"));
}
Destroy(collision.gameObject);
PortalControl.portalControlInstance.EnableColliders();
var clone = GameObject.Find("Clone");
clone.name = "Character";
DontDestroyOnLoad(clone);
// It would ofcourse be way more efficient
// if you would store this reference somewhere
FindObjectOfType<CameraFollow>().target = clone;
}
Note that in general usage of Find is expensive and you should avoid it wherever possible! Rather pass on the clone reference between all required scripts.
Regarding coding style: passing around string parameters is not really good code.
I would suggest e.g. an enum like
public enum PortalSide
{
orange,
blue
}
and then use
private Dictionary<PortalSide, Transform> portalSpawns;
private Dictionary<PortalSide, Collider> portalColliders;
private void Awake()
{
portalSpawns = new Dictionary<PortalSide, Transform> { {PortalSide.blue, bluePortalSpawnPoint} , {PortalSide.orange, orangePortalSpawnPoint}};
portalColliders = new Dictionary<PortalSide, Collider> { {PortalSide.blue, bluePortalCollider}, {PortalSide.orange, orangePortalCollider} };
}
public void CreateClone(PortalSide whereToCreate)
{
var spawnPoint = PortalSides[whereToCreate];
var instantiatedClone = Instantiate(clone, spawnPoint.position, Quaternion.identity);
instantiatedClone.gameObject.name = "clone";
}
public void DisableCollider(PortalSide ColliderToDisable)
{
var colliderToDisable = portalColliders[ColliderToDisable];
colliderToDisable.enabled = false;
}

Unity Quiz Game Score in C#

I am making a quiz game in the unity from the unity live session quiz game tutorials everything is working fine except somehow the score isn't working when i Click the button it should add 10 score to the Score. Here are the tutorials : https://unity3d.com/learn/tutorials/topics/scripting/intro-and-setup?playlist=17117 and the code for my Game Controller :
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.Collections.Generic;
public class GameController : MonoBehaviour {
public Text questionDisplayText;
public Text scoreDisplayText;
public Text timeRemainingDisplayText;
public SimpleObjectPool answerButtonObjectPool;
public Transform answerButtonParent;
public GameObject questionDisplay;
public GameObject roundEndDisplay;
private DataController dataController;
private RoundData currentRoundData;
private QuestionData[] questionPool;
private bool isRoundActive;
private float timeRemaining;
private int questionIndex;
private int playerScore;
private List<GameObject> answerButtonGameObjects = new List<GameObject>();
// Use this for initialization
void Start ()
{
dataController = FindObjectOfType<DataController> ();
currentRoundData = dataController.GetCurrentRoundData ();
questionPool = currentRoundData.questions;
timeRemaining = currentRoundData.timeLimitInSeconds;
UpdateTimeRemainingDisplay();
playerScore = 0;
questionIndex = 0;
ShowQuestion ();
isRoundActive = true;
}
private void ShowQuestion()
{
RemoveAnswerButtons ();
QuestionData questionData = questionPool [questionIndex];
questionDisplayText.text = questionData.questionText;
for (int i = 0; i < questionData.answers.Length; i++)
{
GameObject answerButtonGameObject = answerButtonObjectPool.GetObject();
answerButtonGameObjects.Add(answerButtonGameObject);
answerButtonGameObject.transform.SetParent(answerButtonParent);
AnswerButton answerButton = answerButtonGameObject.GetComponent<AnswerButton>();
answerButton.Setup(questionData.answers[i]);
}
}
private void RemoveAnswerButtons()
{
while (answerButtonGameObjects.Count > 0)
{
answerButtonObjectPool.ReturnObject(answerButtonGameObjects[0]);
answerButtonGameObjects.RemoveAt(0);
}
}
public void AnswerButtonClicked(bool isCorrect)
{
if (isCorrect)
{
playerScore += currentRoundData.pointsAddedForCorrectAnswer;
scoreDisplayText.text = "Score: " + playerScore.ToString();
}
if (questionPool.Length > questionIndex + 1) {
questionIndex++;
ShowQuestion ();
} else
{
EndRound();
}
}
public void EndRound()
{
isRoundActive = false;
questionDisplay.SetActive (false);
roundEndDisplay.SetActive (true);
}
public void ReturnToMenu()
{
SceneManager.LoadScene ("MenuScreen");
}
private void UpdateTimeRemainingDisplay()
{
timeRemainingDisplayText.text = "Time: " + Mathf.Round (timeRemaining).ToString ();
}
// Update is called once per frame
void Update ()
{
if (isRoundActive)
{
timeRemaining -= Time.deltaTime;
UpdateTimeRemainingDisplay();
if (timeRemaining <= 0f)
{
EndRound();
}
}
}
}
and my answer Button Code:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class AnswerButton : MonoBehaviour {
public Text answerText;
private AnswerData answerData;
private GameController GameController;
// Use this for initialization
void Start ()
{
GameController = FindObjectOfType<GameController> ();
}
public void Setup(AnswerData data)
{
answerData = data;
answerText.text = answerData.answerText;
}
public void HandleClick()
{
GameController.AnswerButtonClicked (answerData.isCorrect);
}
}
and Answer Data :
using UnityEngine;
using System.Collections;
[System.Serializable]
public class AnswerData
{
public string answerText;
public bool isCorrect;
}
If everything is working fine (the whole code gets executed correctly, which I presume at this point), you probably did not set the data correctly. In your Game Controller, you have the line
playerScore += currentRoundData.pointsAddedForCorrectAnswer;
in your AnswerButtonClicked method which should add an amount you defined to the score if the answer is correct. Since I presume that your whole code is running fine (I can't see your in-engine setup, only the code here, which looks like the one in the tutorial), this is probably the first location where to look at the error. This value is probably set in the Unity Inspector or via another script, so you may want to go check in other files or the Editor.
The next thing to check is, if the buttons are correctly linked via their event handler. This can be checked by looking at the inspector. In the tutorial series this step is done in part Click to answer at the end of the video.

Categories