The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). ## [2.0.0-exp.4] - 2024-05-31 ### Added - Added `NetworkRigidbodyBase.AttachToFixedJoint` and `NetworkRigidbodyBase.DetachFromFixedJoint` to replace parenting for rigid bodies that have `NetworkRigidbodyBase.UseRigidBodyForMotion` enabled. (#2933) - Added `NetworkBehaviour.OnNetworkPreSpawn` and `NetworkBehaviour.OnNetworkPostSpawn` methods that provide the ability to handle pre and post spawning actions during the `NetworkObject` spawn sequence. (#2912) - Added a client-side only `NetworkBehaviour.OnNetworkSessionSynchronized` convenience method that is invoked on all `NetworkBehaviour`s after a newly joined client has finished synchronizing with the network session in progress. (#2912) - Added `NetworkBehaviour.OnInSceneObjectsSpawned` convenience method that is invoked when all in-scene `NetworkObject`s have been spawned after a scene has been loaded or upon a host or server starting. (#2912) ### Fixed - Fixed issue where non-authoritative rigid bodies with `NetworkRigidbodyBase.UseRigidBodyForMotion` enabled would constantly log errors about the renderTime being before `StartTimeConsumed`. (#2933) - Fixed issue where in-scene placed NetworkObjects could be destroyed if a client disconnects early and/or before approval. (#2924) - Fixed issue where a `NetworkObject` component's associated `NetworkBehaviour` components would not be detected if scene loading is disabled in the editor and the currently loaded scene has in-scene placed `NetworkObject`s. (#2912) - Fixed issue where an in-scene placed `NetworkObject` with `NetworkTransform` that is also parented under a `GameObject` would not properly synchronize when the parent `GameObject` had a world space position other than 0,0,0. (#2898) ### Changed - Change all the access modifiers of test class from Public to Internal (#2930) - Changed messages are now sorted by enum values as opposed to ordinally sorting the messages by their type name. (#2929) - Changed `NetworkClient.SessionModeTypes` to `NetworkClient.NetworkTopologyTypes`. (#2875) - Changed `NetworkClient.SessionModeType` to `NetworkClient.NetworkTopologyType`. (#2875) - Changed `NetworkConfig.SessionMode` to `NeworkConfig.NetworkTopology`. (#2875)
723 lines
28 KiB
C#
723 lines
28 KiB
C#
#if COM_UNITY_MODULES_PHYSICS
|
|
using System.Runtime.CompilerServices;
|
|
using UnityEngine;
|
|
|
|
namespace Unity.Netcode.Components
|
|
{
|
|
/// <summary>
|
|
/// NetworkRigidbodyBase is a unified <see cref="Rigidbody"/> and <see cref="Rigidbody2D"/> integration that helps to synchronize physics motion, collision, and interpolation
|
|
/// when used with a <see cref="NetworkTransform"/>.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// For a customizable netcode Rigidbody, create your own component from this class and use <see cref="Initialize(RigidbodyTypes, NetworkTransform, Rigidbody2D, Rigidbody)"/>
|
|
/// during instantiation (i.e. invoked from within the Awake method). You can re-initialize after having initialized but only when the <see cref="NetworkObject"/> is not spawned.
|
|
/// </remarks>
|
|
public abstract class NetworkRigidbodyBase : NetworkBehaviour
|
|
{
|
|
/// <summary>
|
|
/// When enabled, the associated <see cref="NetworkTransform"/> will use the Rigidbody/Rigidbody2D to apply and synchronize changes in position, rotation, and
|
|
/// allows for the use of Rigidbody interpolation/extrapolation.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// If <see cref="NetworkTransform.Interpolate"/> is enabled, non-authoritative instances can only use Rigidbody interpolation. If a network prefab is set to
|
|
/// extrapolation and <see cref="NetworkTransform.Interpolate"/> is enabled, then non-authoritative instances will automatically be adjusted to use Rigidbody
|
|
/// interpolation while the authoritative instance will still use extrapolation.
|
|
/// </remarks>
|
|
[Tooltip("When enabled and a NetworkTransform component is attached, the NetworkTransform will use the rigid body for motion and detecting changes in state.")]
|
|
public bool UseRigidBodyForMotion;
|
|
|
|
/// <summary>
|
|
/// When enabled (default), automatically set the Kinematic state of the Rigidbody based on ownership.
|
|
/// When disabled, Kinematic state needs to be set by external script(s).
|
|
/// </summary>
|
|
public bool AutoUpdateKinematicState = true;
|
|
|
|
/// <summary>
|
|
/// Primarily applies to the <see cref="AutoUpdateKinematicState"/> property when disabled but you still want
|
|
/// the Rigidbody to be automatically set to Kinematic when despawned.
|
|
/// </summary>
|
|
public bool AutoSetKinematicOnDespawn = true;
|
|
|
|
// Determines if this is a Rigidbody or Rigidbody2D implementation
|
|
private bool m_IsRigidbody2D => RigidbodyType == RigidbodyTypes.Rigidbody2D;
|
|
// Used to cache the authority state of this Rigidbody during the last frame
|
|
private bool m_IsAuthority;
|
|
private Rigidbody m_Rigidbody;
|
|
private Rigidbody2D m_Rigidbody2D;
|
|
internal NetworkTransform NetworkTransform;
|
|
private enum InterpolationTypes
|
|
{
|
|
None,
|
|
Interpolate,
|
|
Extrapolate
|
|
}
|
|
private InterpolationTypes m_OriginalInterpolation;
|
|
|
|
/// <summary>
|
|
/// Used to define the type of Rigidbody implemented.
|
|
/// <see cref=""/>
|
|
/// </summary>
|
|
public enum RigidbodyTypes
|
|
{
|
|
Rigidbody,
|
|
Rigidbody2D,
|
|
}
|
|
|
|
public RigidbodyTypes RigidbodyType { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Initializes the networked Rigidbody based on the <see cref="RigidbodyTypes"/>
|
|
/// passed in as a parameter.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Cannot be initialized while the associated <see cref="NetworkObject"/> is spawned.
|
|
/// </remarks>
|
|
/// <param name="rigidbodyType">type of rigid body being initialized</param>
|
|
/// <param name="rigidbody2D">(optional) The <see cref="Rigidbody2D"/> to be used</param>
|
|
/// <param name="rigidbody">(optional) The <see cref="Rigidbody"/> to be used</param>
|
|
protected void Initialize(RigidbodyTypes rigidbodyType, NetworkTransform networkTransform = null, Rigidbody2D rigidbody2D = null, Rigidbody rigidbody = null)
|
|
{
|
|
// Don't initialize if already spawned
|
|
if (IsSpawned)
|
|
{
|
|
Debug.LogError($"[{name}] Attempting to initialize while spawned is not allowed.");
|
|
return;
|
|
}
|
|
RigidbodyType = rigidbodyType;
|
|
m_Rigidbody2D = rigidbody2D;
|
|
m_Rigidbody = rigidbody;
|
|
NetworkTransform = networkTransform;
|
|
|
|
if (m_IsRigidbody2D && m_Rigidbody2D == null)
|
|
{
|
|
m_Rigidbody2D = GetComponent<Rigidbody2D>();
|
|
|
|
}
|
|
else if (m_Rigidbody == null)
|
|
{
|
|
m_Rigidbody = GetComponent<Rigidbody>();
|
|
}
|
|
|
|
SetOriginalInterpolation();
|
|
|
|
if (NetworkTransform == null)
|
|
{
|
|
NetworkTransform = GetComponent<NetworkTransform>();
|
|
}
|
|
|
|
if (NetworkTransform != null)
|
|
{
|
|
NetworkTransform.RegisterRigidbody(this);
|
|
}
|
|
else
|
|
{
|
|
throw new System.Exception($"[Missing {nameof(NetworkTransform)}] No {nameof(NetworkTransform)} is assigned or can be found during initialization!");
|
|
}
|
|
|
|
if (AutoUpdateKinematicState)
|
|
{
|
|
SetIsKinematic(true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the position of the Rigidbody
|
|
/// </summary>
|
|
/// <returns><see cref="Vector3"/></returns>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Vector3 GetPosition()
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
return m_Rigidbody2D.position;
|
|
}
|
|
else
|
|
{
|
|
return m_Rigidbody.position;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the rotation of the Rigidbody
|
|
/// </summary>
|
|
/// <returns><see cref="Quaternion"/></returns>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Quaternion GetRotation()
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
var quaternion = Quaternion.identity;
|
|
var angles = quaternion.eulerAngles;
|
|
angles.z = m_Rigidbody2D.rotation;
|
|
quaternion.eulerAngles = angles;
|
|
return quaternion;
|
|
}
|
|
else
|
|
{
|
|
return m_Rigidbody.rotation;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Moves the rigid body
|
|
/// </summary>
|
|
/// <param name="position">The <see cref="Vector3"/> position to move towards</param>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void MovePosition(Vector3 position)
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
m_Rigidbody2D.MovePosition(position);
|
|
}
|
|
else
|
|
{
|
|
m_Rigidbody.MovePosition(position);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Directly applies a position (like teleporting)
|
|
/// </summary>
|
|
/// <param name="position"><see cref="Vector3"/> position to apply to the Rigidbody</param>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void SetPosition(Vector3 position)
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
m_Rigidbody2D.position = position;
|
|
}
|
|
else
|
|
{
|
|
m_Rigidbody.position = position;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies the rotation and position of the <see cref="GameObject"/>'s <see cref="Transform"/>
|
|
/// </summary>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void ApplyCurrentTransform()
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
m_Rigidbody2D.position = transform.position;
|
|
m_Rigidbody2D.rotation = transform.eulerAngles.z;
|
|
}
|
|
else
|
|
{
|
|
m_Rigidbody.position = transform.position;
|
|
m_Rigidbody.rotation = transform.rotation;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rotatates the Rigidbody towards a specified rotation
|
|
/// </summary>
|
|
/// <param name="rotation">The rotation expressed as a <see cref="Quaternion"/></param>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void MoveRotation(Quaternion rotation)
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
var quaternion = Quaternion.identity;
|
|
var angles = quaternion.eulerAngles;
|
|
angles.z = m_Rigidbody2D.rotation;
|
|
quaternion.eulerAngles = angles;
|
|
m_Rigidbody2D.MoveRotation(quaternion);
|
|
}
|
|
else
|
|
{
|
|
m_Rigidbody.MoveRotation(rotation);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies a rotation to the Rigidbody
|
|
/// </summary>
|
|
/// <param name="rotation">The rotation to apply expressed as a <see cref="Quaternion"/></param>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void SetRotation(Quaternion rotation)
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
m_Rigidbody2D.rotation = rotation.eulerAngles.z;
|
|
}
|
|
else
|
|
{
|
|
m_Rigidbody.rotation = rotation;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the original interpolation of the Rigidbody while taking the Rigidbody type into consideration
|
|
/// </summary>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void SetOriginalInterpolation()
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
switch (m_Rigidbody2D.interpolation)
|
|
{
|
|
case RigidbodyInterpolation2D.None:
|
|
{
|
|
m_OriginalInterpolation = InterpolationTypes.None;
|
|
break;
|
|
}
|
|
case RigidbodyInterpolation2D.Interpolate:
|
|
{
|
|
m_OriginalInterpolation = InterpolationTypes.Interpolate;
|
|
break;
|
|
}
|
|
case RigidbodyInterpolation2D.Extrapolate:
|
|
{
|
|
m_OriginalInterpolation = InterpolationTypes.Extrapolate;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (m_Rigidbody.interpolation)
|
|
{
|
|
case RigidbodyInterpolation.None:
|
|
{
|
|
m_OriginalInterpolation = InterpolationTypes.None;
|
|
break;
|
|
}
|
|
case RigidbodyInterpolation.Interpolate:
|
|
{
|
|
m_OriginalInterpolation = InterpolationTypes.Interpolate;
|
|
break;
|
|
}
|
|
case RigidbodyInterpolation.Extrapolate:
|
|
{
|
|
m_OriginalInterpolation = InterpolationTypes.Extrapolate;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wakes the Rigidbody if it is sleeping
|
|
/// </summary>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void WakeIfSleeping()
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
if (m_Rigidbody2D.IsSleeping())
|
|
{
|
|
m_Rigidbody2D.WakeUp();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_Rigidbody.IsSleeping())
|
|
{
|
|
m_Rigidbody.WakeUp();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Puts the Rigidbody to sleep
|
|
/// </summary>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void SleepRigidbody()
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
m_Rigidbody2D.Sleep();
|
|
}
|
|
else
|
|
{
|
|
m_Rigidbody.Sleep();
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public bool IsKinematic()
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
return m_Rigidbody2D.isKinematic;
|
|
}
|
|
else
|
|
{
|
|
return m_Rigidbody.isKinematic;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the kinematic state of the Rigidbody and handles updating the Rigidbody's
|
|
/// interpolation setting based on the Kinematic state.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// When using the Rigidbody for <see cref="NetworkTransform"/> motion, this automatically
|
|
/// adjusts from extrapolation to interpolation if:
|
|
/// - The Rigidbody was originally set to extrapolation
|
|
/// - The NetworkTransform is set to interpolate
|
|
/// When the two above conditions are true:
|
|
/// - When switching from non-kinematic to kinematic this will automatically
|
|
/// switch the Rigidbody from extrapolation to interpolate.
|
|
/// - When switching from kinematic to non-kinematic this will automatically
|
|
/// switch the Rigidbody from interpolation back to extrapolation.
|
|
/// </remarks>
|
|
/// <param name="isKinematic"></param>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void SetIsKinematic(bool isKinematic)
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
m_Rigidbody2D.isKinematic = isKinematic;
|
|
}
|
|
else
|
|
{
|
|
m_Rigidbody.isKinematic = isKinematic;
|
|
}
|
|
|
|
// If we are not spawned, then exit early
|
|
if (!IsSpawned)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (UseRigidBodyForMotion)
|
|
{
|
|
// Only if the NetworkTransform is set to interpolate do we need to check for extrapolation
|
|
if (NetworkTransform.Interpolate && m_OriginalInterpolation == InterpolationTypes.Extrapolate)
|
|
{
|
|
if (IsKinematic())
|
|
{
|
|
// If not already set to interpolate then set the Rigidbody to interpolate
|
|
if (m_Rigidbody.interpolation == RigidbodyInterpolation.Extrapolate)
|
|
{
|
|
// Sleep until the next fixed update when switching from extrapolation to interpolation
|
|
SleepRigidbody();
|
|
SetInterpolation(InterpolationTypes.Interpolate);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Switch it back to the original interpolation if non-kinematic (doesn't require sleep).
|
|
SetInterpolation(m_OriginalInterpolation);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetInterpolation(m_IsAuthority ? m_OriginalInterpolation : (NetworkTransform.Interpolate ? InterpolationTypes.None : m_OriginalInterpolation));
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void SetInterpolation(InterpolationTypes interpolationType)
|
|
{
|
|
switch (interpolationType)
|
|
{
|
|
case InterpolationTypes.None:
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
m_Rigidbody2D.interpolation = RigidbodyInterpolation2D.None;
|
|
}
|
|
else
|
|
{
|
|
m_Rigidbody.interpolation = RigidbodyInterpolation.None;
|
|
}
|
|
break;
|
|
}
|
|
case InterpolationTypes.Interpolate:
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
m_Rigidbody2D.interpolation = RigidbodyInterpolation2D.Interpolate;
|
|
}
|
|
else
|
|
{
|
|
m_Rigidbody.interpolation = RigidbodyInterpolation.Interpolate;
|
|
}
|
|
break;
|
|
}
|
|
case InterpolationTypes.Extrapolate:
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
m_Rigidbody2D.interpolation = RigidbodyInterpolation2D.Extrapolate;
|
|
}
|
|
else
|
|
{
|
|
m_Rigidbody.interpolation = RigidbodyInterpolation.Extrapolate;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void ResetInterpolation()
|
|
{
|
|
SetInterpolation(m_OriginalInterpolation);
|
|
}
|
|
|
|
protected override void OnOwnershipChanged(ulong previous, ulong current)
|
|
{
|
|
UpdateOwnershipAuthority();
|
|
base.OnOwnershipChanged(previous, current);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the authority based on whether it is server or owner authoritative
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Distributed authority sessions will always be owner authoritative.
|
|
/// </remarks>
|
|
internal void UpdateOwnershipAuthority()
|
|
{
|
|
if (NetworkManager.DistributedAuthorityMode)
|
|
{
|
|
// When in distributed authority mode, always use HasAuthority
|
|
m_IsAuthority = HasAuthority;
|
|
}
|
|
else
|
|
{
|
|
if (NetworkTransform.IsServerAuthoritative())
|
|
{
|
|
m_IsAuthority = NetworkManager.IsServer;
|
|
}
|
|
else
|
|
{
|
|
m_IsAuthority = IsOwner;
|
|
}
|
|
}
|
|
|
|
if (AutoUpdateKinematicState)
|
|
{
|
|
SetIsKinematic(!m_IsAuthority);
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnNetworkSpawn()
|
|
{
|
|
UpdateOwnershipAuthority();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnNetworkDespawn()
|
|
{
|
|
if (UseRigidBodyForMotion && HasAuthority)
|
|
{
|
|
DetachFromFixedJoint();
|
|
NetworkRigidbodyConnections.Clear();
|
|
}
|
|
|
|
// If we are automatically handling the kinematic state...
|
|
if (AutoUpdateKinematicState || AutoSetKinematicOnDespawn)
|
|
{
|
|
// Turn off physics for the rigid body until spawned, otherwise
|
|
// non-owners can run fixed updates before the first full
|
|
// NetworkTransform update and physics will be applied (i.e. gravity, etc)
|
|
SetIsKinematic(true);
|
|
}
|
|
SetInterpolation(m_OriginalInterpolation);
|
|
}
|
|
|
|
// TODO: Possibly provide a NetworkJoint that allows for more options than fixed.
|
|
// Rigidbodies do not have the concept of "local space", and as such using a fixed joint will hold the object
|
|
// in place relative to the parent so jitter/stutter does not occur.
|
|
// Alternately, users can affix the fixed joint to a child GameObject (without a rigid body) of the parent NetworkObject
|
|
// and then add a NetworkTransform to that in order to get the parented child NetworkObject to move around in "local space"
|
|
public FixedJoint FixedJoint { get; private set; }
|
|
public FixedJoint2D FixedJoint2D { get; private set; }
|
|
|
|
internal System.Collections.Generic.List<NetworkRigidbodyBase> NetworkRigidbodyConnections = new System.Collections.Generic.List<NetworkRigidbodyBase>();
|
|
internal NetworkRigidbodyBase ParentBody;
|
|
|
|
private bool m_FixedJoint2DUsingGravity;
|
|
private bool m_OriginalGravitySetting;
|
|
private float m_OriginalGravityScale;
|
|
|
|
/// <summary>
|
|
/// When using a custom <see cref="NetworkRigidbodyBase"/>, this virtual method is invoked when the
|
|
/// <see cref="FixedJoint"/> is created in the event any additional adjustments are needed.
|
|
/// </summary>
|
|
protected virtual void OnFixedJointCreated()
|
|
{
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// When using a custom <see cref="NetworkRigidbodyBase"/>, this virtual method is invoked when the
|
|
/// <see cref="FixedJoint2D"/> is created in the event any additional adjustments are needed.
|
|
/// </summary>
|
|
protected virtual void OnFixedJoint2DCreated()
|
|
{
|
|
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void ApplyFixedJoint2D(NetworkRigidbodyBase bodyToConnect, Vector3 position, float connectedMassScale = 0.0f, float massScale = 1.0f, bool useGravity = false, bool zeroVelocity = true)
|
|
{
|
|
transform.position = position;
|
|
m_Rigidbody2D.position = position;
|
|
m_OriginalGravitySetting = bodyToConnect.m_Rigidbody.useGravity;
|
|
m_FixedJoint2DUsingGravity = useGravity;
|
|
|
|
if (!useGravity)
|
|
{
|
|
m_OriginalGravityScale = m_Rigidbody2D.gravityScale;
|
|
m_Rigidbody2D.gravityScale = 0.0f;
|
|
}
|
|
|
|
if (zeroVelocity)
|
|
{
|
|
m_Rigidbody2D.velocity = Vector2.zero;
|
|
m_Rigidbody2D.angularVelocity = 0.0f;
|
|
}
|
|
|
|
FixedJoint2D = gameObject.AddComponent<FixedJoint2D>();
|
|
FixedJoint2D.connectedBody = bodyToConnect.m_Rigidbody2D;
|
|
OnFixedJoint2DCreated();
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void ApplyFixedJoint(NetworkRigidbodyBase bodyToConnectTo, Vector3 position, float connectedMassScale = 0.0f, float massScale = 1.0f, bool useGravity = false, bool zeroVelocity = true)
|
|
{
|
|
transform.position = position;
|
|
m_Rigidbody.position = position;
|
|
if (zeroVelocity)
|
|
{
|
|
m_Rigidbody.linearVelocity = Vector3.zero;
|
|
m_Rigidbody.angularVelocity = Vector3.zero;
|
|
}
|
|
m_OriginalGravitySetting = m_Rigidbody.useGravity;
|
|
m_Rigidbody.useGravity = useGravity;
|
|
FixedJoint = gameObject.AddComponent<FixedJoint>();
|
|
FixedJoint.connectedBody = bodyToConnectTo.m_Rigidbody;
|
|
FixedJoint.connectedMassScale = connectedMassScale;
|
|
FixedJoint.massScale = massScale;
|
|
OnFixedJointCreated();
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Authority Only:
|
|
/// When invoked and not already attached to a fixed joint, this will connect two rigid bodies with <see cref="UseRigidBodyForMotion"/> enabled.
|
|
/// Invoke this method on the rigid body you wish to attach to another (i.e. weapon to player, sticky bomb to player/object, etc).
|
|
/// <seealso cref="FixedJoint"/>
|
|
/// <seealso cref="FixedJoint2D"/>
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Parenting relative:
|
|
/// - This instance can be viewed as the child.
|
|
/// - The <param name="objectToConnectTo"/> can be viewed as the parent.
|
|
/// <br/>
|
|
/// This is the recommended way, as opposed to parenting, to attached/detatch two rigid bodies to one another when <see cref="UseRigidBodyForMotion"/> is enabled.
|
|
/// For more details on using <see cref="UnityEngine.FixedJoint"/> and <see cref="UnityEngine.FixedJoint2D"/>.
|
|
/// <br/>
|
|
/// This provides a simple joint solution between two rigid bodies and serves as an example. You can add different joint types by creating a customized/derived
|
|
/// version of <see cref="NetworkRigidbodyBase"/>.
|
|
/// </remarks>
|
|
/// <param name="objectToConnectTo">The target object to attach to.</param>
|
|
/// <param name="positionOfConnection">The position of the connection (i.e. where you want the object to be affixed).</param>
|
|
/// <param name="connectedMassScale">The target object's mass scale relative to this object being attached.</param>
|
|
/// <param name="massScale">This object's mass scale relative to the target object's.</param>
|
|
/// <param name="useGravity">Determines if this object will have gravity applied to it along with the object you are connecting this one to (the default is to not use gravity for this object)</param>
|
|
/// <param name="zeroVelocity">When true (the default), both linear and angular velocities of this object are set to zero.</param>
|
|
/// <param name="teleportObject">When true (the default), this object will teleport itself to the position of connection.</param>
|
|
/// <returns>true (success) false (failed)</returns>
|
|
public bool AttachToFixedJoint(NetworkRigidbodyBase objectToConnectTo, Vector3 positionOfConnection, float connectedMassScale = 0.0f, float massScale = 1.0f, bool useGravity = false, bool zeroVelocity = true, bool teleportObject = true)
|
|
{
|
|
if (!UseRigidBodyForMotion)
|
|
{
|
|
Debug.LogError($"[{GetType().Name}] {name} does not have {nameof(UseRigidBodyForMotion)} set! Either enable {nameof(UseRigidBodyForMotion)} on this component or do not use a {nameof(FixedJoint)} when parenting under a {nameof(NetworkObject)}.");
|
|
return false;
|
|
}
|
|
|
|
if (IsKinematic())
|
|
{
|
|
Debug.LogError($"[{GetType().Name}] {name} is currently kinematic! You cannot use a {nameof(FixedJoint)} with Kinematic bodies!");
|
|
return false;
|
|
}
|
|
|
|
if (objectToConnectTo != null)
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
ApplyFixedJoint2D(objectToConnectTo, positionOfConnection, connectedMassScale, massScale, useGravity, zeroVelocity);
|
|
}
|
|
else
|
|
{
|
|
ApplyFixedJoint(objectToConnectTo, positionOfConnection, connectedMassScale, massScale, useGravity, zeroVelocity);
|
|
}
|
|
|
|
ParentBody = objectToConnectTo;
|
|
ParentBody.NetworkRigidbodyConnections.Add(this);
|
|
if (teleportObject)
|
|
{
|
|
NetworkTransform.SetState(teleportDisabled: false);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void RemoveFromParentBody()
|
|
{
|
|
ParentBody.NetworkRigidbodyConnections.Remove(this);
|
|
ParentBody = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Authority Only:
|
|
/// When invoked and already connected to an object via <see cref="FixedJoint"/> or <see cref="FixedJoint2D"/> (depending upon the type of rigid body),
|
|
/// this will detach from the fixed joint and destroy the fixed joint component.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This is the recommended way, as opposed to parenting, to attached/detatch two rigid bodies to one another when <see cref="UseRigidBodyForMotion"/> is enabled.
|
|
/// </remarks>
|
|
public void DetachFromFixedJoint()
|
|
{
|
|
if (!HasAuthority)
|
|
{
|
|
Debug.LogError($"[{name}] Only authority can invoke {nameof(DetachFromFixedJoint)}!");
|
|
}
|
|
if (UseRigidBodyForMotion)
|
|
{
|
|
if (m_IsRigidbody2D)
|
|
{
|
|
if (FixedJoint2D != null)
|
|
{
|
|
if (!m_FixedJoint2DUsingGravity)
|
|
{
|
|
FixedJoint2D.connectedBody.gravityScale = m_OriginalGravityScale;
|
|
}
|
|
FixedJoint2D.connectedBody = null;
|
|
Destroy(FixedJoint2D);
|
|
FixedJoint2D = null;
|
|
ResetInterpolation();
|
|
RemoveFromParentBody();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FixedJoint != null)
|
|
{
|
|
FixedJoint.connectedBody = null;
|
|
m_Rigidbody.useGravity = m_OriginalGravitySetting;
|
|
Destroy(FixedJoint);
|
|
FixedJoint = null;
|
|
ResetInterpolation();
|
|
RemoveFromParentBody();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // COM_UNITY_MODULES_PHYSICS
|
|
|