diff --git a/Assets/Prefabs/Enemies/Basic Shooting Patrolling Enemy Variant.prefab b/Assets/Prefabs/Enemies/Basic Shooting Patrolling Enemy Variant.prefab new file mode 100644 index 0000000..ae0c9f6 --- /dev/null +++ b/Assets/Prefabs/Enemies/Basic Shooting Patrolling Enemy Variant.prefab @@ -0,0 +1,110 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &7969067840941533599 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1458954295016120560} + - component: {fileID: 4699332713859496028} + m_Layer: 7 + m_Name: Shooting Behaviour + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1458954295016120560 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7969067840941533599} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2215831379607469555} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &4699332713859496028 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7969067840941533599} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9308b2482216814428bfc53e4e5113c2, type: 3} + m_Name: + m_EditorClassIdentifier: + attacksPerSecond: 1 + attackRange: 5 +--- !u!1001 &3612889356597698756 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 3234632762428300599, guid: c0a2079a443363b4da73a0d425221f6c, type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3234632762428300599, guid: c0a2079a443363b4da73a0d425221f6c, type: 3} + propertyPath: m_LocalPosition.x + value: 2.39 + objectReference: {fileID: 0} + - target: {fileID: 3234632762428300599, guid: c0a2079a443363b4da73a0d425221f6c, type: 3} + propertyPath: m_LocalPosition.y + value: 0.34 + objectReference: {fileID: 0} + - target: {fileID: 3234632762428300599, guid: c0a2079a443363b4da73a0d425221f6c, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3234632762428300599, guid: c0a2079a443363b4da73a0d425221f6c, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 3234632762428300599, guid: c0a2079a443363b4da73a0d425221f6c, type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3234632762428300599, guid: c0a2079a443363b4da73a0d425221f6c, type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3234632762428300599, guid: c0a2079a443363b4da73a0d425221f6c, type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3234632762428300599, guid: c0a2079a443363b4da73a0d425221f6c, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3234632762428300599, guid: c0a2079a443363b4da73a0d425221f6c, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3234632762428300599, guid: c0a2079a443363b4da73a0d425221f6c, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7391517555913877016, guid: c0a2079a443363b4da73a0d425221f6c, type: 3} + propertyPath: m_Name + value: Basic Shooting Patrolling Enemy + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: c0a2079a443363b4da73a0d425221f6c, type: 3} +--- !u!4 &2215831379607469555 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 3234632762428300599, guid: c0a2079a443363b4da73a0d425221f6c, type: 3} + m_PrefabInstance: {fileID: 3612889356597698756} + m_PrefabAsset: {fileID: 0} diff --git a/Assets/Prefabs/Enemies/Basic Shooting Patrolling Enemy Variant.prefab.meta b/Assets/Prefabs/Enemies/Basic Shooting Patrolling Enemy Variant.prefab.meta new file mode 100644 index 0000000..2179a1b --- /dev/null +++ b/Assets/Prefabs/Enemies/Basic Shooting Patrolling Enemy Variant.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 166a1e67df866c64990d98902fcbeb71 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources.meta b/Assets/Resources.meta new file mode 100644 index 0000000..988d311 --- /dev/null +++ b/Assets/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 436fdb1deefe5cc4a9c51d2eb81f080a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Projectiles.meta b/Assets/Resources/Projectiles.meta new file mode 100644 index 0000000..a179331 --- /dev/null +++ b/Assets/Resources/Projectiles.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 389c3841b59dfb64faf2da6b226740ed +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Projectiles/Basic Projectile.prefab b/Assets/Resources/Projectiles/Basic Projectile.prefab new file mode 100644 index 0000000..7be048d --- /dev/null +++ b/Assets/Resources/Projectiles/Basic Projectile.prefab @@ -0,0 +1,139 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &2196707779812385060 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2787928938746060052} + - component: {fileID: 4056950978563145095} + - component: {fileID: 4596650117854378813} + - component: {fileID: 5084819958108693599} + - component: {fileID: 6837629660644296000} + m_Layer: 0 + m_Name: Basic Projectile + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2787928938746060052 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2196707779812385060} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 1.9541792, y: -1.0376638, z: 0} + m_LocalScale: {x: 0.25, y: 0.25, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &4056950978563145095 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2196707779812385060} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: -2413806693520163455, guid: a86470a33a6bf42c4b3595704624658b, type: 3} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 1, y: 1} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 +--- !u!58 &4596650117854378813 +CircleCollider2D: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2196707779812385060} + m_Enabled: 1 + m_Density: 1 + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_UsedByEffector: 0 + m_UsedByComposite: 0 + m_Offset: {x: 0, y: 0} + serializedVersion: 2 + m_Radius: 0.5 +--- !u!50 &5084819958108693599 +Rigidbody2D: + serializedVersion: 4 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2196707779812385060} + m_BodyType: 0 + m_Simulated: 1 + m_UseFullKinematicContacts: 0 + m_UseAutoMass: 0 + m_Mass: 1 + m_LinearDrag: 0 + m_AngularDrag: 0.05 + m_GravityScale: 1 + m_Material: {fileID: 0} + m_Interpolate: 0 + m_SleepingMode: 1 + m_CollisionDetection: 0 + m_Constraints: 0 +--- !u!114 &6837629660644296000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2196707779812385060} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1451960c367cc30478631b32ba4f8356, type: 3} + m_Name: + m_EditorClassIdentifier: + damageOnContact: 50 diff --git a/Assets/Resources/Projectiles/Basic Projectile.prefab.meta b/Assets/Resources/Projectiles/Basic Projectile.prefab.meta new file mode 100644 index 0000000..10acb8e --- /dev/null +++ b/Assets/Resources/Projectiles/Basic Projectile.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3bedc9ab6de1d0048a0b2cbbaa00ade1 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AI/Projectile.cs b/Assets/Scripts/AI/Projectile.cs new file mode 100644 index 0000000..e747a7e --- /dev/null +++ b/Assets/Scripts/AI/Projectile.cs @@ -0,0 +1,45 @@ +using Pausable; +using UnityEngine; + +namespace AI +{ + [RequireComponent(typeof(Rigidbody2D))] + public class Projectile : MonoBehaviour, IPausable + { + [SerializeField] protected float damageOnContact = 50f; + private Rigidbody2D _rigidbody = null; + + private void Awake() + { + _rigidbody = GetComponent(); + } + + public void SetVelocity(Vector2 velocity) + { + _rigidbody.velocity = velocity; + } + + private void OnCollisionEnter2D(Collision2D other) + { + ProjectilePool.Instance.Return(this); + } + + public bool IsPaused { get; protected set; } = false; + + public void Pause() + { + IsPaused = true; + UpdateRigidbody(); + } + public void Resume() + { + IsPaused = false; + UpdateRigidbody(); + } + + private void UpdateRigidbody() + { + _rigidbody.simulated = !IsPaused; + } + } +} diff --git a/Assets/Scripts/AI/Projectile.cs.meta b/Assets/Scripts/AI/Projectile.cs.meta new file mode 100644 index 0000000..76024ef --- /dev/null +++ b/Assets/Scripts/AI/Projectile.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1451960c367cc30478631b32ba4f8356 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AI/ProjectilePool.cs b/Assets/Scripts/AI/ProjectilePool.cs new file mode 100644 index 0000000..500fc7b --- /dev/null +++ b/Assets/Scripts/AI/ProjectilePool.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace AI +{ + public class ProjectilePool : MonoBehaviour + { + private static ProjectilePool _instance = null; + public static ProjectilePool Instance + { + get + { + if (_instance == null) + { + GameObject go = new GameObject(typeof(ProjectilePool).Name); + _instance = go.AddComponent(); + } + return _instance; + } + } + + public Stack pool = null; + private GameObject prefab; + + private void Awake() + { + if (_instance != this && _instance != null) + { + Destroy(this); + return; + } + + pool = new Stack(64); + prefab = Resources.Load("Projectiles/Basic Projectile"); + } + + public Projectile Get() + { + Projectile projectile = null; + pool.TryPop(out projectile); + + if (projectile == null) + projectile = Instantiate(prefab).GetComponent(); + + projectile.gameObject.SetActive(true); + + return projectile; + } + + public void Return(Projectile projectile) + { + projectile.gameObject.SetActive(false); + pool.Push(projectile); + } + } +} diff --git a/Assets/Scripts/AI/ProjectilePool.cs.meta b/Assets/Scripts/AI/ProjectilePool.cs.meta new file mode 100644 index 0000000..7074bea --- /dev/null +++ b/Assets/Scripts/AI/ProjectilePool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9d37792a64c70ac4eb6971ac3a062f15 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AI/ShootingEnemyAI.cs b/Assets/Scripts/AI/ShootingEnemyAI.cs new file mode 100644 index 0000000..6c22c96 --- /dev/null +++ b/Assets/Scripts/AI/ShootingEnemyAI.cs @@ -0,0 +1,82 @@ +using Pausable; +using UnityEngine; +using UnityEngine.Events; + +namespace AI +{ + public class ShootingEnemyAI : MonoBehaviour, IPausable + { + [SerializeField] protected float attacksPerSecond = 1f; + [SerializeField] protected float attackRange = 5f; + [SerializeField] protected float timeForProjectileToHit = .25f; + + protected float cooldownPerShoot = 0f; + protected float remainingCooldown = 0f; + + private float attackRangeSquared = 5f; + private Transform target = null; + + private bool canShoot => (target.transform.position - transform.position).sqrMagnitude < attackRangeSquared && target != null; + + public UnityEvent OnShoot { get; protected set; } = null; + + #region IPausable + public bool IsPaused { get; protected set; } = false; + public virtual void Pause() => IsPaused = true; + public virtual void Resume() => IsPaused = false; + #endregion + + protected void Awake() + { + cooldownPerShoot = 1f / attacksPerSecond; + attackRangeSquared = attackRange * attackRange; + OnShoot = new UnityEvent(); + UpdateTarget(FindObjectOfType()?.transform); + } + + protected void Update() + { + remainingCooldown -= Time.deltaTime; + + if (remainingCooldown <= 0f && canShoot) + Shoot(); + } + + protected void Shoot() + { + + Projectile projectile = ProjectilePool.Instance.Get(); + projectile.transform.position = transform.position; + + Vector3 velocity = GetVelocityForProjectile(timeForProjectileToHit); + + RaycastHit2D raycastHit2D = Physics2D.Raycast(transform.position, target.position - transform.position, attackRange); + if (raycastHit2D.transform != target) + velocity = GetVelocityForProjectile(timeForProjectileToHit * 2); + + projectile.SetVelocity(velocity); + + remainingCooldown = cooldownPerShoot; + OnShoot?.Invoke(); + } + + private Vector3 GetVelocityForProjectile(float time) + { + Vector3 vector3 = target.position - transform.position; + vector3.z = 0f; + vector3.y -= (Physics2D.gravity.y / 2) * (time * time); + vector3 /= time; + return vector3; + } + + public void UpdateTarget(Transform transform) => target = transform; + + private void OnDrawGizmosSelected() + { + Gizmos.color = Color.yellow; + Gizmos.DrawWireSphere(transform.position, 0.125f); + Gizmos.color = Color.red; + Gizmos.DrawWireSphere(transform.position, attackRange); + } + } +} diff --git a/Assets/Scripts/AI/ShootingEnemyAI.cs.meta b/Assets/Scripts/AI/ShootingEnemyAI.cs.meta new file mode 100644 index 0000000..f34558d --- /dev/null +++ b/Assets/Scripts/AI/ShootingEnemyAI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9308b2482216814428bfc53e4e5113c2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: