Unity + FMOD health player variable to getParameter doesn't work - c#

Can you please help me with this problem, I'm adding FMOD to UNITY and want to change my music when Player gets damage, from FMOD side is OK, but in Unity it gives me an error: NullReferenceException: Object reference not set to an instance of an object MusicControl.Update () (at Assets/MusicControl.cs:
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
using FMOD.Studio;
public class MusicControl : MonoBehaviour {
[FMODUnity.EventRef]
public string explosion = "event:/EXPLOSION";
[FMODUnity.EventRef]
public string shoot = "event:/SHOOT SOUND";
[FMODUnity.EventRef]
public string menuMusic = "event:/MENU MUSIC";
int val;
public FMOD.Studio.EventInstance musicEv;
public FMOD.Studio.ParameterInstance musicPar;
void Start()
{
}
//music for menu, I'm call this function when my stage starts(menu game)
public void MenuMusic()
{
musicEv = FMODUnity.RuntimeManager.CreateInstance(menuMusic);
musicEv.start();
}
//music for level 1, I'm call this function when my stage starts(level game)
public void LevelMusic()
{
musicEv = FMODUnity.RuntimeManager.CreateInstance(menuMusic);
musicEv.setParameterValue("FIGHT MUSIC", 100f);
musicEv.getParameter("HEALTH", out musicPar);
musicPar.setValue(100);
musicEv.start();
}
//I'm call this function when stages is close up
public void StopMusic()
{
musicEv.stop(FMOD.Studio.STOP_MODE.IMMEDIATE);
}
// I'm take current Health from Player script
void Update()
val = GameObject.Find("Player").GetComponent<Player>().stats.curHealth;
musicPar.setValue(val); //Unity gives me an error - NullReferenceException: Object reference not set to an instance of an object MusicControl.Update () (at Assets/MusicControl.cs:147)
}
}
Thanks for advance

The musicEv and musicPar variables are declared but never initialized before use in the Update() function.
You tried to to initialize them in the MenuMusic() and LevelMusic() functions but there is no guarantee that these functions will be called before the Update() function where you actually use them.
Remove musicEv = FMODUnity.RuntimeManager.CreateInstance(menuMusic); from both MenuMusic() and LevelMusic() functions and move it to the Start() or Awake() function in order to initialize musicEv.
After that, you can then initialize musicPar by calling musicEv.getParameter("HEALTH", out musicPar);.
Also, don't do val = GameObject.Find("Player") in the Update function. Do it once in Start() or Awake() function then save it to a global variable. In fact, it would be good to just cache the Player script that is attached to it.
public class MusicControl : MonoBehaviour {
[FMODUnity.EventRef]
public string explosion = "event:/EXPLOSION";
[FMODUnity.EventRef]
public string shoot = "event:/SHOOT SOUND";
[FMODUnity.EventRef]
public string menuMusic = "event:/MENU MUSIC";
int val;
public FMOD.Studio.EventInstance musicEv;
public FMOD.Studio.ParameterInstance musicPar;
private Player player;
void Awake()
{
//Initialize musicEv
musicEv = FMODUnity.RuntimeManager.CreateInstance(menuMusic);
//Initialize musicPar(done with the out keyword)
musicEv.getParameter("HEALTH", out musicPar);
//Initialize player
player = GameObject.Find("Player").GetComponent<Player>();
}
//music for menu, I'm call this function when my stage starts(menu game)
public void MenuMusic()
{
musicEv.start();
}
//music for level 1, I'm call this function when my stage starts(level game)
public void LevelMusic()
{
musicEv.setParameterValue("FIGHT MUSIC", 100f);
musicEv.getParameter("HEALTH", out musicPar);
musicPar.setValue(100);
musicEv.start();
}
//I'm call this function when stages is close up
public void StopMusic()
{
musicEv.stop(FMOD.Studio.STOP_MODE.IMMEDIATE);
}
// I'm take current Health from Player script
void Update()
{
val = player.stats.curHealth;
musicPar.setValue(val);
}
}

Related

GameObject won't change after updating it with a Method (object reference not set)

Unity newb here :)
I have a button which calls this function which basicly sets a prefab to instantiate.
public void setTurret()
{
towerNode.setTurretType(turret)
Debug.Log("selected shop turret:" + turret.name);
}
My Tower_Node class handles the actual instantiate
public class Tower_Node : MonoBehaviour
{
private GameObject turret;
public void setTurretType(TowerType _turret)
{
turret= _turret.prefab;
}
private void OnMouseDown()
{
Instantiate(turret,GetBuildPosition(),Quaternion.identity);
}
...}
EDIT: What I also tried
public class Tower_Node : MonoBehaviour
{
private TowerType turret;
public void setTurretType(TowerType _turret)
{
turret = _turret;
}
private void OnMouseDown()
{
Instantiate(turret.prefab,GetBuildPosition(),Quaternion.identity);
}
...}
EDIT:
This is how the references in the inspector look. The shop script is the script with the setTurret() method
--
Setting the turret with the setTurretType method works. If i check it with Debug.Log() i get the right TowerType but outside of the function the gameobject is still =null and when i try to instantiate it gives me an NullReferenceException (because Gameobject is null obviously)
What am i missing here?
Thank you for your answers.
switch OnMouseDown() for
if (Input.GetMouseButtonDown(0))
in your update loop
just make sure that turret.prefab is a prefab
Instantiate(turret.prefab, GetBuildPosition(),Quaternion.identity);
I would say that if your TowerType class has a GameObject var named prefab, you should either assign the prefab to the prefab, or assign the turret to the _turret and the access the prefab.
Either this:
public void setTurretType(TowerType _turret) {
turret = _turret;
hoverTurret = _turret.hoverPrefab;
}
private void OnMouseDown()
{
Instantiate(turret.prefab, GetBuildPosition(),Quaternion.identity);
}
or
public void setTurretType(TowerType _turret) {
turret.prefab = _turret.prefab;
hoverTurret = _turret.hoverPrefab;
}
private void OnMouseDown()
{
Instantiate(turret.prefab, GetBuildPosition(),Quaternion.identity);
}
should work.
Edit:
You could set the turret while instiantiating:
public void setTurretType(TowerType _turret) {
turret.prefab = _turret.prefab;
hoverTurret = _turret.hoverPrefab;
}
private void OnMouseDown()
{
turret = Instantiate(turret.prefab, GetBuildPosition(),Quaternion.identity);
}

How do I make my cloned game objects global, so I can use them outside of the function in Unity?

I just recently got into Unity 3D and currently working on one of my first own projects. For the game im making, I need a spawner function, that respawns a clone of the enemy as soon as the enemy falls off the platform. This is the code I have right now:
using UnityEngine;
public class spawner : MonoBehaviour
{
public GameObject enemyPrefab;
public float spawnHeight = 0.75f;
// Start is called before the first frame update
void Start()
{
spawnEnemy();
}
// Update is called once per frame
void Update()
{
if (enemyClone.transform.position.y < -10)
{
Destroy(enemyClone);
spawnEnemy();
}
}
public void spawnEnemy()
{
var enemyPosition = new Vector3(Random.Range(-5, 5), spawnHeight, Random.Range(-5, 5));
var enemyClone = Instantiate(enemyPrefab, enemyPosition, Quaternion.identity);
}
}
The function spawnEnemy itself works fine, since it creates an enemy on game start, tho further enemies aren't spawned. I get the message: "Assets\spawner.cs(21,21): error CS0103: The name 'enemyClone' does not exist in the current context".
I do see why I get the message, don't know how to make enemyClone globally available however.
Thanks to everybody in advance,
bezunyl
In the spawnEnemy() function, you say var enemyClone = Instantiate(...);. enemyClone is a local variable that can only be used within the spawnEnemy function, or at least that's how you've written it.
If you want to use the enemyClone outside of the spawnEnemy function, you need to declare the enemyClone variable outside of the function. (The example below will work if you DON'T want enemyClone to be accessible to other GameObjects)
using UnityEngine;
public class spawner : MonoBehaviour
{
public GameObject enemyPrefab;
public float spawnHeight = 0.75f;
private GameObject enemyClone //Added to allow enemyClone to be used anywhere in the class
// Start is called before the first frame update
void Start()
{
spawnEnemy();
}
// Update is called once per frame
void Update()
{
if (enemyClone.transform.position.y < -10)
{
Destroy(enemyClone);
spawnEnemy();
}
}
public void spawnEnemy()
{
var enemyPosition = new Vector3(Random.Range(-5, 5), spawnHeight, Random.Range(-5, 5));
enemyClone = Instantiate(enemyPrefab, enemyPosition, Quaternion.identity);
}
}
Now if you want enemyClone to be accessible by other GameObjects, then you'll want to make the enemyClone variable public instead of private. If you don't want it to show up in the inspector, add [HideInInspector] above the declaration of enemyClone, as shown below:
[HideInInspector]
public GameObject enemyClone;
Your issue is based on scope. You might want to research it, it's important to know.
The scope of a variable determines its visibility to the rest of a program.
http://www.blackwasp.co.uk/CSharpVariableScopes.aspx
http://www.informit.com/articles/article.aspx?p=1609145&seqNum=4
Spawning GameObject on demand is expensive. Instead of spawning it everytime, you should pool the GameObject.
public class Spawner : MonoBehaviour {
public Enemy enemyPrefab;
public List<Enemy> enemyPool;
public const SPAWN_HEIGHT = 0.75f;
// Start is called before the first frame update
void Start()
{
enemyPool = new List<Enemy>();
spawnEnemy();
}
// Update is called once per frame
public void Despawn(Enemy deadEnemy)
{
deadEnemy.gameObject.SetActive(false);
enemyPool.Add(deadEnemy);
}
public void spawnEnemy() {
Enemy newEnemy;
if (enemyPool.Count > 0) {
newEnemy = enemyPool[0];
enemyPool.Remove(0);
} else {
newEnemy = Instantiate(enemyPrefab);
}
newEnemy.Init(this);
newEnemy.position = new Vector3(Random.Range(-5, 5), SPAWN_HEIGHT, Random.Range(-5, 5));
newEnemy.gameObject.SetActive(true);
}
}
public class Enemy : MonoBehaviour {
private Spawner spawner;
private const float DEATH_POSITION_Y = -10;
public void Init(Spawner spawner) {
this.spawner = spawner;
}
void Update() {
if (transform.position.y < DEATH_POSITION_Y) {
spawner.Despawn(this);
}
}
}

Client doesnt get spawned gameObjects from server, and the other way around

So, I'm struggling with the whole Unet system. right now, all I want to do is to spawn object from one end to the other. but even that doesn't work. It spawns the object on each the server and the client. but doesn't sync. I've searched so many places and watched so many videos - none helped. here are the code and the inspector details
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class NetworkController : NetworkBehaviour
{
public GameObject[] Spawns;
GameObject Canvas;
GameObject spawn;
[SyncVar]
public NetworkInstanceId ParentId;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
}
[Command]
public void CmdspawnPrefab()
{
Transform trns = GameObject.Find("Canvas").transform;
trns.position = trns.position + new Vector3(100, 200, 0);
GameObject go = Instantiate(Spawns[0], trns);
NetworkServer.Spawn(go);
}
}
What am I missing?
As it seems, my whole workflow was not correct. the solution is firstly to attach a listener to the button, instead of using the onclick function on the inspector -
Button b = BoardController.Buttons[0].GetComponent<Button>();
b.onClick.AddListener(delegate () { Cmd_ButtonPressed(1); });
the second is to create gameobjects, containing a script "GameManager" who controls your board, and one "RpcManager" which contains the Rpc functions. then in the playerobject script, use the commands.
Here are the classes i have used, to update an image instead of spawning an object, the idea is basically the same - to undersand the fundamentals of Unet and passing commands.
public class PlayerObject : NetworkBehaviour {
public BoardManager BoardController;
public ClientRPCmanager ClientRPCManager;
// Use this for initialization
void Start () {
ClientRPCManager = GameObject.FindGameObjectWithTag("ClientRPCmanager").GetComponent<ClientRPCmanager>();
BoardController = ClientRPCManager.BoardController;
Button b = BoardController.Buttons[0].GetComponent<Button>();
b.onClick.AddListener(delegate () { Cmd_ButtonPressed(1); });
}
// Update is called once per frame
void Update () {
}
public void SetButton(int i)
{
BoardController.SetButton(i);
}
[Command]
public void Cmd_ButtonPressed(int i)
{
ClientRPCManager.Rpc_ButtonPressed(i);
}
boardmanager
public class BoardManager : NetworkBehaviour
{
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public GameObject[] Buttons;
public Sprite[] Sprites;
public void SetButton(int index)
{
GameObject img = GameObject.Find("X");
img.GetComponent<Image>().sprite = Sprites[0];
}
RpcManager
public class ClientRPCmanager : NetworkBehaviour {
public BoardManager BoardController;
public NetworkManager Manager;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
[ClientRpc]
public void Rpc_ButtonPressed(int index)
{
BoardController.SetButton(index);
}
hopefully, this will somewhat help people with understanding Unet.
I learned this by carefully looking at this project https://www.youtube.com/watch?v=8Kd2RAfgzW0

How to achieve awareness of "kill" events in a scene

I have been doing a RPG game in Unity with C # and when doing a system of quests, specifically those of killing a certain number of enemies, I found the problem of having 3 enemies in the scene and being the target of the quest: Kill 3 enemies. If I kill them before activating the quest and later active the quest does not give me the reward (in this case experience). How can I tell the enemies and make that if the quest detects that I have already killed the necessary enemies to get the quest give me the reward equally?
Here the two needed scripts i think:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class QuestObject : MonoBehaviour {
public int questNumber;
public QuestManager qManager;
public string startText;
public string endText;
public bool isItemQuest;
public string targetItem;
public bool isEnemyQuest;
public string targetEnemy;
public int enemiesToKill;
private int enemyKillCount;
private PlayerStats playerStats;
public int EXPToGive;
void Start () {
playerStats = FindObjectOfType <PlayerStats> ();
}
void Update () {
if (isItemQuest) {
if (qManager.itemCollected == targetItem) {
qManager.itemCollected = null;
EndQuest ();
}
}
if (isEnemyQuest) {
if (qManager.enemyKilled == targetEnemy) {
qManager.enemyKilled = null;
enemyKillCount++;
}
if (enemyKillCount >= enemiesToKill) {
EndQuest ();
}
}
}
public void StartQuest (){
qManager.ShowQuestText (startText);
}
public void EndQuest (){
qManager.ShowQuestText (endText);
playerStats.AddEXP (EXPToGive);
qManager.questCompleted [questNumber] = true;
gameObject.SetActive (false);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyHealth : MonoBehaviour {
public int startingHealth;
public int currentHealth;
public GameObject damageBurst;
private PlayerStats playerStats;
public int EXPToGive;
public string enemyQuestName;
private QuestManager qManager;
void Start ()
{
// Setting up the references.
//anim = GetComponent <Animator> ();
//enemyAudio = GetComponent <AudioSource> ();
//enemyMovement = GetComponent <EnemyMovement> ();
//enemyAttacking = GetComponentInChildren <EnemyAttack> ();
// Set the initial health of the player.
currentHealth = startingHealth;
playerStats = FindObjectOfType <PlayerStats> ();
qManager = FindObjectOfType <QuestManager> ();
}
void Update ()
{
if (currentHealth <= 0) {
qManager.enemyKilled = enemyQuestName;
Destroy (gameObject);
playerStats.AddEXP (EXPToGive);
}
}
public void TakeDamage (int amountDamage)
{
// Reduce the current health by the damage amount.
currentHealth -= amountDamage;
Instantiate (damageBurst, transform.position, transform.rotation);
}
public void SetMaxHelth () {
currentHealth = startingHealth;
}
}
One Aproach would be to create some type of "WorldManager" which counts every Enemy which has been slain. And when Starting a quest this quest could check the WorldManagers kill count and add it to it's own count.
public void StartQuest (){
qManager.ShowQuestText (startText);
this.enemyKillCount += worldManager.GetKillCount();
}
In your enemy class you have to add a kill to your worldManager.
void Update ()
{
if (currentHealth <= 0) {
qManager.enemyKilled = enemyQuestName;
this.worldManager.AddKill(this)
Destroy (gameObject);
playerStats.AddEXP (EXPToGive);
}
}
Alternative:
Make your QManager be aware of every kill in a Scene.
You can achieve this through many ways.
One of them is passing your EnemyObject an reference of your Qmanager and do the same as with the "WorldManager" provided above, or you use Messaging and fire a Message targeting the QManager when an enemy is slain.
Alternative 2:
Throw an Event when an enemy has been slain and subscribe to it on your QManager/WorldManager. This way u can reuse your enemy class in every game. From my point of view static dependencies are evil, but there are many discussions and SO and everywhere on the internet about that.
You can several approach. The most straight-forward is to use static.
The purpose of static is for the variable/method to belong to the class instead of an instance of the class.
In your case, you want each enemy to have its own health, this cannot be static.
And you want to count how many instances there are in the scene from the class. So static is fine.
public class Enemy:MonoBehaviour
{
private static int enemyCount = 0;
public static int EnemyCount {get{ return enemyCount;} }
public event Action<int> RaiseEnemyDeath;
public static void ResetEnemyCount(){
enemyCount = 0;
}
private int health;
public void Damage(int damage)
{
CheckForDamage(); // here you check that damage is not neg or too big...
this.health -= damage;
if(this.health <= 0)
{
OnDeath();
}
}
void OnActivate()
{
enemyCount++;
this.health = 20;
}
void OnDeath()
{
enemyCount--;
RaiseEnemyDeath(enemyCount); // Should check for nullity...
}
}
This one is fairly simple. The first part is all static and is relevant to the class. The second part is relevant to the instance. If you use a pool of enemy and then reuse the same instance multiple times, the OnActivate method is called when you make the enemy alive in the scene (it may have been there for a while as inactive). Then when the health is down, kill the enemy (there are not all the required actions there...) and trigger the event.
Using the public static property, you can know what is the enemy count from a GameManager (Enemy should not affect the gameplay, only takes care of the enemy).
public class GameManager:MonoBehaviour
{
void Start()
{
Enemy.RaiseEnemyDeath += Enemy_RaiseEnemyDeath;
}
void Enemy_RaiseEnemyDeath(int count)
{
if(count < 0){ // End of level }
// You can also access enemyCount
int count = Enemy.EnemyCount;
}
}
The good point of using this principle is that Enemy has no clue about GameManager and can be reused in another game without any modification. The GameManager is a higher level entity and knows about it.

C# UNITY Instantiate Class (Create Object) without using new operator

example I have class name "PlayerClass" and "StatClass"
PlayerClass
public float health = 100 ;
public float experience;
StatClass
PlayerClass playerclass = new PlayerClass()
public float health2;
public float experience2;
health2 = playerclass.health;
experience2 = playerclas.experience;
usually I have to create "PlayerClass playerclass = new PlayerClass()"
the result will be the same health2 and experience2;
but whenever i made a change on healt from player class with code of course, the result healt2 from StatClass will be the same it was 100
Sory for my bad english.
----------------- Edited -----
GameControl
public class GameControl : MonoBehaviour {
public static GameControl control;
public float health = 100;
public float experience = 1000;
// Use this for initialization
void Awake () {
/*
* Awake Start Before Start() Happen
*/
if (control == null) {
DontDestroyOnLoad (gameObject);
control = this;
} else if (control != this) {
Destroy (gameObject);
}
}
}
GetHealth
public class GetHealth : MonoBehaviour {
public static GetHealth getHealth;
GameControl gameControl = new GameControl();
//GameControl gameControl;
public static float health;
Text text;
void Awake () {
/*
* Awake Start Before Start() Happen
*/
if (getHealth == null) {
DontDestroyOnLoad (gameObject);
getHealth = this;
} else if (getHealth != this) {
Destroy (gameObject);
}
text = GetComponent<Text> ();
health = 0;
}
void Update(){
health = gameControl.health;
text.text = "Health: " + health;
}
}
I made Change on value variable Health in GameControl. but Get Health always access 100 the same as first time when variable GameControl haven't changed. I think it was maybe because we use "new" on GetHealth class.
GameControl gameControl = new GameControl();
Is there another way to solve this without "new".
If instantiation of player was in updateStat method and it is calling in void update() method, it cause to instantiate player object again and again. You can try this or instantiate playerclass2 in constructor. Also making health and experience static will help if only player object exist.
Class StatClass{
PlayerClass playerclass2;
public float health2;
public float experience2;
public void updateStat(){
if(playerclass2==null) playerclass2 = new PlayerClass();
health2 = playerclass2.health;
experience2 = playerclas2.experience;
}
}
EDIT
You can't use new keyword since it is derived from MonoBehavior. Have to use ClassName.method() format to call methods of derived classes from MonoBehavior
Thanks all, I have Found it.
It just have to
health = GameControl.control.health;
I don't have to instantiate it I just Call class and then initialize of class and then variable of class.

Categories