Squashed commit of the following:
commit428243f4a6Author: AsanoRema <asanorema2036@gmail.com> Date: Sat Aug 29 13:43:13 2026 +0300 Squashed commit of the following: commit68e78fa464Author: AsanoRema <asanorema2036@gmail.com> Date: Sat Aug 29 13:41:49 2026 +0300 Updated .gitignore commit9f87122a34Author: AsanoRema <asanorema2036@gmail.com> Date: Sat Aug 29 13:34:57 2026 +0300 Added Dohya commitf6a7547e84Author: AsanoRema <asanorema2036@gmail.com> Date: Fri Aug 21 17:59:13 2026 +0300 Fixed commitb98901c366Author: AsanoRema <asanorema2036@gmail.com> Date: Tue Aug 18 17:37:22 2026 +0300 Added new particle Package commitbb2f4a3b16Author: AsanoRema <asanorema2036@gmail.com> Date: Tue Aug 18 17:17:48 2026 +0300 Map mehanic ready to multiplayer commitc01ff800f6Author: AsanoRema <asanorema2036@gmail.com> Date: Tue Aug 18 16:16:19 2026 +0300 Squashed commit of the following: commit173bbb165cAuthor: henjkey <henjkey@gmail.com> Date: Fri Aug 14 18:51:23 2026 +0500 Mappp commitd559ab338aAuthor: henjkey <henjkey@gmail.com> Date: Thu Aug 13 18:42:41 2026 +0500 Map Controller Coordinates Target System Crosshair System Radar System
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 03ef14f0e5db05941afa0b0132e3bfb3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,126 @@
|
||||
using System.Collections.Generic;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class NetworkShockwaveEmitter : NetworkBehaviour
|
||||
{
|
||||
private const int MaximumOverlaps = 256;
|
||||
|
||||
[SerializeField] private ShockwaveProfile profile;
|
||||
[SerializeField] private Transform origin;
|
||||
[SerializeField] private LayerMask physicsLayers = ~0;
|
||||
[SerializeField] private UnityEvent shockwaveEmitted;
|
||||
|
||||
private readonly NetworkVariable<uint> sequence = new(
|
||||
0,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private readonly Collider[] overlapBuffer = new Collider[MaximumOverlaps];
|
||||
private readonly HashSet<Rigidbody> affectedBodies = new();
|
||||
|
||||
public ShockwaveProfile Profile => profile;
|
||||
public Vector3 Origin => origin != null ? origin.position : transform.position;
|
||||
public uint Sequence => sequence.Value;
|
||||
|
||||
public event System.Action Emitted;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
sequence.OnValueChanged += HandleSequenceChanged;
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
sequence.OnValueChanged -= HandleSequenceChanged;
|
||||
}
|
||||
|
||||
public bool EmitShockwave() => EmitShockwave(Origin);
|
||||
|
||||
public bool EmitShockwave(Vector3 worldOrigin)
|
||||
{
|
||||
if (!IsServer || !IsSpawned || profile == null)
|
||||
return false;
|
||||
|
||||
ApplyToPlayers(worldOrigin);
|
||||
ApplyToPhysics(worldOrigin);
|
||||
sequence.Value++;
|
||||
return true;
|
||||
}
|
||||
|
||||
[ContextMenu("Debug/Emit Shockwave (Server Play Mode)")]
|
||||
private void EmitFromContextMenu()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
EmitShockwave();
|
||||
}
|
||||
|
||||
private void ApplyToPlayers(Vector3 worldOrigin)
|
||||
{
|
||||
foreach (PlayerImpactController player in
|
||||
FindObjectsByType<PlayerImpactController>(FindObjectsSortMode.None))
|
||||
{
|
||||
if (!player.IsSpawned)
|
||||
continue;
|
||||
|
||||
Vector3 offset = player.transform.position - worldOrigin;
|
||||
float distance = offset.magnitude;
|
||||
if (!profile.TryEvaluate(distance, out ShockwaveZone zone))
|
||||
continue;
|
||||
|
||||
Vector3 direction = CreateDirection(offset, zone.UpwardBias);
|
||||
player.ApplyImpactOnServer(direction * zone.PlayerImpulse, zone);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyToPhysics(Vector3 worldOrigin)
|
||||
{
|
||||
int count = Physics.OverlapSphereNonAlloc(
|
||||
worldOrigin,
|
||||
profile.MaximumRadius,
|
||||
overlapBuffer,
|
||||
physicsLayers,
|
||||
QueryTriggerInteraction.Ignore);
|
||||
|
||||
affectedBodies.Clear();
|
||||
for (int index = 0; index < count; index++)
|
||||
{
|
||||
Collider hit = overlapBuffer[index];
|
||||
Rigidbody body = hit != null ? hit.attachedRigidbody : null;
|
||||
if (body == null || body.isKinematic || !affectedBodies.Add(body) ||
|
||||
body.GetComponentInParent<PlayerImpactController>() != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector3 offset = body.worldCenterOfMass - worldOrigin;
|
||||
if (!profile.TryEvaluate(offset.magnitude, out ShockwaveZone zone) ||
|
||||
zone.RigidbodyImpulse <= 0f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
body.AddForce(
|
||||
CreateDirection(offset, zone.UpwardBias) * zone.RigidbodyImpulse,
|
||||
ForceMode.Impulse);
|
||||
}
|
||||
}
|
||||
|
||||
private static Vector3 CreateDirection(Vector3 offset, float upwardBias)
|
||||
{
|
||||
if (offset.sqrMagnitude < 0.0001f)
|
||||
offset = Vector3.up;
|
||||
offset.Normalize();
|
||||
return (offset + Vector3.up * Mathf.Clamp01(upwardBias)).normalized;
|
||||
}
|
||||
|
||||
private void HandleSequenceChanged(uint previous, uint current)
|
||||
{
|
||||
if (current == previous)
|
||||
return;
|
||||
Emitted?.Invoke();
|
||||
shockwaveEmitted?.Invoke();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43f56ec799f9aec4ca1bf372c88592cd
|
||||
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
public struct PlayerImpactState : INetworkSerializable, IEquatable<PlayerImpactState>
|
||||
{
|
||||
public uint Sequence;
|
||||
public Vector3 Impulse;
|
||||
public float ShakeAmplitude;
|
||||
public float ShakeDuration;
|
||||
public float ShakeFrequency;
|
||||
public float ShakeSeed;
|
||||
|
||||
public PlayerImpactState(
|
||||
uint sequence,
|
||||
Vector3 impulse,
|
||||
float shakeAmplitude,
|
||||
float shakeDuration,
|
||||
float shakeFrequency,
|
||||
float shakeSeed)
|
||||
{
|
||||
Sequence = sequence;
|
||||
Impulse = impulse;
|
||||
ShakeAmplitude = shakeAmplitude;
|
||||
ShakeDuration = shakeDuration;
|
||||
ShakeFrequency = shakeFrequency;
|
||||
ShakeSeed = shakeSeed;
|
||||
}
|
||||
|
||||
public void NetworkSerialize<T>(BufferSerializer<T> serializer)
|
||||
where T : IReaderWriter
|
||||
{
|
||||
serializer.SerializeValue(ref Sequence);
|
||||
serializer.SerializeValue(ref Impulse);
|
||||
serializer.SerializeValue(ref ShakeAmplitude);
|
||||
serializer.SerializeValue(ref ShakeDuration);
|
||||
serializer.SerializeValue(ref ShakeFrequency);
|
||||
serializer.SerializeValue(ref ShakeSeed);
|
||||
}
|
||||
|
||||
public bool Equals(PlayerImpactState other) =>
|
||||
Sequence == other.Sequence && Impulse == other.Impulse &&
|
||||
ShakeAmplitude.Equals(other.ShakeAmplitude) &&
|
||||
ShakeDuration.Equals(other.ShakeDuration) &&
|
||||
ShakeFrequency.Equals(other.ShakeFrequency) &&
|
||||
ShakeSeed.Equals(other.ShakeSeed);
|
||||
}
|
||||
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class PlayerImpactController : NetworkBehaviour, IPlayerSystem
|
||||
{
|
||||
private readonly NetworkVariable<PlayerImpactState> impactState = new(
|
||||
default,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private PlayerController movement;
|
||||
private PlayerCameraShakeController cameraShake;
|
||||
|
||||
public uint LastImpactSequence => impactState.Value.Sequence;
|
||||
public event Action<PlayerImpactState> ImpactApplied;
|
||||
|
||||
public void Initialize(PlayerFacade facade)
|
||||
{
|
||||
movement = GetComponent<PlayerController>();
|
||||
cameraShake = GetComponent<PlayerCameraShakeController>();
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
impactState.OnValueChanged += HandleImpactChanged;
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
impactState.OnValueChanged -= HandleImpactChanged;
|
||||
}
|
||||
|
||||
public bool ApplyImpactOnServer(Vector3 impulse, ShockwaveZone zone)
|
||||
{
|
||||
if (!IsServer || !IsSpawned)
|
||||
return false;
|
||||
|
||||
impactState.Value = new PlayerImpactState(
|
||||
impactState.Value.Sequence + 1,
|
||||
impulse,
|
||||
zone.CameraShakeAmplitude,
|
||||
zone.CameraShakeDuration,
|
||||
zone.CameraShakeFrequency,
|
||||
UnityEngine.Random.Range(0f, 1000f));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void HandleImpactChanged(PlayerImpactState previous, PlayerImpactState current)
|
||||
{
|
||||
if (current.Sequence == 0 || current.Sequence == previous.Sequence)
|
||||
return;
|
||||
|
||||
ImpactApplied?.Invoke(current);
|
||||
if (!IsOwner)
|
||||
return;
|
||||
|
||||
movement?.AddImpulse(current.Impulse);
|
||||
cameraShake?.PlayShake(
|
||||
current.ShakeAmplitude,
|
||||
current.ShakeDuration,
|
||||
current.ShakeFrequency,
|
||||
current.ShakeSeed);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a7ddedc58d3467449dfbe83af1c9c51
|
||||
@@ -0,0 +1,57 @@
|
||||
using System.Collections;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
[DisallowMultipleComponent]
|
||||
[RequireComponent(typeof(NetworkRocketItem), typeof(NetworkShockwaveEmitter))]
|
||||
public sealed class RocketImpactExplosion : NetworkBehaviour
|
||||
{
|
||||
[SerializeField] private NetworkRocketItem rocket;
|
||||
[SerializeField] private NetworkShockwaveEmitter shockwave;
|
||||
[SerializeField] private bool despawnAfterExplosion = true;
|
||||
[SerializeField, Min(0f)] private float despawnDelay = 0.15f;
|
||||
|
||||
private bool exploded;
|
||||
|
||||
private void Reset() => ResolveReferences();
|
||||
private void Awake() => ResolveReferences();
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
rocket.CriticalImpact += HandleCriticalImpact;
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
if (rocket != null)
|
||||
rocket.CriticalImpact -= HandleCriticalImpact;
|
||||
}
|
||||
|
||||
private void HandleCriticalImpact()
|
||||
{
|
||||
if (!IsServer || exploded)
|
||||
return;
|
||||
|
||||
exploded = true;
|
||||
shockwave.EmitShockwave(rocket.LastCriticalImpactPoint);
|
||||
if (despawnAfterExplosion)
|
||||
StartCoroutine(DespawnAfterDelay());
|
||||
}
|
||||
|
||||
private IEnumerator DespawnAfterDelay()
|
||||
{
|
||||
if (despawnDelay > 0f)
|
||||
yield return new WaitForSeconds(despawnDelay);
|
||||
|
||||
if (IsServer && NetworkObject != null && NetworkObject.IsSpawned)
|
||||
NetworkObject.Despawn(true);
|
||||
}
|
||||
|
||||
private void ResolveReferences()
|
||||
{
|
||||
if (rocket == null)
|
||||
rocket = GetComponent<NetworkRocketItem>();
|
||||
if (shockwave == null)
|
||||
shockwave = GetComponent<NetworkShockwaveEmitter>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac11f143457835f46a1a2defaa624354
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
[Serializable]
|
||||
public struct ShockwaveZone
|
||||
{
|
||||
[Min(0.1f)] public float Radius;
|
||||
[Min(0f)] public float PlayerImpulse;
|
||||
[Min(0f)] public float RigidbodyImpulse;
|
||||
[Range(0f, 1f)] public float UpwardBias;
|
||||
[Min(0f)] public float CameraShakeAmplitude;
|
||||
[Min(0f)] public float CameraShakeDuration;
|
||||
[Min(0.1f)] public float CameraShakeFrequency;
|
||||
}
|
||||
|
||||
[CreateAssetMenu(
|
||||
fileName = "ShockwaveProfile",
|
||||
menuName = "FORT-BO/Effects/Shockwave Profile")]
|
||||
public sealed class ShockwaveProfile : ScriptableObject
|
||||
{
|
||||
[Tooltip("Zones are evaluated from the smallest radius to the largest.")]
|
||||
[SerializeField] private ShockwaveZone[] zones = Array.Empty<ShockwaveZone>();
|
||||
|
||||
public float MaximumRadius
|
||||
{
|
||||
get
|
||||
{
|
||||
float maximum = 0f;
|
||||
foreach (ShockwaveZone zone in zones)
|
||||
maximum = Mathf.Max(maximum, zone.Radius);
|
||||
return maximum;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryEvaluate(float distance, out ShockwaveZone result)
|
||||
{
|
||||
result = default;
|
||||
float bestRadius = float.PositiveInfinity;
|
||||
bool found = false;
|
||||
|
||||
foreach (ShockwaveZone zone in zones)
|
||||
{
|
||||
if (distance > zone.Radius || zone.Radius >= bestRadius)
|
||||
continue;
|
||||
|
||||
result = zone;
|
||||
bestRadius = zone.Radius;
|
||||
found = true;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
for (int index = 0; index < zones.Length; index++)
|
||||
{
|
||||
ShockwaveZone zone = zones[index];
|
||||
zone.Radius = Mathf.Max(0.1f, zone.Radius);
|
||||
zone.PlayerImpulse = Mathf.Max(0f, zone.PlayerImpulse);
|
||||
zone.RigidbodyImpulse = Mathf.Max(0f, zone.RigidbodyImpulse);
|
||||
zone.CameraShakeAmplitude = Mathf.Max(0f, zone.CameraShakeAmplitude);
|
||||
zone.CameraShakeDuration = Mathf.Max(0f, zone.CameraShakeDuration);
|
||||
zone.CameraShakeFrequency = Mathf.Max(0.1f, zone.CameraShakeFrequency);
|
||||
zones[index] = zone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96e16f6f88696ea4c8b884f3e7cef9f7
|
||||
Reference in New Issue
Block a user