I need to save the player's position and rotation in-game. I use Binary Formatter and 2 buttons: 'Save' and 'Load' for this purpose. The script saves public Vector3 data if I write it down manually, save it, and then load the scene again. However, the player (just a cube) does not change its position and rotation. To fix this, I added:
void FixedUpdate()
{
position = player.transform.position;
rotation = player.transform.rotation;
}
But that did not help. I use 2 scripts in order to make this work:
Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public GameObject player;
public Vector3 position;
public Quaternion rotation;
void FixedUpdate()
{
position = player.transform.position;
rotation = player.transform.rotation;
}
public void Save()
{
SaveLoadManager.SavePlayer(this);
}
public void Load()
{
float[] loadedStats = SaveLoadManager.LoadPlayer();
Vector3 loadedPos = new Vector3(loadedStats[0], loadedStats[1], loadedStats[2]);
Vector3 loadedRot = new Vector3(loadedStats[3], loadedStats[4], loadedStats[5]);
player.transform.position = loadedPos;
player.transform.rotation = Quaternion.Euler(loadedRot);
}
}
SaveLoadManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public static class SaveLoadManager
{
public static void SavePlayer(Player player)
{
BinaryFormatter bf = new BinaryFormatter();
FileStream stream = new FileStream(Application.persistentDataPath + "/player.save", FileMode.Create);
PlayerData data = new PlayerData(player);
bf.Serialize(stream, data);
stream.Close();
}
public static float[] LoadPlayer()
{
if (File.Exists(Application.persistentDataPath + "/player.save"))
{
BinaryFormatter bf = new BinaryFormatter();
FileStream stream = new FileStream(Application.persistentDataPath + "/player.save", FileMode.Open);
PlayerData data = bf.Deserialize(stream) as PlayerData;
stream.Close();
return data.stats;
}
else
{
Debug.LogError("File does not exist.");
return new float[6];
}
}
}
[Serializable]
public class PlayerData
{
public float[] stats;
public PlayerData(Player player)
{
stats = new float[6];
stats[0] = player.position.x;
stats[1] = player.position.y;
stats[2] = player.position.z;
stats[3] = player.rotation.x;
stats[4] = player.rotation.y;
stats[5] = player.rotation.z;
}
}
I'm quite new to Save-Load systems and Binary Formatting, so I hope you'll give me a hand.
Thanks in advance!
If someone else encounters a similar problem: besides adding the code in the Answer below, I also changed player.transform.rotation.(x/y/z); to player.transform.eulerAngles.(x/y/z); in SaveLoadManager.cs to make rotation work.
I'm not sure if I'm missing something, but I don't see anywhere in your Load() method that actually sets the Player's position to the loaded values. Instead it looks like you're passing the loaded values into unnecessary variables, then overwriting those constantly in FixedUpdate().
Maybe try something like:
public void Load()
{
float[] loadedStats = SaveLoadManager.LoadPlayer();
Vector3 loadedPos = new Vector3(loadedStats[0], loadedStats[1], loadedStats[2]);
Vector3 loadedRot = new Vector3(loadedStats[3], loadedStats[4], loadedStats[5]);
player.transform.position = loadedPos ;
player.transform.rotation = Quaternion.Euler(loadedRot);
}
And you can just get rid of the position and rotation variables on the Player script.
Related
I am fresh to C# (I do programm in other languages) and I got a task to record the position and rotation of an object inside Unity3D game.
I successfully created code that prints in Unity console current position and rotation with set timing (parts of void RecPoint without lista), and that at the end it saves all of the position data in one file (void SaveToFile).
What I wanted to do is to create one file for saving both position and rotation at the same time that looks like:
xposition; yposition; zposition; wrotation; xrotation; yrotation; zrotation
xposition; yposition; zposition; wrotation; xrotation; yrotation; zrotation
I wanted to achieve this by creating empty string list at the void Start and then adding position and rotation one step at the time (in void RecPoint). After that I would modify void SaveToFile to save everything simmiliar to what is currently in void SaveToFile.
The problem is that no matter if I use var lista = new List<string>() or lista = new List<string>().
I get the same error CS0103 with the only difference being that when used var lista I don't get the CS0103 error for this line.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ReadData : MonoBehaviour
{
public string fileName = "D:/position.txt";
public List<Vector3> positions;
public List<Quaternion> rotations;
public float interval = 0.2f;
public float tSample = 1.0f;
void Start() {
positions = new List<Vector3>();
rotations = new List<Quaternion>();
var lista = new List<string>();
InvokeRepeating("RecPoint", tSample, interval);
}
void RecPoint()
{
positions.Add(transform.position);
rotations.Add(transform.rotation);
lista.Add(transform.position.ToString());
lista.Add(transform.rotation.ToString());
Debug.Log("position " + transform.position + " rotation " + transform.rotation);
}
void SaveToFile(string fileName)
{
foreach (Vector3 pos in positions)
{
string line = System.String.Format("{0,3:f2};{1,3:f2};{2,3:f2}\r\n", pos.x, pos.y, pos.z);
System.IO.File.AppendAllText(fileName, line);
}
}
void Update()
{
if (Input.GetKeyDown(KeyCode.O))
{
CancelInvoke("RecPoint");
SaveToFile(fileName);
Debug.Log("Koniec czytania danych, zapisujÄ™ do pliku");
}
}
}
Well the problem is that you did not define the List "lista" anywhere.
Declare like so : public List<string> lista;
And then in Start() do : lista = new List<string>();
You can compress all of this into one line when declaring, like so : public List<string> lista = new List<string>();
Hope it helped!
I'm trying to write a program in unity. I'm quite new to unity, so i don't know much.i want to be able to make a new material in my code with only a texture2d to pass into it. So far im trying this:
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
public class Search : MonoBehaviour
{
public void SearchEnter()
{
MyObject.GetComponent<Image>().material = LoadImage(name.ToLower());
}
Texture2D LoadImage(string ImageName)
{
string url = "file:///C:/MyFilePath" + ImageName + ".jpg";
Texture2D tex;
tex = new Texture2D(4, 4, TextureFormat.DXT1, false);
using (WWW www = new WWW(url))
{
www.LoadImageIntoTexture(tex);
GetComponent<Renderer>().material.mainTexture = tex;
}
return tex;
}
}
I cannot do this as it cannot convert from texture2d to material.
Can anyone help me?
First of all you are not waiting until the WWW request finishes so you are trying to access the result to early.
Instead of the WWW you should rather use a UnityWebRequestTexture.GetTexture in a Coroutine like
public class Search : MonoBehaviour
{
public void SearchEnter()
{
StartCoroutine(LoadImage(name.ToLower()))
}
IEnumerator LoadImage(string ImageName)
{
using (UnityWebRequest uwr = UnityWebRequestTexture.GetTexture("file:///C:/MyFilePath" + ImageName + ".jpg"))
{
yield return uwr.SendWebRequest();
if (uwr.isNetworkError || uwr.isHttpError)
{
Debug.Log(uwr.error);
}
else
{
// Get downloaded texture
var texture = DownloadHandlerTexture.GetContent(uwr);
//TODO now use it
...
}
}
}
}
The second issue: You can not simply convert a Texture2D to Material. You rather have to either create a new Material from a given shader or a given Material using new Material() e.g. using Shader.Find like
//TODO now use it
// ofcourse again you can not simply create a new material from a texture
// you would rather need to pass in a shader like
var material = new Material(Shader.Find("UI/Default"));
material.mainTexture = texture;
MyObject.GetComponent<Image>().material = material;
GetComponent<Renderer>().material.mainTexture = texture;
However, for an Image you would actually probably rather simply stick with the default UI material but use a Sprite instead
MyObject.GetComponent<Image>().sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.one * 0.5f);
I have some images in "assets/resource/mat" . I want to get this images and put them to array . But when I try to get this images I'm getting ArrayIndexOutOfBoundsException . I think that there is problem with Resource.LoadAll("mat") method . But I can't fix it . Please help me
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class test : MonoBehaviour
{
private string t;
public Sprite[] Icons;
void Start()
{
Object[] loadedIcons = Resources.LoadAll("mat");
Icons = new Sprite[loadedIcons.Length];
for (int x = 0; x < loadedIcons.Length; x++)
{
Icons[x] = (Sprite)loadedIcons[x];
Debug.Log("Loading....");
}
GameObject sp = new GameObject();
sp.GetComponent<SpriteRenderer>().sprite = Icons[0];
}
}
Loading all images from a particular folder, with LINQ, would look like this...
using UnityEngine;
using System.Linq;
public class Four : MonoBehaviour
{
public Sprite[] icons;
void Start()
{
icons= Resources.LoadAll("met", typeof(Sprite)).Cast<Sprite>().ToArray();
}
}
I'm not sure but I guess you instead of Sprite first have to load a Texture2D and then create a Sprite from it.
Also note you used GetComponent<SpriteRenderer>() on a newly created empty GameObject so obviously there will never be a SpriteRenderer component attached. Instead use AddComponent.
Sprite[] Icons;
Texture2D LoadedTextures;
private void Start()
{
LoadedTextures = (Texture2D[])Resources.LoadAll("mat", typeof(Texture2D));
Icons = new Sprite[loadedIcons.Length];
for (int x = 0; x < loadedIcons.Length; x++)
{
Icons[x] = Sprite.Create(
LoadedTextures[x],
new Rect(0.0f, 0.0f, LoadedTextures[x].width, LoadedTextures[x].height),
new Vector2(0.5f, 0.5f),
100.0f);
}
GameObject sp = new GameObject();
// Note that here you created a new empty GameObject
// so it obviously won't have any component of type SpriteRenderer
// so add it instead of GetComponent
sp.AddComponent<SpriteRenderer>().sprite = Icons[0];
}
In my Unity project I would like to use the FastObjImporter class found on the internet to put obj on the scene. Do I have to create an empty GameObject and assign processed obj to it?
Try with an empty game object:
GameObject go = new GameObject("obj");
myMesh = FastObjImporter.Instance.ImportFile(objPath);
Instantiate(myMesh, Vector3.zero, Quaternion.identity);
go.AddComponent<MeshRenderer>();
go.GetComponent<MeshFilter>().mesh = myMesh;
Earlier I tried this way but it also did not work:
GameObject go;
myMesh = FastObjImporter.Instance.ImportFile(objPath);
Instantiate(myMesh, Vector3.zero, Quaternion.identity);
go.GetComponent<MeshFilter>().mesh = myMesh;
Could someone explain what I'm doing wrong?
CODE:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.IO;
/// <summary>
/// Sample
/// </summary>
public class DriveTest : MonoBehaviour
{
//public static readonly string REQUIRED_MIME = "image/jpeg";
//public string filePath;
public string objPath;
bool initInProgress = false;
GoogleDrive drive;
public string url;
List<GoogleDrive.File> remoteObjFiles;
public Mesh myMesh;
public GameObject go;
/* ---------------------------------------------------------------------------------------------------------------------------------- */
void Start()
{
objPath = Application.persistentDataPath + "/testObj.obj";
StartCoroutine(ImportObj());
go = new GameObject("obj");
}
/* ---------------------------------------------------------------------------------------------------------------------------------- */
void Update()
{
if (Input.GetKey(KeyCode.Escape))
Application.Quit();
}
/* ---------------------------------------------------------------------------------------------------------------------------------- */
IEnumerator ImportObj()
{
initInProgress = true;
drive = new GoogleDrive();
var driveInit = drive.Initialize();
while (driveInit.MoveNext())
yield return null;
initInProgress = false;
for (;;)
{
( SEARCHING AND SETTING DOWNLOAD FOLDER IN GDRIVE )
var download = drive.DownloadFile(remoteObjFiles[0]);
yield return StartCoroutine(download);
var data = GoogleDrive.GetResult<byte[]>(download);
if (File.Exists(objPath))
{
File.Delete(objPath);
File.WriteAllBytes(objPath, data);
}
else
File.WriteAllBytes(objPath, data);
Debug.Log("Obj: " + remoteObjFiles[0].ToString());
if (File.Exists(objPath))
{
Debug.Log("Loading OBJ from the device");
myMesh = FastObjImporter.Instance.ImportFile(objPath);
Debug.Log("Obj imported...");
Instantiate(myMesh, Vector3.zero, Quaternion.identity);
go.AddComponent<MeshRenderer>();
go.GetComponent<MeshFilter>().mesh = myMesh;
}
break;
}
}
Regards
There is no need to instantiate the Mesh. It is a component and I am not sure if you can even instantiate it. Just assign it to the material.
The main problem in your code is that you are not creating a material and a shader. You need to create a material, then create a "Standard" shader and assign that shader to the that material.
I noticed that the FastObjImporter script is old and decided to try it to make sure it is functioning properly. I ran into an index exception bug in it then fixed it. You can grab the copy of the fixed version here. See line #57 for the actual fix I made.
Functions to create MeshFilder, Material and MeshRenderer:
void attachMeshFilter(GameObject target, Mesh mesh)
{
MeshFilter mF = target.AddComponent<MeshFilter>();
mF.mesh = mesh;
}
Material createMaterial()
{
Material mat = new Material(Shader.Find("Standard"));
return mat;
}
void attachMeshRenderer(GameObject target, Material mat)
{
MeshRenderer mR = target.AddComponent<MeshRenderer>();
mR.material = mat;
}
Function to create new GameObject, load the model and attach every necessary components to it in order to display it:
GameObject loadAndDisplayMesh(string path)
{
//Create new GameObject to hold it
GameObject meshHolder = new GameObject("Loaded Mesh");
//Load Mesh
Mesh mesh = FastObjImporter.Instance.ImportFile(path);
//return null;
//Attach Mesh Filter
attachMeshFilter(meshHolder, mesh);
//Create Material
Material mat = createMaterial();
//Attach Mesh Renderer
attachMeshRenderer(meshHolder, mat);
return meshHolder;
}
Usage:
void Start()
{
string objPath = Application.persistentDataPath + "/testObj.obj";
GameObject obj = loadAndDisplayMesh(objPath);
//Position it in front od=f the camera. Your ZOffset may be different
Camera cam = Camera.main;
float zOffset = 40f;
obj.transform.position = cam.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, cam.nearClipPlane + zOffset));
}
EDIT:
In some rare cases, Unity cannot find the shader on Android when Shader.Find("Standard") is used. You get the error when that happens.
A work around for this would be to create a Material in the Editor and assign the "Standard" shader to it. It should use "Standard" shader by default.
1.Create a material and name is "StandardMat".
2.Put that material inside a folder called "Resources". Create it if it doesn't exist. You must spell this correctly. Again, the folder must be named "Resources" and placed inside the "Asset" folder.
3.You can load the material with Resources.Load("StandardMat") as Material.
In the createMaterial function above,
Change
Material mat = new Material(Shader.Find("Standard"));
to
Material mat = Resources.Load("StandardMat") as Material;
I would like to edit 1 vertex on a cube, but I don't know how to. I've tried looking everywhere for this function, but I can't find a solution.
Here is an image of what I want to achieve:
http://answers.unity3d.com/questions/14567/editing-mesh-vertices-in-unity.html
This code is not mine. Bellow is the same code as the link above. I just separated it into two files. (one per class)
It work quite good. BUT make sure to save you scene before using it, it's a bit buggy.
Don't forget to leave edit mode when you are done whit your modification.
You don't need to add "editMesh" tag to the gameobject you are modifying, or it will be erased when you leave edit mode.
Lastly, if you modify a primitive from unity, the modification will be applied for every instance of that primitive ! (if you change a cube for a pyramid, every cube will become a pyramid)
To avoid that make a copy of your primitive mesh, change the mesh you are using in your mesh renderer by your copy and then modify it. (bellow a script to add simple copy function in unity menu)
EditMesh.cs
#if UNITY_EDITOR
using UnityEngine;
using System.Collections;
/// <summary>
/// http://answers.unity3d.com/questions/14567/editing-mesh-vertices-in-unity.html
/// </summary>
[AddComponentMenu("Mesh/Vert Handler")]
[ExecuteInEditMode]
public class EditMesh : MonoBehaviour {
public bool _destroy;
private Mesh mesh;
private Vector3[] verts;
private Vector3 vertPos;
private GameObject[] handles;
private const string TAG_HANDLE = "editMesh";
void OnEnable() {
mesh = GetComponent<MeshFilter>().sharedMesh; // sharedMesh seem equivalent to .mesh
verts = mesh.vertices;
foreach (Vector3 vert in verts) {
vertPos = transform.TransformPoint(vert);
GameObject handle = new GameObject(TAG_HANDLE);
// handle.hideFlags = HideFlags.DontSave;
handle.transform.position = vertPos;
handle.transform.parent = transform;
handle.tag = TAG_HANDLE;
handle.AddComponent<EditMeshGizmo>()._parent = this;
}
}
void OnDisable() {
GameObject[] handles = GameObject.FindGameObjectsWithTag(TAG_HANDLE);
foreach (GameObject handle in handles) {
DestroyImmediate(handle);
}
}
void Update() {
if (_destroy) {
_destroy = false;
DestroyImmediate(this);
return;
}
handles = GameObject.FindGameObjectsWithTag(TAG_HANDLE);
for (int i = 0; i < verts.Length; i++) {
verts[i] = handles[i].transform.localPosition;
}
mesh.vertices = verts;
mesh.RecalculateBounds();
mesh.RecalculateNormals();
}
}
#endif
EditMeshGizmo.cs
#if UNITY_EDITOR
using UnityEngine;
using System.Collections;
/// <summary>
/// http://answers.unity3d.com/questions/14567/editing-mesh-vertices-in-unity.html
/// </summary>
[ExecuteInEditMode]
public class EditMeshGizmo : MonoBehaviour {
private static float CURRENT_SIZE = 0.1f;
public float _size = CURRENT_SIZE;
public EditMesh _parent;
public bool _destroy;
private float _lastKnownSize = CURRENT_SIZE;
void Update() {
// Change the size if the user requests it
if (_lastKnownSize != _size) {
_lastKnownSize = _size;
CURRENT_SIZE = _size;
}
// Ensure the rest of the gizmos know the size has changed...
if (CURRENT_SIZE != _lastKnownSize) {
_lastKnownSize = CURRENT_SIZE;
_size = _lastKnownSize;
}
if (_destroy)
DestroyImmediate(_parent);
}
void OnDrawGizmos() {
Gizmos.color = Color.red;
Gizmos.DrawCube(transform.position, Vector3.one * CURRENT_SIZE);
}
}
#endif
CopyMesh.cs (put it in a directory named "Editor") (you should then find it in your menu bar)
using UnityEditor;
using UnityEngine;
namespace Assets {
/// <summary>
///
/// </summary>
public class CopyMesh : MonoBehaviour {
[MenuItem("Assets/CopyMesh")]
static void DoCopyMesh() {
Mesh mesh = Selection.activeObject as Mesh;
Mesh newmesh = new Mesh();
newmesh.vertices = mesh.vertices;
newmesh.triangles = mesh.triangles;
newmesh.uv = mesh.uv;
newmesh.normals = mesh.normals;
newmesh.colors = mesh.colors;
newmesh.tangents = mesh.tangents;
AssetDatabase.CreateAsset(newmesh, AssetDatabase.GetAssetPath(mesh) + " copy.asset");
}
[MenuItem("Assets/CopyMeshGameObject")]
static void DoCopyMeshGameObject() {
Mesh mesh = (Selection.activeGameObject.GetComponent<MeshFilter>()).sharedMesh;
Mesh newmesh = new Mesh();
newmesh.vertices = mesh.vertices;
newmesh.triangles = mesh.triangles;
newmesh.uv = mesh.uv;
newmesh.normals = mesh.normals;
newmesh.colors = mesh.colors;
newmesh.tangents = mesh.tangents;
print(AssetDatabase.GetAllAssetPaths()[0]);
AssetDatabase.CreateAsset(newmesh, AssetDatabase.GetAllAssetPaths()[0] + "/mesh_copy.asset");
}
}
}
Lately Unity has added access to the ProBuilder package via "Package Manager".
More info here:
https://docs.unity3d.com/Packages/com.unity.probuilder#4.0/manual/index.html
Unity editor has no built-in mesh editor capabilities at the moment.
I can advise you using Prototype plugin for that.
You can easily do that by iterating through the Vertices, which Unity will give you as a Vector3[] through the someObject.GetComponent<MeshFilter>().vertices field. See http://docs.unity3d.com/ScriptReference/Mesh-vertices.html for an example where the vertices are moved upwards with time.