Forked from andrew-raphael-lukasik/.AttackSpotProvider.cs.md
Created
August 2, 2025 14:33
-
-
Save HajiyevEl/3a83a3d88ae2ca5c9d44508d3da43ab7 to your computer and use it in GitHub Desktop.
Revisions
-
andrew-raphael-lukasik revised this gist
Oct 5, 2023 . 3 changed files with 4 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,3 +2,5 @@ Answers a question: "How to make enemies surround player" - `AttackSpotProvider.cs` provides attack positions to attackers. - `Attacker.cs` simulates an attacker  This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,4 @@ // src*: https://gist.github.com/andrew-raphael-lukasik/95ee4c1ee27a0087e7e8dc1785b6c542 using System.Collections.Generic; using UnityEngine; using UnityEngine.Assertions; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,4 @@ // src*: https://gist.github.com/andrew-raphael-lukasik/95ee4c1ee27a0087e7e8dc1785b6c542 using UnityEngine; public class Attacker : MonoBehaviour -
andrew-raphael-lukasik created this gist
Oct 5, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,4 @@ Answers a question: "How to make enemies surround player" - `AttackSpotProvider.cs` provides attack positions to attackers. - `Attacker.cs` simulates an attacker This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,127 @@ // src*: using System.Collections.Generic; using UnityEngine; using UnityEngine.Assertions; using BurstCompile = Unity.Burst.BurstCompileAttribute; public class AttackSpotProvider : MonoBehaviour { [SerializeField][Min(0)] float _playerRadius = 1.0f; [SerializeField][Min(0)] float _enemyRadius = 0.5f; HashSet<int> _takenSpots = new (); #if UNITY_EDITOR void OnDrawGizmos () { Vector2 origin = transform.position; UnityEditor.Handles.color = Color.yellow; UnityEditor.Handles.CircleHandleCap( 0 , origin , Quaternion.identity , _playerRadius , EventType.Repaint ); foreach( int spotIndex in _takenSpots ) { AttackSpotProviderUtilities.GetRing( spotIndex , _playerRadius , _enemyRadius , out int ring , out int firstSpotIndexForThisRing ); float innerCircle = _playerRadius + ring * _enemyRadius * 2f; int cmax = AttackSpotProviderUtilities.NumSpotsAroundACircleApprox( circleRadius:innerCircle , spotRadius:_enemyRadius ); int c = spotIndex - firstSpotIndexForThisRing; AttackSpotProviderUtilities.SpotOffset( circleIndex:c , circleRadius:innerCircle , spotRadius:_enemyRadius , cmax , out Vector2 spotOffset ); UnityEditor.Handles.CircleHandleCap( 0 , origin + spotOffset , Quaternion.identity , _enemyRadius , EventType.Repaint ); } } #endif public void RegisterAttacker ( Vector2 attackerPosition , out int assignedSpotIndex , out Vector2 assignedOffset ) { Vector2 spotOrigin = transform.position; int spotIndex = -1; for( int ring=0 ; ring<10 ; ring++ ) { float innerCircle = _playerRadius + ring * _enemyRadius * 2f; int cmax = AttackSpotProviderUtilities.NumSpotsAroundACircleApprox( circleRadius:innerCircle , spotRadius:_enemyRadius ); int nearestSpotIndex = -1; float nearestDist = float.MaxValue; Vector2 nearestOffset = Vector2.zero; for( int c=0 ; c<cmax ; c++ ) { spotIndex++; if( !_takenSpots.Contains(spotIndex) ) { AttackSpotProviderUtilities.SpotOffset( circleIndex:c , circleRadius:innerCircle , spotRadius:_enemyRadius , numSpotsAroundCircle:cmax , out Vector2 spotOffset ); float spotDistance = Vector2.Distance( attackerPosition , spotOrigin + spotOffset ); if( spotDistance<nearestDist ) { nearestSpotIndex = spotIndex; nearestDist = spotDistance; nearestOffset = spotOffset; } } } if( nearestSpotIndex!=-1 ) { assignedSpotIndex = nearestSpotIndex; _takenSpots.Add( nearestSpotIndex ); assignedOffset = nearestOffset; return; } } throw new System.Exception("Something went wrong here."); } public void UnregisterAttacker ( int spotIndex ) { Assert.IsTrue( _takenSpots.Contains(spotIndex) ); _takenSpots.Remove( spotIndex ); } } // burst-compiled to make these calculations go brrrrr fast [BurstCompile] public static class AttackSpotProviderUtilities { /// <summary> quick and dirty approximation </summary> [BurstCompile] public static int NumSpotsAroundACircleApprox ( float circleRadius , float spotRadius ) { Assert.IsTrue( circleRadius>0 , $"{circleRadius} > 0" ); Assert.IsTrue( spotRadius>0 , $"{spotRadius} > 0" ); return Mathf.FloorToInt( ( 2f*Mathf.PI*( circleRadius + spotRadius ) )/(2f*spotRadius) ); } /// <summary> Calculates local spot position </summary> [BurstCompile] public static void SpotOffset ( int circleIndex , float circleRadius , float spotRadius , int numSpotsAroundCircle , out Vector2 spotOffset ) { Assert.IsTrue( circleIndex>=0 , $"{circleIndex} >= 0" ); Assert.IsTrue( circleRadius>0 , $"{circleRadius} > 0" ); Assert.IsTrue( spotRadius>0 , $"{spotRadius} > 0" ); Assert.IsTrue( numSpotsAroundCircle>0 , $"{numSpotsAroundCircle} > 0" ); float angle = Mathf.PI*2f * circleIndex/numSpotsAroundCircle; Vector2 unitVector = new Vector2( Mathf.Cos(angle) , Mathf.Sin(angle) ); spotOffset = unitVector*circleRadius + unitVector*spotRadius; } /// <summary> Calculates ring index from spot index </summary> [BurstCompile] public static void GetRing ( int spotIndex , float initialCircleRadius , float spotRadius , out int ring , out int firstSpotIndexForThisRing ) { Assert.IsTrue( spotIndex>=0 , $"{spotIndex} >= 0" ); Assert.IsTrue( initialCircleRadius>0 , $"{initialCircleRadius} > 0" ); Assert.IsTrue( spotRadius>0 , $"{spotRadius} > 0" ); int cmaxSum = 0; for( ring=0 ; ring<100 ; ring++ ) { float innerCircle = initialCircleRadius + ring * spotRadius * 2f; firstSpotIndexForThisRing = cmaxSum; cmaxSum += NumSpotsAroundACircleApprox( circleRadius:innerCircle , spotRadius:spotRadius ); if( cmaxSum>spotIndex) return; } throw new System.Exception("Something went wrong here."); } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,57 @@ // src*: using UnityEngine; public class Attacker : MonoBehaviour { [SerializeField] GameObject _attacked; [SerializeField] float _tickTime = 0.23f;// seconds AttackSpotProvider _attackSpotProvider; int _assignedSpotIndex; Vector2 _assignedOffset; #if UNITY_EDITOR void OnDrawGizmos () { UnityEditor.Handles.color = Color.yellow; UnityEditor.Handles.CircleHandleCap( 0 , transform.position , Quaternion.identity , 0.1f , EventType.Repaint ); if( _attackSpotProvider!=null ) { Vector3 src = transform.position; Vector3 dst = _attackSpotProvider.transform.position + (Vector3)_assignedOffset; Vector3 dir = dst - src; Gizmos.color = Color.cyan; Gizmos.DrawLine( src , dst ); UnityEditor.Handles.color = Gizmos.color; UnityEditor.Handles.ArrowHandleCap( 0 , dst-dir.normalized*0.6f , Quaternion.LookRotation(dir) , 0.6f , EventType.Repaint ); } } #endif void OnEnable () { _attackSpotProvider = _attacked.GetComponent<AttackSpotProvider>(); if( _attackSpotProvider!=null ) { _attackSpotProvider.RegisterAttacker( transform.position , out _assignedSpotIndex , out _assignedOffset ); InvokeRepeating( nameof(Tick) , Random.Range(0,_tickTime) , _tickTime ); } } void OnDisable () { if( _attackSpotProvider!=null ) { _attackSpotProvider.UnregisterAttacker( _assignedSpotIndex ); } } void Tick () { _attackSpotProvider.UnregisterAttacker( _assignedSpotIndex ); _attackSpotProvider.RegisterAttacker( transform.position , out _assignedSpotIndex , out _assignedOffset ); } }