I am building a game on unity on real old version like 5.3. I have a main scene and a menu scene.Each scene got an audio manager script which controls the audio. My problem is with this function called DontDestroyOnLoad. More specifically the problem is that when I build the game the game runs perfectly and the menu too but the menu got sounds and the game not.The game has only some sounds that is out of audio manager and I have put them by hand.
My code of audiomanager is here
using UnityEngine;
[System.Serializable]
public class Sound
{
public string name;
public AudioClip clip;
[Range(0f, 1f)]
public float volume = 0.7f;
[Range(0.5f, 1.5f)]
public float pitch = 1f;
[Range(0f, 0.5f)]
public float randomVolume = 0.1f;
[Range(0f, 0.5f)]
public float randomPitch = 0.1f;
public bool loop = false;
private AudioSource source;
public void SetSource(AudioSource _source)
{
source = _source;
source.clip = clip;
source.loop = loop;
}
public void Play()
{
source.volume = volume * (1 + Random.Range(-randomVolume / 2f, randomVolume / 2f));
source.pitch = pitch * (1 + Random.Range(-randomPitch / 2f, randomPitch / 2f));
source.Play();
}
public void Stop()
{
source.Stop();
}
}
public class AudioManager : MonoBehaviour
{
public static AudioManager instance;
[SerializeField]
Sound[] sounds;
void Awake()
{
if (instance != null)
{
if (instance != this)
{
Destroy(this.gameObject);
}
}
else
{
instance = this;
DontDestroyOnLoad(this);
}
}
void Start()
{
for (int i = 0; i < sounds.Length; i++)
{
GameObject _go = new GameObject("Sound_" + i + "_" + sounds[i].name);
_go.transform.SetParent(this.transform);
sounds[i].SetSource(_go.AddComponent<AudioSource>());
}
PlaySound("Music");
}
public void PlaySound(string _name)
{
for (int i = 0; i < sounds.Length; i++)
{
if (sounds[i].name == _name)
{
sounds[i].Play();
return;
}
}
// no sound with _name
Debug.LogWarning("AudioManager: Sound not found in list, " + _name);
}
public void StopSound(string _name)
{
for (int i = 0; i < sounds.Length; i++)
{
if (sounds[i].name == _name)
{
sounds[i].Stop();
return;
}
}
// no sound with _name
Debug.LogWarning("AudioManager: Sound not found in list, " + _name);
}
}
You need to specify that DontDestroyOnLoad will affect the gameObject itself, not only the MonoBehaviour.
void Awake()
{
if (instance != null)
{
if (instance != this)
{
Destroy(this.gameObject);
}
}
else
{
instance = this;
DontDestroyOnLoad(this.gameObject); // <-- Here
}
}
Related
I wanted to add multiplayer to my game but i quickly faced many many problems, i cant find any clear solution online. i made a weapon script where the player can shoot and reload.
problems:
when i shoot, on 1 screen both players shoot, but at the other screen nothing happens
when i swap weapons, on 1 screen both players swap weapons, on the second screen nothing happens
note: im still a complete beginner so go easy on me please :)) thanks!
weapon script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using Photon.Pun;
public class weaponScript : MonoBehaviour
{
public weaponSO weaponStats;
public GameObject reloadingText;
public TMP_Text ammoValue;
public Transform firePoint;
public GameObject bulletPrefab;
public float bulletForce = 2f;
public bool isShooting;
public float pistolDamage;
public int currentAmmoClip = 1;
public int maxAmmoClip;
public int currentReserve = 1;
public int maxReserve;
public float reloadTime = 2f;
public bool isReloading = false;
public int bulletsShot;
public float startingDamage;
PhotonView view;
public void Start()
{
view = GetComponent<PhotonView>();
if(view.IsMine)
{
view.RPC("Update", RpcTarget.AllBuffered);
}
WeaponStats();
currentAmmoClip = maxAmmoClip;
currentReserve = maxReserve;
bulletsShot = maxAmmoClip - currentAmmoClip;
isShooting = false;
weaponStats.damage = startingDamage;
}
[PunRPC]
public void Update()
{
ammoValue.text = currentAmmoClip.ToString("0") + "/" + currentReserve.ToString("0");
if (Input.GetKeyDown(KeyCode.R))
{
if(currentReserve <= 0)
{
return;
}
if (currentAmmoClip == maxAmmoClip && !isReloading)
{
return;
}
else
{
StartCoroutine(Reload());
return;
}
}
if(Input.GetButtonDown("Fire1") && currentAmmoClip >= 1)
{
if(isReloading == false)
{
bulletsShot += 1;
currentAmmoClip -= 1;
isShooting = true;
Shoot();
FindObjectOfType<AudioManager>().Play("shot1");
return;
}
}
if(isReloading)
return;
if(currentAmmoClip <= 0 && currentReserve >= 1)
{
StartCoroutine(Reload());
return;
}
}
IEnumerator Reload()
{
reloadingText.SetActive(true);
isReloading = true;
yield return new WaitForSeconds(reloadTime);
if(currentReserve <= bulletsShot)
{
currentAmmoClip += currentReserve;
currentReserve = 0;
}
else
{
currentReserve -= bulletsShot;
currentAmmoClip = maxAmmoClip;
}
bulletsShot = 0;
reloadingText.SetActive(false);
isReloading = false;
}
void Shoot()
{
GameObject bullet = Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
rb.AddForce(firePoint.up * bulletForce, ForceMode2D.Impulse);
}
public void WeaponStats()
{
pistolDamage = weaponStats.initialDamage;
maxAmmoClip = weaponStats.maxAmmoClip;
maxReserve = weaponStats.maxReserve;
reloadTime = weaponStats.reloadTime;
bulletForce = weaponStats.bulletForce;
startingDamage = weaponStats.initialDamage;
}
}
weapon holder:
using UnityEngine;
public class WeaponHolder : MonoBehaviour
{
public int selectedWeapon = 0;
void Start()
{
SelectWeapon();
}
void Update()
{
if(FindObjectsOfType<weaponScript>()[0].isReloading == true)
{
return;
}
else
{
switchWeapon();
}
}
public void switchWeapon()
{
int previousSelectedWeapon = selectedWeapon;
if(Input.GetAxis("Mouse ScrollWheel") > 0f)
{
if(selectedWeapon >= transform.childCount - 1)
{
selectedWeapon = 0;
}
else
{
selectedWeapon++;
}
}
if(Input.GetAxis("Mouse ScrollWheel") < 0f)
{
if(selectedWeapon <= 0)
{
selectedWeapon = transform.childCount - 1;
}
else
{
selectedWeapon--;
}
}
if(previousSelectedWeapon != selectedWeapon)
{
SelectWeapon();
}
}
public void SelectWeapon()
{
int i = 0;
foreach (Transform weapon in transform)
{
if(i == selectedWeapon)
{
weapon.gameObject.SetActive(true);
}
else
{
weapon.gameObject.SetActive(false);
}
i++;
}
}
}
i tried adding
if(view.IsMine == true)
{
Debug.Log("true");
}
else
{
Debug.Log("false");
}
but nothing happened and i couldnt shoot anymore. thanks for reading
The problem is you don't sync anything in your game. This means the other computer can't know what it has to do.
Whenever you want to sync something you have to call an RPC function.
Maybe watch some tutorials about how to do this properly and how to set up a simple multiplayer lobby.
EDIT:
What the other comments above mentioned with .IsMine is true, but you're doing it wrong. You have to call this in start or awake and whenever the view is not yours you disable the camera of the person only and disable shooting. Its important to only do this on your computer! If you are not doing everyone will shoot when you press Shoot and everyone will walk when you walk.
In the Unity editor, every scene loads smoothly, no errors in the console. But once built and running on an android device (OnePlus3 and Galaxy A50), just 2 specific scenes make the whole app/game crash.
I checked on the profiler if my assets were heavy in case it uses a lot of memory but that wasn't the case.
public class SceneController : MonoBehaviour {
[SerializeField] Transform[] enableOnStart;
[SerializeField] Image fade;
[SerializeField] float fadeTime;
[SerializeField] PoiController poiController;
void Awake() {
if(poiController != null)
poiController.OnInitializationFinished += OnInitialized;
else
StartCoroutine(StartScene());
}
void OnDisable() {
if(poiController != null)
poiController.OnInitializationFinished -= OnInitialized;
}
void OnInitialized() {
StartCoroutine(StartScene());
}
public void GoToScene(string sceneName) {
StartCoroutine(EndScene(sceneName));
}
public void GoToScene(int sceneIndex)
{
StartCoroutine(EndScene(sceneIndex));
}
public void GoToScene(string sceneName, float delay) {
StartCoroutine(EndScene(sceneName, delay));
}
IEnumerator StartScene() {
if(fade != null) {
yield return StartCoroutine(Fade(true));
}
foreach(Transform transform in enableOnStart) {
transform.gameObject.SetActive(true);
}
yield return 0;
}
IEnumerator EndScene(string sceneName) {
yield return StartCoroutine(Fade(false));
SceneManager.LoadScene(sceneName);
}
IEnumerator EndScene(int sceneIndex)
{
yield return StartCoroutine(Fade(false));
SceneManager.LoadScene(sceneIndex);
}
IEnumerator EndScene(string sceneName, float delay) {
yield return new WaitForSeconds(delay);
StartCoroutine(EndScene(sceneName));
}
IEnumerator Fade(bool isIn) {
fade.gameObject.SetActive(true);
float timer = 0, start, end;
if(isIn) {
start = 1;
end = 0;
} else {
start = 0;
end = 1;
}
while(timer / fadeTime < 1) {
timer += Time.deltaTime;
Color color = fade.color;
color.a = Mathf.Lerp(start, end, timer / fadeTime);
fade.color = color;
yield return new WaitForEndOfFrame();
}
}
}
Here's the link for logcat crash logs:
https://drive.google.com/drive/folders/1IH1hJuSU-HL-nEsT9e5qms5JUIJRPIGV?usp=sharing
I have a small game where the player flies a rocket to a green platform to progress. If they crash, they start over. The levels simply loop and the gameplay works fine. The issue I'm having is with the timer. I'm trying to make a system that keeps track of how long it takes the player to beat every level, and then save their fastest time under the record variable. I tried adding a hotkey in the Timer script to reset the timer, but if I run the same ResetTimer function from the Rocket script, the currentTime variable will not change. It still prints "Timer reset" but the variable stays the same. Any ideas?
Rocket.cs
using UnityEngine.SceneManagement;
public class Rocket : MonoBehaviour
{
Timer timer = new Timer();
[SerializeField] float rotationThrust = 100f;
[SerializeField] float mainThrust = 50f;
[SerializeField] float levelLoadDelay = 2f;
[SerializeField] AudioClip mainEngine;
[SerializeField] AudioClip success;
[SerializeField] AudioClip death;
[SerializeField] ParticleSystem mainEngineParticles;
[SerializeField] ParticleSystem successParticles;
[SerializeField] ParticleSystem deathParticles;
Rigidbody rigidBody;
AudioSource audioSource;
enum State { Alive, Dying, Transcending }
State state = State.Alive;
bool collisionsEnabled = true;
void Start()
{
rigidBody = GetComponent<Rigidbody>();
audioSource = GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.Escape))
{
Application.Quit();
}
if (state == State.Alive)
{
RespondToThrustInput();
RespondToRotateInput();
}
if (Debug.isDebugBuild)
{
RespondToDebugKeys();
}
}
private void RespondToDebugKeys()
{
if (Input.GetKeyDown(KeyCode.L))
{
LoadNextLevel();
}
else if (Input.GetKeyDown(KeyCode.K))
{
LoadFirstLevel();
}
else if (Input.GetKeyDown(KeyCode.C))
{
collisionsEnabled = !collisionsEnabled;
}
else if (Input.GetKeyDown(KeyCode.P))
{
timer.ResetRecord();
}
}
private void RespondToThrustInput()
{
if (Input.GetKey(KeyCode.Space) || Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow))
{
ApplyThrust();
}
else
{
audioSource.Stop();
mainEngineParticles.Stop();
}
}
private void ApplyThrust()
{
rigidBody.AddRelativeForce(Vector3.up * mainThrust);
if (!audioSource.isPlaying)
{
audioSource.PlayOneShot(mainEngine);
}
mainEngineParticles.Play();
}
private void RespondToRotateInput()
{
rigidBody.freezeRotation = true;
float rotationSpeed = rotationThrust * Time.deltaTime;
if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
{
transform.Rotate(Vector3.forward * rotationSpeed);
}
else if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
{
transform.Rotate(Vector3.back * rotationSpeed);
}
rigidBody.freezeRotation = false;
}
void OnCollisionEnter(Collision collision)
{
if (state != State.Alive || !collisionsEnabled)
{
return;
}
switch (collision.gameObject.tag)
{
case "Friendly":
break;
case "Finish":
StartSuccessSequence();
break;
default:
StartDeathSequence();
break;
}
}
private void StartSuccessSequence()
{
state = State.Transcending;
audioSource.PlayOneShot(success);
successParticles.Play();
Invoke("LoadNextLevel", levelLoadDelay);
}
private void StartDeathSequence()
{
state = State.Dying;
audioSource.Stop();
deathParticles.Play();
audioSource.PlayOneShot(death);
Invoke("LoadFirstLevel", levelLoadDelay);
}
public void LoadFirstLevel()
{
timer.ResetTimer();
SceneManager.LoadScene(1);
}
private void LoadNextLevel()
{
int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
if (currentSceneIndex + 1 == SceneManager.sceneCountInBuildSettings)
{
timer.Save();
LoadFirstLevel();
}
else
{
SceneManager.LoadScene(currentSceneIndex + 1);
}
}
}
Timer.cs
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
using UnityEngine.UI;
using System;
public class Timer : MonoBehaviour
{
float currentTime;
float record = -1f;
public Text currentText;
public Text recordText;
private void Start()
{
DontDestroyOnLoad(gameObject);
}
public void Save()
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(Application.persistentDataPath + "/playerInfo.dat");
PlayerData data = new PlayerData();
if (RecordTest() == true)
{
if (currentTime < record)
{
record = currentTime;
data.recordSave = record;
}
}
else record = currentTime;
data.recordSave = record;
bf.Serialize(file, data);
file.Close();
}
public void Load()
{
if(File.Exists(Application.persistentDataPath + "/playerInfo.dat"))
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/playerInfo.dat", FileMode.Open);
PlayerData data = (PlayerData)bf.Deserialize(file);
file.Close();
record = data.recordSave;
}
}
private void Update()
{
CheckForResetInput();
SetCurrentTimeText();
SetRecordTimeText();
}
private void SetRecordTimeText()
{
if (record == -1)
{
recordText.text = "N/A";
}
else
{
string minutesRecord = Mathf.Floor(record / 60).ToString("00");
string secondsRecord = Mathf.Floor(record % 60).ToString("00");
recordText.text = minutesRecord + ":" + secondsRecord;
}
}
private void CheckForResetInput()
{
if (Input.GetKeyDown(KeyCode.R))
{
ResetTimer();
}
}
private void SetCurrentTimeText()
{
currentTime += Time.deltaTime;
string minutes = Mathf.Floor(currentTime / 60).ToString("00");
string seconds = (currentTime % 60).ToString("00");
currentText.text = minutes + ":" + seconds;
}
public void ResetTimer()
{
print("Timer reset");
currentTime = 0;
}
public void ResetRecord()
{
record = -1f;
}
public bool RecordTest()
{
if (record == -1)
{
return false;
}
else return true;
}
public void Collided()
{
print("Ouch");
}
}
[Serializable]
class PlayerData
{
public float recordSave;
}
Serializedfields with no other modifier are PRIVATE to the class they are in. Serializedfields are purely for them to show in the unity inspector, they need to be public for you to access them from other classes
So
[SerializeField] float rotationThrust = 100f; is private to Rocket
[SerializeField] public float rotationThrust = 100f; will be visible to other classes with a reference to a Rocket instance - Technically you dont need to serialize the field as public will always show, but it makes it look nice in your code if they all line up :D
You are creating a new instance of Timer for your Rocket component.
Timer timer = new Timer();
This means that when rocket calls timer.ResetTimer() is resetting the newly instanced Timer class, but NOT the MonoBehaviour attached to your scene object.
Rather than declare a new timer in your rocket class, create a reference to it in the inspector via:
[SerializeField] Timer timer;
and drag/drop your Timer object to that reference.
The above solution is probably the best pattern, but if you want a quick and easy solution, change your currentTime to static:
private static float currentTime
Making this value static means that all Timer instances will share the same value. Since the variable is static, it is not specific to a single class instance. With this solution you are not eliminating the multiple instances of Timer, but you are forcing them to share a currentTime variable. This doesn't fix the design that caused your problem, but it does fix the symptom. This is why I say it is not the best solution.
I have a script that currently spawns enemies by set positions (point spawn). I am wondering how am I able to make them spawn ONLY when player is, for example, within 50 meters from spawn-point.
Wander Manager:
public class WanderingManager : MonoBehaviour {
public Transform[] wanderingPoints;
void getNewPos(GameObject target){
target.SendMessage("setNewWanderPos", wanderingPoints[Random.Range(0, wanderingPoints.Length)].position, SendMessageOptions.DontRequireReceiver);
}
}
Enemy Manager script:
[RequireComponent (typeof (WanderingManager))]
public class EnemyManager: MonoBehaviour {
public int maxZombies = 7;
public float spawnInterval = 5.0f;
public string zombiesTag = "Zombie", playerTag = "Player";
public GameObject[] ZombiePrefabs = null;
private GameObject player = null;
private ArrayList Zombies = new ArrayList();
private float lastTime = -10.0f;
private bool loaded = false;
private WanderingManager manager;
void Start () {
manager = GetComponent<WanderingManager>();
findPlayer();
}
// Update is called once per frame
void LateUpdate () {
if(player == null && loaded){
StartCoroutine(Restart());
}
if(Time.time > lastTime){
for(int i=0; i<Zombies.Count; i++){
if(Zombies[i] == null){
Zombies.RemoveAt(i);
}
}
if(Zombies.Count > maxZombies){
Zombies.RemoveAt(Zombies.Count - 1);
}else{
Transform point = manager.wanderingPoints[Random.Range(0, manager.wanderingPoints.Length)];
GameObject Z = Instantiate(ZombiePrefabs[Random.Range(0, ZombiePrefabs.Length)], point.position, point.rotation * Quaternion.Euler(0.0f, Random.Range(0.0f, 180.0f), 0.0f)) as GameObject;
Zombies.Add(Z);
}
lastTime = Time.time + spawnInterval;
}
}
void findPlayer(){
GameObject newPlayer = GameObject.FindWithTag(playerTag);
if(newPlayer != null && !newPlayer.name.Contains("Clone")){
player = newPlayer;
loaded = true;
}else if(newPlayer != null && newPlayer.name.Contains("Clone")){
Destroy(newPlayer);
retrySearch();
}
}
void retrySearch(){
findPlayer();
}
IEnumerator Restart(){
yield return new WaitForSeconds(10.0f);
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
}
Any help will be appreciated. Thanks!
Just do a distance check. Something like this:
if (Vector3.Distance(player.transform.position,enemySpawn.transform.position) < 50):
{
spawnEnemyLogic();
}
I keep getting the error Object reference not set to an instance of an object on line 64 and I can't figure out what I need to do
Everything is working like the lives are showing up and the timer and the score is but the score isn't increasing if you can tell me what is wrong I would be so thankful
Here's my code:
using UnityEngine;
using System.Collections;
public class Player : MonoBehaviour {
private CountdownTimer myTimer;
private int score = 0;
private int lives = 3;
private int DEATH_Y = -10;
public Texture2D LivesLeft1;
public Texture2D LivesLeft2;
public Texture2D LivesLeft3;
public int GetScore(){
return score;
}
public int GetLives()
{
return lives;
}
private void Start()
{
myTimer = GetComponent<CountdownTimer>();
}
private void Update()
{
float y = transform.position.y;
if (y < DEATH_Y) {
MoveToStartPosition();
lives--;
}
if (score == 10)
{
Application.LoadLevel("Level2");
}
if (lives == 0)
{
Application.LoadLevel("GameOver");
}
}
private void OnGUI()
{
GUILayout.BeginHorizontal ();
DisplayLives();
int secondsLeft = myTimer.GetSecondsRemaining();//this is line 64
string timeMessage = "Seconds left = " + secondsLeft;
GUILayout.Label(timeMessage);
string scoreMessage = "Score = " + score;
GUILayout.Label (scoreMessage);
}
private void DisplayLives()
{
int playerLives = GetLives();
if (1 == playerLives) {
GUILayout.Label(LivesLeft1);
}
if (2 == playerLives)
{
GUILayout.Label(LivesLeft2);
}
if(3 == playerLives){
GUILayout.Label(LivesLeft3);
}
}
private void MoveToStartPosition()
{
Vector3 startPosition = new Vector3(0,5,0);
transform.position = startPosition;
}
/**
* what increases the score
* anything with the tag Hidden
*/
private void OnTriggerEnter(Collider c)
{
string tag = c.tag;
if("Hidden" == tag)
{
score++;
}
}
}
Are you sure that your GameObject have the Component called CountdownTimer?
Also, change the Start function to Awake, because that line is not depending on anything else.