fishnet installed
This commit is contained in:
@ -0,0 +1,59 @@
|
||||
using FishNet;
|
||||
using FishNet.Object;
|
||||
using FishNet.Object.Synchronizing;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class BulletTest : NetworkBehaviour
|
||||
{
|
||||
[SyncObject]
|
||||
public readonly SyncList<int> SyncListNumbers = new SyncList<int>();
|
||||
|
||||
[SyncVar]
|
||||
public Vector3 SyncVarScale = Vector3.one;
|
||||
|
||||
public float Force;
|
||||
|
||||
public void SendSpeed()
|
||||
{
|
||||
//Debug.Log("Local tick " + InstanceFinder.TimeManager.LocalTick);
|
||||
Rigidbody rb = GetComponent<Rigidbody>();
|
||||
rb.AddForce((transform.forward * Force), ForceMode.Impulse);
|
||||
}
|
||||
|
||||
public override void OnStartNetwork()
|
||||
{
|
||||
base.OnStartNetwork();
|
||||
transform.localScale = SyncVarScale;
|
||||
|
||||
Debug.Log("Reading numbers");
|
||||
foreach (var item in SyncListNumbers)
|
||||
Debug.Log(item);
|
||||
|
||||
}
|
||||
|
||||
public override void OnStartServer()
|
||||
{
|
||||
base.OnStartServer();
|
||||
SendSpeed();
|
||||
StartCoroutine(__DelayDestroy(3f));
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (base.TimeManager == null)
|
||||
return;
|
||||
if (base.TimeManager.FrameTicked)
|
||||
{
|
||||
// Debug.Log(_scale);
|
||||
//Rigidbody rb = GetComponent<Rigidbody>();
|
||||
//Debug.Log(rb.velocity.z);
|
||||
}
|
||||
}
|
||||
private IEnumerator __DelayDestroy(float time)
|
||||
{
|
||||
yield return new WaitForSeconds(time);
|
||||
base.Despawn();
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fc7792d9dc338aa4dbc8a2f0533cf6ec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,106 @@
|
||||
using FishNet.Component.Prediction;
|
||||
using FishNet.Object;
|
||||
using FishNet.Object.Synchronizing;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
public class PredictedBullet : NetworkBehaviour
|
||||
{
|
||||
//SyncVar to set spawn force. This is set by predicted spawner and sent to the server.
|
||||
[SyncVar(OnChange = nameof(_startingForce_OnChange))]
|
||||
private Vector3 _startingForce;
|
||||
//Tick to set rb to kinematic.
|
||||
private uint _stopTick = uint.MaxValue;
|
||||
|
||||
public void SetStartingForce(Vector3 value)
|
||||
{
|
||||
/* If the object is not yet initialized then
|
||||
* this is being set prior to network spawning.
|
||||
* Such a scenario occurs because the client which is
|
||||
* predicted spawning sets the synctype value before network
|
||||
* spawning to ensure the server receives the value.
|
||||
* Just as when the server sets synctypes, if they are set
|
||||
* before the object is spawned it's gauranteed clients will
|
||||
* get the value in the spawn packet; same practice is used here. */
|
||||
if (!base.IsSpawned)
|
||||
SetVelocity(value);
|
||||
|
||||
_startingForce = value;
|
||||
}
|
||||
|
||||
//Simple delay destroy so object does not exist forever.
|
||||
public override void OnStartServer()
|
||||
{
|
||||
base.OnStartServer();
|
||||
StartCoroutine(__DelayDestroy(3f));
|
||||
}
|
||||
|
||||
public override void OnStartNetwork()
|
||||
{
|
||||
base.OnStartNetwork();
|
||||
uint timeToTicks = base.TimeManager.TimeToTicks(0.65f);
|
||||
/* If server or predicted spawner then add the kinematic
|
||||
* tick onto local. Predicted spawner and server should behave
|
||||
* as though no time has elapsed since this spawned. */
|
||||
if (base.IsServer || base.Owner.IsLocalClient)
|
||||
{
|
||||
_stopTick = base.TimeManager.LocalTick + timeToTicks;
|
||||
}
|
||||
/* If not server or a client that did not predicted spawn this
|
||||
* then calculate time passed and set kinematic tick to the same
|
||||
* amount in the future while subtracting already passed ticks. */
|
||||
else
|
||||
{
|
||||
uint passed = (uint)Mathf.Max(1, base.TimeManager.Tick - base.TimeManager.LastPacketTick);
|
||||
long stopTick = (base.TimeManager.Tick + timeToTicks - passed - 1);
|
||||
if (stopTick > 0)
|
||||
_stopTick = (uint)stopTick;
|
||||
else
|
||||
_stopTick = 0;
|
||||
}
|
||||
|
||||
base.TimeManager.OnTick += TimeManager_OnTick;
|
||||
}
|
||||
|
||||
public override void OnStopNetwork()
|
||||
{
|
||||
base.OnStopNetwork();
|
||||
base.TimeManager.OnTick -= TimeManager_OnTick;
|
||||
}
|
||||
private void TimeManager_OnTick()
|
||||
{
|
||||
if (base.TimeManager.LocalTick >= _stopTick)
|
||||
{
|
||||
Rigidbody rb = GetComponent<Rigidbody>();
|
||||
rb.isKinematic = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When starting force changes set that velocity to the rigidbody.
|
||||
/// This is an example as how a predicted spawn can modify sync types for server and other clients.
|
||||
/// </summary>
|
||||
private void _startingForce_OnChange(Vector3 prev, Vector3 next, bool asServer)
|
||||
{
|
||||
SetVelocity(next);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets velocity of the rigidbody.
|
||||
/// </summary>
|
||||
public void SetVelocity(Vector3 value)
|
||||
{
|
||||
Rigidbody rb = GetComponent<Rigidbody>();
|
||||
rb.velocity = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroy object after time.
|
||||
/// </summary>
|
||||
private IEnumerator __DelayDestroy(float time)
|
||||
{
|
||||
yield return new WaitForSeconds(time);
|
||||
base.Despawn();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2878f245b0c06ce49b3e98d5984e8d41
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,285 @@
|
||||
using FishNet;
|
||||
using FishNet.Object;
|
||||
using FishNet.Object.Prediction;
|
||||
using FishNet.Transporting;
|
||||
using UnityEngine;
|
||||
|
||||
/*
|
||||
*
|
||||
* See TransformPrediction.cs for more detailed notes.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace FishNet.Example.Prediction.Rigidbodies
|
||||
{
|
||||
|
||||
public class RigidbodyPrediction : NetworkBehaviour
|
||||
{
|
||||
#region Types.
|
||||
public struct MoveData : IReplicateData
|
||||
{
|
||||
public bool Jump;
|
||||
public float Horizontal;
|
||||
public float Vertical;
|
||||
public MoveData(bool jump, float horizontal, float vertical)
|
||||
{
|
||||
Jump = jump;
|
||||
Horizontal = horizontal;
|
||||
Vertical = vertical;
|
||||
_tick = 0;
|
||||
}
|
||||
|
||||
private uint _tick;
|
||||
public void Dispose() { }
|
||||
public uint GetTick() => _tick;
|
||||
public void SetTick(uint value) => _tick = value;
|
||||
}
|
||||
public struct ReconcileData : IReconcileData
|
||||
{
|
||||
public Vector3 Position;
|
||||
public Quaternion Rotation;
|
||||
public Vector3 Velocity;
|
||||
public Vector3 AngularVelocity;
|
||||
public ReconcileData(Vector3 position, Quaternion rotation, Vector3 velocity, Vector3 angularVelocity)
|
||||
{
|
||||
Position = position;
|
||||
Rotation = rotation;
|
||||
Velocity = velocity;
|
||||
AngularVelocity = angularVelocity;
|
||||
_tick = 0;
|
||||
}
|
||||
|
||||
private uint _tick;
|
||||
public void Dispose() { }
|
||||
public uint GetTick() => _tick;
|
||||
public void SetTick(uint value) => _tick = value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Serialized.
|
||||
[SerializeField]
|
||||
private float _jumpForce = 15f;
|
||||
[SerializeField]
|
||||
private float _moveRate = 15f;
|
||||
#endregion
|
||||
|
||||
#region Private.
|
||||
/// <summary>
|
||||
/// Rigidbody on this object.
|
||||
/// </summary>
|
||||
private Rigidbody _rigidbody;
|
||||
/// <summary>
|
||||
/// Next time a jump is allowed.
|
||||
/// </summary>
|
||||
private float _nextJumpTime;
|
||||
/// <summary>
|
||||
/// True to jump next frame.
|
||||
/// </summary>
|
||||
private bool _jump;
|
||||
#endregion
|
||||
|
||||
#region Predicted spawning.
|
||||
/// <summary>
|
||||
/// Prefab to spawn for predicted spawning.
|
||||
/// </summary>
|
||||
public NetworkObject BulletPrefab;
|
||||
/// <summary>
|
||||
/// True if a spawn is queued from input.
|
||||
/// </summary>
|
||||
private bool _spawnBullet;
|
||||
/// <summary>
|
||||
/// True if a despawn is queued from input.
|
||||
/// </summary>
|
||||
private bool _despawnBullet;
|
||||
/// <summary>
|
||||
/// Last spawned bullet. Used to test predicted despawn.
|
||||
/// </summary>
|
||||
private NetworkObject _lastSpawnedBullet;
|
||||
#endregion
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
|
||||
_rigidbody = GetComponent<Rigidbody>();
|
||||
InstanceFinder.TimeManager.OnTick += TimeManager_OnTick;
|
||||
InstanceFinder.TimeManager.OnPostTick += TimeManager_OnPostTick;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (InstanceFinder.TimeManager != null)
|
||||
{
|
||||
InstanceFinder.TimeManager.OnTick -= TimeManager_OnTick;
|
||||
InstanceFinder.TimeManager.OnPostTick -= TimeManager_OnPostTick;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnStartClient()
|
||||
{
|
||||
base.OnStartClient();
|
||||
base.PredictionManager.OnPreReplicateReplay += PredictionManager_OnPreReplicateReplay;
|
||||
}
|
||||
public override void OnStopClient()
|
||||
{
|
||||
base.OnStopClient();
|
||||
base.PredictionManager.OnPreReplicateReplay -= PredictionManager_OnPreReplicateReplay;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (base.IsOwner)
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.RightAlt))
|
||||
{
|
||||
_rigidbody.velocity = Vector3.zero;
|
||||
_rigidbody.angularVelocity = Vector3.zero;
|
||||
}
|
||||
if (Input.GetKeyDown(KeyCode.Space) && Time.time > _nextJumpTime)
|
||||
{
|
||||
_nextJumpTime = Time.time + 1f;
|
||||
_jump = true;
|
||||
}
|
||||
else if (Input.GetKeyDown(KeyCode.LeftShift))
|
||||
{
|
||||
_spawnBullet = true;
|
||||
}
|
||||
//else if (Input.GetKeyDown(KeyCode.LeftAlt))
|
||||
//{
|
||||
// _despawnBullet = true;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called every time any predicted object is replaying. Replays only occur for owner.
|
||||
/// Currently owners may only predict one object at a time.
|
||||
/// </summary>
|
||||
private void PredictionManager_OnPreReplicateReplay(uint arg1, PhysicsScene arg2, PhysicsScene2D arg3)
|
||||
{
|
||||
/* Server does not replay so it does
|
||||
* not need to add gravity. */
|
||||
if (!base.IsServer)
|
||||
AddGravity();
|
||||
}
|
||||
|
||||
|
||||
private void TimeManager_OnTick()
|
||||
{
|
||||
if (base.IsOwner)
|
||||
{
|
||||
Reconciliation(default, false);
|
||||
BuildMoveData(out MoveData md);
|
||||
Move(md, false);
|
||||
//Predicted spawning example.
|
||||
TryDespawnBullet();
|
||||
TrySpawnBullet();
|
||||
}
|
||||
if (base.IsServer)
|
||||
{
|
||||
Move(default, true);
|
||||
}
|
||||
|
||||
/* Server and all clients must add the additional gravity.
|
||||
* Adding gravity is not necessarily required in general but
|
||||
* to make jumps more snappy extra gravity is added per tick.
|
||||
* All clients and server need to simulate the gravity to keep
|
||||
* prediction equal across the network. */
|
||||
AddGravity();
|
||||
}
|
||||
|
||||
private void TimeManager_OnPostTick()
|
||||
{
|
||||
/* Reconcile is sent during PostTick because we
|
||||
* want to send the rb data AFTER the simulation. */
|
||||
if (base.IsServer)
|
||||
{
|
||||
ReconcileData rd = new ReconcileData(transform.position, transform.rotation, _rigidbody.velocity, _rigidbody.angularVelocity);
|
||||
Reconciliation(rd, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Builds a MoveData to use within replicate.
|
||||
/// </summary>
|
||||
/// <param name="md"></param>
|
||||
private void BuildMoveData(out MoveData md)
|
||||
{
|
||||
md = default;
|
||||
|
||||
float horizontal = Input.GetAxisRaw("Horizontal");
|
||||
float vertical = Input.GetAxisRaw("Vertical");
|
||||
|
||||
if (horizontal == 0f && vertical == 0f && !_jump)
|
||||
return;
|
||||
|
||||
md = new MoveData(_jump, horizontal, vertical);
|
||||
_jump = false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// PredictedObject example (unpolished)
|
||||
/// </summary>
|
||||
private void TrySpawnBullet()
|
||||
{
|
||||
if (_spawnBullet)
|
||||
{
|
||||
_spawnBullet = false;
|
||||
|
||||
NetworkObject nob = Instantiate(BulletPrefab, transform.position + (transform.forward * 1f), transform.rotation);
|
||||
//Set last spawned to test destroy with ALT key.
|
||||
_lastSpawnedBullet = nob;
|
||||
|
||||
//Set force to 100f at current forward.
|
||||
PredictedBullet bt = nob.GetComponent<PredictedBullet>();
|
||||
bt.SetStartingForce(transform.forward * 20f);
|
||||
//Spawn client side, which will send the predicted spawn to server.
|
||||
base.Spawn(nob, base.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PredictedObject example (unpolished)
|
||||
/// </summary>
|
||||
private void TryDespawnBullet()
|
||||
{
|
||||
if (_despawnBullet)
|
||||
{
|
||||
_despawnBullet = false;
|
||||
_lastSpawnedBullet?.Despawn();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds gravity to the rigidbody.
|
||||
/// </summary>
|
||||
private void AddGravity()
|
||||
{
|
||||
_rigidbody.AddForce(Physics.gravity * 2f);
|
||||
}
|
||||
|
||||
[Replicate]
|
||||
private void Move(MoveData md, bool asServer, Channel channel = Channel.Unreliable, bool replaying = false)
|
||||
{
|
||||
Vector3 forces = new Vector3(md.Horizontal, 0f, md.Vertical) * _moveRate;
|
||||
_rigidbody.AddForce(forces);
|
||||
|
||||
if (md.Jump)
|
||||
_rigidbody.AddForce(new Vector3(0f, _jumpForce, 0f), ForceMode.Impulse);
|
||||
}
|
||||
|
||||
[Reconcile]
|
||||
private void Reconciliation(ReconcileData rd, bool asServer, Channel channel = Channel.Unreliable)
|
||||
{
|
||||
transform.position = rd.Position;
|
||||
transform.rotation = rd.Rotation;
|
||||
_rigidbody.velocity = rd.Velocity;
|
||||
_rigidbody.angularVelocity = rd.AngularVelocity;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e123f937d4b4f94c9cb6cc03944f786
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user