I am making a basic shop system includes car name, price and buyable. I am saving data to a text file. When I replay, I can't get the last variables I've changed. But, if I refresh at Editor by CTRL+R and start the game, variables are loading correct. What am I doing wrong? I am new at storage and JSON issues. Thanks for answers...
Here is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System;
public class GameHandler : MonoBehaviour
{
[Serializable]
public class Car
{
public string name;
public int price;
public bool unlocked;
}
[Serializable]
public class CarList{
public Car[] cars;
}
private Car cars = new Car();
public CarList myCars = new CarList();
//-------
public int chosenCar;
//-------
public TextAsset CARS;
//-------
private void Start() {
GetFromJSON();
}
private void Update() {
if(Input.GetKeyDown(KeyCode.L))
GetFromJSON();
if(Input.GetKeyDown(KeyCode.S))
SaveToJSON();
}
public void ChangeCarIndex(int a){
chosenCar = a;
}
//This is a button func.
public void ChangePrice(int a){
myCars.cars[chosenCar].price = a;
SaveToJSON();
}
public void GetCarName(){
Debug.Log(myCars.cars[chosenCar].name);
}
public void GetCarPrice(){
Debug.Log(myCars.cars[chosenCar].price);
}
public void SaveToJSON(){
string jsOutput = JsonUtility.ToJson(myCars);
File.WriteAllText(Application.dataPath + "/CARS.txt", jsOutput);
Debug.Log("SaveToJSON() called");
}
public void GetFromJSON(){
myCars = JsonUtility.FromJson<CarList>(CARS.text);
Debug.Log("GetFromJSON() called");
}
}
When running this in the editor try to add
public void SaveToJSON()
{
string jsOutput = JsonUtility.ToJson(myCars);
File.WriteAllText(Application.dataPath + "/CARS.txt", jsOutput);
Debug.Log("SaveToJSON() called");
#if UNITY_EDITOR
UnityEditor.AssetDatabase.Refresh();
#endif
}
Though actually this isn't really necessary! Afaik you could also simply set the text of the textasset:
public void SaveToJSON()
{
string jsOutput = JsonUtility.ToJson(myCars);
CARS.text = jsOutput;
Debug.Log("SaveToJSON() called");
}
BUT NOTE:
in general note that this makes only sense in the editor itself.
If you target to do this in an actually later built application you would rather go via a file in Application.persistentDataPath. Problem with that though: The data can easily be seen and edited by the user. So if this is anything sensitive you will need to go for a central database server with user login.
Related
For example, there's two classes here.
Class One:
using UnityEngine;
using System.Collections.Generic;
public class One:MonoBehavior
{
private List<string> _assetBundleList;
private void Start()
{
InitializeList();
}
private void InitializeList()
{
//Add assetBundleList field some value.
}
public IEnumerator<string> GetEnumerator()
{
return _assetBundleList.GetEnumerator();
}
}
Class two:
public class Two:MonoBehavior
{
public GameObject gameObjectWithScriptOne;
private One _scriptOne;
private void Start()
{
scriptOne = gameObjectWithScriptOne.GetComponent<One>();
DoSomething();
}
private void DoSomething()
{
foreach(var assetBundle in scriptOne)
{
//Load asset
}
}
}
Script one is just like a manager things, I use this for storing asset bundle data, perhaps the list value will change. Script two must wait for initializing done. Is there any way to wait for it except adjusting script order?
It can be handled with easily creating a context that initialize things sequentially. In short you need to block the main thread.
Let's say you have a root or persistent scene that do must things like loading assets etc.
//The only object in the Root Scene.
public class RootContext : MonoBehaviour
{
private const string _playerAddress = "Player";
public void Awake()
{
//Load assets
//wait for assets loading
//instantiate things.
//new instantiated things can also initalize with this way.
var handle = Addressables.LoadAssetAsync<GameObject>(_playerAddress);
var asset = handle.WaitForCompletion();
var go = Instantiate(bla bla bla);
}
}
I think the what you want is a framework that organize things. You can look at Strange. It is MVCS + IoC framework. Addionality what you said about "when field or property initialization is done" with [PostConstruct] attribute with Strange.
[Inject(ContextKeys.CONTEXT)]
public IContext Context { get; set; }
/*Initialization of injections are guaranteed at here. Their initialization
is also quaranteed like this class.
*/
[PostConstruct]
public void Initalize()
{
}
I'd go with refactoring, but if this is not the case C# events might be the solution.
Class One
public class One:MonoBehavior
{
private List<string> _assetBundleList;
public event Func Initialized;
private void Start()
{
InitializeList();
}
private void InitializeList()
{
//Add assetBundleList field some value.
Initialized?.Invoke();
}
public IEnumerator<string> GetEnumerator()
{
return _assetBundleList.GetEnumerator();
}
}
Class Two:
public class Two:MonoBehavior
{
public GameObject gameObjectWithScriptOne;
private One _scriptOne;
private void Start()
{
scriptOne = gameObjectWithScriptOne.GetComponent<One>();
scriptOne.Initialized += DoSomething;
}
private void DoSomething()
{
foreach(var assetBundle in scriptOne)
{
//Load asset
}
}
}
Also, you should unsubscribe from events when disabling an object, but you'll figure it out by yourself.
I'm currently working on a sonar named "Imagenex 831L" and I'm trying to extract the data published by a driver with ROS and send it to Unity with RosBridge and ROS#. So I'm trying to subscribe to this topic and visualize it on Unity.
My problem is that the topic generated by the driver isn't a native topic of ROS, it's created by the driver and it's not known by the ROS# library.
I tried this method to create a new MessageType : https://github.com/siemens/ros-sharp/wiki/Dev_NewMessageTypes but that doesn't work at all.
I've also thought to change the MessageType created by the driver but I'm a begginer in ROS and I don't want to messed up the driver.
So I started a script in C# to create a new message type but I'm really not sure if this gonna work. As you can see below my code to create a new MessageType :
using RosSharp.RosBridgeClient.MessageTypes.Std;
namespace RosSharp.RosBridgeClient.MessageTypes.imagenex831l
{
public class ProcessedRange : Message
{
public const string RosMessageName = "imagenex831l/ProcessedRange";
public float Head_position { get; set; }
public UInt8 Intensity { get; set; }
public ProcessedRange() { }
public ProcessedRange(Header header, UInt8[] Intensity, float Head_position) { }
public Header header { get; set; }
}
}
Then I tried to code a subscriber :
using RosSharp.RosBridgeClient.MessageTypes.imagenex831l;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using RosSharp.RosBridgeClient.MessageTypes.Std;
namespace RosSharp.RosBridgeClient
{
public class ProcessedRangeSuscriber : UnitySubscriber<MessageTypes.imagenex831l.ProcessedRange>
{
//public Transform PublishedTransform;
public UInt8[] Intensity;
public Float32 Head_position;
private bool isMessageReceived;
protected override void Start()
{
base.Start();
}
private void Update()
{
if (isMessageReceived)
ProcessMessage();
}
protected override void ReceiveMessage(MessageTypes.imagenex831l.ProcessedRange message)
{
//position = GetPosition(message).Ros2Unity();
//rotation = GetIntensity(message).Ros2Unity();
//isMessageReceived = true;
}
private void ProcessMessage()
{
//PublishedTransform.position = position;
//PublishedTransform.rotation = rotation;
}
private Float32 GetPosition(MessageTypes.imagenex831l.ProcessedRange message)
{
return new Float32(message.head_position);
}
private UInt8 GetIntensity(MessageTypes.imagenex831l.ProcessedRange message)
{
return new UInt8(message.intensity);
}
}
}
I get the following error message : CS1503 C# Argument 1: cannot convert from to 'byte'
If someone has also use this sonar or know ROS# I'm very interesting to discuss with them.
Thank you for your consideration regarding my request
I'm sending info from 3 input fields on a mobile app made in Unity (Coded in C#) to Firebase database, but as you can see it seems to send each input field's data individually then stacks it on top of one another.See attached Ideally I'd only want the last set of complete information.
using System.Collections;
using System.Collections.Generic;
using Proyecto26;
using UnityEngine;
using UnityEngine.UI;
public class PlayerScores : MonoBehaviour
{
public InputField dateText;
public InputField classText;
public InputField informationText;
User user = new User();
public static string Information;
public static string Class;
public static string Date;
// Start is called before the first frame update
private void Start()
{
}
public void OnSubmit()
{
Date = dateText.text;
PostToDatabase();
{
Class = classText.text;
PostToDatabase();
}
{
Information = informationText.text;
PostToDatabase();
}
}
public void OnGetScore()
{
RetrieveFromDatabase();
}
private void PostToDatabase()
{
User user = new User();
RestClient.Put("https://anti-bullying-demo.firebaseio.com/" + Date + Class + Information + ".json", user);
}
private void RetrieveFromDatabase()
{
RestClient.Get<User>("https://anti-bullying-demo.firebaseio.com/" + Date + Class + Information + ".json").Then(response =>
{
user = response;
});
}
}
In your OnSubmit() function call, you call PostToDatabase() three times - you only need call it once at the end.
public void OnSubmit()
{
Date = dateText.text;
{
Class = classText.text;
}
{
Information = informationText.text;
}
PostToDatabase();
}
I have a class with a PictureBox created as followed:
public class Tile
{
public PictureBox tilePB = new PictureBox(); //properties don't matter in this case
}
I also have a class GameManager. This is like a referee.
I want to make it so the BackColor of Tile.tilePB can only be edited by Gamemanager and nothing else, and no other class.
I currently have a public PictureBox for Gamemanager (to edit) and a public get function for other classes, but I want to actually make this a valid system instead of what I have right now.
Is this even possible? Please include explenation for the required code.
EDIT: I ran into an issue that I hadn't thought off: class Gamemanager is a static class. I do everything in that class via public static functions. Is this still possible? Since this doesn't work.
You can't do this at compile time, but it can be done at runtime:
public class PictureBox
{
private Color _backColor;
public void SetBackColor(Color color)
{
//getting class type that called this method
var stackTrace = new StackTrace();
var stackFrames = stackTrace.GetFrames();
var callingFrame = stackFrames[1];
var method = callingFrame.GetMethod();
//checking if the class type is GameManager
if (!method.DeclaringType.IsAssignableFrom(typeof(GameManager)))
{
throw new FieldAccessException("Only GameManager can set the background color of a PictureBox!");
}
_backColor = color;
}
public Color BackColor => _backColor;
}
public class Tile
{
public PictureBox tilePB { get; set; }
}
//example GameManager class
public class GameManager
{
public void SetBackground()
{
var someTile = new Tile()
{
tilePB = new PictureBox()
};
var someColor = new Color();
someTile.tilePB.SetBackColor(someColor);
}
}
//example class that may want to set picturebox background color
public class MaliciousClass
{
public void SetBackground()
{
var someTile = new Tile()
{
tilePB = new PictureBox()
};
var someColor = new Color();
someTile.tilePB.SetBackColor(someColor);
}
}
Then somewhere:
var gm = new GameManager();
var mc = new MaliciousClass();
gm.SetBackground(); //this is fine
mc.SetBackground(); //this will throw an exception
If you don't want to throw an exception or you want to do something different when "not authorized" class is trying to access the SetBackColor method then just replace throw new FieldAccessException() with return or whatever you want.
Bare in mind the approach presented here is inefficent and it just presents that in can be done at runtime and nothing more than that.
Not sure if this is exactly what you are looking for, but I made this quick test and it seems to be able to differentiate the calling class:
class Program
{
static void Main(string[] args)
{
Type1 something1 = new Type1();
Type2 something2 = new Type2();
something1.runTest();
something2.runTest();
Console.ReadKey();
}
public class Type1
{
public void runTest()
{
Testing.edit(this);
}
}
public class Type2
{
public void runTest()
{
Testing.edit(this);
}
}
public static class Testing
{
public static void edit(object obj)
{
// This is where you test the calling class to make sure
// it is allowed to edit.
Console.WriteLine(obj.GetType().ToString());
}
}
}
The only way I can think of where you enforce this at compile time, end up being a bit complicated. I don't think you'll want to do this.
You can create an interface with properties/methods for everything that only the GameManager is allowed to do. You can implement this interface in a private inner class below Tile, and make sure the only way this object is created is by passing in a GameManager that receives it. Now, the only way the access can 'leak' is if the GameManager 'gives away' the object.
public class GameManager {
public void AddTile(Tile t, Tile.IManagerHook m) {
m.SomeProperty = "set from manager";
}
}
public class Tile
{
public object SomeProperty { get; private set; }
public Tile(GameManager manager) {
manager.AddTile(this, new ManagerHook(this));
}
public interface IManagerHook {
object SomeProperty {get; set;}
}
private class ManagerHook : IManagerHook {
private Tile _tile;
public ManagerHook(Tile t) {
_tile = t;
}
public object SomeProperty {
get{ return _tile.SomeProperty;}
set { _tile.SomeProperty = value; }
}
}
}
(seems) Simply not possible
After asking several programmers, the way I have coded everything and what I want seems to be simply impossible without immensely complicated code - to the point you are better off refacturing everything. Since class Gamemanager is a static class, there will be no instances of it so you can not check if the 'object' that called it is of class Gamemanager. this also doesn't work since Gamemanager is, agian, static.
I have some trouble initializing strings from their constructor when a new List element is created from the Unity inspector while in edit mode.
All it does is writing "Call test" to console, but not initializing its name and description to the placeholder texts.
public class StatsManager : MonoBehaviour {
[System.Serializable]
public class StatValue {
public string name;
public string description;
public int currentValue;
public StatValue(){
this.name = "Times hit";
this.description = "This is how much you have been hit since the start of the game.";
Debug.Log("Call test");
}
}
[SerializeField]
public List<StatsManager.StatValue> stats;
}
Ok I found a solution in case somebody has the same problem. No need of cTor or ISerializableCallbackReceiver. This uses the Reset Callback (from the coq) that will also call at instanciation. Cheers!
[System.Serializeable]
public class Test {
int value = 500;
}
public class TestManager : MonoBehaviour {
public Test testInt;
public List<Test> testInts;
void Reset(){
testInts = new List<Test>(){
new Test()
};
}
}
from https://answers.unity.com/questions/1467289/default-value-on-list-vanished.html?childToView=1467368#comment-1467368