From b13d748ec43422a6d32ae1170d98fc074c8a084c Mon Sep 17 00:00:00 2001 From: Marek Sorokin Date: Fri, 24 Oct 2025 19:56:38 +0200 Subject: [PATCH] enemy ai --- Game/Enemy/Golem/Golem.prefab | 178 ++++++++- Game/Scripts/Enemy.meta | 8 + Game/Scripts/Enemy/SteamEnemyController.cs | 362 ++++++++++++++++++ .../Enemy/SteamEnemyController.cs.meta | 2 + .../Networking/SteamNetworkTransform.cs | 216 +++++++++++ .../Networking/SteamNetworkTransform.cs.meta | 2 + Scenes/CharacterScene.unity | 118 +++--- 7 files changed, 815 insertions(+), 71 deletions(-) create mode 100644 Game/Scripts/Enemy.meta create mode 100644 Game/Scripts/Enemy/SteamEnemyController.cs create mode 100644 Game/Scripts/Enemy/SteamEnemyController.cs.meta create mode 100644 Game/Scripts/Networking/SteamNetworkTransform.cs create mode 100644 Game/Scripts/Networking/SteamNetworkTransform.cs.meta diff --git a/Game/Enemy/Golem/Golem.prefab b/Game/Enemy/Golem/Golem.prefab index 401a70a..f06c9ad 100644 --- a/Game/Enemy/Golem/Golem.prefab +++ b/Game/Enemy/Golem/Golem.prefab @@ -56,7 +56,7 @@ Transform: m_GameObject: {fileID: 1070748433161756} serializedVersion: 2 m_LocalRotation: {x: 0.05427486, y: 0.395974, z: 0.124479234, w: 0.9081651} - m_LocalPosition: {x: -0.3286817, y: -5.684342e-16, z: 1.4654943e-16} + m_LocalPosition: {x: -0.3286817, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: @@ -477,6 +477,13 @@ GameObject: - component: {fileID: 2197706699938650528} - component: {fileID: 6042376723608263729} - component: {fileID: -1978034200425575233} + - component: {fileID: 5386150419490112215} + - component: {fileID: -2049111374676499537} + - component: {fileID: 1868764211993700903} + - component: {fileID: -8655886036466571170} + - component: {fileID: 4325330991877430748} + - component: {fileID: 8191023303709792670} + - component: {fileID: -3415813308782895168} m_Layer: 8 m_Name: Golem m_TagString: Untagged @@ -509,6 +516,7 @@ Transform: - {fileID: 4435288916602084} - {fileID: 4622407164390344} - {fileID: 4810071759811394} + - {fileID: 8960331849695004692} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!95 &95902137545226380 @@ -649,6 +657,143 @@ MonoBehaviour: SwitchTransformSpaceWhenParented: 0 Interpolate: 1 SlerpPosition: 0 +--- !u!114 &5386150419490112215 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1170732337855516} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1e6f99ca4475ead269829ba672213d5c, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::MegaKoop.Game.Networking.NetworkIdentity + networkId: 0 + assignOnAwake: 1 +--- !u!114 &-2049111374676499537 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1170732337855516} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 88efaaed4187a0ee8ad63b619f65e970, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::MegaKoop.Game.Networking.SteamNetworkTransform + targetTransform: {fileID: 0} + identity: {fileID: 0} + trackedRigidbody: {fileID: 0} + trackedNavMeshAgent: {fileID: 0} + authorityIsHost: 1 + simulateWhenDisconnected: 1 + broadcastInterval: 0.05 + remoteLerpSpeed: 12 + teleportThreshold: 4 +--- !u!114 &1868764211993700903 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1170732337855516} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e6e77e83dcc282364afb3428637c723c, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::MegaKoop.Game.WeaponSystem.WeaponController + ownerTeam: 2 + weaponSocket: {fileID: 0} + acquisitionRadius: 30 + retargetInterval: 0.25 + targetMask: + serializedVersion: 2 + m_Bits: 499 + lineOfSightMask: + serializedVersion: 2 + m_Bits: 499 + requireLineOfSight: 1 + startingWeapons: + - {fileID: 11400000, guid: 912de7c15ecf9d38d9be364bc0ac0f70, type: 2} + autoFire: 1 +--- !u!114 &-8655886036466571170 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1170732337855516} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 44c91a3744d2af1e4b9ee0f82451691a, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::MegaKoop.Game.Enemy.SteamEnemyController + moveSpeed: 3.5 + turnSpeed: 6 + stoppingDistance: 4 + detectionRadius: 30 + leashDistance: 60 + retargetInterval: 0.4 + repathThreshold: 1.5 + returnToSpawnWhenIdle: 1 + navMeshAgent: {fileID: -3415813308782895168} + weaponController: {fileID: 1868764211993700903} + health: {fileID: 6003919833639142508} + identity: {fileID: 5386150419490112215} + networkTransform: {fileID: -2049111374676499537} +--- !u!114 &4325330991877430748 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1170732337855516} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dad61aa7b24bddb6b9add5a461263779, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::MegaKoop.Game.Networking.SteamWeaponNetworkBridge + weaponController: {fileID: 1868764211993700903} + identity: {fileID: 5386150419490112215} + disableLocalFiringWhenClient: 1 +--- !u!114 &8191023303709792670 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1170732337855516} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a55005e32d7c7fdd89d4c15872d0466d, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::MegaKoop.Game.Networking.SteamHealthNetworkBridge + health: {fileID: 6003919833639142508} + identity: {fileID: 5386150419490112215} +--- !u!195 &-3415813308782895168 +NavMeshAgent: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1170732337855516} + m_Enabled: 1 + m_AgentTypeID: 0 + m_Radius: 0.5 + m_Speed: 3.5 + m_Acceleration: 8 + avoidancePriority: 50 + m_AngularSpeed: 120 + m_StoppingDistance: 0 + m_AutoTraverseOffMeshLink: 1 + m_AutoBraking: 1 + m_AutoRepath: 1 + m_Height: 2 + m_BaseOffset: 0 + m_WalkableMask: 4294967295 + m_ObstacleAvoidanceType: 4 --- !u!1 &1186076032050976 GameObject: m_ObjectHideFlags: 0 @@ -3291,3 +3436,34 @@ Transform: - {fileID: 4359018392181336} m_Father: {fileID: 4923149105459994} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &5677012161785445265 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8960331849695004692} + m_Layer: 0 + m_Name: weaponSocket + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8960331849695004692 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5677012161785445265} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.37696, y: 0, z: -17.87976} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4526463827110206} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Game/Scripts/Enemy.meta b/Game/Scripts/Enemy.meta new file mode 100644 index 0000000..6e064ad --- /dev/null +++ b/Game/Scripts/Enemy.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: aef1c1cc20669f95d8a2ccf0c0b3e6f5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Scripts/Enemy/SteamEnemyController.cs b/Game/Scripts/Enemy/SteamEnemyController.cs new file mode 100644 index 0000000..c6a7b77 --- /dev/null +++ b/Game/Scripts/Enemy/SteamEnemyController.cs @@ -0,0 +1,362 @@ +using System.Collections.Generic; +using MegaKoop.Game.Combat; +using MegaKoop.Game.Networking; +using MegaKoop.Game.WeaponSystem; +using UnityEngine; +using UnityEngine.AI; + +namespace MegaKoop.Game.Enemy +{ + /// + /// Host-driven enemy brain that chases the closest hero, fires using the shared weapon system, + /// and mirrors movement state to peers through SteamCoopNetworkManager. + /// + [DisallowMultipleComponent] + [RequireComponent(typeof(NetworkIdentity))] + [RequireComponent(typeof(Health))] + [RequireComponent(typeof(SteamNetworkTransform))] + [RequireComponent(typeof(WeaponController))] + public class SteamEnemyController : MonoBehaviour + { + private static readonly List SharedHealthBuffer = new(32); + + [Header("Movement")] + [SerializeField] private float moveSpeed = 3.5f; + [SerializeField] private float turnSpeed = 6f; + [SerializeField] private float stoppingDistance = 4f; + [SerializeField] private float detectionRadius = 30f; + [SerializeField] private float leashDistance = 60f; + [SerializeField] private float retargetInterval = 0.4f; + [SerializeField] private float repathThreshold = 1.5f; + [SerializeField] private bool returnToSpawnWhenIdle = true; + + [Header("References")] + [SerializeField] private NavMeshAgent navMeshAgent; + [SerializeField] private WeaponController weaponController; + [SerializeField] private Health health; + [SerializeField] private NetworkIdentity identity; + [SerializeField] private SteamNetworkTransform networkTransform; + + private SteamCoopNetworkManager networkManager; + private Transform currentTarget; + private Health currentTargetHealth; + private Vector3 lastTargetPosition; + private float retargetTimer; + private Vector3 spawnPosition; + private bool cachedNavAgentState; + + private void Awake() + { + spawnPosition = transform.position; + + if (navMeshAgent == null) + { + navMeshAgent = GetComponent(); + } + + if (weaponController == null) + { + weaponController = GetComponent(); + } + + if (health == null) + { + health = GetComponent(); + } + + if (identity == null) + { + identity = GetComponent(); + } + + if (networkTransform == null) + { + networkTransform = GetComponent(); + } + + if (navMeshAgent != null) + { + navMeshAgent.updateRotation = false; + navMeshAgent.stoppingDistance = stoppingDistance; + navMeshAgent.speed = moveSpeed; + cachedNavAgentState = navMeshAgent.enabled; + } + + lastTargetPosition = Vector3.positiveInfinity; + retargetTimer = Random.Range(0f, Mathf.Max(0.05f, retargetInterval)); + } + + private void OnEnable() + { + networkManager = SteamCoopNetworkManager.Instance; + SyncNavMeshAgentState(ShouldSimulate()); + } + + private void Update() + { + networkManager ??= SteamCoopNetworkManager.Instance; + + bool simulate = ShouldSimulate(); + SyncNavMeshAgentState(simulate); + + if (weaponController != null) + { + weaponController.enabled = simulate; + } + + if (!simulate) + { + return; + } + + if (health != null && !health.IsAlive) + { + StopAgentMovement(); + return; + } + + retargetTimer -= Time.deltaTime; + if (!IsTargetValid() || retargetTimer <= 0f) + { + AcquireTarget(); + } + + if (currentTarget == null) + { + HandleIdle(); + return; + } + + TickMovement(Time.deltaTime); + FaceTarget(Time.deltaTime); + } + + private bool ShouldSimulate() + { + if (networkManager == null) + { + return true; + } + + if (!networkManager.IsConnected) + { + return true; + } + + return networkManager.IsHost; + } + + private void SyncNavMeshAgentState(bool simulate) + { + if (navMeshAgent == null) + { + return; + } + + if (cachedNavAgentState == simulate) + { + return; + } + + navMeshAgent.enabled = simulate; + cachedNavAgentState = simulate; + } + + private bool IsTargetValid() + { + if (currentTarget == null) + { + return false; + } + + if (currentTargetHealth != null && !currentTargetHealth.IsAlive) + { + return false; + } + + float sqrDistance = (currentTarget.position - transform.position).sqrMagnitude; + if (detectionRadius > 0f && sqrDistance > detectionRadius * detectionRadius) + { + return false; + } + + if (leashDistance > 0f) + { + float sqrLeash = (currentTarget.position - spawnPosition).sqrMagnitude; + if (sqrLeash > leashDistance * leashDistance) + { + return false; + } + } + + return true; + } + + private void AcquireTarget() + { + retargetTimer = retargetInterval; + currentTarget = null; + currentTargetHealth = null; + float bestScore = float.MaxValue; + + SharedHealthBuffer.Clear(); + SharedHealthBuffer.AddRange(FindObjectsOfType(false)); + + foreach (Health candidate in SharedHealthBuffer) + { + if (candidate == null || candidate == health) + { + continue; + } + + if (!candidate.IsAlive || candidate.Team != Team.Heroes) + { + continue; + } + + Transform candidateTransform = candidate.transform; + float sqrDistance = (candidateTransform.position - transform.position).sqrMagnitude; + if (detectionRadius > 0f && sqrDistance > detectionRadius * detectionRadius) + { + continue; + } + + if (sqrDistance < bestScore) + { + bestScore = sqrDistance; + currentTarget = candidateTransform; + currentTargetHealth = candidate; + } + } + + if (currentTarget == null) + { + lastTargetPosition = Vector3.positiveInfinity; + } + + SharedHealthBuffer.Clear(); + } + + private void TickMovement(float deltaTime) + { + Vector3 targetPosition = currentTarget.position; + + if (navMeshAgent != null && navMeshAgent.enabled && navMeshAgent.isOnNavMesh) + { + navMeshAgent.speed = moveSpeed; + navMeshAgent.stoppingDistance = stoppingDistance; + + if (!navMeshAgent.hasPath || (targetPosition - lastTargetPosition).sqrMagnitude >= repathThreshold * repathThreshold) + { + navMeshAgent.SetDestination(targetPosition); + lastTargetPosition = targetPosition; + } + } + else + { + ManualMove(targetPosition, deltaTime); + } + } + + private void ManualMove(Vector3 targetPosition, float deltaTime) + { + Vector3 currentPosition = transform.position; + Vector3 direction = targetPosition - currentPosition; + direction.y = 0f; + float distance = direction.magnitude; + + if (distance <= Mathf.Max(0.1f, stoppingDistance)) + { + return; + } + + Vector3 normalized = direction / distance; + float step = moveSpeed * deltaTime; + Vector3 newPosition = currentPosition + normalized * Mathf.Min(step, distance - stoppingDistance); + transform.position = newPosition; + } + + private void FaceTarget(float deltaTime) + { + Vector3 toTarget = currentTarget.position - transform.position; + toTarget.y = 0f; + if (toTarget.sqrMagnitude < 0.0001f) + { + return; + } + + Quaternion desiredRotation = Quaternion.LookRotation(toTarget.normalized, Vector3.up); + transform.rotation = Quaternion.Slerp(transform.rotation, desiredRotation, Mathf.Clamp01(turnSpeed * deltaTime)); + } + + private void HandleIdle() + { + StopAgentMovement(); + + if (!returnToSpawnWhenIdle) + { + return; + } + + if ((transform.position - spawnPosition).sqrMagnitude <= 0.25f) + { + return; + } + + if (navMeshAgent != null && navMeshAgent.enabled && navMeshAgent.isOnNavMesh) + { + navMeshAgent.SetDestination(spawnPosition); + } + else + { + ManualMove(spawnPosition, Time.deltaTime); + FacePoint(spawnPosition, Time.deltaTime); + } + } + + private void FacePoint(Vector3 point, float deltaTime) + { + Vector3 toPoint = point - transform.position; + toPoint.y = 0f; + if (toPoint.sqrMagnitude < 0.0001f) + { + return; + } + + Quaternion desiredRotation = Quaternion.LookRotation(toPoint.normalized, Vector3.up); + transform.rotation = Quaternion.Slerp(transform.rotation, desiredRotation, Mathf.Clamp01(turnSpeed * deltaTime)); + } + + private void StopAgentMovement() + { + if (navMeshAgent != null && navMeshAgent.enabled) + { + navMeshAgent.ResetPath(); + } + } + + private void OnDrawGizmosSelected() + { + Gizmos.color = new Color(1f, 0.25f, 0f, 0.2f); + Gizmos.DrawWireSphere(transform.position, detectionRadius); + + if (leashDistance > 0f) + { + Gizmos.color = new Color(0.2f, 0.4f, 1f, 0.2f); + Vector3 leashCenter = Application.isPlaying ? spawnPosition : transform.position; + Gizmos.DrawWireSphere(leashCenter, leashDistance); + } + } + + private void OnValidate() + { + moveSpeed = Mathf.Max(0f, moveSpeed); + turnSpeed = Mathf.Max(0f, turnSpeed); + stoppingDistance = Mathf.Max(0f, stoppingDistance); + detectionRadius = Mathf.Max(0f, detectionRadius); + leashDistance = Mathf.Max(0f, leashDistance); + retargetInterval = Mathf.Max(0.05f, retargetInterval); + repathThreshold = Mathf.Max(0.1f, repathThreshold); + } + } +} diff --git a/Game/Scripts/Enemy/SteamEnemyController.cs.meta b/Game/Scripts/Enemy/SteamEnemyController.cs.meta new file mode 100644 index 0000000..aa07500 --- /dev/null +++ b/Game/Scripts/Enemy/SteamEnemyController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 44c91a3744d2af1e4b9ee0f82451691a \ No newline at end of file diff --git a/Game/Scripts/Networking/SteamNetworkTransform.cs b/Game/Scripts/Networking/SteamNetworkTransform.cs new file mode 100644 index 0000000..4f99029 --- /dev/null +++ b/Game/Scripts/Networking/SteamNetworkTransform.cs @@ -0,0 +1,216 @@ +using Steamworks; +using UnityEngine; + +namespace MegaKoop.Game.Networking +{ + /// + /// Host-authoritative transform replication that mirrors state to all peers via Steam P2P messages. + /// + [DisallowMultipleComponent] + public class SteamNetworkTransform : MonoBehaviour + { + [Header("References")] + [SerializeField] private Transform targetTransform; + [SerializeField] private NetworkIdentity identity; + [SerializeField] private Rigidbody trackedRigidbody; + [SerializeField] private UnityEngine.AI.NavMeshAgent trackedNavMeshAgent; + + [Header("Authority")] + [SerializeField] private bool authorityIsHost = true; + [SerializeField] private bool simulateWhenDisconnected = true; + + [Header("Synchronization")] + [SerializeField] private float broadcastInterval = 0.05f; + [SerializeField] private float remoteLerpSpeed = 12f; + [SerializeField] private float teleportThreshold = 4f; + + private SteamCoopNetworkManager networkManager; + private float broadcastTimer; + private bool isRegistered; + private Vector3 remoteTargetPosition; + private Quaternion remoteTargetRotation; + private Vector3 remoteTargetVelocity; + private bool haveRemoteState; + + private void Awake() + { + if (targetTransform == null) + { + targetTransform = transform; + } + + if (identity == null) + { + identity = GetComponent(); + } + + if (trackedRigidbody == null) + { + trackedRigidbody = GetComponent(); + } + + if (trackedNavMeshAgent == null) + { + trackedNavMeshAgent = GetComponent(); + } + + remoteTargetPosition = targetTransform.position; + remoteTargetRotation = targetTransform.rotation; + remoteTargetVelocity = Vector3.zero; + broadcastTimer = Random.Range(0f, Mathf.Max(0.01f, broadcastInterval)); + } + + private void OnEnable() + { + EnsureNetworkManager(); + RegisterHandlers(); + } + + private void OnDisable() + { + UnregisterHandlers(); + } + + private void Update() + { + RefreshNetworkManager(); + + if (HasAuthority()) + { + broadcastTimer -= Time.deltaTime; + if (broadcastTimer <= 0f) + { + BroadcastState(); + broadcastTimer = Mathf.Max(0.01f, broadcastInterval); + } + } + else if (haveRemoteState) + { + float lerpFactor = Mathf.Clamp01(remoteLerpSpeed * Time.deltaTime); + Vector3 currentPosition = targetTransform.position; + float sqrDistance = (remoteTargetPosition - currentPosition).sqrMagnitude; + if (sqrDistance >= teleportThreshold * teleportThreshold) + { + targetTransform.SetPositionAndRotation(remoteTargetPosition, remoteTargetRotation); + } + else + { + targetTransform.position = Vector3.Lerp(currentPosition, remoteTargetPosition, lerpFactor); + targetTransform.rotation = Quaternion.Slerp(targetTransform.rotation, remoteTargetRotation, lerpFactor); + } + } + } + + private void RefreshNetworkManager() + { + if (networkManager == null) + { + networkManager = SteamCoopNetworkManager.Instance; + if (networkManager != null && isActiveAndEnabled) + { + RegisterHandlers(); + } + } + } + + private void EnsureNetworkManager() + { + RefreshNetworkManager(); + } + + private bool HasAuthority() + { + if (!authorityIsHost) + { + return true; + } + + if (networkManager == null) + { + return simulateWhenDisconnected; + } + + if (!networkManager.IsConnected) + { + return simulateWhenDisconnected; + } + + return networkManager.IsHost; + } + + private void BroadcastState() + { + if (identity == null || targetTransform == null || networkManager == null) + { + return; + } + + Vector3 velocity = Vector3.zero; + if (trackedNavMeshAgent != null && trackedNavMeshAgent.enabled) + { + velocity = trackedNavMeshAgent.velocity; + } + else if (trackedRigidbody != null) + { + velocity = trackedRigidbody.linearVelocity; + } + + var message = new CharacterTransformMessage(identity.NetworkId, targetTransform.position, targetTransform.rotation, velocity); + byte[] payload = CharacterTransformMessage.Serialize(message); + networkManager.SendToAll(NetworkMessageType.CharacterTransform, payload, EP2PSend.k_EP2PSendUnreliableNoDelay); + } + + private void RegisterHandlers() + { + if (isRegistered || networkManager == null) + { + return; + } + + networkManager.RegisterHandler(NetworkMessageType.CharacterTransform, HandleTransformMessage); + isRegistered = true; + } + + private void UnregisterHandlers() + { + if (!isRegistered || networkManager == null) + { + return; + } + + networkManager.UnregisterHandler(NetworkMessageType.CharacterTransform, HandleTransformMessage); + isRegistered = false; + } + + private void HandleTransformMessage(NetworkMessage message) + { + if (identity == null || targetTransform == null) + { + return; + } + + if (HasAuthority()) + { + return; + } + + CharacterTransformMessage transformMessage = CharacterTransformMessage.Deserialize(message.Payload); + if (transformMessage.NetworkId != identity.NetworkId) + { + return; + } + + remoteTargetPosition = transformMessage.Position; + remoteTargetRotation = transformMessage.Rotation; + remoteTargetVelocity = transformMessage.Velocity; + haveRemoteState = true; + } + + private void OnValidate() + { + broadcastInterval = Mathf.Max(0.01f, broadcastInterval); + remoteLerpSpeed = Mathf.Max(0f, remoteLerpSpeed); + teleportThreshold = Mathf.Max(0.01f, teleportThreshold); + } + } +} diff --git a/Game/Scripts/Networking/SteamNetworkTransform.cs.meta b/Game/Scripts/Networking/SteamNetworkTransform.cs.meta new file mode 100644 index 0000000..b533725 --- /dev/null +++ b/Game/Scripts/Networking/SteamNetworkTransform.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 88efaaed4187a0ee8ad63b619f65e970 \ No newline at end of file diff --git a/Scenes/CharacterScene.unity b/Scenes/CharacterScene.unity index a0905e2..1825afc 100644 --- a/Scenes/CharacterScene.unity +++ b/Scenes/CharacterScene.unity @@ -216,75 +216,6 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} ---- !u!1001 &196170898 -PrefabInstance: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Modification: - serializedVersion: 3 - m_TransformParent: {fileID: 0} - m_Modifications: - - target: {fileID: 1170732337855516, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} - propertyPath: m_Name - value: Golem (1) - objectReference: {fileID: 0} - - target: {fileID: 4526463827110206, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} - propertyPath: m_LocalScale.x - value: 2.1307 - objectReference: {fileID: 0} - - target: {fileID: 4526463827110206, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} - propertyPath: m_LocalScale.y - value: 2.1307 - objectReference: {fileID: 0} - - target: {fileID: 4526463827110206, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} - propertyPath: m_LocalScale.z - value: 2.1307 - objectReference: {fileID: 0} - - target: {fileID: 4526463827110206, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} - propertyPath: m_LocalPosition.x - value: -12.33 - objectReference: {fileID: 0} - - target: {fileID: 4526463827110206, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} - propertyPath: m_LocalPosition.y - value: 0.16999996 - objectReference: {fileID: 0} - - target: {fileID: 4526463827110206, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} - propertyPath: m_LocalPosition.z - value: 5.49 - objectReference: {fileID: 0} - - target: {fileID: 4526463827110206, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} - propertyPath: m_LocalRotation.w - value: -0.06743933 - objectReference: {fileID: 0} - - target: {fileID: 4526463827110206, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} - propertyPath: m_LocalRotation.x - value: -0 - objectReference: {fileID: 0} - - target: {fileID: 4526463827110206, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} - propertyPath: m_LocalRotation.y - value: -0.9977234 - objectReference: {fileID: 0} - - target: {fileID: 4526463827110206, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} - propertyPath: m_LocalRotation.z - value: -0 - objectReference: {fileID: 0} - - target: {fileID: 4526463827110206, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} - propertyPath: m_LocalEulerAnglesHint.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4526463827110206, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} - propertyPath: m_LocalEulerAnglesHint.y - value: -187.734 - objectReference: {fileID: 0} - - target: {fileID: 4526463827110206, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} - propertyPath: m_LocalEulerAnglesHint.z - value: 0 - objectReference: {fileID: 0} - m_RemovedComponents: [] - m_RemovedGameObjects: [] - m_AddedGameObjects: [] - m_AddedComponents: [] - m_SourcePrefab: {fileID: 100100000, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} --- !u!1 &1124421571 GameObject: m_ObjectHideFlags: 0 @@ -494,6 +425,18 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 6042376723608263729, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} + propertyPath: GlobalObjectIdHash + value: 2082327815 + objectReference: {fileID: 0} + - target: {fileID: 6042376723608263729, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} + propertyPath: SceneMigrationSynchronization + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6042376723608263729, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} + propertyPath: InScenePlacedSourceGlobalObjectIdHash + value: 2899175104 + objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] @@ -666,6 +609,18 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 6042376723608263729, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} + propertyPath: GlobalObjectIdHash + value: 1047745387 + objectReference: {fileID: 0} + - target: {fileID: 6042376723608263729, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} + propertyPath: SceneMigrationSynchronization + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6042376723608263729, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} + propertyPath: InScenePlacedSourceGlobalObjectIdHash + value: 2899175104 + objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] @@ -735,6 +690,18 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 6042376723608263729, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} + propertyPath: GlobalObjectIdHash + value: 1912244675 + objectReference: {fileID: 0} + - target: {fileID: 6042376723608263729, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} + propertyPath: SceneMigrationSynchronization + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6042376723608263729, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} + propertyPath: InScenePlacedSourceGlobalObjectIdHash + value: 2899175104 + objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] @@ -804,6 +771,18 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 6042376723608263729, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} + propertyPath: GlobalObjectIdHash + value: 1968154192 + objectReference: {fileID: 0} + - target: {fileID: 6042376723608263729, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} + propertyPath: SceneMigrationSynchronization + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6042376723608263729, guid: b5051c49d05768c73a8c42e1967fe4b2, type: 3} + propertyPath: InScenePlacedSourceGlobalObjectIdHash + value: 2899175104 + objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] @@ -980,7 +959,6 @@ SceneRoots: - {fileID: 1432618524} - {fileID: 1801103367} - {fileID: 1857588276} - - {fileID: 196170898} - {fileID: 1770640151} - {fileID: 8888013435869854006} - {fileID: 1706180863}