I'm fairly new to C# and Unity so I'm having a little issue here. I'm working on some kind of "photobooth app", which makes the gallery a big part of it. However, I managed to sort out the part of taking screenshots and show them, my problem is that I can't seem to figure out how to delete the pictures from the gallery (I mean, inside the app).
So far, I'm working with this code (just to give it some sense), but this bit erases ALL of the taken pictures, not just the one currently showing.
tring path = Application.persistentDataPath;
DirectoryInfo dir = new DirectoryInfo(path);
FileInfo[] info = dir.GetFiles("*.png");
foreach (FileInfo f in info)
{
File.Delete(f.FullName);
}
I don't know if it'd help, but this is the code I'm using to take and save the screenshots:
yield return new WaitForEndOfFrame();
string timeStamp = System.DateTime.Now.ToString("dd-MM-yyyy-HH-mm-ss");
string fileName = "Screenshot" + timeStamp + ".png";
string pathToSave = fileName;
ScreenCapture.CaptureScreenshot(pathToSave);
yield return new WaitForEndOfFrame();
And, the one I'm using for showing them in the gallery:
public class ScreenShotPreview : MonoBehaviour
{
[SerializeField]
GameObject Panel;
[SerializeField]
string sceneName;
string[] files = null;
int whichScreenShotIsShown = 0;
void Start()
{
files = Directory.GetFiles(Application.persistentDataPath + "/", "*.png");
if (files.Length > 0)
{
GetPictureAndShowIt();
}
}
void GetPictureAndShowIt()
{
string pathToFile = files[whichScreenShotIsShown];
Texture2D texture = GetScreenshotImage(pathToFile);
Sprite sp = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
Panel.GetComponent<Image>().sprite = sp;
}
Texture2D GetScreenshotImage(string filePath)
{
Texture2D texture = null;
byte[] fileBytes;
if (File.Exists(filePath))
{
fileBytes = File.ReadAllBytes(filePath);
texture = new Texture2D(2, 2, TextureFormat.RGB24, false);
texture.LoadImage(fileBytes);
}
return texture;
}
public void NextPicture()
{
if (files.Length > 0)
{
whichScreenShotIsShown += 1;
if (whichScreenShotIsShown > files.Length - 1)
whichScreenShotIsShown = 0;
GetPictureAndShowIt();
}
}
public void PreviousPicture()
{
if (files.Length > 0)
{
whichScreenShotIsShown -= 1;
if (whichScreenShotIsShown < 0)
whichScreenShotIsShown = files.Length - 1;
GetPictureAndShowIt();
}
}
I hope it makes sense? Thank you in advance!
TLDR; Can't figure out how to delete current picture showing in the gallery.
Your file paths are stored in the string[] files variable. The whichScreenShotIsShown variable is the current index that determines which path is currently being displayed. These two variables are declared inside the ScreenShotPreview script.
Therefore to delete the current file, you would do something like this:
string currentFile = files[whichScreenShotIsShown];
File.Delete(currentFile );
Related
I am having a problem with my gallery code, my Android does not recognize the right folder with the .jpg images on the line 20 and 30. It´s a lot different that a PC code cause of the disk details, if someone could help me, i will be a lot gratefull
public class Gallery : MonoBehaviour {
public List<Sprite> gallery = new List<Sprite>();
public Image displayImage;
public Button nextImg;
public Button prevImg;
public int i = 0;
void Start ()
{
// var Images = Directory.GetFiles("C:/Users/Bandeira/Downloads/Menu Start/Assets/Sprite/JPG/","*.jpg");
var Images = Directory.GetFiles("file:///" + "/unitypictures/","*.jpg");
Debug.Log(Images);
StartCoroutine(LoadImages(Images));
}
IEnumerator LoadImages(string[] Images)
{
foreach (var path in Images)
{
Debug.Log(path);
using (var uwr = UnityWebRequestTexture.GetTexture("file:///" + path))
// using (var uwr = UnityWebRequestTexture.GetTexture(path))
{
Debug.Log(path);
yield return uwr.SendWebRequest();
if (uwr.result != UnityWebRequest.Result.Success)
{
Debug.Log(uwr.error);
yield break;
}
var tex = DownloadHandlerTexture.GetContent(uwr);
var sprite = Sprite.Create(tex, new Rect(0f, 0f, tex.width, tex.height), new Vector2(0.5f, 0.5f), 50f, 0, SpriteMeshType.FullRect);
gallery.Add(sprite);
uwr.Dispose();
}
yield return null;
}
}
public void BtnNext (){
if(i + 1 < gallery.Count){
i++;
}
displayImage.sprite = gallery[i];
}
public void BtnPrev () {
if (i - 1 > 0){
i--;
}
displayImage.sprite = gallery[i];
}
}
Dude, I already told you yesterday.
You are using absolute paths, and they cannot work, because for each operating system they are different.
If you connect your Android phone to your computer, or install a file manager, you can see that there should be no folder called "UnityPictures", and even if there is you can't get there easily because I believe they are protected by the device.
The answer is always the same: you can use Persistent data path to save the images and resume them later.
For example you can save them in the Directory (Application.PersistentDataPath + "UnityPictures") and get them back from the same path.
So to be clear: you have to create the folder and insert those images, if you then want to download them.
Alternatively you can also download them from a server. So you could edit them, add more, or remove them without the need for updates, and it would be usable for all devices.
I was wondering if there was a way to load a simple .fbx file with just a cube into my game without using the monogame content pipeline. Here's the code I have currently, but I want to essentially just get rid of the requirement for the file to be .xnb
public static Model LoadModel(string model)
{
Model outModel = null;
// If model is already in memory, reference that instead of having to load it again
if (Loaded3DModels.ContainsKey(model))
{
Loaded3DModels.TryGetValue(model, out outModel);
}
else
{
if (File.Exists(Game.gameWindow.Content.RootDirectory + "/" + model + ".fbx"))
{
outModel = Game.gameWindow.Content.Load<Model>(model + ".fbx");
Loaded3DModels.Add(model, outModel);
}
else
{
Debug.LogError("The Model \"" + model + ".fbx\" does not exist!", true, 2);
}
}
return outModel;
}
So I managed to use AssImp to load a mesh, and then "Convert" it to something Monogame could render
public Renderer3DComponent(Mesh mesh)
{
// Set up material
Material = new BasicEffect(Game.graphics.GraphicsDevice);
Material.Alpha = 1;
Material.VertexColorEnabled = true;
Material.LightingEnabled = false;
// Tris
for (int i = mesh.VertexCount - 1; i >= 0; i--)
{
var Vert = mesh.Vertices[i];
Verts.Add(new VertexPositionColor(new Vector3(Vert.X, Vert.Y, Vert.Z), Color.White));
}
// Buffer
Buffer = new VertexBuffer(Game.graphics.GraphicsDevice, typeof(VertexPositionColor), Verts.Count, BufferUsage.WriteOnly);
Buffer.SetData(Verts.ToArray());
}
I'm using this script but I'm not sure how to convert the info array to textures and assign the info images to the textures array.
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;
public class StreamVideo : MonoBehaviour
{
public Texture[] frames; // array of textures
public float framesPerSecond = 2.0f; // delay between frames
public RawImage image;
void Start()
{
DirectoryInfo dir = new DirectoryInfo(#"C:\tmp");
string[] extensions = new[] { ".jpg", ".JPG", ".jpeg", ".JPEG", ".png", ".PNG", ".ogg", ".OGG" };
FileInfo[] info = dir.GetFiles().Where(f => extensions.Contains(f.Extension.ToLower())).ToArray();
if (image == null)
{
//Get Raw Image Reference
image = gameObject.GetComponent<RawImage>();
}
}
void Update()
{
int index = (int)(Time.time * framesPerSecond) % frames.Length;
image.texture = frames[index]; //Change The Image
}
}
Then I copied the images from the tmp folder to my project new folder but if I select all the images in the assets I can't drag them to the inspector of the script I can only drag them one by one and it will take time so how can I do it with the script ?
Well you would need to read the according file e.g. using File.ReadAllBytes
Then you could use ImageConversion.LoadImage
I would then do the entire loading process already on app start and later only exchange the texture like e.g.
public class StreamVideo : MonoBehaviour
{
public Texture[] frames; // array of textures
public float framesPerSecond = 2.0f; // delay between frames
public RawImage image;
void Start()
{
DirectoryInfo dir = new DirectoryInfo(#"C:\tmp");
// since you use ToLower() the capitalized version are quite redundant btw ;)
string[] extensions = new[] { ".jpg", ".jpeg", ".png", ".ogg" };
FileInfo[] info = dir.GetFiles().Where(f => extensions.Contains(f.Extension.ToLower())).ToArray();
if (!image)
{
//Get Raw Image Reference
image = gameObject.GetComponent<RawImage>();
}
frames = GetTextures(infos);
}
private Texture[] GetTextures(FileInfo[] fileInfos)
{
var output = new Texture[fileInfos.Length];
for(var i = 0; i < fileInfos.Length; i++)
{
var bytes = File.ReadAllBytes(fileInfos[i].fullName);
output[i] = new Texture2D(1,1);
if(!ImageConversion.LoadImage(output[i], bytes, false))
{
Debug.LogError($"Could not load image from {fileInfos.Length}!", this);
}
}
}
void Update()
{
int index = (int)(Time.time * framesPerSecond) % frames.Length;
image.texture = frames[index]; //Change The Image
}
}
Note: Typed on smartphone but I hope the idea gets clear
You can drag multiple selected images.
First you need to lock your inspector view with a padlock (not possible if you skip that).
Then select all the images, and drop then onto the name_of_the_array (not, into the field). This is just a matter of getting the drag mechanic right, nothing else.
Also, fileinfo reference in your question is a bit confusing, as you are not doing anything with the result anyway.
Have not done that myself yet, but I believe that should work with:
https://docs.unity3d.com/ScriptReference/ImageConversion.LoadImage.html
In the code example there, insteed of "imageAsset.bytes", apply this function to your files (calling FullPath on your FileInfo instances):
https://docs.unity3d.com/ScriptReference/Windows.File.ReadAllBytes.html
I have a Unity game where the player gets one's inventory from the database after starting the game.
I got the pictures of items stored in Firebase Storage, and I can download them.
However, I don't know how to make sprites from the byte[] (fileContents) I get by downloading.
(I'd load these sprites in the player's inventory later)
I got the following C# script:
private void getPlayersInventory()
{
FirebaseDatabase.DefaultInstance.GetReference("users").Child(auth.CurrentUser.UserId)
.Child("inventory").GetValueAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
print("unable to get snapshot for the inventory");
}
else if (task.IsCompleted)
{
snapshot = task.Result;
if(snapshot.HasChildren)
{
foreach(var current in snapshot.Children)
{
string currentName = current.Value.ToString();
print(currentName.ToLower() + ".png");
const long maxAllowedSize = 10 * 1024 * 1024;
storageReference.Child(currentName.ToLower() + ".png")
.GetBytesAsync(maxAllowedSize)
.ContinueWith((Task<byte[]> task_) =>
{
if (task_.IsFaulted || task_.IsCanceled)
{
Debug.Log(task_.Exception.ToString());
}
else
{
byte[] fileContents = task_.Result;
Debug.Log("Finished downloading!");
}
});
}
}
}
});
}
I believe that you're looking for ImageConversion.LoadImage
Your full code will look something like:
var tex = new Texture(1,1); // note that the size is overridden
tex.LoadImage(task_.Result);
var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(tex.width/2, tex.height/2));
You can also pack textures at runtime if that's your eventual goal. Once you get through the tex.LoadImage chunk, you should be able to work out Texture2D.PackTextures.
--Patrick
I'm making a game with musical instruments and a recording device in-game. I want the user to be able to play the instruments and record them to an audioclip - and to a wav file - which they can then play back as accompaniment to themselves playing another instrument, the output again recorded, allowing them to make tracks with combined instruments. So far I have everything working except I can't figure out how to get the audioclip - I have .wav files being saved to the asset folder but I don't understand the code enough to figure out where to set the audioclip's data to the data being written.
I'm using code from Olkor here to save the recording as wav, which is working great: https://unitylist.com/p/za/Output-Audio-Recorder
My two options to save an audioclip from this seem to be a) save to an audioclip at the same time as saving to disk (I can't figure this out) or save it to disk and then load it into the game as an audioclip - using Unity Web Request which I have tried to do but I get an error either cannot access the .audioclip property of an aborted DownloadHandlerAudioClip or if I invoke the function with a delay to load the file, there is an error decoding the audio. Either way I have a saved .wav audio file in my assets folder but no audioclip.
InvalidOperationException: Cannot access the .audioClip property of an aborted DownloadHandlerAudioClip
UnityEngine.Networking.DownloadHandlerAudioClip.GetContent
(UnityEngine.Networking.UnityWebRequest www) (at
C:/buildslave/unity/build/Modules/UnityWebRequestAudio/Public/DownloadHandler
Audio.bindings.cs:49)
OutputAudioRecorder+<GetAudioClip>d__27.MoveNext () (at
Assets/Scripts/OutputAudioRecorder.cs:202)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator
enumerator, System.IntPtr returnValueAddress) (at C:/buildslave/unity/build/Runtime/Export/Scripting/Coroutines.cs:17)
The script below that I'm using is attached to the audiolistener and so it's picking up all the output of audio from the game. I have looked at doing something with audioListener GetOutputData and passing it to audioClip SetData but I haven't been able to figure it out and I admit that this is beyond my current ability - I'd really appreciate some insight into how to approach this problem in any of the ways possible.
using System;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class OutputAudioRecorder : MonoBehaviour
{
public const string DEFAULT_FILENAME = "record";
public const string FILE_EXTENSION = ".wav";
public bool IsRecording { get { return recOutput; } }
private int bufferSize;
private int numBuffers;
private int outputRate;
private int headerSize = 44; //default for uncompressed wav
private String fileName;
private bool recOutput = false;
private AudioClip newClip;
private FileStream fileStream;
private AudioClip[] audioClips;
private AudioSource[] audioSources;
public int currentSlot;
float[] tempDataSource;
void Awake()
{
outputRate = AudioSettings.outputSampleRate;
}
void Start()
{
AudioSettings.GetDSPBufferSize(out bufferSize, out numBuffers);
audioSources = new AudioSource[3];
audioSources[0] = GameObject.FindWithTag("RecSlot1").GetComponent<AudioSource>();
audioSources[1] = GameObject.FindWithTag("RecSlot2").GetComponent<AudioSource>();
audioSources[2] = GameObject.FindWithTag("RecSlot3").GetComponent<AudioSource>();
}
public void StartRecording(string recordFileName)
{
fileName = Path.GetFileNameWithoutExtension(recordFileName) + FILE_EXTENSION;
if (!recOutput)
{
StartWriting(fileName);
recOutput = true;
}
else
{
Debug.LogError("Recording is in progress already");
}
}
public void StopRecording()
{
recOutput = false;
WriteHeader();
UpdateClip();
}
private void StartWriting(String name)
{
fileStream = new FileStream(Application.dataPath + "/" + name, FileMode.Create);
var emptyByte = new byte();
for (int i = 0; i < headerSize; i++) //preparing the header
{
fileStream.WriteByte(emptyByte);
}
}
private void OnAudioFilterRead(float[] data, int channels)
{
if (recOutput)
{
ConvertAndWrite(data); //audio data is interlaced
}
}
private void ConvertAndWrite(float[] dataSource)
{
var intData = new Int16[dataSource.Length];
//converting in 2 steps : float[] to Int16[], //then Int16[] to Byte[]
var bytesData = new Byte[dataSource.Length * 2];
//bytesData array is twice the size of
//dataSource array because a float converted in Int16 is 2 bytes.
var rescaleFactor = 32767; //to convert float to Int16
for (var i = 0; i < dataSource.Length; i++)
{
intData[i] = (Int16)(dataSource[i] * rescaleFactor);
var byteArr = new Byte[2];
byteArr = BitConverter.GetBytes(intData[i]);
byteArr.CopyTo(bytesData, i * 2);
}
fileStream.Write(bytesData, 0, bytesData.Length);
tempDataSource = new float[dataSource.Length];
tempDataSource = dataSource;
}
private void WriteHeader()
{
fileStream.Seek(0, SeekOrigin.Begin);
var riff = System.Text.Encoding.UTF8.GetBytes("RIFF");
fileStream.Write(riff, 0, 4);
var chunkSize = BitConverter.GetBytes(fileStream.Length - 8);
fileStream.Write(chunkSize, 0, 4);
var wave = System.Text.Encoding.UTF8.GetBytes("WAVE");
fileStream.Write(wave, 0, 4);
var fmt = System.Text.Encoding.UTF8.GetBytes("fmt ");
fileStream.Write(fmt, 0, 4);
var subChunk1 = BitConverter.GetBytes(16);
fileStream.Write(subChunk1, 0, 4);
UInt16 two = 2;
UInt16 one = 1;
var audioFormat = BitConverter.GetBytes(one);
fileStream.Write(audioFormat, 0, 2);
var numChannels = BitConverter.GetBytes(two);
fileStream.Write(numChannels, 0, 2);
var sampleRate = BitConverter.GetBytes(outputRate);
fileStream.Write(sampleRate, 0, 4);
var byteRate = BitConverter.GetBytes(outputRate * 4);
fileStream.Write(byteRate, 0, 4);
UInt16 four = 4;
var blockAlign = BitConverter.GetBytes(four);
fileStream.Write(blockAlign, 0, 2);
UInt16 sixteen = 16;
var bitsPerSample = BitConverter.GetBytes(sixteen);
fileStream.Write(bitsPerSample, 0, 2);
var dataString = System.Text.Encoding.UTF8.GetBytes("data");
fileStream.Write(dataString, 0, 4);
var subChunk2 = BitConverter.GetBytes(fileStream.Length - headerSize);
fileStream.Write(subChunk2, 0, 4);
fileStream.Close();
}
void UpdateClip()
{
StartCoroutine(GetAudioClip());
}
IEnumerator GetAudioClip()
{
using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip("file://" + Application.dataPath + "myRecord1.wav", AudioType.WAV))
{
yield return www.Send();
if (www.isNetworkError)
{
Debug.Log(www.error);
}
else
{
AudioClip newClip = DownloadHandlerAudioClip.GetContent(www);
Debug.Log(newClip.name + "name " + newClip.length);
keyboardScript.audioClip = newClip;
}
}
}
Try to load as audio clip using this:
private IENumerator LoadAudio(string url) {
WWW www;
www = new WWW("file:///" + url);
yield return www;
if (www != null && www.isDone)
{
AudioClip audioClip;
audioClip = www.GetAudioClip(true, false, AudioType.WAV);
}
}
That works for me at least.
Thought this was solution but still not working to load at runtime. (It just refers to the old copy of the clip until I reload the game. Didn't notice this at first.)
While I wasn't able to get the web request to work for me or get WWW to work either, I did get the effect I wanted
by creating a blank clip in the assets folder where the .wav saves, then dragging it in the properties window as a reference, setting the audio clip to streaming and then using the code above to write the wav, it automatically overwrites the clip with the data I recorded.
The audio has a bit of distortion on it (crackling) which I'll look into next.
This solution is I'm aware probably incredibly obvious (i guess I had to give unity something to refer to but didn't realise it) and doesn't solve the problem of loading files at runtime if they aren't predefined like mine are, but it fixed my problem at least.