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();
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.
kinda new to coding so help would be appreciated. I'm trying to duplicate this GameObject "cube" in unity and I'm having trouble with it. what im trying to do is duplicate the cube and get it to stack on top of each other over and over.
I know if i got this to work it would duplicte it in the same postion so you would only see it duplicate in the higharchy.
using System.Collections;
using UnityEngine;
public class cube : MonoBehaviour
{
public GameObject cube1;
public void update()
if(input.getKeyDown(KeyCode.Space))
{
instantiate cube1;
}
}
I assume you know the height of the cube, you are working with. In unity the default height is 1.0f(For the primitive cube).
Btw if your code is a pseudo code then its okey but if not, you need more training before writing such scripts, even tho this type of script is extremely easy to write.
(ps: i wrote this script in notepad++ hope it compiles :/)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// We start classes with capital letters in c# its a convention :)
public class Cube : MonoBehaviour
{
// Same applies to public class fields & Properties
// Marking a MonoBehaviour field as public will allow you to directly assign values to it
// inside the editor
public GameObject OriginalCube;
// Same can be achieved with private fields using the serializefield attribute
[SerializeField]
private float cubeHeight = 1.0f;
// In case you would like to store the duplicated cubes
public List<GameObject> Cubes = new List<GameObject>();
private void Awake()
{
// Adding the first cube to the list, i assume your cube is already in the scene
Cubes.Add(OriginalCube);
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
// We instantiate a new cube and add it to the list
Cubes.Add(Instantiate(Cubes[Cubes.Count - 1]);
// We ask the previous cube position (the one we copied)
Vector3 previousCubePosition = Cubes[Cubes.Count - 2].transform.position;
// then we assign a new position to our cube raised by "1 unit" on the y axis which is the up axis in unity
Cubes[Cubes.Count - 1].transform.position =
new Vector3(previousCubePosition.x, previousCubePosition.y + cubeHeight, previousCubePosition.z);
}
}
}
One way you can have your goal achieved, is to add to your newly instantiated object's y position a constant amount. This constant amount will be increased each time you create a new duplicate of your object.
public GameObject cube1;
private int instantiateCounter = 0;
public float PULL_UP_AMOUNT = 30f;
public void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
instantiateCounter++;
GameObject newCube = Instantiate(cube1);
newCube.transform.position = new Vector3(cube1.transform.position.x, cube1.transform.position.y + instantiateCounter * PULL_UP_AMOUNT, cube1.transform.position.z);
}
}
The constant amount we're talking about is PULL_UP_AMOUNT.
Keep in mind, you can access your new duplicate's properties by saving the result of Instantiate method inside a new GameObject, just like I did.
I have a Script in my main Assets folder called BezierWalk.cs
In another script, Spawn, I'm trying to instantiate objects from Prefab Sphere and attach BezierWalk.cs to them.
Spawn script:
public class Spawn : MonoBehaviour
{
public GameObject Sphere;
//string ScriptName = "BezierWalk";
void Start()
{
Vector3 vSpawnPos = transform.position;
for (int i = 0; i < 20; i++)
{
var objectYouCreate = Instantiate(Sphere, vSpawnPos, transform.rotation);
//objectYouCreate.AddComponent<T>("Assets/BezierWalk.cs");
//objectYouCreate.AddComponent(typeof(ScriptName)) as ScriptName;
//objectYouCreate.AddComponent<ScriptName>();
//var myScript = Sphere.AddComponent<BezierWalk.cs>();
vSpawnPos.z += 20;
}
}
You can see commented out attempts...
How I am supposed to do this properly? Thank you.
If you look at how you reference components in unity, the answer should be clear - did you try any of the ones you listed?
Reference: https://docs.unity3d.com/ScriptReference/GameObject.AddComponent.html
The normally used is as it is easy reading
objectYouCreate.AddComponent<ClassName>();
You can use
objectYouCreate.AddComponent(typeof(ClassName));
the .cs is for humans, its your readable version. so you would never need the .cs reference in your code.
note: I mentioned it as ClassName rather than scriptname, as while they are the same in monobehaviors in unity, it isnt the same anywhere else in c# so, the important bit is not the name of the file you made, but the name of the class within it.
Another way is to have prefabs, make a prefab of your object with all the components you need already on it.
If your script lies within certain namespace, you should follow the following format
GameObject.AddComponent(typeof(namespace.className));
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".
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();
}
}