Теперь проблема, с которой я столкнулся, заключается в том, что когда я прикрепляю компонент Network Transform к плоскости игрока, я испытываю сильное дрожание в положении и вращении плоскости независимо от того, является ли игрок сервером или клиентом.
Если я удалю Network Transform, игроки больше не синхронизируются.
RigidBody прикреплен к planePrefab.
вот мой код:
INETWORK CALLBACK
Код: Выделить всё
OnInput()Код: Выделить всё
public void OnInput(NetworkRunner runner, NetworkInput input)
{
if (!AllowInput)
return;
PlayerInput playerInput = new PlayerInput();
// Roll & Pitch (WASD)
Vector2 rollPitch = Vector2.zero;
// Roll = A/D (left/right)
if (Input.GetKey(KeyCode.A)) rollPitch.x = -1f;
else if (Input.GetKey(KeyCode.D)) rollPitch.x = 1f;
else rollPitch.x = 0f;
// Pitch = W/S (forward/back)
if (Input.GetKey(KeyCode.W)) rollPitch.y = 1f;
else if (Input.GetKey(KeyCode.S)) rollPitch.y = -1f;
else rollPitch.y = 0f;
playerInput.RollPitch = rollPitch;
// Yaw (Q/E)
if (Input.GetKey(KeyCode.Q)) playerInput.Yaw = 1f;
else if (Input.GetKey(KeyCode.E)) playerInput.Yaw = -1f;
else playerInput.Yaw = 0f;
// Throttle (Z/X)
if (Input.GetKey(KeyCode.Z)) playerInput.Throttle = 1f;
else if (Input.GetKey(KeyCode.X)) playerInput.Throttle = -1f;
else playerInput.Throttle = 0f;
// Flaps toggle (F key)
playerInput.Flaps = Input.GetKeyDown(KeyCode.F);
// Weapons
playerInput.FireMissile = Input.GetMouseButtonDown(0); // LMB
playerInput.FireCannon = Input.GetMouseButton(1); // RMB hold
// Camera input (mouse delta)
playerInput.Camera = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
// Send struct to Fusion
input.Set(playerInput);
}
Код: Выделить всё
public override void FixedUpdateNetwork()
{
if (Player && Player.InputEnabled)
{
if (GetInput(out PlayerInput data)) {
if (plane != null)
{
plane.SetControlInput(new Vector3(data.RollPitch.y, data.Yaw, -data.RollPitch.x));
plane.SetThrottleInput(data.Throttle);
if (data.Flaps)
plane.ToggleFlaps();
if (data.FireMissile)
plane.TryFireMissile();
plane.SetCannonInput(data.FireCannon);
}
}
}
}
}
Код: Выделить всё
using Fusion;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Plane : NetworkBehaviour {
[SerializeField]
float maxHealth;
[SerializeField]
float health;
[SerializeField]
float maxThrust;
[SerializeField]
float throttleSpeed;
[SerializeField]
float gLimit;
[SerializeField]
float gLimitPitch;
[Header("Lift")]
[SerializeField]
float liftPower;
[SerializeField]
AnimationCurve liftAOACurve;
[SerializeField]
float inducedDrag;
[SerializeField]
AnimationCurve inducedDragCurve;
[SerializeField]
float rudderPower;
[SerializeField]
AnimationCurve rudderAOACurve;
[SerializeField]
AnimationCurve rudderInducedDragCurve;
[SerializeField]
float flapsLiftPower;
[SerializeField]
float flapsAOABias;
[SerializeField]
float flapsDrag;
[SerializeField]
float flapsRetractSpeed;
[Header("Steering")]
[SerializeField]
Vector3 turnSpeed;
[SerializeField]
Vector3 turnAcceleration;
[SerializeField]
AnimationCurve steeringCurve;
[Header("Drag")]
[SerializeField]
AnimationCurve dragForward;
[SerializeField]
AnimationCurve dragBack;
[SerializeField]
AnimationCurve dragLeft;
[SerializeField]
AnimationCurve dragRight;
[SerializeField]
AnimationCurve dragTop;
[SerializeField]
AnimationCurve dragBottom;
[SerializeField]
Vector3 angularDrag;
[SerializeField]
float airbrakeDrag;
[Header("Misc")]
[SerializeField]
List landingGear;
[SerializeField]
PhysicMaterial landingGearBrakesMaterial;
[SerializeField]
List graphics;
[SerializeField]
GameObject damageEffect;
[SerializeField]
GameObject deathEffect;
[SerializeField]
bool flapsDeployed;
[SerializeField]
float initialSpeed;
[Header("Weapons")]
[SerializeField]
List hardpoints;
[SerializeField]
float missileReloadTime;
[SerializeField]
float missileDebounceTime;
[SerializeField]
GameObject missilePrefab;
[SerializeField]
Target target;
[SerializeField]
float lockRange;
[SerializeField]
float lockSpeed;
[SerializeField]
float lockAngle;
[SerializeField]
[Tooltip("Firing rate in Rounds Per Minute")]
float cannonFireRate;
[SerializeField]
float cannonDebounceTime;
[SerializeField]
float cannonSpread;
[SerializeField]
Transform cannonSpawnPoint;
[SerializeField]
GameObject bulletPrefab;
new PlaneAnimation animation;
float throttleInput;
Vector3 controlInput;
Vector3 lastVelocity;
PhysicMaterial landingGearDefaultMaterial;
int missileIndex;
List missileReloadTimers;
float missileDebounceTimer;
Vector3 missileLockDirection;
bool cannonFiring;
float cannonDebounceTimer;
float cannonFiringTimer;
public float MaxHealth {
get {
return maxHealth;
}
set {
maxHealth = Mathf.Max(0, value);
}
}
public float Health {
get {
return health;
}
private set {
health = Mathf.Clamp(value, 0, maxHealth);
if (health 0) {
damageEffect.SetActive(true);
} else {
damageEffect.SetActive(false);
}
if (health == 0 && MaxHealth != 0 && !Dead) {
Die();
}
}
}
public bool Dead { get; private set; }
public Rigidbody Rigidbody;
public float Throttle { get; private set; }
public Vector3 EffectiveInput { get; private set; }
public Vector3 Velocity { get; private set; }
public Vector3 LocalVelocity { get; private set; }
public Vector3 LocalGForce { get; private set; }
public Vector3 LocalAngularVelocity { get; private set; }
public float AngleOfAttack { get; private set; }
public float AngleOfAttackYaw { get; private set; }
public bool AirbrakeDeployed { get; private set; }
public bool FlapsDeployed {
get {
return flapsDeployed;
}
private set {
flapsDeployed = value;
foreach (var lg in landingGear) {
lg.enabled = value;
}
}
}
public bool MissileLocked { get; private set; }
public bool MissileTracking { get; private set; }
public Target Target {
get {
return target;
}
}
public Vector3 MissileLockDirection {
get {
return Rigidbody.rotation * missileLockDirection;
}
}
void Awake() {
animation = GetComponent
();
Rigidbody = GetComponent();
if (landingGear.Count > 0) {
landingGearDefaultMaterial = landingGear[0].sharedMaterial;
}
missileReloadTimers = new List(hardpoints.Count);
foreach (var h in hardpoints) {
missileReloadTimers.Add(0);
}
missileLockDirection = Vector3.forward;
Rigidbody.velocity = Rigidbody.rotation * new Vector3(0, 0, initialSpeed);
}
public void SetThrottleInput(float input) {
if (Dead) return;
throttleInput = input;
}
public void SetControlInput(Vector3 input) {
if (Dead) return;
controlInput = Vector3.ClampMagnitude(input, 1);
}
public void SetCannonInput(bool input) {
if (Dead) return;
cannonFiring = input;
}
public void ToggleFlaps() {
if (LocalVelocity.z < flapsRetractSpeed) {
FlapsDeployed = !FlapsDeployed;
}
}
public void ApplyDamage(float damage) {
Health -= damage;
}
void Die() {
throttleInput = 0;
Throttle = 0;
Dead = true;
cannonFiring = false;
damageEffect.GetComponent().Pause();
deathEffect.SetActive(true);
}
void UpdateThrottle(float dt) {
float target = 0;
if (throttleInput > 0) target = 1;
//throttle input is [-1, 1]
//throttle is [0, 1]
Throttle = Utilities.MoveTo(Throttle, target, throttleSpeed * Mathf.Abs(throttleInput), dt);
AirbrakeDeployed = Throttle == 0 && throttleInput == -1;
if (AirbrakeDeployed) {
foreach (var lg in landingGear) {
lg.sharedMaterial = landingGearBrakesMaterial;
}
} else {
foreach (var lg in landingGear) {
lg.sharedMaterial = landingGearDefaultMaterial;
}
}
}
void UpdateFlaps() {
if (LocalVelocity.z > flapsRetractSpeed) {
FlapsDeployed = false;
}
}
void CalculateAngleOfAttack() {
if (LocalVelocity.sqrMagnitude < 0.1f) {
AngleOfAttack = 0;
AngleOfAttackYaw = 0;
return;
}
AngleOfAttack = Mathf.Atan2(-LocalVelocity.y, LocalVelocity.z);
AngleOfAttackYaw = Mathf.Atan2(LocalVelocity.x, LocalVelocity.z);
}
void CalculateGForce(float dt) {
var invRotation = Quaternion.Inverse(Rigidbody.rotation);
var acceleration = (Velocity - lastVelocity) / dt;
LocalGForce = invRotation * acceleration;
lastVelocity = Velocity;
}
void CalculateState(float dt) {
var invRotation = Quaternion.Inverse(Rigidbody.rotation);
Velocity = Rigidbody.velocity;
LocalVelocity = invRotation * Velocity; //transform world velocity into local space
LocalAngularVelocity = invRotation * Rigidbody.angularVelocity; //transform into local space
CalculateAngleOfAttack();
}
void UpdateThrust() {
Rigidbody.AddRelativeForce(Throttle * maxThrust * Vector3.forward);
}
void UpdateDrag() {
var lv = LocalVelocity;
var lv2 = lv.sqrMagnitude; //velocity squared
float airbrakeDrag = AirbrakeDeployed ? this.airbrakeDrag : 0;
float flapsDrag = FlapsDeployed ? this.flapsDrag : 0;
//calculate coefficient of drag depending on direction on velocity
var coefficient = Utilities.Scale6(
lv.normalized,
dragRight.Evaluate(Mathf.Abs(lv.x)), dragLeft.Evaluate(Mathf.Abs(lv.x)),
dragTop.Evaluate(Mathf.Abs(lv.y)), dragBottom.Evaluate(Mathf.Abs(lv.y)),
dragForward.Evaluate(Mathf.Abs(lv.z)) + airbrakeDrag + flapsDrag, //include extra drag for forward coefficient
dragBack.Evaluate(Mathf.Abs(lv.z))
);
var drag = coefficient.magnitude * lv2 * -lv.normalized; //drag is opposite direction of velocity
Rigidbody.AddRelativeForce(drag);
}
Vector3 CalculateLift(float angleOfAttack, Vector3 rightAxis, float liftPower, AnimationCurve aoaCurve, AnimationCurve inducedDragCurve) {
var liftVelocity = Vector3.ProjectOnPlane(LocalVelocity, rightAxis); //project velocity onto YZ plane
var v2 = liftVelocity.sqrMagnitude; //square of velocity
//lift = velocity^2 * coefficient * liftPower
//coefficient varies with AOA
var liftCoefficient = aoaCurve.Evaluate(angleOfAttack * Mathf.Rad2Deg);
var liftForce = v2 * liftCoefficient * liftPower;
//lift is perpendicular to velocity
var liftDirection = Vector3.Cross(liftVelocity.normalized, rightAxis);
var lift = liftDirection * liftForce;
//induced drag varies with square of lift coefficient
var dragForce = liftCoefficient * liftCoefficient;
var dragDirection = -liftVelocity.normalized;
var inducedDrag = dragDirection * v2 * dragForce * this.inducedDrag * inducedDragCurve.Evaluate(Mathf.Max(0, LocalVelocity.z));
return lift + inducedDrag;
}
void UpdateLift() {
if (LocalVelocity.sqrMagnitude < 1f) return;
float flapsLiftPower = FlapsDeployed ? this.flapsLiftPower : 0;
float flapsAOABias = FlapsDeployed ? this.flapsAOABias : 0;
var liftForce = CalculateLift(
AngleOfAttack + (flapsAOABias * Mathf.Deg2Rad), Vector3.right,
liftPower + flapsLiftPower,
liftAOACurve,
inducedDragCurve
);
var yawForce = CalculateLift(AngleOfAttackYaw, Vector3.up, rudderPower, rudderAOACurve, rudderInducedDragCurve);
Rigidbody.AddRelativeForce(liftForce);
Rigidbody.AddRelativeForce(yawForce);
}
void UpdateAngularDrag() {
var av = LocalAngularVelocity;
var drag = av.sqrMagnitude * -av.normalized; //squared, opposite direction of angular velocity
Rigidbody.AddRelativeTorque(Vector3.Scale(drag, angularDrag), ForceMode.Acceleration); //ignore rigidbody mass
}
Vector3 CalculateGForce(Vector3 angularVelocity, Vector3 velocity) {
//estiamte G Force from angular velocity and velocity
//Velocity = AngularVelocity * Radius
//G = Velocity^2 / R
//G = (Velocity * AngularVelocity * Radius) / Radius
//G = Velocity * AngularVelocity
//G = V cross A
return Vector3.Cross(angularVelocity, velocity);
}
Vector3 CalculateGForceLimit(Vector3 input) {
return Utilities.Scale6(input,
gLimit, gLimitPitch, //pitch down, pitch up
gLimit, gLimit, //yaw
gLimit, gLimit //roll
) * 9.81f;
}
float CalculateGLimiter(Vector3 controlInput, Vector3 maxAngularVelocity) {
if (controlInput.magnitude < 0.01f) {
return 1;
}
//if the player gives input with magnitude less than 1, scale up their input so that magnitude == 1
var maxInput = controlInput.normalized;
var limit = CalculateGForceLimit(maxInput);
var maxGForce = CalculateGForce(Vector3.Scale(maxInput, maxAngularVelocity), LocalVelocity);
if (maxGForce.magnitude > limit.magnitude) {
//example:
//maxGForce = 16G, limit = 8G
//so this is 8 / 16 or 0.5
return limit.magnitude / maxGForce.magnitude;
}
return 1;
}
float CalculateSteering(float dt, float angularVelocity, float targetVelocity, float acceleration) {
var error = targetVelocity - angularVelocity;
var accel = acceleration * dt;
return Mathf.Clamp(error, -accel, accel);
}
void UpdateSteering(float dt) {
var speed = Mathf.Max(0, LocalVelocity.z);
var steeringPower = steeringCurve.Evaluate(speed);
var gForceScaling = CalculateGLimiter(controlInput, turnSpeed * Mathf.Deg2Rad * steeringPower);
var targetAV = Vector3.Scale(controlInput, turnSpeed * steeringPower * gForceScaling);
var av = LocalAngularVelocity * Mathf.Rad2Deg;
var correction = new Vector3(
CalculateSteering(dt, av.x, targetAV.x, turnAcceleration.x * steeringPower),
CalculateSteering(dt, av.y, targetAV.y, turnAcceleration.y * steeringPower),
CalculateSteering(dt, av.z, targetAV.z, turnAcceleration.z * steeringPower)
);
Rigidbody.AddRelativeTorque(correction * Mathf.Deg2Rad, ForceMode.VelocityChange); //ignore rigidbody mass
var correctionInput = new Vector3(
Mathf.Clamp((targetAV.x - av.x) / turnAcceleration.x, -1, 1),
Mathf.Clamp((targetAV.y - av.y) / turnAcceleration.y, -1, 1),
Mathf.Clamp((targetAV.z - av.z) / turnAcceleration.z, -1, 1)
);
var effectiveInput = (correctionInput + controlInput) * gForceScaling;
EffectiveInput = new Vector3(
Mathf.Clamp(effectiveInput.x, -1, 1),
Mathf.Clamp(effectiveInput.y, -1, 1),
Mathf.Clamp(effectiveInput.z, -1, 1)
);
}
public void TryFireMissile() {
if (Dead) return;
//try all available missiles
for (int i = 0; i < hardpoints.Count; i++) {
var index = (missileIndex + i) % hardpoints.Count;
if (missileDebounceTimer == 0 && missileReloadTimers[index] == 0) {
FireMissile(index);
missileIndex = (index + 1) % hardpoints.Count;
missileReloadTimers[index] = missileReloadTime;
missileDebounceTimer = missileDebounceTime;
animation.ShowMissileGraphic(index, false);
break;
}
}
}
void FireMissile(int index) {
var hardpoint = hardpoints[index];
var missileGO = Instantiate(missilePrefab, hardpoint.position, hardpoint.rotation);
var missile = missileGO.GetComponent();
missile.Launch(this, MissileLocked ? Target : null);
}
void UpdateWeapons(float dt) {
UpdateWeaponCooldown(dt);
UpdateMissileLock(dt);
UpdateCannon(dt);
}
void UpdateWeaponCooldown(float dt) {
missileDebounceTimer = Mathf.Max(0, missileDebounceTimer - dt);
cannonDebounceTimer = Mathf.Max(0, cannonDebounceTimer - dt);
cannonFiringTimer = Mathf.Max(0, cannonFiringTimer - dt);
for (int i = 0; i < missileReloadTimers.Count; i++) {
missileReloadTimers[i] = Mathf.Max(0, missileReloadTimers[i] - dt);
if (missileReloadTimers[i] == 0)
{
animation.ShowMissileGraphic(i, true);
}
}
}
void UpdateMissileLock(float dt) {
//default neutral position is forward
Vector3 targetDir = Vector3.forward;
MissileTracking = false;
if (Target != null && !Target.Plane.Dead) {
var error = target.Position - Rigidbody.position;
var errorDir = Quaternion.Inverse(Rigidbody.rotation) * error.normalized; //transform into local space
if (error.magnitude
Подробнее здесь: [url]https://stackoverflow.com/questions/79780889/unity-fusion-network-transform-jitter[/url]
Мобильная версия