How to properly use prefabs from AsssetBundles? - unity3d

I've recently encountered AssetBundles and tried to implement it in my project. I have a pretty simple game where controlling a character you should collect coins. I created the AssetBundle where I made prefabs and put everything from the game scene (background, player, terrain, etc...) into the AssetBundle. However, when loading objects from the bundle to the game scene, despite having the same size and transform parameters in the inspector, they are bigger than their original prefabs when starting a game. When it comes to the loaded character, not only is it ten times the size of the original but it also needs to be readjusted in script dependencies during the game to control it with a joystick. In terms of prefab size discrepancy, I think it has something to do with the loading screen as everything that comes out of the bundle is scaled to its size (see Fig.1) but I don't know why it happens nor how to fix it.
The script which loads prefabs:
public class LoadAssetBundles : MonoBehaviour
{
AssetBundle loadedAssetBundle;
public string path;
public string assetName;
void Start()
{
LoadAssetBundle(path);
InstantiateObjectFromBundle(assetName);
}
void LoadAssetBundle(string bundleUrl)
{
loadedAssetBundle = AssetBundle.LoadFromFile(bundleUrl);
}
void InstantiateObjectFromBundle(string assetName)
{
var prefab = loadedAssetBundle.LoadAsset(assetName);
Instantiate(prefab);
}
}

Try setting your prefab root (the most outer gameObject) scale to 1, them rescale your sprites inside it.
If you have a CanvasScaler on your canvas, the canvas scale will probably be lower than 1, something small like: 0.02321f, your object seems to have a high scale like 107.f or something, this may be the cause of the discrepancies.
When instantiating, try passing a parent transform (where you want to place your prefab) and change the bool instantiateInWorldSpace (on/off) and see how it affects your prefab.
public static Object Instantiate(Object original, Transform parent, bool instantiateInWorldSpace);
Check docs: https://docs.unity3d.com/ScriptReference/Object.Instantiate.html

Related

Unity scaling an object when duplicating (not wanted)

I'm making a mining game, where you have a rest area, and a mine area (all of this stuff works well) but when I "hire a new worker" the main miner clones, and is put in the starting area. When I clone the main miner, the cloned miner's size goes to 6k something something.
public void moreMiners(){
if (gm.Diamonds >= gm.mmcost){
gm.Diamonds -= gm.mmcost;
GameObject newplr = GameObject.Instantiate(plr);
newplr.gameObject.transform.localPosition = plr.transform.position;
}
}
duplicating code ^
help is appreciated
Make sure that "plr" reference is the original Prefab from your assets folder and not from your scene, then what you can do is to check your original prefab inside your assets folder and see if the scale is the one that you want to have.
Also when you Instantiate an object you can parse the position too as a parameter like this:
GameObject.Instantiate(plr,position);

Get the object or renderer displayed (rendered on top)

Is it possible to know I a mesh renderer is not visible due to the fact its being occluded by another gameObject? Up to what I researched, Rederer.isVisible responds to "isRendered" and scene params needs to be baked. So I wonder if there could be a simple way to know if an scene element is rendered on top of other in a simple manner. Larger explanation below.
Consider a simplified use case, with a camera (with occlusion culling activated), an occluder and an occludee such as this:
With the shadows options disabled (for both occluder and occludee. I would think disabling this is only needed for the ocludee but disabled for both just in case):
And a simple script to check visibility options:
using UnityEngine;
public class RenderCheck : MonoBehaviour
{
MeshRenderer mr;
private void Start()
{
mr = GetComponent<MeshRenderer>();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space)) {
bool isVisible = mr.isVisible;
Debug.Log($"cube visible: {isVisible}");
}
}
void OnBecameVisible() {
Debug.LogError($"{this.GetHashCode()} VISIBLE!!");
}
void OnWillRenderObject() {
Debug.LogError($"{this.GetHashCode()} VISIBLE!!");
}
}
The result I expected is that the ocludee .isVisible would return false, and that OnWillRenderObject() would be executed constantly after OnBecameVisible() is executed on the event of the occludee been moved out the occluders back, so into the sight of the camera. However I get .isVisible = true and the log thrown by the OnWillRenderObject().
I am aware of the fact that the Scene view cameras will also cause this value to be true. The explained outcome considers no scene camera and only the gamePlay camera (with occlusion culling activated).
I got this approach to work baking the scene occlusion options following the documentation steps. But my question here more if its rendered or not regarding the oclussion settings, is to know if a gameobject or a mesh is rendered "on top" of other (in the case of culling not being applied). Unity needs to handle that as it is how the render takes place in the scene.
There seem to be a bunch of useful options here for what I am after, however, for .isVisible to work I need to set and bake the occusion scene options (afaik). So .isVisible responds more to "isRendered"
fact. According to the documentation shouldn't Renderer.isVisible be giving the result I expected in a direct/simple manner and not be influenced by the fact of being rendered or not by dynamic occlusion?
Edit: As far as I checked, seems that only a raycast can address blocking by geometry. All these other things mean "visible" in the sense that the camera's rendering pipeline has to consider the object in some way.
With a raycast I can get the "physical" block, determined by a collision. Isn't there any way to determine the "graphical" block? As I said, that is figured out somewhere soas to be renedered accordingly in the display screen, so is that info available somehow?

Unity3D Text not changing after being set in Start()

I have a Canvas (World Space Render mode) with a Text and a Button component displayed in a tridimensional space (it's a VR app). The canvas instantiated at runtime using a prefab.
I get a reference to the Text object using:
_codeTextLabel = canvasPrefab.transform.Find("CodeTextLabel").gameObject.GetComponent<Text>();
I want to update the text at run-time using:
void Update()
{
_codeTextLabel.text = _codeText;
}
where _codeText is just a variable I update based on specific events.
The problem is that the Text gets updated only the first time, but if I try to change the variable nothing happens. I have tried several combinations and also the method _codeTextLabel.SetAllDirty() but it doesn't work.
The only way to update the text is to re-instantiate the prefab.
Are you instantiating your prefab before setting the values. If you are storing the _codeTextLabel reference before instantiating then your reference will point to the prefab not the runtime object. I can't see the rest of your code, so I can't say for sure. (I would have asked as a comment, but as I'm new I don't have the reputation to do so)
edit: I did a test to try and recreate your problem. I made the following script and it appears to work as expected. CanvasPrefab is a worldspace canvas with a UnityEngine.UI.Text component attached. (The script is attached on an empty game object in the scene btw)
public class ChangeText : MonoBehaviour
{
public GameObject CanvasPrefab;
private GameObject runtimeCanvas;
public string runtimeText = "something";
private Text textRef;
// Start is called before the first frame update
void Start()
{
runtimeCanvas = GameObject.Instantiate(CanvasPrefab);
textRef = runtimeCanvas.GetComponentInChildren<Text>();
}
// Update is called once per frame
void Update()
{
textRef.text = runtimeText;
}
}
as long as you did something wrong, It works absolutely so I guess there are several cases
Failed to do "_codeTextLabel = canvasPrefab.transform.Find("CodeTextLabel").gameObject.GetComponent();"
'_codeTextLabel' lost reference from 'GameObject.
Doesn't change runtimeText' change at all
Subscription of events failed I mean, your updating scripts doesn't get proper event to update that text.
Without codes, this is only thing I can guess for yours so please check above I hope there is case among above.

SceneManager.LoadScene(string) doesn't recognize scene name

I've seen similar questions all over the internet, but none seem to apply to my situation.
I have a very simple project with two scenes: MainMenuScene and OtherScene. OtherScene is nothing but a blank canvas, and MainMenuScene just has a button which is meant to be used to navigate to OtherScene. The Main Camera has the following script attached to it:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LaunchScene : MonoBehaviour {
public void LaunchSceneOnButtonClick(int index)
{
SceneManager.LoadScene(index);
}
public void LaunchSceneOnButtonClick(string sceneName)
{
SceneManager.LoadScene(sceneName);
}
}
and in the OnClick() of the button, this script is called. I have made sure to add both levels to the Build Settings.
If I call the version of the LanchSceneOnButtonClick which takes an integer argument (and specify the appropriate value in the OnClick() method) then the desired scene loads.
However, if I call the version which takes a string argument and use the exact name of the scene I want to load (and I've quadruple checked that it is indeed the exact string, that the string name has no spaces or hidden characters, and have copy pasted the scene name into the OnClick() argument field) the scene does not load and instead I get the error message
Scene "OtherScene" couldn't be loaded because it has not been added to
the build settings or the AssetBundle has not been loaded.
Since using the scene index works, we know it was added to the Build Settings properly, so I reason that there is some issue with the scene name. But I've copied it exactly. I want to avoid using the full path of the scene in case I later decide to move scenes around to different folders, so what are my options here?
Thanks in advance.
EDIT:
Adding screenshots.
Scene in folder structure:
Scenes in build settings:
OnClick() input:
Keep in mind I've also tried the input to OnClick() with and without quotation marks.
The only possible problems are these:
The string passed is different from the scene name; bear in mind, the string used in LoadScene is not case sensitive, i.e. you can use OtherScene, OTHERscene, etc.
The scene in the build settings is not active (i.e. with the checkmark on the left disabled).
The scene isn't present at all in the build settings.
The full path of the scene is irrelevant btw, you can have OtherScene inside any dir/subdir in the Assets, and you'll need only the name of the scene. You need the full path of the scene only if you have more than scene with the same name in different paths (and anyway you shouldn't, it's such an obvious terrible practice).
EDIT: You say that you tried even without " (as you should do, when entering string fields in the inspector you don't need them), what happens if you modify the method body to:
public void LaunchSceneOnButtonClick(string sceneName)
{
SceneManager.LoadScene("OtherScene");
}
Try this and report back what happens please.
I had also the problem that
public void loadScene(string scene)
{
SceneManager.LoadScene(scene);
}
did not work for me in the first place. What did I do? I'm saving the names of my scenes in a List (read from a file) and from that List I choose the scene I need.
But somehow the reading progress seems to give me some unwanted characters so the scene loading does not work as intended. When I added .Trim() it started to work.
For a better understanding, here's an example:
string s = sceneList[1].Trim();
this.loadScene(s);
public void loadScene(string scene)
{
SceneManager.LoadScene(scene);
}
So make sure that you get rid of unwanted characters.

Add particles on button click in unity

I am new to unity 3d, I have to do some enhancement in exiting project.. if user choose correct option then I have to show some particles around the button at runtime.
My code for adding particles is below ..not working:
ParticleSystem ps = GetComponent<ParticleSystem>();
ps.Play ();
I have also added particles component from unity editor..
Thanks in advance
Edit :
as #kardux suggested:
declaration :
[SerializeField] private ParticleSystem ps;
on method :
ps.Play()
Screenshot from inspector:
Error:
I/Unity (23313): NullReferenceException
I/Unity (23313): at UnityEngine.ParticleSystem.<Play>m__0 (UnityEngine.ParticleSystem ps) [0x00001] in /Users/builduser/buildslave/unity/build/artifacts/generated/common/modules/ParticleSystem/ParticleSystemBindings.gen.cs:3666
I/Unity (23313): at UnityEngine.ParticleSystem.IterateParticleSystems (Boolean recurse, UnityEngine.IteratorDelegate func) [0x00003] in /Users/builduser/buildslave/unity/build/artifacts/generated/common/modules/ParticleSystem/ParticleSystemBindings.gen.cs:3780
I/Unity (23313): at UnityEngine.ParticleSystem.Play (Boolean withChildren) [0x00020] in /Users/builduser/buildslave/unity/build/artifacts/generated/common/modules/ParticleSystem/ParticleSystemBindings.gen.cs:3666
I/Unity (23313): at UnityEngine.ParticleSystem.Play () [0x00005] in /Users/builduser/buildslave/unity/build/artifacts/generated/common/modules/ParticleSystem/ParticleSystemBindings.gen.cs:3661
First of all if you're using particles inside Unity UI I highly advise you looking to UIParticleSystem.cs script from Unity UI Extension repository: this is a community gathering of many useful UI tools :)
(simply don't forget to add the UI/Particles/Hidden shader that you can find here)
You can change the sprite you want to use here:
Also keep in mind when using this script that you will have to scale your particles according to your screen (particles are initialized at a size of 1 because that's 1 meter in Unity 3D world: but now you will probably be in canvas space which will be something like 1920x1080px so 1px will be very small). You can find some base settings below:
Now coming to your scrip I suspect you simply have to call Stop() before Play() like this (note I used a burst emission type in my particle system settings):
ParticleSystem ps = GetComponent<ParticleSystem>();
ps.Stop ();
ps.Play ();
P.-S. Please note that if you use UIParticleSystem script you will have to consider your particles system as an UI item (will be rendered on top of other items based on hierarchy order)
Hope this helps,
EDIT:
You have two ways of setting up your GameObjects:
you have all component on the same GameObject (ParticleSystem, UIParticleSystem and YOUR_SCRIPT): this way you can get the ParticleSystem reference by calling GetComponent<ParticleSystem>() inside your script
you have one particle GameObject (with ParticleSystem and UIParticleSystem) and YOUR_SCRIPT is on another GameObject: you can't call GetComponent<ParticleSystem>() in your script since it will search on the components of this GameObject so you declare a ParticleSystem ps; variable (either public or [SerializeField] private) that you assign through the Inspector by dragging your particle GameObject to it.
Note that implicitly, GetComponent<ParticleSystem>() equals this.gameObject.GetComponent<ParticleSystem>(): that's why it will search components from the current GameObject.
EDIT 2:
Not sure why your script throw this NullReference exception: I just tried with a very short script and it works perfectly...
public class TestScript: MonoBehaviour
{
[SerializeField]
private ParticleSystem ps;
void Start()
{
// This one is not even needed
ps.Stop();
}
public void PlayParticles()
{
ps.Stop();
ps.Play();
}
}
Provided you have a particle system on the same gameObject as the script that is calling it, it should be fine.
Are you using a UI button? If so, have a look here..
http://answers.unity3d.com/questions/852397/particle-system-in-46-ui.html
It's old but still relevant.
Are you using the New Unity UI System or GUI, Is the UI worldspace?
Create a empty gameobject - attach the particle to that.
Whenever you want emit particles call Gameobject.SetActive(true);
Make sure Play on Awake option is checked in the particle system.
Set the position of the particle according to your UI.
public GameObject particle;
//include the particle in the gameobject
void Start() {
particle.SetActive(false);
}
void button() {
particle.SetActive(true);
}
//this works.