[EditorBrowsable(EditorBrowsableState.Never)] // public only for tests public static void SerializeIntoWriter(NetworkWriter writer, Vector3 position, Quaternion rotation, Compression compressRotation, Vector3 scale) { // serialize position writer.WriteVector3(position); // serialize rotation // writing quaternion = 16 byte // writing euler angles = 12 byte // -> quaternion->euler->quaternion always works. // -> gimbal lock only occurs when adding. Vector3 euler = rotation.eulerAngles; if (compressRotation == Compression.None) { // write 3 floats = 12 byte writer.WriteSingle(euler.x); writer.WriteSingle(euler.y); writer.WriteSingle(euler.z); } else if (compressRotation == Compression.Much) { // write 3 byte. scaling [0,360] to [0,255] writer.WriteByte(FloatBytePacker.ScaleFloatToByte(euler.x, 0, 360, byte.MinValue, byte.MaxValue)); writer.WriteByte(FloatBytePacker.ScaleFloatToByte(euler.y, 0, 360, byte.MinValue, byte.MaxValue)); writer.WriteByte(FloatBytePacker.ScaleFloatToByte(euler.z, 0, 360, byte.MinValue, byte.MaxValue)); } else if (compressRotation == Compression.Lots) { // write 2 byte, 5 bits for each float writer.WriteUInt16(FloatBytePacker.PackThreeFloatsIntoUShort(euler.x, euler.y, euler.z, 0, 360)); } // serialize scale writer.WriteVector3(scale); }
public void TestScaleFloatToByte() { Assert.That(FloatBytePacker.ScaleFloatToByte(-1f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(0)); Assert.That(FloatBytePacker.ScaleFloatToByte(0f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(127)); Assert.That(FloatBytePacker.ScaleFloatToByte(0.5f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(191)); Assert.That(FloatBytePacker.ScaleFloatToByte(1f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(255)); }
public void ScaleByteToFloat() { Assert.That(FloatBytePacker.ScaleByteToFloat(0, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(-1).Within(0.0001f)); Assert.That(FloatBytePacker.ScaleByteToFloat(127, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(-0.003921569f).Within(0.0001f)); Assert.That(FloatBytePacker.ScaleByteToFloat(191, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(0.4980392f).Within(0.0001f)); Assert.That(FloatBytePacker.ScaleByteToFloat(255, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(1).Within(0.0001f)); }
// serialization is needed by OnSerialize and by manual sending from authority void DeserializeFromReader(NetworkReader reader) { // put it into a data point immediately DataPoint temp = new DataPoint { // deserialize position localPosition = reader.ReadVector3() }; // deserialize rotation if (compressRotation == Compression.None) { // read 3 floats = 16 byte float x = reader.ReadSingle(); float y = reader.ReadSingle(); float z = reader.ReadSingle(); temp.localRotation = Quaternion.Euler(x, y, z); } else if (compressRotation == Compression.Much) { // read 3 byte. scaling [0,255] to [0,360] float x = FloatBytePacker.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360); float y = FloatBytePacker.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360); float z = FloatBytePacker.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360); temp.localRotation = Quaternion.Euler(x, y, z); } else if (compressRotation == Compression.Lots) { // read 2 byte, 5 bits per float Vector3 xyz = FloatBytePacker.UnpackUShortIntoThreeFloats(reader.ReadUInt16(), 0, 360); temp.localRotation = Quaternion.Euler(xyz.x, xyz.y, xyz.z); } temp.localScale = reader.ReadVector3(); temp.timeStamp = Time.time; // movement speed: based on how far it moved since last time // has to be calculated before 'start' is overwritten temp.movementSpeed = EstimateMovementSpeed(goal, temp, targetComponent.transform, syncInterval); // reassign start wisely // -> first ever data point? then make something up for previous one // so that we can start interpolation without waiting for next. if (start == null) { start = new DataPoint { timeStamp = Time.time - syncInterval, // local position/rotation for VR support localPosition = targetComponent.transform.localPosition, localRotation = targetComponent.transform.localRotation, localScale = targetComponent.transform.localScale, movementSpeed = temp.movementSpeed }; } // -> second or nth data point? then update previous, but: // we start at where ever we are right now, so that it's // perfectly smooth and we don't jump anywhere // // example if we are at 'x': // // A--x->B // // and then receive a new point C: // // A--x--B // | // | // C // // then we don't want to just jump to B and start interpolation: // // x // | // | // C // // we stay at 'x' and interpolate from there to C: // // x..B // \ . // \. // C // else { float oldDistance = Vector3.Distance(start.localPosition, goal.localPosition); float newDistance = Vector3.Distance(goal.localPosition, temp.localPosition); start = goal; // teleport / lag / obstacle detection: only continue at current // position if we aren't too far away // // // local position/rotation for VR support if (Vector3.Distance(targetComponent.transform.localPosition, start.localPosition) < oldDistance + newDistance) { start.localPosition = targetComponent.transform.localPosition; start.localRotation = targetComponent.transform.localRotation; start.localScale = targetComponent.transform.localScale; } } // set new destination in any case. new data is best data. goal = temp; }