How to Change AudioMixer source at runtime in Unity - c#

I'm trying to make a music application.
I would like to be able to change within the AudioMixer between different sound groups using the same GameObject.
Is it possible to change the PianoMixer source with another script at runtime?
public class PianoAudioManager : MonoBehaviour{
public PianoSound[] PianoSounds;
public static PianoAudioManager instance;
public AudioMixerGroup PianoMixer;
void Awake()
{
if (instance == null)
instance = this;
else
{
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
foreach(PianoSound s in PianoSounds)
{
s.source = gameObject.AddComponent<AudioSource>();
s.source.outputAudioMixerGroup = PianoMixer;
s.source.clip = s.clip;
s.source.volume = s.volume;
s.source.pitch = s.pitch;
}
}
}
I tried something like this, but I always get errors
public class Octaves : MonoBehaviour
{
public GameObject PianoAudioManager;
public void OctaveDown()
{
AudioMixer audioMixer = Resources.Load<AudioMixer>("Scales/Sounds");
AudioMixerGroup[] audioMixGroup = audioMixer.FindMatchingGroups("Master/PianoDown");
PianoAudioManager.outputAudioMixerGroup = audioMixGroup[0];
}
}
I would be very grateful for any help, thank you very much.

I finally get the answer.
Right click on the parameter name "Pitch" and choose "Expose 'Pitch' to script".
using UnityEngine.Audio;
public class Octavas : MonoBehaviour
{
public GameObject PianoAudioManager;
public void OctaveDown()
{
AudioMixer audioMixer = Resources.Load<AudioMixer>("Scales/Sounds");
audioMixer.SetFloat("PianoPitch", 0.5f);
}
}

Related

cannot convert from 'System.Collections.Generic.Queue<System.Collections.Generic.KeyValuePair<PotionCraft.PotionRecipe.Ingredient, uint>>'

I have this error when i try to complile this C# code on my Unity:
Assets\Scripts\Cauldron.cs(50,57): error CS1503: Argument 1: cannot convert from 'System.Collections.Generic.Queue<System.Collections.Generic.KeyValuePair<PotionCraft.PotionRecipe.Ingredient, uint>>' to 'System.Collections.Generic.Queue<System.Collections.Generic.KeyValuePair<Ingredient, uint>>'
This is the code
using System.Collections.Generic;
using UnityEngine;
namespace PotionCraft.Components
{
public interface IBrewingCauldron
{
public void AddIngredient(PotionRecipe.Ingredient ingredient);
public GameObject BrewPotion();
}
[RequireComponent(typeof(SphereCollider))]
[RequireComponent(typeof(Rigidbody))]
public class Cauldron : MonoBehaviour, IBrewingCauldron
{
public Dictionary<PotionRecipe.Ingredient, uint> Ingredients { get; private set; } = new();
[SerializeField] private SphereCollider cauldronCollider;
private readonly PotionBrewer _potionBrewer = new();
private uint _numberOfIngredients;
private void Awake()
{
cauldronCollider ??= GetComponent<SphereCollider>();
// Set the collider as trigger to interact with ingredients GameObject
cauldronCollider.isTrigger = true;
}
public void AddIngredient(PotionRecipe.Ingredient ingredient)
{
// Keep track of the number of ingredients added
_numberOfIngredients++;
if (!Ingredients.ContainsKey(ingredient))
{
Ingredients[ingredient] = 1;
}
else
{
Ingredients[ingredient]++;
}
}
public GameObject BrewPotion()
{
var ingredientQueue = new Queue<KeyValuePair<PotionRecipe.Ingredient, uint>>(Ingredients);
var potionObject = _potionBrewer.MakePotion(ingredientQueue, _numberOfIngredients);
if (potionObject is not null)
{
Debug.Log($"We made a {potionObject.name} !");
potionObject.transform.position = transform.position;
}
else
{
Debug.Log("We failed to make any potion !!!");
}
Ingredients = new Dictionary<PotionRecipe.Ingredient, uint>();
_numberOfIngredients = 0;
return potionObject;
}
}
}
Edit: This is another code might be related to the problem
using UnityEngine;
namespace PotionCraft.Components
{
public class Ingredients : MonoBehaviour
{
[SerializeField] private GameObject cauldronGameObject;
[SerializeField] private PotionRecipe.Ingredient ingredient;
private SphereCollider _cauldronCollider;
private IBrewingCauldron _cauldron;
private void Awake()
{
if (cauldronGameObject is not null)
{
_cauldron = cauldronGameObject.GetComponent<IBrewingCauldron>();
if (_cauldron is not null) return;
}
var ingredientObject = gameObject;
ingredientObject.name += " [IN ERROR]";
ingredientObject.SetActive(false);
throw new MissingComponentException($"{ingredientObject.name} is missing the cauldron gameobject");
}
private void Start()
{
_cauldronCollider = cauldronGameObject.GetComponent<SphereCollider>();
gameObject.name = ingredient.ToString();
}
private void OnTriggerEnter(Collider other)
{
if (other != _cauldronCollider) return;
_cauldron.AddIngredient(ingredient);
Destroy(gameObject);
}
}
}
So what am I doing wrong here?
The PotionBrewer.MakePotion method accepts an argument of type Queue<KeyValuePair<Ingredient, uint>>.
You are trying to pass it an argument of type Queue<KeyValuePair<PotionCraft.PotionRecipe.Ingredient, uint>> instead.
If PotionBrewer.MakePotion is supposed to accept an argument of the latter type, you might need to add this using alias to your PotionBrewer class:
using Ingredient = PotionCraft.PotionRecipe.Ingredient;

How do I pass a variable between scripts in Unity 2d C#

For example, I have a variable "Wisps" that I want to change when the player picks up an object. But I don't know how to do it. I tried to add a WispDisplay object to call the classes, like in Java, but it doesn't seem to work.
public class WispCode : MonoBehaviour
{
WispDisplay wd = new WispDisplay();
private void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Player")
{
wd.setWisp(wd.getWisp()+1);
Destroy(gameObject);
}
}
}
public class WispDisplay : MonoBehaviour
{
public int Wisp = 5;
public Text WispText;
void Start()
{
}
void Update()
{
WispText.text = "Wisp: " + Wisp.ToString();
}
public int getWisp()
{
return Wisp;
}
public void setWisp(int newWisp)
{
Wisp = newWisp;
}
}
Easiest (a tiny bit dirty) way is to use a static variable. Downside: you can only have exactly ONE.
Example:
public class MyClass: MonoBehaviour {
public static int wisps;
}
Then, in ANY class, just use this to access it:
MyClass.wisps = 1234;
The more elegant way, working with multiple class instances, is using references.
Example:
public class PlayerClass: MonoBehaviour {
public int wisps = 0;
}
public class MyClass: MonoBehaviour {
public PlayerClass player;
void Update(){
player.wisps += 1;
}
}
Then, you need to drag-drop (aka "assign") the "PlayerClass" Component (attached to the player) to the the Gameobject that should increase the Wisps count. You can duplicate these objects after assigning the reference.
Now, if you actually want to have some sort of collectible, I'd suggest this approach:
You Have a Player "PlayerClass" and some Objects that are collectible, which have Trigger Colliders.
The objects have this code:
public class Example : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
// probably a good idea to check for player tag:
// other.compareTag("Player");
// but you need to create the "Player" Tag and assign it to Player Collider Object.
if(TryGetComponent(out PlayerClass player))
{
player.wisps += 1;
}
}
}

Is there a better way than this to create a character selection and spawner?

This is the code I have for my spawner object on the level scene:
public float spawnShipID;
public GameObject basicShip;
void Start()
{
if (spawnShipID == 1)
{
Instantiate(basicShip, transform.position, Quaternion.identity);
}
}
And this is the code I have on a button on the start menu:
public float ShipID = 1f;
public void shipchoice()
{
SceneManager.LoadScene("watcherqueen");
GameObject.Find("playerspawner").GetComponent<spawnplayer>().spawnShipID = ShipID;
}
The reason I am using different floats on the button and the spawner is because I would like to have different buttons spawn different ships.
Using this code, the scene changes (only with the Play button, but not in the build, though that is a question for another time), and the player ship does not spawn. Are there any solutions or better ways I could have done this?
When you call LoadScene, the scene does not fully load until the next frame. Therefore, your GameObject.Find("playerspawner") doesn't even find the instance of playerspawner that is in that scene. (And, even if it were the same instance, it wouldn't run through its Start again.)
Instead, you should set the variable to something that already exists in the scene currently running, which also will not destroy on load. A Singleton is especially useful for this application. Starting from this example by PearsonArtPhoto:
public class ShipSingleton: MonoBehaviour {
private static ShipSingleton _instance;
public static ShipSingleton Instance { get { return _instance; } }
private void Awake()
{
if (_instance != null && _instance != this)
{
Destroy(this.gameObject);
} else {
_instance = this;
DontDestroyOnLoad(this.gameObject);
}
}
public int spawnShipID;
}
Then in the start menu button:
public int ShipID = 1f;
public void shipchoice()
{
SceneManager.LoadScene("watcherqueen");
ShipSingleton.Instance.spawnShipID = ShipID;
}
and in the spawner:
public GameObject basicShip;
void Start()
{
if (ShipSingleton.Instance.spawnShipID == 1)
{
Instantiate(basicShip, transform.position, Quaternion.identity);
}
}
I used an int for spawnShipID for simplicity, but an enum would be better practice, including how it could help avoid entering invalid ids.
Using an enum could look something like this:
public class ShipSingleton: MonoBehaviour {
private static ShipSingleton _instance;
public static ShipSingleton Instance { get { return _instance; } }
private void Awake()
{
if (_instance != null && _instance != this)
{
Destroy(this.gameObject);
} else {
_instance = this;
DontDestroyOnLoad(this.gameObject);
}
}
public enum ShipType
{
small,
medium,
large,
boss
};
public ShipType spawnShipID;
}
Then in the start menu button:
public ShipSingleton.ShipType ShipID = ShipSingleton.ShipType.small;
public void shipchoice()
{
SceneManager.LoadScene("watcherqueen");
ShipSingleton.Instance.spawnShipID = ShipID;
}
and in the spawner:
public GameObject basicShip;
void Start()
{
if (ShipSingleton.Instance.spawnShipID == ShipSingleton.ShipType.small)
{
Instantiate(basicShip, transform.position, Quaternion.identity);
}
}

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;
}

Item/Ability system using scriptableobjects in Unity

I'm new to Unity and I'm having some trouble creating scriptable objects for items to pick up and use in my project. I've created the initial set-up to make the items with variables that I can plug-and-play with.
What I'm trying to do is:
1 - plug in a monobehavior script for each new item's behavior when used.
2 - Allow the player to pick up the item on collision and use the item on key command. The player should only carry one item at a time.
Here is what I have so far.
the scriptable object (itemObject.cs)
[CreateAssetMenu(fileName = "New Item", menuName = "Item")]
public abstract class Item : ScriptableObject {
public new string name;
public string description;
public int damageAmount;
public int affectDelay;
public int affectDuration;
public int affectRange;
public float dropChance;
public float itemLife;
}
I have a script that will trigger if the player collides with the object (itemDisplay.cs)
Currently, this doesn't do anything but destroys the item.
public class itemDisplay : MonoBehaviour {
public Item item;
public static bool isActive = false;
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player")
{
var playerPickUp = other.gameObject;
var playerScript = playerPickUp.GetComponent<Player>();
var playerItem = playerScript.playerItem;
playerScript.pickUpItem();
bool isActive = true;
Object.Destroy(gameObject);
}
}
void Start () {
}
void Update(){
}
}
Currently, I have a pickup function and a use item function that's being handled by my player script(Player.cs). Currently, this just toggles a boolean saying that it did something when the item is collided with. My big question is how/where should I create and reference the item ability and how do I pass it to the player script here?
public void pickUpItem()
{
playerItem = true;
//itemInHand = itemInHand;
Debug.Log("You Picked Up An Item!");
// Debug.Log(playerItem);
if(playerItem == true){
Debug.Log("This is an item in your hands!");
}
}
public void useItem()
{
//use item and set
//playerItem to false
playerItem = false;
}
If your question is "how do I create an instance of an Item in my project", you do that by right clicking in your Project view, then selecting Create -> Item. Then drag the Item asset that you created from your Project view into the ItemDisplay scene object's item field in the Inspector.
Regarding how to pick up the item, you would pass the item to your Player script from your ItemDisplay script. You've got most of that already wired up.
public class ItemDisplay : MonoBehaviour
{
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player")
{
var playerPickUp = other.gameObject;
var playerScript = playerPickUp.GetComponent<Player>();
var playerItem = playerScript.playerItem;
// This is where you would pass the item to the Player script
playerScript.pickUpItem(this.item);
bool isActive = true;
Object.Destroy(gameObject);
}
}
}
Then in your Player script...
public class Player : MonoBehaviour
{
public void PickUpItem(Item item)
{
this.playerItem = true;
this.itemInHand = item;
Debug.Log(string.Format("You picked up a {0}.", item.name));
}
public void UseItem()
{
if (this.itemInHand != null)
{
Debug.Log(string.Format("You used a {0}.", this.itemInHand.name));
}
}
}
You should also make your Item class non-abstract, or leave it abstract and create the appropriate non-abstract derived classes (Weapon, Potion, etc.). I put an example of that below.
You can't attach a MonoBehaviour to a ScriptableObject if that's what you're trying to do. A ScriptableObject is basically just a data/method container that isn't tied to a scene. A MonoBehaviour has to live on an object within a scene. What you could do however, is add a "use" method to your Item class, and call that from a MonoBehaviour within the scene when the player uses an item.
public abstract class Item : ScriptableObject
{
// Properties that are common for all types of items
public int weight;
public Sprite sprite;
public virtual void UseItem(Player player)
{
throw new NotImplementedException();
}
}
[CreateAssetMenu(menuName = "Items/Create Potion")]
public class Potion : Item
{
public int healingPower;
public int manaPower;
public override void UseItem(Player player)
{
Debug.Log(string.Format("You drank a {0}.", this.name));
player.health += this.healingPower;
player.mana += this.manaPower;
}
}
[CreateAssetMenu(menuName = "Items/Create Summon Orb")]
public class SummonOrb : Item
{
public GameObject summonedCreaturePrefab;
public override void UseItem(Player player)
{
Debug.Log(string.Format("You summoned a {0}", this.summonedCreaturePrefab.name));
Instantiate(this.summonedCreaturePrefab);
}
}
Then change your UseItem method in Player:
public class Player : MonoBehaviour
{
public void UseItem()
{
if (this.itemInHand != null)
{
this.itemInHand.UseItem(this);
}
}
}

Categories