pass int value from one script to another script in unity - unity3d

I'm trying to pass a public int score value from one script to another script but it is giving me the error an object reference is required to access non-static member , here it is what I have done
public class firearrow : MonoBehaviour {
public GameObject Arrow;
public GameObject apple;
public int score = 0;
// Use this for initialization
void Start () {
this.gameObject.GetComponent<Rigidbody2D> ().AddForce (transform.right*1500.0f);
}
// Update is called once per frame
void Update () {
Vector3 diff = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
diff.Normalize();
float rot_z = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0f, 0f, rot_z - 0);
if (Input.GetMouseButtonUp (0)) {
GameObject bullet_new;
bullet_new = Instantiate (Arrow,new Vector2 (-0.23f, -3.78f), Quaternion.identity) as GameObject;
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition),Vector2.zero);
if (hit.collider!= null ) {
LeanTween.move(bullet_new, hit.collider.transform.localPosition, 1);
if(hit.collider.tag == "fruit")
{
score++;
Destroy(hit.collider.gameObject,1);
Destroy(bullet_new,1);
}
}
}
}
}
the class I want to access the score
public class tick : MonoBehaviour {
public Text wintext;
// Use this for initialization
void Start () {
wintext.text = "";
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButtonUp (0)) {
if(firearrow.score == 3)
{
wintext.text="You Win";
}
}
}
}
Any suggestions?

Change line
public int score = 0;
to
public static int score = 0;
Note that you must only have one single instance of class firearrow, otherwise you might run into concurrency issues.

Related

How do i scale an object based on a variable from a different script?

I created a force-field for my third-person player-basically a sphere around him.
My player has floats for stamina (curStamina and maxStamina).
I'm trying to figure out how to automatically scale the size of the sphere based on the players current stamina. So the force-field (sphere) is basically meant to be a visual intepretation of the amount of stamina the player currently has (curStamina) and either increases or decreases in size based on curStamina.
Here's my stamina script for reference. Any help on how i would go about this is appreciated!
public class Stamina : MonoBehaviour
{
public float stamina = 100;
public float maxStamina = 100;
public float drainPerSecond = 2.0f, gainPerSecond = 1.0f;
public bool online = false, usingStamina = false;
//public int lives;
public GameObject staminaImagePrefab;
private Transform staminaPanel;
private Image staminaImage;
//private Color lerpedColor; // Not currently used
// Start is called before the first frame update
void Start()
{
if (transform.CompareTag("Player"))
{
staminaPanel = GameObject.FindGameObjectWithTag("StaminaPanel").transform;
GameObject staminaObject = Instantiate(staminaImagePrefab, staminaPanel);
staminaImage = staminaObject.transform.GetChild(1).GetComponent<Image>();
}
}
// Update is called once per frame
void Update()
{
if (usingStamina)
{
DrainStamina();
}
else
{
RegenerateStamina();
}
if (transform.CompareTag("Player"))
{
staminaImage.fillAmount = (float)stamina / maxStamina;
}
}
private void RegenerateStamina()
{
stamina += gainPerSecond * Time.deltaTime;
if (stamina > maxStamina) stamina = maxStamina;
}
public void StaminaDamage(float value)
{
stamina -= value;
}
private void DrainStamina()
{
stamina -= drainPerSecond * Time.deltaTime;
if (stamina < 0)
{
// Out of stamina
stamina = 0;
}
}
public void ResetStamina(float _stamina, float _maxStamina)
{
stamina = _stamina;
maxStamina = _maxStamina;
if (transform.CompareTag("Player"))
{
staminaImage.fillAmount = (float)stamina / maxStamina;
}
}
}
Here's an example of how to implement it :
public float MinForceFieldScale = 1f;
public float MaxForceFieldScale = 4f;
[SerializeField]
private Transform forceFieldTransform;
private float DeltaForceFieldScale => MaxForceFieldScale - MinForceFieldScale;
// Update is called once per frame
void Update()
{
if (usingStamina)
{
DrainStamina();
}
else
{
RegenerateStamina();
}
if (transform.CompareTag("Player"))
{
staminaImage.fillAmount = (float)stamina / maxStamina;
}
UpdateForceFieldSize();
}
private void UpdateForceFieldSize()
{
float actualStaminaPercent = stamina / maxStamina;
float forceFieldSize = MinForceFieldScale + DeltaForceFieldScale * actualStaminaPercent;
forceFieldTransform.localScale = Vector3.one * forceFieldSize;
}

How can I check FindGameObjectsWithTag during runtime?

I'm having troubles with my enemy NavMeshAgent AI, First it searches for GameObjects tagged with "Defenses", then I set the first destination based in how close the defense is by my enemy, when the defense is destroyed, the array value is increased by one and it changes the destination to the next defense.
My problem is, my player can create (Instance) new defenses during the game, when all my defenses are destroyed, my enemy turns crazy, so, i need a way to add those new defenses to my array, below is my enemy script.
public class NavMeshEnemy : MonoBehaviour
{
[Header("References", order = 0)]
[SerializeField] NavMeshAgent enemyAgent;
[Space(10)]
[SerializeField] GameObject[] destinations;
[Space(10)]
[SerializeField] float distanceObjects;
[SerializeField] int arrayElements = 0;
void Awake()
{
enemyAgent = GetComponent<NavMeshAgent>();
destinations = GameObject.FindGameObjectsWithTag("Defenses");
}
void Start()
{
destinations = destinations.OrderBy((d) => (d.transform.position - this.transform.position).sqrMagnitude).ToArray();
}
void Update()
{
distanceObjects = Vector3.Distance(this.transform.position, destinations[arrayElements].transform.position);
enemyAgent.destination = destinations[arrayElements].transform.position;
CheckArray();
}
void CheckArray()
{
if (destinations[arrayElements].gameObject.activeInHierarchy == false)
{
if (arrayElements < destinations.Length - 1)
{
arrayElements++;
enemyAgent.destination = destinations[arrayElements].transform.position;
}
else
{
arrayElements = 0;
}
}
}
}
Thank you for reading! :)
I would rather implement a component on your prefab with a static list like e.g.
public class NavMeshDestination : MonoBehaviour
{
public static HashSet<Transform> existingDestinations = new HashSet<Transform>();
private void Awake()
{
if(!existingDestinations.Contains(this)) existingDestinations.Add(this);
}
private void OnDestroy()
{
if(existingDestinations.Contains(this)) existingDestinations.Remove(this);
}
}
And then don't even go by tag but simply do
public class NavMeshEnemy : MonoBehaviour
{
[Header("References", order = 0)]
[SerializeField] NavMeshAgent enemyAgent;
[Space(10)]
[SerializeField] float distanceObjects;
[SerializeField] int arrayElements = 0;
void Awake()
{
if(!enemyAgent) enemyAgent = GetComponent<NavMeshAgent>();
}
void Update()
{
destinations = NavMeshDestination.existingDestinations.OrderBy(d => (d.transform.position - this.transform.position).sqrMagnitude).ToArray();
distanceObjects = Vector3.Distance(this.transform.position, destinations[arrayElements].transform.position);
enemyAgent.destination = destinations[arrayElements].transform.position;
CheckArray();
}
void CheckArray()
{
if (destinations[arrayElements].gameObject.activeInHierarchy == false)
{
if (arrayElements < destinations.Length - 1)
{
arrayElements++;
enemyAgent.destination = destinos[arrayElements].transform.position;
}
else
{
arrayElements = 0;
}
}
}
}
Note however that this still won't fix your issues with no available destination. So you probably should stop the execution if the array is empty like
if(NavMeshDestination.existingDestinations.Count == 0)
{
// Stop the enemyAgent
return;
}

How to prevent same prefab from spawning twice in a row in Unity

Unity beginner here, I have a random prefab spawner attached to my game in Unity which randomly spawns 3 prefabs. The problem is, sometimes I get the same prefab like 5 times in a row. How can I prevent the same prefab from spawning twice in a row? Here is my code:
public class randomspawnscript : MonoBehaviour
{
public GameObject prefab1, prefab2, prefab3;
public float spawnRate = 2f;
float nextSpawn = 0f;
int whatToSpawn;
void Update()
{
if (collisionbutton.end != true || gameoverscreenrestart.restartPressed==true || gameovermainmenu.menuPressed==true)
{
if (Time.time > nextSpawn)
{
whatToSpawn = Random.Range(1, 4);
Debug.Log(whatToSpawn);
switch (whatToSpawn)
{
case 1:
Instantiate(prefab1, transform.position, Quaternion.identity);
break;
case 2:
Instantiate(prefab2, transform.position, Quaternion.identity);
break;
case 3:
Instantiate(prefab3, transform.position, Quaternion.identity);
break;
}
nextSpawn = Time.time + spawnRate;
}
}
else
{
return;
}
}
}
A simple way using the Unity's in build Random system is just to create a list of possible generated numbers, and pick a random number from that list, like so:
public class randomspawnscript : MonoBehaviour {
public GameObject prefab1, prefab2, prefab3;
public float spawnRate = 2f;
float nextSpawn = 0f;
int whatToSpawn;
private void Awake() {
// To not get a null ref error when generating the controlled random
// for the first time.
whatToSpawn = 0;
}
void Update() {
if (/* ... */) {
if (Time.time > nextSpawn) {
whatToSpawn = GetControlledRandom();
Debug.Log(whatToSpawn);
switch (whatToSpawn) {
//...
}
nextSpawn = Time.time + spawnRate;
}
} else {
return;
}
}
int GetControlledRandom() {
List<int> possibleChoices = new List<int> {
1, 2, 3
};
// Removes what was spawned before from the possible choices.
possibleChoices.Remove(whatToSpawn);
return possibleChoices[Random.Range(0, possibleChoices.Count)];
}
}
Alternatively, the more simpler way is to just keep generating a number until you get the one you are satisfied with, like so:
int RetardedControlledRandom() {
int generatedNumber;
do {
generatedNumber = Random.Range(1, 4);
} while (generatedNumber == whatToSpawn);
return generatedNumber;
}
This can help if you decide to use the .NET provided System.Random instead.
Also, note that currently most of your values/variables are hardcode.
(Aka it does not dynamically suit to spawning more than 4 types of prefab)
Unity Inspector accepts an array too, so you can make use of that and refactor your code like so:
public class randomspawnscript : MonoBehaviour {
public GameObject[] possibleSpawnPrefabs;
public float spawnRate = 2f;
float nextSpawn = 0f;
int whatToSpawn;
private void Awake() {
whatToSpawn = 0;
}
void Update() {
if (collisionbutton.end != true || gameoverscreenrestart.restartPressed == true || gameovermainmenu.menuPressed == true) {
if (Time.time > nextSpawn) {
whatToSpawn = GetControlledRandom();
Debug.Log(whatToSpawn);
var prefabToSpawn = possibleSpawnPrefabs[whatToSpawn];
Instantiate(prefabToSpawn, transform.position, Quaternion.identity);
nextSpawn = Time.time + spawnRate;
}
} else {
return;
}
}
int GetControlledRandom() {
List<int> possibleChoices = new List<int>();
for (int i = 0; i < possibleSpawnPrefabs.Length; ++i) {
possibleChoices.Add(i);
}
// Removes what was spawned before from the possible choices.
possibleChoices.Remove(whatToSpawn);
return possibleChoices[Random.Range(0, possibleChoices.Count)];
}
}
Well, just make a statement that checks the new randomized Prefab with the previous randomized prefab.
For the lazy code, you can just
GameObject previousPrefab;
and in Randomization, set the previousPrefab to the randomized prefab.
and in next Randomization, check if the previousPrefab == randomizedPrefab, if yes,
randomize again.
^ Also to achieve something like this you have to create a method from your Update() section and call it in Update() so you can call the method again if the previousPrefab is the same as the randomized one.

Reference to a particular Prefab clone

I am developing a simple 2D game. In game, I've created a prefab for charcaters. and I am changing sprite of prefab runtime. This all execute fine. Now I want to apply click event on a particular prefab clone and want to increase scale of prefab. I am attaching a c# script what I have did till now.
public class ShoppingManager : MonoBehaviour {
public static ShoppingManager instance;
[System.Serializable]
public class Shopping
{
public string CharacterName;
public Sprite CharacterSprite;
}
public GameObject CharacterPrefab;
public Transform CharacterSpacer;
public List<Shopping> ShoppingList;
private CharacterScript NewCharacterScript;
/*********************************************Awake()******************************************/
void Awake()
{
MakeSingleton ();
}
/******************************Create object of the script**********************************/
void MakeSingleton()
{
instance = this;
DontDestroyOnLoad (gameObject);
}
// Use this for initialization
void Start () {
LoadCharacters ();
}
void LoadCharacters()
{
foreach (var characters in ShoppingList) {
GameObject NewCharacter = Instantiate (CharacterPrefab) as GameObject;
NewCharacterScript = NewCharacter.GetComponent<CharacterScript> ();
NewCharacterScript.CharacterName = characters.CharacterName;
NewCharacterScript.Charcater.GetComponent<Image> ().sprite = characters.CharacterSprite;
NewCharacterScript.GetComponent<Button> ().onClick.AddListener (() => CharacterClicked (NewCharacterScript.CharacterName, NewCharacterScript.Charcater));
NewCharacter.transform.SetParent (CharacterSpacer, false);
}
}
void CharacterClicked(string CharacterName, GameObject Char)
{
StartCoroutine (IncreaseScale (Char));
}
IEnumerator IncreaseScale(GameObject TempCharacter)
{
int i = 5;
while (i > 0) {
yield return new WaitForSeconds (0.1f);
Vector3 TempVector = TempCharacter.GetComponent<RectTransform> ().localScale;
TempVector.x = TempVector.x + 0.2f;
TempVector.y = TempVector.y + 0.2f;
TempCharacter.GetComponent<RectTransform> ().localScale = TempVector;
i--;
}
}
}
This code triggers click event and also it increases scale but of last clone, not of clicked prefab clone. What I am missing, I can't understand. What should I correct in this. and Yeah! I am also attaching code of a script that I've added on prefab.
public class CharacterScript : MonoBehaviour {
public string CharacterName;
public GameObject Charcater;
}
create collider for your object attach the script below to it this way each object is responsible for handling its own functionalities like increasing its own size
public class characterFunctionalities: MonoBehaviour{
void OnMouseDown()
{
StartCoroutine (IncreaseScale (this.gameobject));
}
IEnumerator IncreaseScale(GameObject TempCharacter)
{
int i = 5;
while (i > 0) {
yield return new WaitForSeconds (0.1f);
Vector3 TempVector = TempCharacter.GetComponent<RectTransform> ().localScale;
TempVector.x = TempVector.x + 0.2f;
TempVector.y = TempVector.y + 0.2f;
TempCharacter.GetComponent<RectTransform> ().localScale = TempVector;
i--;
}
}
}

Unity Movement - Okay, so I am trying to build a simple script for elevator movement

The issue is I can only move upwards if the user holds the E key. Is there a way to have the user press the E key then just have the lift start?
Here's my code:
using UnityEngine;
using System.Collections;
public class liftScript : MonoBehaviour {
public int speed = 1;
private int i = 10;
void OnTriggerStay()
{
startLift ();
}
void startLift()
{
if(Input.GetKey(KeyCode.E)) {
transform.position = Vector3.Lerp (transform.position, new Vector3 (transform.position.x, 10, transform.position.z), Time.deltaTime * speed);
}
}
}
i think you can modify your code
public class liftScript : MonoBehaviour {
public int speed = 1;
private int i = 10;
bool keyPressed = false;
// Update is called once per frame
void Update () {
if (Input.GetKey (KeyCode.E)) {
keyPressed = true;
}
if (keyPressed == true)
{
startLift();
}
}
void startLift()
{
transform.position = Vector3.Lerp (transform.position, new Vector3 (transform.position.x, 10, transform.position.z), Time.deltaTime * speed);
}
void stopLift()
{
keyPressed = false;
}
}
Hope it can help you