I have a particle effect that I would like to trigger to play, and then stop. I'm sure this is an easy fix that I am over looking.
The particle can instantiate and play, but this obviously leaves overhead and particles that are active in the hierarchy when they don't need to be.
public void EmitFX(ParticleSystem particle)
{
Instantiate(particle, particlePos, Qauternion.identity)
}
I would like to use methods within ParticleSystem but am running into some problems. I have been using the manual and am still running into a block. I've googled this up and down, based on problems others had I changed my code to the following. It still does not work and is now a monster based on hacks other people found useful :/
public void EmitFX(ParticleSystem particle)
{
particle = particle.GetComponent<ParticleSystem>();
particle.transform.position = ballPos;
var em = particle.emission;
em.enabled = true;
particle.Play();
}
Here is a s/c of a particle in the Inspector.
First of all, not sure what the particle = particle.GetComponent<ParticleSystem>(); line is supposed to do? The particle variable is the ParticleSystem provide to your EmitFX() method so no need to call for this.
My guess is you have some references problems in your script (some variables referring to your prefab, then what you instantiate overrides this reference, ...) so I wrote you a "cleaner" version of your code (by merging your two scripts):
#region Attributes
[SerializeField]
private ParticleSystem particle;
private ParticleSystem generatedParticle;
#endregion
#region MonoBehaviour
protected void Start()
{
generatedParticle = null;
}
protected void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
EmitFX(particle);
}
}
#endregion
public void EmitFX(ParticleSystem a_Particle)
{
if(generatedParticle == null)
{
generatedParticle = Instantiate(particle, particlePos, Qauternion.identity);
}
generatedParticle.transform.position = ballPos;
generatedParticle.Play();
// You can set a fixed duration here if your particle system is looping
// (I assumed it was not so I used the duration of the particle system to detect the end of it)
StartCoroutine(StopFXAfterDelay(generatedParticle.main.duration));
}
private IEnumerator StopFXAfterDelay(float a_Delay)
{
yield return new WaitForSeconds(a_Delay);
generatedParticle.Stop();
}
What it does is it store the instantiated particle in a variable so it can access it later and remember it has been generated. Also I added a coroutine to deactivate it at the end of the effect.
Hope this helps,
I know this is an old question but I am going to place here what worked for me since the problem persists in the later versions of Unity.
Instead of just calling: particle.Play() first stop the particle system like the code below.
particles.Stop();
if (particles.isStopped)
{
particles.Play();
}
Where 'particles' is a component of type "ParticleSystem".
Related
I have a game wehre you are a cube and you dodge obstacles, I just implemented the ability to change the color of the cube like changing skin. I did that by assigning a different material to the player when he presses "2".
Here is the script:
void Update()
{
if (Input.GetKeyDown("2"))
{
Object.GetComponent<MeshRenderer>().material = Material1;
}
}
When you die the scene resets and when you win a new scene is loaded, I would like the game to remember the material change even after the scene is reset or a new scene is loaded. I have done some research and found something called "PlayerPrefs" and I have been playing around with it but nothing even got close to working and I didn't really understand what i was doing.
I really want to understand how this works becuase I know i will be using it alot when making games. Can someone help me understand?
Thanks.
Create one gameobject and apply this script
public class SavingMaterial : MonoBehaviour
{
public static SavingMaterial instance;
public Material mat;
void Awake()
{
if(instance == null)
{
instance = this;
DontDestroyOnLoad(base.gameObject);
}
else
{
Destroy(base.gameObject);
}
}
public void StoreMaterial(MeshRenderer mesh)
{
mat = mesh.material;
}
}
When you want to store material call this function like this
SavingMaterial.instance.StoreMaterial(you meshrenderer component);
and when you need this material just get from this class like this
material = SavingMaterial.instance.mat;
and note that if you quit the game you loose saved material as it is store in variable otherwise scene change and reset won't affect it.
you only save int,string and bool in playerprefs. you have to store values of materials into string and then save this string into playerprefs.
alternatively you can assign this material to local variable and set that script on Dontdestroyonload so it will not reset as scene is destroy or reset.
I created a dot following my mouse around in 2D and I created a cube object changing position on x and y. Now when I point my mouse to cube, it deactivates I set that, and now I want to activate it again. I try on trigger exit, but it didn't work.
public GameObject tapObject;
private float respawnTime = 1f;
public float xMin;
public float xMax;
public float yMin;
public float yMax;
void Start()
{
StartCoroutine(spawnEnemyTime());
}
private void RandomSpawnObject()
{
tapObject.transform.position = new Vector2(Random.Range(xMin, xMax), Random.Range(yMin, yMax));
}
private void OnTriggerEnter2D(Collider2D collision)
{
tapObject.SetActive(false);
}
IEnumerator spawnEnemyTime()
{
while (true)
{
yield return new WaitForSeconds(respawnTime);
RandomSpawnObject();
}
}
Once inactive the scripts on that object are not executed anymore => messages like OnTriggerExit are not called/executed.
One solution is to simply wrap the target object in a parent object and attach your script to the parent instead but make it (de)activate the child.
So the parent stays active and receives the message.
I am just going to repeat what everyone else here said:
A inactive object in Unity is truly inactive, meaning it does not receive any updates, can't collide with stuff and all the MonoBehaviour stuff that usually calls your code does not work either. You have to manually re-activate the object using a reference that you cached somewhere.
But, instead of just flat out disabling the whole object you could disable the components that you don't want to be active.
Example:
private void OnTriggerEnter2D(Collider2D collision)
{
tapObject.GetComponent<Renderer>().enabled = false;
}
private void OnTriggerExit2D(Collider2D collision)
{
tapObject.GetComponent<Renderer>().enabled = true;
}
This only deactivates your renderer component but leaves everything else as it is. So your object can still collide and it's still registered via e.g. OnTriggerExit.
Keep in mind that GetComponent<T>() is a pretty expensive operation so caching your component references is a good idea. The best solution would be to start out with a reference by creating a variable for it and assign it in the inspector.
Example:
//Set in inspector
public Renderer renderer
private void OnTriggerEnter2D(Collider2D collision)
{
renderer.enabled = false;
}
private void OnTriggerExit2D(Collider2D collision)
{
renderer.enabled = true;
}
When a GameObject is not active in Unity , you can't click it(no rendering,no colliding , nothing )
But ,You can create a hotkey (new script or in other script) , that can set it back to active , if it is not active.
public GameObject GO;
Use GO.setactive(true);
whereas gameobject is the object use to define the specific thing or object which needs to be active and the whole code needs to written in the method "spawnEnemyTime" so that it could be get active after the specific time period
You can just use an empty GameObject and get a reference the object that you want to enable/disable. If you get the reference before you disable it you will be able to activate it again.
the alternative is to do what TehMightyPotato said. Disable components it's actually the best way to solve this problem, but if you have lot's of components/subcomponents disable the gameobjects is faster.
I'm using this method and tried some others as well but unable to play the particle system(i.e for bullet shells ).
public ParticleSystem particleSystem;
void Start() {
particleSystem = GetComponent<ParticleSystem>();
}
void Update() {
particleSystem.Play();
}
Do not put your play triggers in the Update() of a MonoBehaviour because it will call play on every frame.
Try something like this instead:
public ParticleSystem particleSystem;
private bool isPlaying = false;
void Start() {
particleSystem = GetComponent<ParticleSystem>();
particleSystem.Clear(); // Reset the particles
}
void Update() {
if(!isPlaying) {
particleSystem.Play();
isPlaying = true;
}
}
Using particleSystem.Clear() before calling particleSystem.Play() can also help with particle issues.
The easiest way to stop and play a particle system is to deactivate and activate the particle system game object. To do this, the controlling script must be placed in a parent or separate object.
You have to make sure it's not already playing, or you will reset it
void Update() {
if(!particleSystem.isPlaying) particleSystem.Play();
}
okay so after trouble of hours i found that the best way to manipulate the particle system i.e activate and detivate on your will is to create an empty gameobject assign the particle system you made to it and then on and off this gameobject exactly at which part in the code you want to by setActivce command. I hope it will help someone in someway
I am trying to create particles emission for my objects which are actually pawns in a chess game. I changed particles material using Inspector for particle system and it works fine, however, when I try to change it with a script, nothing happens (particles stay pink for the black pawn).
I am not sure what is wrong with the script, whether it's a change of material problem or maybe I need to set the path for material. I would be glad if someone suggested a solution!
Here is the script:
public class BlackPawnParticleSystem : MonoBehaviour
{
private Material m_Material;
private ParticleSystemRenderer psr;
private void Start()
{
var ps = GetComponent<ParticleSystem>();
m_Material = GetComponent<Renderer>).material;
psr = GetComponent<ParticleSystemRenderer>);
psr.material = Resources.GetBuiltinResource<Material>("BlackBishop.mat");
}
This is how it looks like:
EDIT:
Changes in code:
public class BlackPawnParticleSystem : MonoBehaviour
{
private Material m_Material;
private ParticleSystemRenderer psr;
private void Start()
{
var ps = GetComponent<ParticleSystem>();
m_Material = GetComponent<Renderer>().material;
psr = GetComponent<ParticleSystemRenderer>();
psr.material = Resources.Load<Material>("BlackBishop");
}
Location of my materials:
Inspector for the material:
Script attached to the object:
The problem is this line: Resources.GetBuiltinResource<Material>("BlackBishop.mat")
It is returning null. The Resources.GetBuiltinResource function is used to load resources that are built-in into Unity not ones created by you. For example, "Sprites-Default.mat". There is no build-in material named "BlackBishop.mat" so it returns null.
To load material you created, use rhe Resources.Load function.
First, create a folder called "Resources" then put your "BlackBishop.mat" material there. You can now load it as below:
Resources.Load<Material>("BlackBishop");
Notice how I did not include ".mat" in the name. You don't include the extension name when using the Resources.Load function. If you do, it will not find it.
EDIT:
But still nothing happens on my game scene
Please carefully:
1.Like I said in my original answer, you must create a folder named "Resources" and must put the "Black Bishop" material inside that folder. You have not done this and it should not work. The spellings of thisfolder must be correct so copy it directly from this answer. Again, it should be named "Resources".
2.Your material is named "Black Bishop" not "BlackBishop" . See the space. Please fix this in the code. That should be Resources.Load<Material>("Black Bishop"); not Resources.Load<Material>("BlackBishop");.
3.Your material is currently using the "Standard" shader. Change that to Particle shader. You can do that by selecting the Material and changing the shader from "Standard" to "Particles/Alpha Blended Premultiply". That's it. Your problem should now be solved.
Extra:
I noticed you are using folders to organize your resources. Your "Black Bishop.mat" is inside Arcane Cyber --> Chess --> Materials --> Pieces --> Plastic. I suggest you move the "Materials" folder to the "Resources" folder created in #1 above.
Now, your "Black Bishop.mat" should be inside Assets --> Resources --> Materials --> Pieces --> Plastic.
You can now load it as:
Resources.Load<Material>("Materials/Pieces/Plastic/Black Bishop");
If you still have problems, please update your code and new Inspector images again. Remember that spellings count.
Why not just make a SerializedField with the Materials you need? If you don't want materials and trails to be random, set them to the exact spot in the array you need for the situation.
I did it like this:
using UnityEngine;
public class CorrectParticles : MonoBehaviour
{
private static ParticleSystem correctPart;
private static ParticleSystemRenderer correctRend;
public static CorrectParticles Instace;
[SerializeField] Material[] mainMaterials;
[SerializeField] Material[] trailMaterials;
private void Awake()
{
Instace = this;
}
void Start()
{
correctPart = GetComponent<ParticleSystem>();
correctRend = GetComponent<ParticleSystemRenderer>();
correctPart.Stop();
}
public void StartParticles()
{
int mainMaterial = Random.Range(0, mainMaterials.Length);
correctRend.material = mainMaterials[mainMaterial];
int trailMaterial = Random.Range(0, trailMaterials.Length);
correctRend.trailMaterial = trailMaterials[trailMaterial];
correctPart.Play();
}
public void StopParticles()
{
correctPart.Stop(withChildren: true, stopBehavior: ParticleSystemStopBehavior.StopEmittingAndClear);
}
}
Then I can start and stop the particle system as needed on other scripts.
CorrectParticles.Instace.StartParticles();
or
CorrectParticles.Instace.StopParticles();
I'm working with Unity 5.3 with C# as code editor
These are what I have :
I have 2 scenes in my project : home and options. I have bg object on both scenes. Both bg objects have Audio Source component, which contains same background music that play on awake. I don't use any codes for these background musics, I only click the Add Component button from Unity and add Audio Source.
This is what I want :
Options scene can toggle the background music on/off for all scenes. Therefore, there are btnOn and btnOff in Options scene.
This is my code in Audio Manager.cs :
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class AudioManager : MonoBehaviour {
public Button btnOn;
public Button btnOff;
// Use this for initialization
void Start () {
btnOn = GetComponent<Button>();
btnOff = GetComponent<Button>();
btnOn.onClick.AddListener(() => PlayAudio());
btnOff.onClick.AddListener(() => StopAudio());
}
void PlayAudio()
{
AudioSource.volume = 0.5f;
}
void StopAudio()
{
AudioSource.volume = 0f;
}
}
This is the problem :
I have this error : An object reference is required to access non-static member UnityEngine.AudioSource.volume. Maybe, this is because I don't write public AudioSource audioSource in my code. But, if I write this, I have to add another audio in Get Component box, and I will have double Audio Source in one scene. What should I do? Thanks
As you have a bg object in both scenes and as your AudioManager isn't marked as [DontDestroyOnLoad] (then I assume you also have one AudioManager in both scenes), it's Start() function will fire each time you load a scene.
So you can declare a private AudioSource and get a reference of it by finding your bg object in your scene and calling GetComponent on it :
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class AudioManager : MonoBehaviour
{
public Button btnOn;
public Button btnOff;
private AudioSource audioSource;
// Use this for initialization
void Start()
{
btnOn = GetComponent<Button>();
btnOff = GetComponent<Button>();
btnOn.onClick.AddListener(() => PlayAudio());
btnOff.onClick.AddListener(() => StopAudio());
audioSource = GameObject.Find("bg").GetComponent<AudioSource>();
}
void PlayAudio()
{
audioSource.volume = 0.5f;
}
void StopAudio()
{
audioSource.volume = 0f;
}
}
The error is raised because you need to assign the property on an individual object; the volume is not shared between sources. For this reason you will either need a field to assign in the inspector, or get a reference with GetComponent.
While using different scenes for handling options is not wrong, it is a tad clumsy; the current scene has to be unloaded (destroying all object not marked as DontDestroyOnLoad and information associated with them), after which the options are loaded, to then load the previous scene. While unloading the music most likely stops playing, which, after loading, starts at the beginning again. Not mention any settings on these objects are lost (volume, change of track, etc.).
The afore mentioned DontDestroyOnLoad can help since you make your changes on the same object, but you will have to deal with duplicates each time a scene is loaded where such an object exists... you can use the function OnLevelWasLoaded (documentation is a tad lacking at the moment) as a moment to determine which objects to destroy.
Another point is that you currently have public Button fields. This allows the for the assignment of them via the inspector, but that is rather moot as you overwrite them in Start (new value being the first button component assigned to the same object). I would make these fields private, or at least make sure they are not assigned. But I'm getting slightly of topic on how to keep settings persistent between scenes.
Here is some code to give you an idea, but be warned it is untested as I currently have no access to an environment to test it. This solution uses an object which is persistent between scenes. Keep in mind that any connections established to this object via the editor are lost after another scene has loaded.
public class AudioManager : MonoBehaviour
{
private AudioSource source;
// Called regardless of whether the object is enabled or not.
// Should not be called in a new scene.
private void Awake()
{
// Protect this object from being destroyed so its volume
// is maintained between scenes.
DontDestroyOnLoad(this.gameObject);
source = GetComponent<AudioSource>();
}
public void DestroyPossibleDuplicates()
{
AudioManager[] managers = FindObjectsOfType(typeof(AudioManager)) as AudioManager[];
foreach (AudioManager manager in managers)
{
// Use something to determine whether a manager is a duplicate.
// It must not be this, but have something in common; a name perhaps?
if ((manager != this) && (manager.gameObject.name == this.gameObject.name))
{
// Destroy the duplicates so their sound won't interfere.
Destroy(manager.gameObject);
}
}
}
private void BindToInterface()
{
Slider[] sliders = FindObjectsOfType(typeof(Slider)) as Slider[];
foreach (Slider slider in sliders)
{
// Determine whether the specified slider should have effect on this object.
// If the slider's name contains this object's name assume it should.
if (slider.gameObject.name.indexOf(this.gameObject.name)!=-1)
{
slider.onValueChanged.addListener((float value)=>
{
// In this case a slider is used for more control over the volume.
// Different elements require other logic to function.
source.volume = value;
});
}
}
}
// If my memory serves correct this method is only called on objects
// that were in the scene before it started loading.
// Just to be safe, don't have it do anything depending on this difference.
private void OnLevelWasLoaded()
{
DestroyPossibleDuplicates();
BindToInterface();
}
}