├── ProjectSettings ├── ProjectVersion.txt ├── TagManager.asset ├── VFXManager.asset ├── AudioManager.asset ├── InputManager.asset ├── NavMeshAreas.asset ├── NavMeshLayers.asset ├── PresetManager.asset ├── TimeManager.asset ├── DynamicsManager.asset ├── EditorSettings.asset ├── NetworkManager.asset ├── ProjectSettings.asset ├── QualitySettings.asset ├── GraphicsSettings.asset ├── Physics2DSettings.asset ├── ClusterInputManager.asset ├── EditorBuildSettings.asset └── UnityConnectSettings.asset ├── Assets ├── Materials │ ├── Hard.mat │ ├── Ground.mat │ ├── Obstacle.mat │ ├── Ground.mat.meta │ ├── Hard.mat.meta │ └── Obstacle.mat.meta ├── Scenes │ ├── Main.unity │ └── Main.unity.meta ├── Prefabs │ ├── Boid.prefab │ ├── Path.prefab │ ├── Plane.prefab │ ├── Obstacle.prefab │ ├── Plane.prefab.meta │ ├── Boid.prefab.meta │ ├── Path.prefab.meta │ └── Obstacle.prefab.meta ├── Textures │ ├── Ground.png │ └── Ground.png.meta ├── Materials.meta ├── Scenes.meta ├── Scripts │ ├── EntityInterface.cs │ ├── Boid.cs.meta │ ├── Path.cs.meta │ ├── Team.cs.meta │ ├── Obstacle.cs.meta │ ├── EntityInterface.cs.meta │ ├── SteerManager.cs.meta │ ├── VectorExtension.cs.meta │ ├── VectorExtension.cs │ ├── Obstacle.cs │ ├── Team.cs │ ├── Path.cs │ ├── Boid.cs │ └── SteerManager.cs ├── Textures.meta ├── Prefabs.meta └── Scripts.meta ├── README.md ├── .gitignore ├── LICENSE └── Packages └── manifest.json /ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 2018.4.7f1 2 | -------------------------------------------------------------------------------- /Assets/Materials/Hard.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/Assets/Materials/Hard.mat -------------------------------------------------------------------------------- /Assets/Scenes/Main.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/Assets/Scenes/Main.unity -------------------------------------------------------------------------------- /Assets/Materials/Ground.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/Assets/Materials/Ground.mat -------------------------------------------------------------------------------- /Assets/Prefabs/Boid.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/Assets/Prefabs/Boid.prefab -------------------------------------------------------------------------------- /Assets/Prefabs/Path.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/Assets/Prefabs/Path.prefab -------------------------------------------------------------------------------- /Assets/Prefabs/Plane.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/Assets/Prefabs/Plane.prefab -------------------------------------------------------------------------------- /Assets/Textures/Ground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/Assets/Textures/Ground.png -------------------------------------------------------------------------------- /Assets/Materials/Obstacle.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/Assets/Materials/Obstacle.mat -------------------------------------------------------------------------------- /Assets/Prefabs/Obstacle.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/Assets/Prefabs/Obstacle.prefab -------------------------------------------------------------------------------- /ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/TagManager.asset -------------------------------------------------------------------------------- /ProjectSettings/VFXManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/VFXManager.asset -------------------------------------------------------------------------------- /ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/AudioManager.asset -------------------------------------------------------------------------------- /ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/InputManager.asset -------------------------------------------------------------------------------- /ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/NavMeshAreas.asset -------------------------------------------------------------------------------- /ProjectSettings/NavMeshLayers.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/NavMeshLayers.asset -------------------------------------------------------------------------------- /ProjectSettings/PresetManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/PresetManager.asset -------------------------------------------------------------------------------- /ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/TimeManager.asset -------------------------------------------------------------------------------- /ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/DynamicsManager.asset -------------------------------------------------------------------------------- /ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/EditorSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/NetworkManager.asset -------------------------------------------------------------------------------- /ProjectSettings/ProjectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/ProjectSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/QualitySettings.asset -------------------------------------------------------------------------------- /Assets/Scenes/Main.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 38cc34d2cfef3464d8f4a8da8385bb89 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/GraphicsSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/Physics2DSettings.asset -------------------------------------------------------------------------------- /Assets/Materials/Ground.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 076ef7de68dde61469fa94f2950eb63d 3 | NativeFormatImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/Prefabs/Plane.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 87ca43d262a87f944af78136879504f3 3 | NativeFormatImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /ProjectSettings/ClusterInputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/ClusterInputManager.asset -------------------------------------------------------------------------------- /ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/EditorBuildSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/UnityConnectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iWoz/path_follow_steer/HEAD/ProjectSettings/UnityConnectSettings.asset -------------------------------------------------------------------------------- /Assets/Materials.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7623b0ffd4f37c147a7072c74559c299 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/Scenes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dd2240875fd65e5499acf7f1f42ca226 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/Scripts/EntityInterface.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | public interface EntityInterface 3 | { 4 | Vector2 Position { get; set; } 5 | } 6 | -------------------------------------------------------------------------------- /Assets/Textures.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 71e55086b87f2cb4383a8e38d58dea85 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # path_follow_steer 2 | https://wuzhiwei.net/group-path-movement/ 3 | 4 | 欢迎关注公众号: 5 | ![image](https://user-images.githubusercontent.com/1621110/215088294-fc24b001-23d3-40e4-be2e-1f50a0d6d936.png) 6 | 7 | -------------------------------------------------------------------------------- /Assets/Prefabs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 73e274d0b8625b043bbdabebb8308dcc 3 | folderAsset: yes 4 | timeCreated: 1541149115 5 | licenseType: Pro 6 | DefaultImporter: 7 | externalObjects: {} 8 | userData: 9 | assetBundleName: 10 | assetBundleVariant: 11 | -------------------------------------------------------------------------------- /Assets/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4ea2d541123ac3e4a97188357f4ee637 3 | folderAsset: yes 4 | timeCreated: 1541148518 5 | licenseType: Pro 6 | DefaultImporter: 7 | externalObjects: {} 8 | userData: 9 | assetBundleName: 10 | assetBundleVariant: 11 | -------------------------------------------------------------------------------- /Assets/Materials/Hard.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a5ca78c28ae5f624482f15773369953e 3 | timeCreated: 1541390474 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | externalObjects: {} 7 | mainObjectFileID: 0 8 | userData: 9 | assetBundleName: 10 | assetBundleVariant: 11 | -------------------------------------------------------------------------------- /Assets/Prefabs/Boid.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c0a49daf705269c449a223beb620acb5 3 | timeCreated: 1541148292 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | externalObjects: {} 7 | mainObjectFileID: 100100000 8 | userData: 9 | assetBundleName: 10 | assetBundleVariant: 11 | -------------------------------------------------------------------------------- /Assets/Prefabs/Path.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f47d53732234f7040bef17ea3a714d2f 3 | timeCreated: 1541148375 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | externalObjects: {} 7 | mainObjectFileID: 100100000 8 | userData: 9 | assetBundleName: 10 | assetBundleVariant: 11 | -------------------------------------------------------------------------------- /Assets/Materials/Obstacle.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9b8fae8fa4be92a42b004a98589d5d6b 3 | timeCreated: 1541214368 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | externalObjects: {} 7 | mainObjectFileID: 2100000 8 | userData: 9 | assetBundleName: 10 | assetBundleVariant: 11 | -------------------------------------------------------------------------------- /Assets/Prefabs/Obstacle.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 73a20d9d458ea6a419e8d424706f9e7a 3 | timeCreated: 1541214411 4 | licenseType: Pro 5 | NativeFormatImporter: 6 | externalObjects: {} 7 | mainObjectFileID: 100100000 8 | userData: 9 | assetBundleName: 10 | assetBundleVariant: 11 | -------------------------------------------------------------------------------- /Assets/Scripts/Boid.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4bdeaf2703575d54c8a49f28954f90c4 3 | timeCreated: 1541149099 4 | licenseType: Pro 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Assets/Scripts/Path.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cc2aebe6a96d09a46a617d1a285722c3 3 | timeCreated: 1541149211 4 | licenseType: Pro 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Assets/Scripts/Team.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 28133a3e1c460f34bb309a3bdb25d85f 3 | timeCreated: 1541388695 4 | licenseType: Pro 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Assets/Scripts/Obstacle.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f026065170f2eb544a9b9c63f166ddc1 3 | timeCreated: 1541214176 4 | licenseType: Pro 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Assets/Scripts/EntityInterface.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e00d3b0d830188b43a146e6bd78f9d64 3 | timeCreated: 1541407496 4 | licenseType: Pro 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Assets/Scripts/SteerManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 23ca3cb52eb23ff43add74c725381f5d 3 | timeCreated: 1541148752 4 | licenseType: Pro 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Assets/Scripts/VectorExtension.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fdc12a1e4727dfe45a040dd1229e90be 3 | timeCreated: 1541216173 4 | licenseType: Pro 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Assets/Scripts/VectorExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | public static class VectorExtension 7 | { 8 | public static Vector2 GetParallelComponent(this Vector2 vec, Vector2 unitBasis) 9 | { 10 | return Vector2.Dot(vec, unitBasis) * unitBasis; 11 | } 12 | public static Vector2 GetVerticalComponent(this Vector2 vec, Vector2 unitBasis) 13 | { 14 | return vec - vec.GetParallelComponent(unitBasis); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Ll]ibrary/ 2 | [Tt]emp/ 3 | [Oo]bj/ 4 | [Bb]uild/ 5 | [Bb]uilds/ 6 | Assets/AssetStoreTools* 7 | 8 | # Visual Studio cache directory 9 | .vs/ 10 | 11 | # Autogenerated VS/MD/Consulo solution and project files 12 | ExportedObj/ 13 | .consulo/ 14 | *.csproj 15 | *.unityproj 16 | *.sln 17 | *.suo 18 | *.tmp 19 | *.user 20 | *.userprefs 21 | *.pidb 22 | *.booproj 23 | *.svd 24 | *.pdb 25 | *.opendb 26 | 27 | # Unity3D generated meta files 28 | *.pidb.meta 29 | *.pdb.meta 30 | 31 | # Unity3D Generated File On Crash Reports 32 | sysinfo.txt 33 | 34 | # Builds 35 | *.apk 36 | *.unitypackage 37 | -------------------------------------------------------------------------------- /Assets/Scripts/Obstacle.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class Obstacle : MonoBehaviour, EntityInterface 6 | { 7 | 8 | public float Radius = 0.5f; 9 | public float HardRadiusRatio = 0.5f; 10 | public float HardRadius = 0.5f; 11 | public Transform HardTransform; 12 | private Vector2 m_position; 13 | public Vector2 Position 14 | { 15 | get { return m_position; } 16 | set 17 | { 18 | m_position = value; 19 | transform.position = new Vector3(value.x, 0, value.y); 20 | } 21 | } 22 | void Start () 23 | { 24 | HardRadius = Radius * HardRadiusRatio; 25 | transform.localScale = (Radius / 0.5f) * Vector3.one; 26 | HardTransform.localScale = new Vector3(HardRadiusRatio, 1.01f, HardRadiusRatio); 27 | m_position = new Vector2(transform.position.x, transform.position.z); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Assets/Scripts/Team.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | 7 | public class Team 8 | { 9 | public Vector2 MeanPosition; 10 | public Vector2 Direction = new Vector2(1, 0); 11 | private List Members = new List(); 12 | 13 | public void AddMember(Boid member) 14 | { 15 | member.Team = this; 16 | member.Direction = Direction; 17 | Members.Add(member); 18 | } 19 | 20 | public void RemoveMember(Boid member) 21 | { 22 | member.Team = null; 23 | Members.Remove(member); 24 | } 25 | 26 | public void PreUpdate() 27 | { 28 | if (Members.Count == 0) 29 | return; 30 | 31 | foreach (var boid in Members) 32 | { 33 | MeanPosition += boid.Position; 34 | } 35 | MeanPosition /= Members.Count; 36 | } 37 | 38 | public void Update() 39 | { 40 | foreach (var boid in Members) 41 | { 42 | boid.LogicUpdate(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Assets/Textures/Ground.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1245d42ea2b3d514b982929c9df10c32 3 | TextureImporter: 4 | serializedVersion: 2 5 | mipmaps: 6 | mipMapMode: 0 7 | enableMipMap: 1 8 | linearTexture: 0 9 | correctGamma: 0 10 | fadeOut: 0 11 | borderMipMap: 0 12 | mipMapFadeDistanceStart: 1 13 | mipMapFadeDistanceEnd: 3 14 | bumpmap: 15 | convertToNormalMap: 0 16 | externalNormalMap: 0 17 | heightScale: .25 18 | normalMapFilter: 0 19 | isReadable: 0 20 | grayScaleToAlpha: 0 21 | generateCubemap: 0 22 | seamlessCubemap: 0 23 | textureFormat: -1 24 | maxTextureSize: 1024 25 | textureSettings: 26 | filterMode: -1 27 | aniso: -1 28 | mipBias: -1 29 | wrapMode: -1 30 | nPOTScale: 1 31 | lightmap: 0 32 | compressionQuality: 50 33 | spriteMode: 0 34 | spriteExtrude: 1 35 | spriteMeshType: 1 36 | alignment: 0 37 | spritePivot: {x: .5, y: .5} 38 | spritePixelsToUnits: 100 39 | alphaIsTransparency: 0 40 | textureType: -1 41 | buildTargetSettings: [] 42 | spriteSheet: 43 | sprites: [] 44 | spritePackingTag: 45 | userData: 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Tim Wu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Packages/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.unity.ads": "2.0.8", 4 | "com.unity.analytics": "3.2.2", 5 | "com.unity.collab-proxy": "1.2.15", 6 | "com.unity.package-manager-ui": "2.0.7", 7 | "com.unity.purchasing": "2.0.3", 8 | "com.unity.textmeshpro": "1.4.1", 9 | "com.unity.modules.ai": "1.0.0", 10 | "com.unity.modules.animation": "1.0.0", 11 | "com.unity.modules.assetbundle": "1.0.0", 12 | "com.unity.modules.audio": "1.0.0", 13 | "com.unity.modules.cloth": "1.0.0", 14 | "com.unity.modules.director": "1.0.0", 15 | "com.unity.modules.imageconversion": "1.0.0", 16 | "com.unity.modules.imgui": "1.0.0", 17 | "com.unity.modules.jsonserialize": "1.0.0", 18 | "com.unity.modules.particlesystem": "1.0.0", 19 | "com.unity.modules.physics": "1.0.0", 20 | "com.unity.modules.physics2d": "1.0.0", 21 | "com.unity.modules.screencapture": "1.0.0", 22 | "com.unity.modules.terrain": "1.0.0", 23 | "com.unity.modules.terrainphysics": "1.0.0", 24 | "com.unity.modules.tilemap": "1.0.0", 25 | "com.unity.modules.ui": "1.0.0", 26 | "com.unity.modules.uielements": "1.0.0", 27 | "com.unity.modules.umbra": "1.0.0", 28 | "com.unity.modules.unityanalytics": "1.0.0", 29 | "com.unity.modules.unitywebrequest": "1.0.0", 30 | "com.unity.modules.unitywebrequestassetbundle": "1.0.0", 31 | "com.unity.modules.unitywebrequestaudio": "1.0.0", 32 | "com.unity.modules.unitywebrequesttexture": "1.0.0", 33 | "com.unity.modules.unitywebrequestwww": "1.0.0", 34 | "com.unity.modules.vehicles": "1.0.0", 35 | "com.unity.modules.video": "1.0.0", 36 | "com.unity.modules.vr": "1.0.0", 37 | "com.unity.modules.wind": "1.0.0", 38 | "com.unity.modules.xr": "1.0.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Assets/Scripts/Path.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class Path : MonoBehaviour 6 | { 7 | 8 | public Vector3[] Waypoints; 9 | public LineRenderer LineRenderer; 10 | public float Radius = 2f; 11 | public float TotalPathLength = 0; 12 | private float m_radiusSqr; 13 | private List m_path = new List(), m_normals = new List(); 14 | private List m_lengths = new List(); 15 | 16 | void Start() 17 | { 18 | LineRenderer.GetPositions(Waypoints); 19 | Init(); 20 | } 21 | 22 | public void Init() 23 | { 24 | m_radiusSqr = Radius * Radius; 25 | m_lengths.Add(0); 26 | m_normals.Add(Vector2.zero); 27 | foreach (var point in Waypoints) 28 | { 29 | m_path.Add(new Vector2(point.x, point.z)); 30 | } 31 | 32 | for (int i = 1; i < m_path.Count; i++) 33 | { 34 | Vector2 normal = m_path[i] - m_path[i - 1]; 35 | float length = normal.magnitude; 36 | m_lengths.Add(length); 37 | m_normals.Add(normal / length); 38 | TotalPathLength += length; 39 | } 40 | } 41 | 42 | public float GetDistanceByMapPoint(Vector2 point, out bool isOutOfPath, out Vector2 segmentNomal) 43 | { 44 | float pathDistance = 0, segmentLengthTotal = 0; 45 | float minDistSqr = float.MaxValue; 46 | Vector2 mapPoint; 47 | segmentNomal = Vector2.zero; 48 | for (int i = 1; i < m_path.Count; i++) 49 | { 50 | float projectionLength = 0; 51 | float distSqr = SteerManager.GetPointToSegmentDistanceSqr(point, m_path[i - 1], m_path[i], m_normals[i], m_lengths[i], out mapPoint, out projectionLength); 52 | if (distSqr < minDistSqr) 53 | { 54 | minDistSqr = distSqr; 55 | segmentNomal = m_normals[i]; 56 | pathDistance = segmentLengthTotal + projectionLength; 57 | } 58 | segmentLengthTotal += m_lengths[i]; 59 | } 60 | isOutOfPath = minDistSqr > m_radiusSqr; 61 | return pathDistance; 62 | } 63 | 64 | public Vector2 GetPathPointByDistance(float distance) 65 | { 66 | if (distance < 0) 67 | return m_path[0]; 68 | if (distance >= TotalPathLength) 69 | return m_path[m_path.Count - 1]; 70 | 71 | float remainLength = distance; 72 | for (int i = 1; i < m_path.Count; i++) 73 | { 74 | float segmentLength = m_lengths[i]; 75 | if (segmentLength < remainLength) 76 | remainLength -= segmentLength; 77 | else 78 | return Vector2.Lerp(m_path[i - 1], m_path[i], remainLength / segmentLength); 79 | } 80 | return m_path[0]; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Assets/Scripts/Boid.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class Boid : MonoBehaviour, EntityInterface 6 | { 7 | public static int CurrentID; 8 | public int ID; 9 | public Path Path; 10 | public float Radius = 0.5f; 11 | public float HardRadius = 0.5f; 12 | public float HardRadiusRatio = 0.5f; 13 | public Transform HardTransform; 14 | public List Neighbors; 15 | public Team Team; 16 | private Vector2 m_position; 17 | public Vector2 Position 18 | { 19 | get { return m_position; } 20 | set 21 | { 22 | m_position = value; 23 | transform.position = new Vector3(value.x, 0, value.y); 24 | } } 25 | public Vector2 NextPosition 26 | { 27 | get { return m_position + Direction * Speed * Time.fixedDeltaTime; } 28 | } 29 | public Vector2 VeryNextPosition 30 | { 31 | get { return m_position + Direction * Speed; } 32 | } 33 | public Vector2 Forward; 34 | private Vector2 m_direction; 35 | public Vector2 Direction 36 | { 37 | get { return m_direction; } 38 | set 39 | { 40 | m_direction = value; 41 | transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(new Vector3(m_direction.x, 0, m_direction.y)), 2 * Time.fixedDeltaTime); 42 | } } 43 | public float Speed = 2f; 44 | 45 | void Start() 46 | { 47 | ID = CurrentID++; 48 | HardTransform.localScale = new Vector3(HardRadiusRatio, 1.01f, HardRadiusRatio); 49 | HardRadius = Radius * HardRadiusRatio; 50 | float scale = Radius / 0.5f; 51 | transform.eulerAngles = scale * Vector3.one; 52 | Forward = new Vector2(Random.Range(-1,1), Random.Range(-1,1)).normalized; 53 | Direction = Forward; 54 | } 55 | 56 | private int stuckFrames = 0; 57 | 58 | public void LogicUpdate() 59 | { 60 | Obstacle collider; 61 | Vector2 obstacleAvoidSteer = SteerManager.GetObstacleAvoidSteer(this, SteerManager.Instance.Obstacles, out collider); 62 | if (collider != null && Random.Range(0, 100) > SteerManager.Instance.IgnoreCollisionProbability) 63 | { 64 | float hardRadiusSum = collider.HardRadius + Radius; 65 | float distanceSqr = (Position - collider.Position).sqrMagnitude; 66 | if (distanceSqr <= hardRadiusSum * hardRadiusSum) 67 | { 68 | Forward = (Forward + obstacleAvoidSteer).normalized; 69 | Direction = (Forward + obstacleAvoidSteer).normalized; 70 | } 71 | else 72 | { 73 | float nextPositionDistanceSqr = (NextPosition - collider.Position).sqrMagnitude; 74 | if (nextPositionDistanceSqr <= hardRadiusSum * hardRadiusSum) 75 | { 76 | Forward = (Forward + obstacleAvoidSteer).normalized; 77 | stuckFrames++; 78 | if (stuckFrames > 2) 79 | { 80 | Forward = SteerManager.GetStuckSolveSteer(this, SteerManager.Instance.Obstacles); 81 | Direction = Forward; 82 | } 83 | else 84 | return; 85 | } 86 | } 87 | } 88 | else 89 | { 90 | Boid collideNeighbor; 91 | Vector2 seprationSteer = SteerManager.GetSeprationSteer(this, Neighbors, out collideNeighbor); 92 | if (collideNeighbor != null && Random.Range(0, 100) > SteerManager.Instance.IgnoreCollisionProbability) 93 | { 94 | Direction = (Forward + seprationSteer).normalized; 95 | } 96 | else 97 | { 98 | seprationSteer *= SteerManager.Instance.SeprationWeight; 99 | Vector2 pathfollowSteer = SteerManager.GetPathFollowSteer(this, Path) * SteerManager.Instance.PathFollowWeight; 100 | Vector2 alignmentSteer = SteerManager.GetAlignmentSteer(this) * SteerManager.Instance.AlignmentWeight; 101 | Vector2 cohesionSteer = SteerManager.GetCohesionSteer(this) * SteerManager.Instance.CohesionWeight; 102 | if (SteerManager.Instance.PathFollowWeight != 0) 103 | Direction = (Forward + pathfollowSteer + seprationSteer).normalized; 104 | else 105 | Direction = (Forward + pathfollowSteer + seprationSteer + alignmentSteer + cohesionSteer).normalized; 106 | } 107 | } 108 | Forward = Direction; 109 | Position = NextPosition; 110 | stuckFrames = 0; 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /Assets/Scripts/SteerManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class SteerManager : MonoBehaviour 6 | { 7 | public static SteerManager Instance; 8 | 9 | public Path Path; 10 | public GameObject BoidPrefab; 11 | public int TeamBoidAmount = 10; 12 | public bool RandomBorn = false; 13 | public float BornVariance = 5; 14 | public int TeamColumns = 10; 15 | public float TeamColGap = 0.2f; 16 | public float TeamColGapVar = 0.1f; 17 | public float TeamRowGap = 1.1f; 18 | public float TeamRowGapVar = 0.2f; 19 | public float NeighborRadiusScale = 2f; 20 | public GameObject ObsatclePrefab; 21 | public int ObstacleAmount = 10; 22 | public List Obstacles = new List(); 23 | public List Teams = new List(); 24 | 25 | public float PathFollowWeight = 1f; 26 | public float SeprationWeight = 0.1f; 27 | public float CohesionWeight = 0.1f; 28 | public float AlignmentWeight = 0.01f; 29 | public float IgnoreCollisionProbability = 10; 30 | 31 | private List m_boids = new List(20); 32 | 33 | private void Awake() 34 | { 35 | Instance = this; 36 | } 37 | 38 | void Start() 39 | { 40 | InitTeams(); 41 | InitObstacles(); 42 | } 43 | 44 | void InitObstacles() 45 | { 46 | for (int i = 0; i < ObstacleAmount; i++) 47 | { 48 | var obstacleGo = Instantiate(ObsatclePrefab); 49 | var obstacle = obstacleGo.GetComponent(); 50 | obstacle.Position = new Vector2(Random.Range(5, 30), Random.Range(8, -8)); 51 | Obstacles.Add(obstacle); 52 | } 53 | } 54 | 55 | void InitTeams() 56 | { 57 | InitTeam(Vector2.zero, new Vector2(1,0)); 58 | //InitTeam(new Vector2(30, 0), new Vector2(-1, 0)); 59 | } 60 | 61 | void InitTeam(Vector2 teamInitPosition, Vector2 teamDirection) 62 | { 63 | Team team = new Team(); 64 | team.Direction = teamDirection; 65 | int row = -1, col = 0; 66 | for (int i = 0; i < TeamBoidAmount; i++) 67 | { 68 | var boidGo = Instantiate(BoidPrefab); 69 | var boid = boidGo.GetComponent(); 70 | if (RandomBorn) 71 | boid.Position = new Vector2(Random.Range(-BornVariance, BornVariance), Random.Range(-BornVariance, BornVariance)); 72 | else 73 | { 74 | if (i % TeamColumns == 0) 75 | { 76 | row += 1; 77 | col = 0; 78 | } 79 | else 80 | col++; 81 | float x = teamInitPosition.x + row * TeamRowGap + Random.Range(-TeamRowGapVar, TeamRowGapVar) + 2 * row * boid.Radius; 82 | float y = teamInitPosition.y + col * TeamColGap + Random.Range(-TeamColGapVar, TeamColGapVar) + 2 * col * boid.Radius; 83 | boid.Position = new Vector2(x, y); 84 | } 85 | boid.Path = Path; 86 | team.AddMember(boid); 87 | m_boids.Add(boid); 88 | } 89 | Teams.Add(team); 90 | } 91 | 92 | void SetAllBoidsNeighbor() 93 | { 94 | foreach (var b1 in m_boids) 95 | { 96 | b1.Neighbors.Clear(); 97 | foreach (var b2 in m_boids) 98 | { 99 | if (b1 == b2) 100 | continue; 101 | var checkRadius = b1.Radius * NeighborRadiusScale + b2.Radius; 102 | if ((b1.Position - b2.Position).sqrMagnitude <= checkRadius * checkRadius) 103 | b1.Neighbors.Add(b2); 104 | } 105 | } 106 | } 107 | 108 | public Boid GetNearestCollider(Vector2 position, Boid askBoid) 109 | { 110 | float nearestDist = 0; 111 | Boid collider = null; 112 | foreach (var boid in m_boids) 113 | { 114 | if (boid == askBoid) 115 | continue; 116 | var dist = (boid.Position - askBoid.Position).magnitude - askBoid.Radius - boid.Radius; 117 | if (dist < nearestDist) 118 | { 119 | collider = boid; 120 | nearestDist = dist; 121 | } 122 | } 123 | return collider; 124 | } 125 | 126 | public static Vector2 GetPathFollowSteer(Boid boid, Path path) 127 | { 128 | Vector2 nextPosition = boid.VeryNextPosition, segmentNormal; 129 | bool isOutOfPathTunnel; 130 | float distance = path.GetDistanceByMapPoint(nextPosition, out isOutOfPathTunnel, out segmentNormal); 131 | if (distance >= path.TotalPathLength) 132 | return -boid.Forward; 133 | bool isWrongDirection = Vector2.Dot(segmentNormal, boid.Forward) < 0; 134 | bool needSteerToFollowPath = isOutOfPathTunnel || isWrongDirection;// || (boid.MoveState == UnitMoveState.StandBy); 135 | if (needSteerToFollowPath) 136 | { 137 | float nextPathDistance = distance + (isWrongDirection ? 100f : 1) * boid.Speed * Time.fixedDeltaTime; 138 | Vector2 targetPoint = path.GetPathPointByDistance(nextPathDistance); 139 | return GetSeekSteer(boid, targetPoint); 140 | } 141 | else 142 | return Vector2.zero; 143 | } 144 | 145 | public static Vector2 GetCohesionSteer(Boid unit) 146 | { 147 | if (unit.Team == null) 148 | return Vector2.zero; 149 | 150 | return (unit.Team.MeanPosition - unit.Position).normalized; 151 | } 152 | public static Vector2 GetAlignmentSteer(Boid unit) 153 | { 154 | if (unit.Team == null) 155 | return Vector2.zero; 156 | 157 | return (unit.Team.Direction - unit.Direction).normalized; 158 | } 159 | 160 | public static Vector2 GetSeprationSteer(Boid unit, List neighbors, out Boid collider) 161 | { 162 | collider = null; 163 | Vector2 separationDir = Vector2.zero; 164 | foreach (var neighbor in neighbors) 165 | { 166 | Vector2 offset = unit.Position - neighbor.Position; 167 | float radiusSum = unit.Radius + neighbor.HardRadius; 168 | if (offset.sqrMagnitude < radiusSum * radiusSum) 169 | { 170 | collider = neighbor; 171 | return GetAvoidSteer(unit, neighbor); 172 | } 173 | separationDir += offset / offset.sqrMagnitude; 174 | } 175 | return separationDir.normalized; 176 | } 177 | 178 | public static Vector2 GetObstacleAvoidSteer(Boid unit, List obstacles, out Obstacle collider) 179 | { 180 | Vector2 nextPosition = unit.NextPosition; 181 | float minGap = 0; 182 | collider = null; 183 | foreach (var neighbor in obstacles) 184 | { 185 | float gap = (neighbor.Position - nextPosition).magnitude - neighbor.Radius - unit.Radius; 186 | if (gap < minGap) 187 | { 188 | minGap = gap; 189 | collider = neighbor; 190 | } 191 | } 192 | if (collider == null) 193 | return Vector2.zero; 194 | return GetAvoidSteer(unit, collider); 195 | } 196 | 197 | public static Vector2 GetStuckSolveSteer(Boid unit, List obstacles) 198 | { 199 | Vector2 steer = Vector2.zero; 200 | float minGap = 0; 201 | foreach (var neighbor in obstacles) 202 | { 203 | Vector2 flee = (unit.Position - neighbor.Position); 204 | float gap = flee.magnitude - neighbor.Radius - unit.Radius; 205 | if (gap < minGap) 206 | { 207 | minGap = gap; 208 | steer += flee; 209 | } 210 | } 211 | return steer.normalized; 212 | } 213 | 214 | public static Vector2 GetAvoidSteer(Boid unit, EntityInterface collider) 215 | { 216 | return (unit.Position - collider.Position).GetVerticalComponent(unit.Forward).normalized; 217 | } 218 | 219 | public static Vector2 GetSeekSteer(Boid boid, Vector2 target) 220 | { 221 | return ((target - boid.Position).normalized - boid.Forward).normalized; 222 | } 223 | 224 | public static float GetPointToSegmentDistanceSqr(Vector2 askPoint, Vector2 p0, Vector2 p1, Vector2 normal, float length, out Vector2 mapPoint, out float projectionLength) 225 | { 226 | Vector2 local = askPoint - p0; 227 | projectionLength = Vector2.Dot(normal, local); 228 | if (projectionLength < 0) 229 | { 230 | mapPoint = p0; 231 | projectionLength = 0; 232 | return local.sqrMagnitude; 233 | } 234 | if (projectionLength > length) 235 | { 236 | mapPoint = p1; 237 | projectionLength = length; 238 | return (p1 - askPoint).sqrMagnitude; 239 | } 240 | 241 | mapPoint = normal * projectionLength + p0; 242 | return (mapPoint - askPoint).sqrMagnitude; 243 | } 244 | 245 | void FixedUpdate() 246 | { 247 | SetAllBoidsNeighbor(); 248 | foreach (var team in Teams) 249 | { 250 | team.Update(); 251 | } 252 | } 253 | } 254 | --------------------------------------------------------------------------------