How do I carry over data between scenes in Unity? - c#

For a game I am developing I have a part when if the user gets too close to an enemy it switches scene to a battle scene. However I have no clue how to load that enemy into the battle screen (given that a user can battle many different enemies). Below is my current cod for the enemy. I was wondering if I could carry over it's name into the next scene or something. I just want my enemy to go from one screen to another when the scene is changed. Code would be appreciated thankyou
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FolllowAndLoad : MonoBehaviour
{
public Transform target;
public Animator anim;
public Rigidbody2D myRigidBody;
public string levelToLoad;
private static string keyname; // value I want to carry over
public float MoveSpeed;
private bool checkTrigger;
public Rigidbody2D targetRigidBody;
void Start()
{
target = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();//getting the position of our player
anim = GetComponent<Animator>();
myRigidBody = GetComponent<Rigidbody2D>(); //getting my components
targetRigidBody = GameObject.FindGameObjectWithTag("Player").GetComponent<Rigidbody2D>();
}
void Update()
{
float distance = Vector2.Distance(target.position, myRigidBody.transform.position); //getting the distance between our player and our enemy
if (distance < 5)
{
transform.position = Vector2.MoveTowards(transform.position, target.position, MoveSpeed * Time.deltaTime); //moving our enemy towards our player
anim.SetBool("checkTrigger", true);
anim.SetFloat("MoveX", moveXvalue()); //updating the animations for our enemy
anim.SetFloat("MoveY", moveYvalue());
}
else if (distance > 5) //if out of range stop walking
{
anim.SetBool("checkTrigger", false);
}
}
int moveXvalue()
{
int value;
if (myRigidBody.transform.position.x < target.transform.position.x && Mathf.Abs(target.position.y - myRigidBody.position.y) < Mathf.Abs(target.position.x - myRigidBody.position.x)) //these are saying if the enemy is closer in x than in y use x animations and vice versa
value = 1;
else if (myRigidBody.transform.position.x > target.transform.position.x && Mathf.Abs(target.position.y - myRigidBody.position.y) < Mathf.Abs(target.position.x - myRigidBody.position.x))
value = -1;
else
value = 0;
return value;
}
int moveYvalue()
{
int value;
if (myRigidBody.transform.position.y < target.transform.position.y && Mathf.Abs(target.position.y - myRigidBody.position.y) > Mathf.Abs(target.position.x - myRigidBody.position.x))
value = 1;
else if (myRigidBody.transform.position.x > target.transform.position.x && Mathf.Abs(target.position.y - myRigidBody.position.y) > Mathf.Abs(target.position.x - myRigidBody.position.x))
value = -1;
else
value = 0;
return value;
}
public void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.name == "Player")
{
Debug.Log(gameObject.name);
anim.SetBool("checkInContact", true);
Application.LoadLevel (levelToLoad); //loading our level
}
}
}

There are a lot of ways to do this, but the simplest way to get something working quickly just until you get more familiar with Unity it to use a simple static class in your project that you can access from any script in any scene.
So if you were to make a new script in your project right now called SharedResources.cs and then pasted this into the script and saved it....
public static class SharedResources
{
public const int kSceneIs_TitleScene = 0;
public const int kSceneIs_ActualGameScene = 1;
public const int kSceneIs_HighScoreScene = 2;
public static int highScore = 0;
public static int enemyID = 0;
public static void sampleFunction()
{
//this is a sample method you can call from any other script
}
}
You could now be in a script in one scene and do this
SharedResources.highScore=SharedResources.highScore+20;
SharedResources.enemyID=5;
You could then open up a new scene and a script in that scene could access the high score
Debug.Log(SharedResources.highScore)
Debug.Log(SharedResources.enemyID)
You can also access constant and run subroutines that are in the static class as shown above.
The correct way to do this is up for debate and really depends on what your ultimate goal is. I will reference another link to a post that goes into more detail....
https://gamedev.stackexchange.com/questions/110958/unity-5-what-is-the-proper-way-to-handle-data-between-scenes
Ideally, you should read and understand the difference between using a simple static class versus one that derives from MonoBehavior, and also the different between a static class and a Singleton, which in many ways is much more powerful (but can also cause issues if you don't code it correctly)
Last but not least, don't forget you can also use the built in PlayerPrefs function in Unity to store scores and other settings that need to carry over between launches of the game....
https://answers.unity.com/questions/1325056/how-to-use-playerprefs-2.html

Related

Score counter not increasing after recalling object from a pool with raycast detection hit

Context
Hello, its me again. I have been playing around with "Object Pool" and it is fascinating the amount of CPU performance can save up. However, I have encountered a new "feature" where it scores once for the object being called and going through the pipe.
In my object pool, I have a pipe GameObject where is being Instantiated and set its method Active(false) until it is spawned. Once spawned, my prefabs will be filled accordingly to the Object Pool's size.
Problem
Once it spawns, it does what it should do, both scoring and the same mechanic as "Flappy Bird". However, once the object gets Active again, it doesn't seem to score anymore because it was passed by a player. What I have done is to have a flag that checks if the player (bird) has passed the pipe or not. However, when I pass through it, it will update it as if it was 6 times (one point per frame). You may ask "have you done another flag for the pipe itself?" then the answer is yes. I tried that way also, but it will only keep the score once and not increase further than 5.
I ran out of ideas with the object pool approach. It seems to work fine if it is WITHOUT object pooling, but the flaw here is that it costs me CPU performance.
It either increases it by just 1, or it increases it by 6 times (because for each frame the object is in the ray, it counts another point).
Attempts
I have browsed on the Unity Learning center to find out how to do the object pooling, and it wasn't too bad as I thought. Later, I found this issue and I thought it was a problem with my logic (which it can be). I have spent several hours already (first mistake) to think it is something easy to fix, but apparently it wasn't due the time I have spent to just figure out why it is not working 😅. I have been fiddling around my RayCastDetection, SpawnManager, ObjectPooling, and PlayerControl logic that interacts accordingly to the way I want, but nada.
Code
ObjectPooling.cs
public static ObjectPooling sharedInstance;
public List<GameObject> pooledObjects;
public GameObject objectToPool;
private PlayerControl playerControllerScript;
public int amountToPool;
void Awake()
{
sharedInstance = this;
}
void Start()
{
playerControllerScript = GameObject.Find("Player").GetComponent<PlayerControl>();
pooledObjects = new List<GameObject>();
GameObject tmp;
for (int i = 0; i < amountToPool; i++) //Add objects to the pool and turns them invisible (false)
{
tmp = Instantiate(objectToPool);
tmp.SetActive(false);
playerControllerScript.passedBeam = false;
pooledObjects.Add(tmp);
}
}
public GameObject GetPooledObject()
{
for (int i = 0; i < amountToPool; i++)
{
if (!pooledObjects[i].activeInHierarchy)
return pooledObjects[i];
}
return null;
}
RayCastDetection.cs
public class RayCastDetection : MonoBehaviour
{
public UIManager UIManagerScript;
public PlayerControl playerControlScript;
private RaycastHit hit;
public bool passed;
void Start()
{
UIManagerScript = GameObject.Find("UI_Manager").GetComponent<UIManager>(); //Used for scoring
playerControlScript = GameObject.Find("Player").GetComponent<PlayerControl>(); //used for player passing through the pipe
passed = false;
playerControlScript.passedBeam = false;
}
void Update()
{
Vector3 beamDown = transform.TransformDirection(Vector3.down);
Ray ray = new Ray(transform.position, beamDown);
if (!passed)
{
if (Physics.Raycast(ray, out hit))
{
if (hit.collider.tag == "Player" && !playerControlScript.passedBeam)
{
playerControlScript.passedBeam = !playerControlScript.passedBeam;
UIManagerScript.score++;
}
Debug.DrawRay(transform.position, hit.point - transform.position);
}
}
else
playerControlScript.passedBeam = false;
}
}
SpawnManager.cs
public class SpawnManager : MonoBehaviour
{
public GameObject[] obstaclesPrefab;
private PlayerControl playerControllerScript;
private float startDelay = 1.69f;
private float repeatRate = 1.1f;
void Start()
{
playerControllerScript = GameObject.Find("Player").GetComponent<PlayerControl>();
InvokeRepeating("SpawnObstacle", startDelay, repeatRate);
}
void Update()
{
}
void SpawnObstacle()
{
// int obstaclesIndex = Random.Range(0, obstaclesPrefab.Length); //This is used only if I don't want to deal with object pooling, but the whole point is to use it. This is just a reference if I want to go back
if (playerControllerScript.gameOver == false)
{
float randomY = Random.Range(-2f, 2f);
Vector3 randomHeight = new Vector3(35, randomY, -7);
GameObject pipe = ObjectPooling.sharedInstance.GetPooledObject();
if (pipe != null)
{
pipe.transform.position = randomHeight;
pipe.SetActive(true);
//My guess is that I want to instantiate the object pipe's beam to false here
}
}
// Instantiate(obstaclesPrefab[obstaclesIndex], randomHeight, obstaclesPrefab[obstaclesIndex].transform.rotation); //This is used only if I don't want to deal with object pooling, but the whole point is to use it. This is just a reference if I want to go back
}
}
Feel free to leave some suggestions in what I have missed out or any questions in regards to fill in. Thank you for your time!
I wanted to share some good news. My problems has been fixed!
Before I step into the solution, big thanks to rotgers and sharing me the approach he used. I actually was stubborn and wanted to feel unique in some way when it was coming to a solution. I had to step back a little bit and look at the big picture.
Reflection
If you ever struggle and spent like 8-12hrs like me for some logic nonsense and you feel it in the tip of the tongue, it is time to step a little bit back when you are not going anywhere after 1 hour.
Few tips to do (which I didn't do but I wish I do at the moment) was have drawings in how you are picturing it out mentally. Not drawing for the first time was my first mistake. I felt challenged by my own mind and sabotaging my own work to continuing being stubborn.
Code Solution
Now, the solution is that I added my UIManager script with my SpawnManager and RayCastDetection in order to make it work. Here is how I did it:
UIManager.cs
public class UIManager : MonoBehaviour
{
public Text scoreBoardDisplay;
public Text scoreBoardDisplayShadowed;
public AudioSource sound_score;
public string scoreText;
public int score;
public int oldScore;
public bool success;
public bool incrementScore;
void Start()
{
success = false; //NEW
incrementScore = false; //NEW
}
void Update()
{
if (score != oldScore)
{
sound_score.Play();
scoreText = score.ToString();
scoreBoardDisplay.text = scoreText;
scoreBoardDisplayShadowed.text = scoreText;
oldScore = score;
}
}
}
SpawnManager.cs
public class SpawnManager : MonoBehaviour
{
public GameObject[] obstaclesPrefab;
private PlayerControl playerControllerScript;
public UIManager UIManagerScript; //NEW
private float startDelay = 1.69f;
private float repeatRate = 1.1f;
void Start()
{
playerControllerScript = GameObject.Find("Player").GetComponent<PlayerControl>();
UIManagerScript = GameObject.Find("UI_Manager").GetComponent<UIManager>();
InvokeRepeating("SpawnObstacle", startDelay, repeatRate);
}
void SpawnObstacle()
{
if (playerControllerScript.gameOver == false)
{
float randomY = Random.Range(-2f, 2f);
Vector3 randomHeight = new Vector3(35, randomY, -7);
GameObject pipe = ObjectPooling.sharedInstance.GetPooledObject();
if (pipe != null)
{
pipe.transform.position = randomHeight;
pipe.SetActive(true);
UIManagerScript.incrementScore = false; //NEW, where my problem happens to be solved
UIManagerScript.success = false; //NEW, where my problems happens to be solved
}
}
}
}
RayCastDetection.cs
public class RayCastDetection : MonoBehaviour
{
public UIManager UIManagerScript;
public PlayerControl playerControlScript;
private RaycastHit hit;
public bool passed;
void Start()
{
UIManagerScript = GameObject.Find("UI_Manager").GetComponent<UIManager>();
playerControlScript = GameObject.Find("Tomato").GetComponent<PlayerControl>();
passed = false;
}
void Update()
{
Vector3 beamDown = transform.TransformDirection(Vector3.down);
Ray ray = new Ray(transform.position, beamDown);
if (!passed)
{
if (Physics.Raycast(ray, out hit))
{
if (hit.collider.tag == "Player" && !UIManagerScript.success) //Setting a condition to check if the beam hasn't been touched YET
{
//Once touched, turn this true and increment
UIManagerScript.incrementScore = true;
UIManagerScript.score++;
}
//If I already incremented my score, then there is no need to re-increment it again from the same ray. Thus, a flag for success is handled
if (UIManagerScript.incrementScore)
UIManagerScript.success = true;
Debug.DrawRay(transform.position, hit.point - transform.position);
}
}
}
}
Again, thank you very much for guiding me to the right track :). If I think in something to add, I will update it under my Reflection header.
If you want to play around or use the code, feel free and play with it in my Git

Crafting System not working in unity why?

So, I made a rudimentary crafting system but the crafting system is not working and I don't know why it doesn't work
public class CraftingItem: MonoBehaviour
{
public int RequiredItems;
public SCItem[] Item;
private GameObject player;
private GameObject playerAction;
public GameObject FinishItem;
public void Start(){
player = GameObject.FindWithTag("Player");
playerAction = GameObject.FindWithTag("ItemManager");
}
public void CheckForRequiredItems(){
int itemsInManager = playerAction.transform.childCount;
if(itemsInManager > 0){
int itemsFound = 0;
for(int i = 0; i < RequiredItems; i++){
for(int z = 0; z < itemsInManager; z++){
if(playerAction.transform.GetChild(z).GetComponent<Item>() == Item[z]){
itemsFound++;
break;
}
}
}
if(itemsFound >= RequiredItems){
Instantiate(FinishItem, transform.position, transform.rotation);
}
}
}
}
unity inspector panel :
I use a scriptable object to make the item, material, and attack items easy to control. Also, this script doesn't tell an error or syntax error, and I little bit confused. can you explain why and how can i fix this?
I bet you wanted "i" there instead of "z" :)
if(playerAction.transform.GetChild(z).GetComponent<Item>() == Item[**i**]){

Why does Audio not Work in my code unity ? unity c#

For some reason, my code is audio not working only... I have no idea why. can someone help? I am using a AudioClip[] my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LevelManage : MonoBehaviour
{
public GameObject[] animals;
public GameObject[] blackAnimals;
Vector3[] animalStartPos;
Vector3[] animalBlackStartPos;
public AudioSource source;
public AudioClip[] Correct;
public AudioClip InCorrect;
void Start()
{
animalStartPos = new Vector3[animals.Length];
animalBlackStartPos = new Vector3[blackAnimals.Length];
for (int i = 0; i < animals.Length; i++)
{
animalStartPos[i] = animals[i].transform.position;
animalBlackStartPos[i] = blackAnimals[i].transform.position;
}
}
//drag
public void dragAnimal(GameObject animal)
{
animal.transform.position = Input.mousePosition;
}
//drop
public void dropAnimal(GameObject animal)
{
int index = System.Array.IndexOf(animals, animal);
float dist = Vector3.Distance(animal.transform.position, blackAnimals[index].transform.position);
if (dist < 75)
{
animal.transform.position = blackAnimals[index].transform.position;
source.clip = Correct[Random.Range(0, Correct.Length)];
source.Play();
}
else
{
animal.transform.position = animalStartPos[index];
source.clip = InCorrect;
source.Play();
}
}
}
The console has errors:
UnassignedReferenceException: The variable source of LevelManage has not been assigned.
You probably need to assign the source variable of the LevelManage script in the inspector.
Please help!
any answers are greatly appreciated.
1) Make sure your clips are imported correctly and actually play in the unity editor by selecting them in the 'Project' panel and playing like this .
2) Make sure your source.volume is NOT 0.
3) Make sure your source.mute is false.
4) Make sure your AudioListener.volume (the global volume) is NOT 0.
5) Make sure your AudioListener.pause (the global pause) is false.
6) Make sure you have 'Mute Audio' toggle in the editor not toggled this is where you can find it

Problems with C# coding in Unity for a specific project

I'm making a simple game for a school project using Unity. The purpose of the game is to control a ball and pick up coins. The game has 3 scenes. I have written some code in C# to count my pick up coins and set a condition to check if all coins are picked up, if so, a wintext appears at the center of the screen.
It works just fine for the first scene (lvl1) but not for the other 2.
All 3 scenes have a different number of coins. C# is new to me and I have tried various combinations but it hasn't worked.
How do I re-write this code so that the wintext appears after I pick up the right number of coins on every scene/level?
This is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerController : MonoBehaviour
{
public float speed;
public Text countText;
public Text winText;
private Rigidbody rb;
private int count;
void Start()
{
rb = GetComponent<Rigidbody>();
count = 0;
SetCountText();
winText.text = "";
}
void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
rb.AddForce(movement * speed);
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Pick Up"))
{
other.gameObject.SetActive(false);
count = count + 1;
SetCountText();
}
}
void SetCountText()
{
countText.text = "Coins: " + count.ToString();
if (count >= 2)
{
winText.text = "You Win!";
}
}
}
Make a new public variable
...
public float speed;
public Text countText;
public Text winText;
public int numberOfCoinsToWin;
...
remember to set this new value in the editor for each scene
Use the variable in your condition.
if (count >= numberOfCoinstoWin)
{
winText.text = "You Win!";
}
Sounds like you're lacking a very basic understanding of C# and programming in general. Here are somethings you could research to make life easier for you:
variables
control flow
access modifiers
classes (in computer science)
object orientation
Also using Unity to learn C# is not great. You will miss a lot of fundamentals. I suggest learning C# without unity for a week or 2 and coming back.
This code snippet would dynamically set win condition based on scene, however it would be better if the scene could hold coinToCollect variable.
void SetCountText()
{
countText.text = "Coins: " + count.ToString();
int coinsToCollect = 0;
switch( /* get current scene here */)
{
case "scene1": // repeat for other scenes
coinsToCollect = 2;
break;
}
if (count >= coinsToCollect)
{
winText.text = "You Win!";
}
}

Android Controller for Unity game

I was making game in Unity 3D
and used the one click converter of unity to convert it in Android .apk
The game is opening in Android phone
but the player is not moving
Player controller Script:
using UnityEngine;
using System.Collections;
public class PlayerController : MonoBehaviour {
public Vector2 moving = new Vector2();
public int Bulletlimit = 0;
public int MaxBulletlimit = 3;
public bool Gun;
private float lastShotTime ;
public float fireDelay = 0.2f;
public Transform BulletDirection;
public Bullet bullet;
// Use this for initialization
void Start () {
lastShotTime = Time.time;
}
// Update is called once per frame
void Update () {
moving.x = moving.y = 0;
if (Input.GetKey ("right")) {
moving.x = 1;
} else if (Input.GetKey ("left")) {
moving.x = -1;
}
if (Input.GetKey ("up")) {
moving.y = 1;
} else if (Input.GetKey ("down")) {
moving.y = -1;
}
if (Input.GetKey ("s")) {
if(Gun){
if(Bulletlimit < MaxBulletlimit)
{
if(Time.time > lastShotTime + fireDelay)
{
Bullet clone = Instantiate (bullet, BulletDirection.position, Quaternion.identity) as Bullet;
Bulletlimit = Bulletlimit + 1;
lastShotTime = Time.time;
}
}
}
}
}
public void BulletCount()
{
Bulletlimit = Bulletlimit - 1;
}
}
How do I make him move in touch screens?
Your code is based on keystrokes - that (probably) wont apply to your touch screen.
There are a few ways you could do this, for something so simple I would probably try adding a Canvas along with some buttons to act as controls.
From there you can use the OnMouseDown()/OnMouseUp()/OnClick() methods (This also converts to touchscreens) instead of keystrokes.
How you code from there is a choice you have to make but in this case I would likely use the buttons to turn on/off movement bools and check/apply them in the Update() method.
If you're unsure of how to use the new unity UI try this...
https://unity3d.com/learn/tutorials/modules/beginner/ui/ui-canvas
If you're running the new version of unity (currently 5.1 I believe), my editor looks different to the tutorials and won't stay as default for some reason. Simply set the editor/inspector view to default if some of the options appear to be missing.
There are more complex things you can do with the actual touch inputs but I don't think you need to worry about it in this particular case.
Hope this helps :)

Categories