JuanLouTech
JuanLouTech4w ago

What is the best way to keep in sync a variable in the form of float array?

I need to keep in sync a float array of two values in a custom model. I need this to go always together since one of the values is a very precise timestamp that I need to keep associated with the other one.
16 Replies
kapa.ai
kapa.ai4w ago
Hi @JuanLouTech. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To keep a float array synchronized in a custom model in NormalVR, you can create a custom realtime component and model. This is done by creating a script for the component and one for the model. In your case, you can create a model that holds your float array and the associated timestamp. You can mark the property as reliable if you want to ensure that the value is synchronized between all clients and that it is in sync before any newer reliable updates are applied to the datastore. This is especially useful for data that you update once and that should be resent if the packet is dropped in transit. Here is an example of how you can create a custom model:
using Normal.Realtime;
using Normal.Realtime.Serialization;

[RealtimeModel]
public class CustomModel {
[RealtimeProperty(1, true)] private float[] _floatArray;
[RealtimeProperty(2, true)] private double _timestamp;
}
using Normal.Realtime;
using Normal.Realtime.Serialization;

[RealtimeModel]
public class CustomModel {
[RealtimeProperty(1, true)] private float[] _floatArray;
[RealtimeProperty(2, true)] private double _timestamp;
}
And here is an example of how you can create a custom realtime component:
using Normal.Realtime;

public class CustomComponent : RealtimeComponent<CustomModel> {
public float[] floatArray {
get {
// Return null if we're not connected to the room yet.
if (model == null) return null;

return model.floatArray;
}
}

public double timestamp {
get {
// Return 0 if we're not connected to the room yet.
if (model == null) return 0.0;

return model.timestamp;
}
}
}
using Normal.Realtime;

public class CustomComponent : RealtimeComponent<CustomModel> {
public float[] floatArray {
get {
// Return null if we're not connected to the room yet.
if (model == null) return null;

return model.floatArray;
}
}

public double timestamp {
get {
// Return 0 if we're not connected to the room yet.
if (model == null) return 0.0;

return model.timestamp;
}
}
}
In this example, the CustomComponent class has two properties: floatArray and timestamp. These properties return the values from the CustomModel instance. If the model is not yet connected to the room, they return default values. Please refer to the Normcore Documentation (https://normcore.io/documentation/realtime/synchronizing-custom-data) for more details on how to synchronize custom data. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai4w ago
<@941753147938984038> kapa.ai is still learning and improving, please let us know how it did by reacting below
JuanLouTech
JuanLouTech4w ago
@Normcore Assistant , but I'm getting an error because the float[] is not natively serialized
kapa.ai
kapa.ai4w ago
Let me check the knowledge base for you. One moment! :books: ---------------------- In Normcore, only primitive types (int, float, Vector3, string, etc.) can be serialized. If you're trying to sync a specific kind of object or a complex data type like a float array, you should create a field for each property on the object that you'd like to sync. In your case, since you have a float array of two values, you can create two separate float fields in your model. Here's an example:
using Normal.Realtime;
using Normal.Realtime.Serialization;

[RealtimeModel]
public class CustomModel {
[RealtimeProperty(1, true)] private float _floatValue1;
[RealtimeProperty(2, true)] private float _floatValue2;
[RealtimeProperty(3, true)] private double _timestamp;
}
using Normal.Realtime;
using Normal.Realtime.Serialization;

[RealtimeModel]
public class CustomModel {
[RealtimeProperty(1, true)] private float _floatValue1;
[RealtimeProperty(2, true)] private float _floatValue2;
[RealtimeProperty(3, true)] private double _timestamp;
}
And in your component:
using Normal.Realtime;

public class CustomComponent : RealtimeComponent<CustomModel> {
public float floatValue1 {
get {
// Return 0 if we're not connected to the room yet.
if (model == null) return 0.0f;

return model.floatValue1;
}
}

public float floatValue2 {
get {
// Return 0 if we're not connected to the room yet.
if (model == null) return 0.0f;

return model.floatValue2;
}
}

public double timestamp {
get {
// Return 0 if we're not connected to the room yet.
if (model == null) return 0.0;

return model.timestamp;
}
}
}
using Normal.Realtime;

public class CustomComponent : RealtimeComponent<CustomModel> {
public float floatValue1 {
get {
// Return 0 if we're not connected to the room yet.
if (model == null) return 0.0f;

return model.floatValue1;
}
}

public float floatValue2 {
get {
// Return 0 if we're not connected to the room yet.
if (model == null) return 0.0f;

return model.floatValue2;
}
}

public double timestamp {
get {
// Return 0 if we're not connected to the room yet.
if (model == null) return 0.0;

return model.timestamp;
}
}
}
This way, you can keep your two float values and the associated timestamp in sync. For more information, please refer to the Normcore Documentation (https://normcore.io/documentation/room/common-questions#when-i-try-to-sync-a-specific-object-to-the-datastore-i-get-an-error-that-says-it-cannot-be-serialized-whats-the-issue). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai4w ago
<@941753147938984038> kapa.ai is still learning and improving, please let us know how it did by reacting below
JuanLouTech
JuanLouTech4w ago
@Normcore Assistant Would Vector2 work natively?
kapa.ai
kapa.ai4w ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Yes, Vector2 is one of the primitive types that can be natively serialized in Normcore. You can use a Vector2 to store your two float values. Here's an example:
using UnityEngine;
using Normal.Realtime;
using Normal.Realtime.Serialization;

[RealtimeModel]
public class CustomModel {
[RealtimeProperty(1, true)] private Vector2 _floatValues;
[RealtimeProperty(2, true)] private double _timestamp;
}
using UnityEngine;
using Normal.Realtime;
using Normal.Realtime.Serialization;

[RealtimeModel]
public class CustomModel {
[RealtimeProperty(1, true)] private Vector2 _floatValues;
[RealtimeProperty(2, true)] private double _timestamp;
}
And in your component:
using UnityEngine;
using Normal.Realtime;

public class CustomComponent : RealtimeComponent<CustomModel> {
public Vector2 floatValues {
get {
// Return Vector2.zero if we're not connected to the room yet.
if (model == null) return Vector2.zero;

return model.floatValues;
}
}

public double timestamp {
get {
// Return 0 if we're not connected to the room yet.
if (model == null) return 0.0;

return model.timestamp;
}
}
}
using UnityEngine;
using Normal.Realtime;

public class CustomComponent : RealtimeComponent<CustomModel> {
public Vector2 floatValues {
get {
// Return Vector2.zero if we're not connected to the room yet.
if (model == null) return Vector2.zero;

return model.floatValues;
}
}

public double timestamp {
get {
// Return 0 if we're not connected to the room yet.
if (model == null) return 0.0;

return model.timestamp;
}
}
}
This way, you can keep your two float values and the associated timestamp in sync. For more information, please refer to the Normcore Documentation (https://normcore.io/documentation/room/common-questions#when-i-try-to-sync-a-specific-object-to-the-datastore-i-get-an-error-that-says-it-cannot-be-serialized-whats-the-issue). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai4w ago
<@941753147938984038> kapa.ai is still learning and improving, please let us know how it did by reacting below
JuanLouTech
JuanLouTech4w ago
@Normcore Assistant , this is not working either. I'm getting this error whe ntrying to compile the model : ArgumentException: Unsupported serialization type: System.Single[] (Field: _animationTriggerHash). Property must be a primitive type or an object that inherits the RealtimeModel class. Normal.Realtime.RealtimeModelCompiler.GetModelLayoutForClass (System.Type modelClass) (at <2fafecb97aaa43138ec971f0d51fa4d7>:0) Normal.Realtime.RealtimeModelCompiler.GenerateModel (UnityEditor.MonoScript script) (at <2fafecb97aaa43138ec971f0d51fa4d7>:0) Normal.Realtime.RealtimeModelCompiler.TryGenerateModel (UnityEditor.MonoScript script) (at <2fafecb97aaa43138ec971f0d51fa4d7>:0) UnityEngine.Debug:LogException(Exception) Normal.Realtime.RealtimeModelCompiler:TryGenerateModel(MonoScript) Normal.Realtime.Serialization.ModelEditor:OnInspectorGUI() UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr, Boolean&) The _animationTriggerHash property is defined as a reliable Vector2 in the model. Since I'm on the 3.0.0 Webgl-alpha this might be a version problem.
kapa.ai
kapa.ai4w ago
Let me check the knowledge base for you. One moment! :books: ---------------------- I'm sorry for the confusion. It seems there might be a misunderstanding. In Normcore, Vector2 is not a supported type for serialization. The supported types are int, uint, bool, string, float, double, Color, Vector3, and Quaternion. If you need to synchronize a pair of float values, you should create two separate float fields in your model. Here's an example:
using Normal.Realtime;
using Normal.Realtime.Serialization;

[RealtimeModel]
public partial class CustomModel {
[RealtimeProperty(1, true)] private float _floatValue1;
[RealtimeProperty(2, true)] private float _floatValue2;
[RealtimeProperty(3, true)] private double _timestamp;
}
using Normal.Realtime;
using Normal.Realtime.Serialization;

[RealtimeModel]
public partial class CustomModel {
[RealtimeProperty(1, true)] private float _floatValue1;
[RealtimeProperty(2, true)] private float _floatValue2;
[RealtimeProperty(3, true)] private double _timestamp;
}
And in your component:
using Normal.Realtime;

public class CustomComponent : RealtimeComponent<CustomModel> {
public float floatValue1 {
get {
// Return 0 if we're not connected to the room yet.
if (model == null) return 0.0f;

return model.floatValue1;
}
}

public float floatValue2 {
get {
// Return 0 if we're not connected to the room yet.
if (model == null) return 0.0f;

return model.floatValue2;
}
}

public double timestamp {
get {
// Return 0 if we're not connected to the room yet.
if (model == null) return 0.0;

return model.timestamp;
}
}
}
using Normal.Realtime;

public class CustomComponent : RealtimeComponent<CustomModel> {
public float floatValue1 {
get {
// Return 0 if we're not connected to the room yet.
if (model == null) return 0.0f;

return model.floatValue1;
}
}

public float floatValue2 {
get {
// Return 0 if we're not connected to the room yet.
if (model == null) return 0.0f;

return model.floatValue2;
}
}

public double timestamp {
get {
// Return 0 if we're not connected to the room yet.
if (model == null) return 0.0;

return model.timestamp;
}
}
}
This way, you can keep your two float values and the associated timestamp in sync. For more information, please refer to the Normcore Documentation (https://normcore.io/documentation/room/realtimemodel). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai4w ago
<@941753147938984038> kapa.ai is still learning and improving, please let us know how it did by reacting below
NormalMark
NormalMark4w ago
Does this model work?
c#
using UnityEngine;

[RealtimeModel]
public partial class TestModel
{
[RealtimeProperty(1, true, true)]
private Vector2 _testVector2;
}
c#
using UnityEngine;

[RealtimeModel]
public partial class TestModel
{
[RealtimeProperty(1, true, true)]
private Vector2 _testVector2;
}
Ah just noticed you're on 3.0.0 Webgl-alpha Maybe there's a difference there
JuanLouTech
JuanLouTech4w ago
I ended up encoding the int and the float I need in a single double since Vector2 did not seem to work and Vector3 was a waste of bandwidth for what I wanted. It was a weird hack but worked as a charm. 🤷‍♂️
NormalMark
NormalMark4w ago
Fwiw I checked with our devs and it might be a quirk with the model compiler in 3.0.0 Webgl-alpha (since it's older than the current 2.9.5 regular Normcore). It's using the reflection data from the last successful compilation, float[] in this case. Before you can change the model property to Vector2, you need to make the project compile successfully again. This could involve commenting out the property for example. After which I'm confident Vector2 would work.
JuanLouTech
JuanLouTech4w ago
Thanks @NormalMark . It worked but I can't use it because one of the ints I need to send is an animation hash (32 bits) and when the Vector2 is received on the other clients it changed the value since floats can't keep 32 bit whole part of the number. I Also tried a walk around the "double" type precision problem (I was having some too with my hacky solution) using a long in which I store in the first 32 bits the animation hash and in the last 32 bits the timestamp (multiplying the time stamp by 1000 and casting it to an int, keeping the 3 decimals precision) . But it turns out longs are not serialized natively neither. I'd rather not use a string to encode this because of it's weight for network load but for now it seems the only truly reliable solution for what I need to achieve with the default supported types.
maxweisel
maxweisel4w ago
sorry to chime in late here but just put two float properties on your model. if you change them in the same frame they’ll always go out together and will be received together due to a legacy architecture thing the change events will fire as they’re deserialized so you may see it read one before the other, but if you wait for the whole model to deserialize that frame, they’ll both arrive at the same time and you won’t have to do all of this work to try to put them into the same field