I was watching this tutorial and did complete it, now I want to extend it.
I want to open multiple images using FileExplorer in unity and then able to show the images based on the slider's value as seen in this image:
Any help appreciated.
This doesn't answer the question but what you asked in the comments
Can you give me a little code for "just create a list and have the opened images in it, when the value of the slider changes, just set the Raw Image's sprite to that of the image in the list by using an int to get the image from the list."?
[RequireComponent(typeof(RawImage))]
public class ImageSwitcher : MonoBehaviour
{
private RawImage image;
public Slider SliderComponent;
// Get the textures somehow
public List<Texture>() textures = new List<Texture>();
private void Awake()
{
image = GetComponent<RawImage>();
if(!SliderComponent)
{
Debug.LogError("No SliderComponent referenced!", this);
return;
}
// Make the slider accept only whole numbers
SliderComponent.wholeNumbers = true;
SliderComponent.value = 0;
SliderComponent.minValue = 0;
// Index is 0 based so can maximal be list count -1
SliderComponent.maxValue = textures.Count - 1;
// Register a listener for onValueChanged
// Remove the listener first to avoid multiple listeners added
SliderComponent.onValueChanged.RemoveListener(OnSliderChanged);
SliderComponent.onValueChanged.AddListener(OnSliderChanged);
}
private void OnDestroy ()
{
// Always clean up listeners when not needed anymore
SliderComponent.onValueChanged.RemoveListener(OnSliderChanged);
}
// Use this to change the Texture list
// and Max value of the slider afterwards
public void UpdateSlider(List<Texture> textures)
{
// Update the texture list
this.textures = textures;
// Update the max value of the slider
SliderComponent.maxValue = textures.Count - 1;
// Unity might automatically clamp the slider value
// after the maxValue was changed
// But just to be sure we can do it as well
SliderComponent.value = Mathf.Clamp(SliderComponent.value, 0, textures.Count - 1);
}
// Called when the slider value is changed
private void OnSliderChanged()
{
// Get the value as int
int index = Mathf.RoundToInt(SliderComponent.value);
if(index < 0 || index > textures.Count - 1)
{
// Should actually be impossible but just in case log it
Debug.Log("Slider produced impossible index: " + index, this);
return;
}
// Get according texture from list
var texture = textures[index];
// Set texture
image.texture = texture;
}
}
However this doesn't completely solve your question
Unity3d(Editor): Opening Multiple files using EditorUtility.OpenFilePanel()
as mentioned in this thread it is not possible since EditorUtility.OpenFilePanel returns only one single filepath as string.
So short answer would be: It's (currently) not possible.
There is an open vote for adding that functionality for multiple selections so you might want to vote there.
One idea of mine would be to try to select a folder path instead and load all textures from that folder but that is only a workaround and not really what you requested.
But I hope the rest helps you a bit :)
Related
I have a Unity script in c# where a public list of sprites is declared and then populated manually in the inspector.
The list consists of 19 sprites of cars.
These cars are available only if the player completed each level.
For example: When you open the game for the first time, you only have 1 available car but if you complete level 1, you unlock another car, when you complete level 2, you get another and so on.
My question is, being that I manually populated the list, how do I go about re-adding the same sprite back when a level is completed?
I was going to use cars.RemoveRange(1, 18) to remove them from the list but do not know a way to add them back without calling every sprite back manually. I believe there is a simpler, better way that I do not know about.
This is the script:
public class CarSelection : MonoBehaviour
{
public List<Sprite> cars; //store all your images in here at design time
public Image displayImage; //The current image thats visible
public Button nextCar; //Button to view next image
public Button previousCar; //Button to view previous image
private int i = 0; //Will control where in the array you are
void OnEnable()
{
//Register Button Events
nextCar.onClick.AddListener(() => NextCarButton());
previousCar.onClick.AddListener(() => PreviousCarButton());
}
private void Start()
{
cars.RemoveRange(1, 18);
if (FinishLineTouch.levelOneComplete == true) {
//Re-adding the same sprites here
}
}
public void NextCarButton()
{
if (i + 1 < cars.Count)
{
i++;
}
}
public void PreviousCarButton()
{
if (i - 1 >= 0)
{
i--;
}
}
void Update()
{
if (LevelSelect.isCarChosen == true) {
nextCar.interactable = false;
previousCar.interactable = false;
}
displayImage.sprite = cars[i];
}
}
Thanks in advance.
You have a small flaw in your logic here.
Saying that you add the car sprites in the inspector, removing all the sprites is not ideal.
You can have a list of all the sprites, and a list with available options (starts with sprite[0] and adds each time you complete the level).
However, if you want to know how to add them back after deleting them, you need to make a copy in a global variable, so it's more efficient to try what I am suggesting above.
You should have two different arrays, one for the sprites, and the other one for if the player unlocked it yet. Or you should create a custom class that has the sprite of the car, and if it is unlocked yet.
Sounds like all you really need to do is make sure the index doesn't exceed the unlocked cars
if(i + 1 < cars.Count && i + 1 <= amountOfFinishedLevels)
{
i++;
// and then you really should do this in both methods and not every frame
displayImage.sprite = cars[i];
}
and don't remove/readd any items at all.
I am quite new in Unity and I have a simple question regarding Animation events.
In my code snipet I have the public variables - I noticed that I can't reference an AnimationEvent - and some simple stuff to do with them.
public GameObject completeLevelUI; // a panel
public Text endUI; // the text on the panel that show your success or fail
public AnimationEvent nextSceneEvent; // AnimatonEvent in the animation of the panel above
public void GameOver() { nextSceneEvent.time = 2; completeLevelUI.SetActive(true); endUI.text = "LEVEL\nFAILED"; }
public void GameWon() { nextSceneEvent.time = 6; completeLevelUI.SetActive(true); endUI.text = "LEVEL\nCOMPLETED"; }
public void LoadEnd() { SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1); }
The AnimationEvent's target method is LoadEnd().
The default firing time is 1.5 seconds and the code won't change it.
The reason why I need different fire times can't be seen in the shared code - it is not important now. I figured out some other ways to solve it, I am just curious why it isn't working.
I have tried to change the activating and the time setting code piece but it is the same.
Somehow I have to reference the AnimationEvent? Is it a problem that this script is called multiple times, even from places where there is no AnimationEvent - I didn't receive any exceptions during my examination.
Any ideas? Thanks for helping!
I guess you have an animation playing and at the end you want to trigger the new scene.
You can use different approaches.
First, you add the animation event directly to the animation clip and you set the method in the inspector. The method has to be on a component attached to the same game object as the animation.
Second, you launch the animation and wait for the end of it to call the method:
void EndProcess()
{
StartCoroutine(EndProcessSequence());
}
IEnumerator EndProcessSequence()
{
Animation anim = GetComponent<Animation>();
anim.Play("animName");
yield return new WaitWhile(()=> anim.isPlaying);
LoadNewScene();
}
Here' s a reusable version
IEnumerator EndProcessSequence(string animName, Action onComplete)
{
Animation anim = GetComponent<Animation>();
anim.Play(animName);
yield return new WaitWhile(()=> anim.isPlaying);
onComplete?.Invoke();
}
In my game, there are many enemies and have this script on it, where I am set their navmesh destination by randomly selecting an element from an array of positions. However, sometimes all enemies go at same position, so I am trying to skip previously generated array indices and find a new index. Any suggestions for this? I don't want to use the goto statement.
Here is code I have done so far:
void move_on_fired_positions()
{
navmesh.Resume ();
again:
new_position = Random.Range (0, firedpoints.Length);
if (prev_num != new_position) {
navmesh.SetDestination (firedpoints [new_position].position);
prev_num = new_position;
} else
{
goto again;
}
}
One thing you can try is keeping a dynamic list of available positions, rather than an array. (Don't worry - they're both serialized and modified the same way in the Unity editor interface.) This way, you can remove positions from the list as they are assigned to enemies, ensuring you never assign duplicates. Here's an idea of how that might look:
public List<Transform> firedpoints;
// Returns available firing position, if any
public Transform GetRandomFiringPosition()
{
if (firedpoints.Count > 0)
{
// Get reference to transform, then remove it from list
int newPositionIndex = Random.Range (0, firedpoints.Length);
Transform returnedPosition = firedpoints[newPositionIndex];
firedpoints.RemoveAt(newPositionIndex);
return returnedPosition;
}
else
{
// Or you can specify some default transform
return null;
}
}
// Makes firing position available for use
public void RegisterFiringPosition(Transform newPosition)
{
firedpoints.Add(newPosition);
}
This code should be in a script on a single object in the scene, which the enemies should have a reference to (or, you can change the code a little and make the class into a singleton, so its methods can be called without a direct reference). Whenever an enemy needs a new position, it can call GetRandomFiringPosition() and store the returned Transform in a variable for future reference.
You haven't determined what conditions make a position available for use again, but when you have, you can call RegisterFiringPosition() to get it back into the list. As a nice side effect, this also makes it possible to assign brand new positions to enemies, for example in response to player-triggered events.
Hope this helps! Let me know if you have any questions.
I've 3 objects in my list, but I only want the first element to be active. Then, when I press a button, I want the list to move forward one, so the next item in the list is now active and the first isn't.
I have the following code:
void OnClick()
{
for(int i = 0; i < activateTexture.Count; i++)
{
activateTexture[0].SetActive(true);
}
}
This only displays the first item in the list (which I want) but I'm stuck on working out how to move through the list.
Could someone please help me.
You're setting the initial texture active multiple times. Instead, keep track of the current one. Then, each time the code is triggered it can deactivate the one it's on, then move to the next one and activate it.
(Overly commented code below simply to make sure it's explained regarding this answer. I wouldn't normally put comments like this in my code)
void Start()
{
// Initialize all textures to be inactive
for(int i = 0; i < activateTexture.Count; i++)
{
activateTexture[i].SetActive(false);
}
// Activate the first texture
activateTexture[0].SetActive(true);
}
// Store the index of the currently active texture
private int activeTextureIndex = 0;
void OnClick()
{
// Disable the current
activateTexture[activeTextureIndex].SetActive(false);
// Increment the index
activeTextureIndex = (activeTextureIndex + 1) % activateTexture.Length;
// Activate a texture based upon the new index
activateTexture[activeTextureIndex].SetActive(true);
}
Note also that I've used the modulo operator % to cycle the list.
EDIT: Corrected due to concerns of integer overflow
I have a working implementation of NAudio's wasapi loopback recording and the FFT of the data.
Most of the data I get is just as it should be but every once in a while (10 sec to minutes intervals) it shows amplitude on almost all frequencies.
Basicly the picture is rolling from right to left with time and frequencies going on logarithmic scale from lowest frequencies on the bottom. The lines are the errors. As far as i can tell those are not supposed to be there.
I get the audio buffer and send the samples to an aggregator (applies Hamming window) which implements the NAudio FFT. I have checked the data (FFT result) before I modify it in any way (the picture is not from the raw FFT output, but desibel scaled) confirming the FFT result is giving those lines. I could also point out the picture is modified with LockBits so I thought I had something wrong with the logic there, but that's why I checked the FFT output data which shows the same problem.
Well I could be wrong and the problem might be somewhere I said it isn't but it really seems it originates from the FFT OR the buffer data (data itself or the aggregation of samples). Somehow I doubt the buffer itself is corrupted like this.
If anyone has any idea what could cause this I would greatly appreciate it!
UPDATE
So I decided to draw the whole FFT result range rather than half of it. It showed something strange. I'm not sure of FFT but I thought Fourier transformation should give a result that is mirrored around the middle. This certainly is not the case here.
The picture is in linear scale so the exact middle of the picture is the middle point of the FFT result. Bottom is the first and top is the last.
I was playing a 10kHz sine wave which gives the two horizontal lines there but the top part is beyond me. It also seems like the lines are mirrored around the bottom quarter of the picture so that seems strange to me as well.
UPDATE 2
So I increased the FFT size from 4096 to 8192 and tried again. This is the output with me messing with the sine frequency.
It would seem the result is mirrored twice. Once in the middle and then again on the top and bottom halves. And the huge lines are now gone.. And it would seem like the lines only appear on the bottom half now.
After some further testing with different FFT lengths it seems the lines are completely random in that account.
UPDATE 3
I have done some testing with many things. The latest thing I added was overlapping of samples so that I reuse the last half of the sample array in the beginning of the next FFT. On Hamming and Hann windows it gives me massive intensities (quite like in the second picture I posted) but not with BlackmannHarris. Disabling overlapping removes the biggest errors on every window function. The smaller errors like in the top picture still remain even with BH window. I still have no idea why those lines appear.
My current form allows control over which window function to use (of the three previously mentioned), overlapping (on/off) and multiple different drawing options. This allows me to compare all the affecting parties effects when changed.
I shall investigate further (I am quite sure I have made a mistake at some point) but good suggestions are more than welcome!
The problem was in the way I handled the data arrays. Working like a charm now.
Code (removed excess and might have added mistakes):
// Other inputs are also usable. Just look through the NAudio library.
private IWaveIn waveIn;
private static int fftLength = 8192; // NAudio fft wants powers of two!
// There might be a sample aggregator in NAudio somewhere but I made a variation for my needs
private SampleAggregator sampleAggregator = new SampleAggregator(fftLength);
public Main()
{
sampleAggregator.FftCalculated += new EventHandler<FftEventArgs>(FftCalculated);
sampleAggregator.PerformFFT = true;
// Here you decide what you want to use as the waveIn.
// There are many options in NAudio and you can use other streams/files.
// Note that the code varies for each different source.
waveIn = new WasapiLoopbackCapture();
waveIn.DataAvailable += OnDataAvailable;
waveIn.StartRecording();
}
void OnDataAvailable(object sender, WaveInEventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new EventHandler<WaveInEventArgs>(OnDataAvailable), sender, e);
}
else
{
byte[] buffer = e.Buffer;
int bytesRecorded = e.BytesRecorded;
int bufferIncrement = waveIn.WaveFormat.BlockAlign;
for (int index = 0; index < bytesRecorded; index += bufferIncrement)
{
float sample32 = BitConverter.ToSingle(buffer, index);
sampleAggregator.Add(sample32);
}
}
}
void FftCalculated(object sender, FftEventArgs e)
{
// Do something with e.result!
}
And the Sample Aggregator class:
using NAudio.Dsp; // The Complex and FFT are here!
class SampleAggregator
{
// FFT
public event EventHandler<FftEventArgs> FftCalculated;
public bool PerformFFT { get; set; }
// This Complex is NAudio's own!
private Complex[] fftBuffer;
private FftEventArgs fftArgs;
private int fftPos;
private int fftLength;
private int m;
public SampleAggregator(int fftLength)
{
if (!IsPowerOfTwo(fftLength))
{
throw new ArgumentException("FFT Length must be a power of two");
}
this.m = (int)Math.Log(fftLength, 2.0);
this.fftLength = fftLength;
this.fftBuffer = new Complex[fftLength];
this.fftArgs = new FftEventArgs(fftBuffer);
}
bool IsPowerOfTwo(int x)
{
return (x & (x - 1)) == 0;
}
public void Add(float value)
{
if (PerformFFT && FftCalculated != null)
{
// Remember the window function! There are many others as well.
fftBuffer[fftPos].X = (float)(value * FastFourierTransform.HammingWindow(fftPos, fftLength));
fftBuffer[fftPos].Y = 0; // This is always zero with audio.
fftPos++;
if (fftPos >= fftLength)
{
fftPos = 0;
FastFourierTransform.FFT(true, m, fftBuffer);
FftCalculated(this, fftArgs);
}
}
}
}
public class FftEventArgs : EventArgs
{
[DebuggerStepThrough]
public FftEventArgs(Complex[] result)
{
this.Result = result;
}
public Complex[] Result { get; private set; }
}
And that is it I think. I might have missed something though.
Hope this helps!