Веб демо под спойлером 8 мб
Скрытый текст:
исходник 80 мб http://dfiles.ru/files/hn4jnq3g5 версия Unity Free 3.5.5 (25.05.13)
build под Win 20 mb http://dfiles.ru/files/kgojt0tzx предположительно с голосовым чатом 25.05.13
И еще интересно что фотон перевел стандартный юнити скрипт этот NetworkRigidbogy который служит для сглаживания движения- интерполяции. И он перевел его сам идеально - мне даже править не пришлось. Интерполяция в игре на машинах работает идеально. получилось типа такого
NetworkRigidbody.CS
Синтаксис:
Используется csharp
using UnityEngine;
using System.Collections;
public class NetworkRigidbody : Photon.MonoBehaviour {
public double m_InterpolationBackTime = 0.1;
public double m_ExtrapolationLimit = 0.5;
internal struct State
{
internal double timestamp;
internal Vector3 pos;
internal Vector3 velocity;
internal Quaternion rot;
internal Vector3 angularVelocity;
}
// We store twenty states with "playback" information
State[] m_BufferedState = new State[20];
// Keep track of what slots are used
int m_TimestampCount;
void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
// Send data to server
if (stream.isWriting)
{
Vector3 pos = rigidbody.position;
Quaternion rot = rigidbody.rotation;
Vector3 velocity = rigidbody.velocity;
Vector3 angularVelocity = rigidbody.angularVelocity;
stream.Serialize(ref pos);
stream.Serialize(ref velocity);
stream.Serialize(ref rot);
stream.Serialize(ref angularVelocity);
}
// Read data from remote client
else
{
Vector3 pos = Vector3.zero;
Vector3 velocity = Vector3.zero;
Quaternion rot = Quaternion.identity;
Vector3 angularVelocity = Vector3.zero;
stream.Serialize(ref pos);
stream.Serialize(ref velocity);
stream.Serialize(ref rot);
stream.Serialize(ref angularVelocity);
// Shift the buffer sideways, deleting state 20
for (int i=m_BufferedState.Length-1;i>=1;i--)
{
m_BufferedState[i] = m_BufferedState[i-1];
}
// Record current state in slot 0
State state;
state.timestamp = info.timestamp;
state.pos = pos;
state.velocity = velocity;
state.rot = rot;
state.angularVelocity = angularVelocity;
m_BufferedState[0] = state;
// Update used slot count, however never exceed the buffer size
// Slots aren't actually freed so this just makes sure the buffer is
// filled up and that uninitalized slots aren't used.
m_TimestampCount = Mathf.Min(m_TimestampCount + 1, m_BufferedState.Length);
// Check if states are in order, if it is inconsistent you could reshuffel or
// drop the out-of-order state. Nothing is done here
for (int i=0;i<m_TimestampCount-1;i++)
{
if (m_BufferedState[i].timestamp < m_BufferedState[i+1].timestamp)
Debug.Log("State inconsistent");
}
}
}
// We have a window of interpolationBackTime where we basically play
// By having interpolationBackTime the average ping, you will usually use interpolation.
// And only if no more data arrives we will use extra polation
void Update () {
// This is the target playback time of the rigid body
double interpolationTime = PhotonNetwork.time - m_InterpolationBackTime;
// Use interpolation if the target playback time is present in the buffer
if (m_BufferedState[0].timestamp > interpolationTime)
{
// Go through buffer and find correct state to play back
for (int i=0;i<m_TimestampCount;i++)
{
if (m_BufferedState[i].timestamp <= interpolationTime || i == m_TimestampCount-1)
{
// The state one slot newer (<100ms) than the best playback state
State rhs = m_BufferedState[Mathf.Max(i-1, 0)];
// The best playback state (closest to 100 ms old (default time))
State lhs = m_BufferedState[i];
// Use the time between the two slots to determine if interpolation is necessary
double length = rhs.timestamp - lhs.timestamp;
float t = 0.0F;
// As the time difference gets closer to 100 ms t gets closer to 1 in
// which case rhs is only used
// Example:
// Time is 10.000, so sampleTime is 9.900
// lhs.time is 9.910 rhs.time is 9.980 length is 0.070
// t is 9.900 - 9.910 / 0.070 = 0.14. So it uses 14% of rhs, 86% of lhs
if (length > 0.0001)
t = (float)((interpolationTime - lhs.timestamp) / length);
// if t=0 => lhs is used directly
transform.localPosition = Vector3.Lerp(lhs.pos, rhs.pos, t);
transform.localRotation = Quaternion.Slerp(lhs.rot, rhs.rot, t);
return;
}
}
}
// Use extrapolation
else
{
State latest = m_BufferedState[0];
float extrapolationLength = (float)(interpolationTime - latest.timestamp);
// Don't extrapolation for more than 500 ms, you would need to do that carefully
if (extrapolationLength < m_ExtrapolationLimit)
{
float axisLength = extrapolationLength * latest.angularVelocity.magnitude * Mathf.Rad2Deg;
Quaternion angularRotation = Quaternion.AngleAxis(axisLength, latest.angularVelocity);
rigidbody.position = latest.pos + latest.velocity * extrapolationLength;
rigidbody.rotation = angularRotation * latest.rot;
rigidbody.velocity = latest.velocity;
rigidbody.angularVelocity = latest.angularVelocity;
}
}
}
}
using System.Collections;
public class NetworkRigidbody : Photon.MonoBehaviour {
public double m_InterpolationBackTime = 0.1;
public double m_ExtrapolationLimit = 0.5;
internal struct State
{
internal double timestamp;
internal Vector3 pos;
internal Vector3 velocity;
internal Quaternion rot;
internal Vector3 angularVelocity;
}
// We store twenty states with "playback" information
State[] m_BufferedState = new State[20];
// Keep track of what slots are used
int m_TimestampCount;
void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
// Send data to server
if (stream.isWriting)
{
Vector3 pos = rigidbody.position;
Quaternion rot = rigidbody.rotation;
Vector3 velocity = rigidbody.velocity;
Vector3 angularVelocity = rigidbody.angularVelocity;
stream.Serialize(ref pos);
stream.Serialize(ref velocity);
stream.Serialize(ref rot);
stream.Serialize(ref angularVelocity);
}
// Read data from remote client
else
{
Vector3 pos = Vector3.zero;
Vector3 velocity = Vector3.zero;
Quaternion rot = Quaternion.identity;
Vector3 angularVelocity = Vector3.zero;
stream.Serialize(ref pos);
stream.Serialize(ref velocity);
stream.Serialize(ref rot);
stream.Serialize(ref angularVelocity);
// Shift the buffer sideways, deleting state 20
for (int i=m_BufferedState.Length-1;i>=1;i--)
{
m_BufferedState[i] = m_BufferedState[i-1];
}
// Record current state in slot 0
State state;
state.timestamp = info.timestamp;
state.pos = pos;
state.velocity = velocity;
state.rot = rot;
state.angularVelocity = angularVelocity;
m_BufferedState[0] = state;
// Update used slot count, however never exceed the buffer size
// Slots aren't actually freed so this just makes sure the buffer is
// filled up and that uninitalized slots aren't used.
m_TimestampCount = Mathf.Min(m_TimestampCount + 1, m_BufferedState.Length);
// Check if states are in order, if it is inconsistent you could reshuffel or
// drop the out-of-order state. Nothing is done here
for (int i=0;i<m_TimestampCount-1;i++)
{
if (m_BufferedState[i].timestamp < m_BufferedState[i+1].timestamp)
Debug.Log("State inconsistent");
}
}
}
// We have a window of interpolationBackTime where we basically play
// By having interpolationBackTime the average ping, you will usually use interpolation.
// And only if no more data arrives we will use extra polation
void Update () {
// This is the target playback time of the rigid body
double interpolationTime = PhotonNetwork.time - m_InterpolationBackTime;
// Use interpolation if the target playback time is present in the buffer
if (m_BufferedState[0].timestamp > interpolationTime)
{
// Go through buffer and find correct state to play back
for (int i=0;i<m_TimestampCount;i++)
{
if (m_BufferedState[i].timestamp <= interpolationTime || i == m_TimestampCount-1)
{
// The state one slot newer (<100ms) than the best playback state
State rhs = m_BufferedState[Mathf.Max(i-1, 0)];
// The best playback state (closest to 100 ms old (default time))
State lhs = m_BufferedState[i];
// Use the time between the two slots to determine if interpolation is necessary
double length = rhs.timestamp - lhs.timestamp;
float t = 0.0F;
// As the time difference gets closer to 100 ms t gets closer to 1 in
// which case rhs is only used
// Example:
// Time is 10.000, so sampleTime is 9.900
// lhs.time is 9.910 rhs.time is 9.980 length is 0.070
// t is 9.900 - 9.910 / 0.070 = 0.14. So it uses 14% of rhs, 86% of lhs
if (length > 0.0001)
t = (float)((interpolationTime - lhs.timestamp) / length);
// if t=0 => lhs is used directly
transform.localPosition = Vector3.Lerp(lhs.pos, rhs.pos, t);
transform.localRotation = Quaternion.Slerp(lhs.rot, rhs.rot, t);
return;
}
}
}
// Use extrapolation
else
{
State latest = m_BufferedState[0];
float extrapolationLength = (float)(interpolationTime - latest.timestamp);
// Don't extrapolation for more than 500 ms, you would need to do that carefully
if (extrapolationLength < m_ExtrapolationLimit)
{
float axisLength = extrapolationLength * latest.angularVelocity.magnitude * Mathf.Rad2Deg;
Quaternion angularRotation = Quaternion.AngleAxis(axisLength, latest.angularVelocity);
rigidbody.position = latest.pos + latest.velocity * extrapolationLength;
rigidbody.rotation = angularRotation * latest.rot;
rigidbody.velocity = latest.velocity;
rigidbody.angularVelocity = latest.angularVelocity;
}
}
}
}
Но что самое интересное - мне кажется что он переконвертировал скрипт голосового чата с офф форума и кажется он работает. Я просто его щас не могу протестировать на 2 каналах. У меня только 1 канал интернета. Но в билде что я прилагаю он может работать. Вот получается скрипт для голосового чата на фотоне - вешать именно на персонажа - расчитан на инстанс и на персонаже должен быть аудио ресурс.
MicTest.CS
Синтаксис:
Используется csharp
// sript of voice chat take from official forum thread
//turn on of this script see in script "IsChild"
using UnityEngine;
using System.Collections;
public class MicTest : Photon.MonoBehaviour {
int lastSample;
AudioClip c;
// int FREQUENCY = 44100;
void Start () {
if (photonView.isMine) {
c = Microphone.Start(null, true, 100, 8000);
while(Microphone.GetPosition(null) < 0) {} // HACK from Riro
}
}
void Update () {
if (photonView.isMine) {
int pos = Microphone.GetPosition(null);
int diff = pos-lastSample;
if (diff > 0) {
float[] samples = new float[diff * c.channels];
c.GetData(samples, lastSample);
byte[] ba = ToByteArray(samples);
photonView.RPC("Send", PhotonTargets.Others, ba, c.channels);
}
lastSample = pos;
}
}
[RPC]
public void Send(byte[] ba, int chan) {
float[] f = ToFloatArray(ba);
audio.clip = AudioClip.Create("test", f.Length, chan, 8000,true,false);
audio.clip.SetData(f, 0);
if (!audio.isPlaying) audio.Play();
}
public byte[] ToByteArray(float[] floatArray) {
int len = floatArray.Length * 4;
byte[] byteArray = new byte[len];
int pos = 0;
foreach (float f in floatArray) {
byte[] data = System.BitConverter.GetBytes(f);
System.Array.Copy(data, 0, byteArray, pos, 4);
pos += 4;
}
return byteArray;
}
public float[] ToFloatArray(byte[] byteArray) {
int len = byteArray.Length / 4;
float[] floatArray = new float[len];
for (int i = 0; i < byteArray.Length; i+=4) {
floatArray[i/4] = System.BitConverter.ToSingle(byteArray, i);
}
return floatArray;
}
}
//turn on of this script see in script "IsChild"
using UnityEngine;
using System.Collections;
public class MicTest : Photon.MonoBehaviour {
int lastSample;
AudioClip c;
// int FREQUENCY = 44100;
void Start () {
if (photonView.isMine) {
c = Microphone.Start(null, true, 100, 8000);
while(Microphone.GetPosition(null) < 0) {} // HACK from Riro
}
}
void Update () {
if (photonView.isMine) {
int pos = Microphone.GetPosition(null);
int diff = pos-lastSample;
if (diff > 0) {
float[] samples = new float[diff * c.channels];
c.GetData(samples, lastSample);
byte[] ba = ToByteArray(samples);
photonView.RPC("Send", PhotonTargets.Others, ba, c.channels);
}
lastSample = pos;
}
}
[RPC]
public void Send(byte[] ba, int chan) {
float[] f = ToFloatArray(ba);
audio.clip = AudioClip.Create("test", f.Length, chan, 8000,true,false);
audio.clip.SetData(f, 0);
if (!audio.isPlaying) audio.Play();
}
public byte[] ToByteArray(float[] floatArray) {
int len = floatArray.Length * 4;
byte[] byteArray = new byte[len];
int pos = 0;
foreach (float f in floatArray) {
byte[] data = System.BitConverter.GetBytes(f);
System.Array.Copy(data, 0, byteArray, pos, 4);
pos += 4;
}
return byteArray;
}
public float[] ToFloatArray(byte[] byteArray) {
int len = byteArray.Length / 4;
float[] floatArray = new float[len];
for (int i = 0; i < byteArray.Length; i+=4) {
floatArray[i/4] = System.BitConverter.ToSingle(byteArray, i);
}
return floatArray;
}
}