└── _Scripts ├── Interfaces ├── IAgent.cs ├── IKnockBack.cs ├── IHittable.cs └── IAgentInput.cs ├── Weapons ├── ImpactScript.cs ├── Bullet.cs ├── WeaponAudio.cs ├── WeaponRenderer.cs ├── RegularBullet.cs └── Weapon.cs ├── AI ├── AIMovementData.cs ├── AIActionData.cs ├── Actions │ ├── IdleAction.cs │ ├── AttackAction.cs │ └── ChaseAction.cs ├── AITransition.cs ├── AIAction.cs ├── AIDecision.cs ├── Decisions │ ├── DistanceDecision.cs │ └── LookDecision.cs └── AIState.cs ├── AgentStepAudioPlayer.cs ├── DataSO ├── EnemyDataSO.cs ├── MovementDataSO.cs ├── ResourceDataSO.cs ├── WeaponDataSO.cs └── BulletDataSO.cs ├── Feedback ├── Feedback.cs ├── FeedbackPlayer.cs ├── TimeFreezeFeedback.cs ├── ShakeFeedback.cs ├── DissolveFeedback.cs ├── FlashLightFeedback.cs ├── BulletShellGenerator.cs ├── ShakeCinemachineFeedback.cs └── FlashSpriteFeedback.cs ├── Enemies ├── EnemyMeleeAttack.cs ├── EnemyAttack.cs ├── EenemySpawner.cs ├── EnemyAIBrain.cs └── Enemy.cs ├── UI ├── UIAmmo.cs └── UIHealth.cs ├── AgentSounds.cs ├── Player ├── PlayerWeapon.cs └── Player.cs ├── GameManager.cs ├── AgentAnimations.cs ├── Resources ├── Resource.cs └── ItemDropper.cs ├── AudioPlayer.cs ├── TimeController.cs ├── ObjectPool.cs ├── AgentRenderer.cs ├── AgentWeapon.cs ├── AgentInput.cs └── AgentMovement.cs /_Scripts/Interfaces/IAgent.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.Events; 3 | 4 | public interface IAgent 5 | { 6 | int Health { get; } 7 | UnityEvent OnDie { get; set; } 8 | UnityEvent OnGetHit { get; set; } 9 | 10 | } -------------------------------------------------------------------------------- /_Scripts/Interfaces/IKnockBack.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public interface IKnockBack 6 | { 7 | void KnockBack(Vector2 direction, float power, float duration); 8 | } 9 | -------------------------------------------------------------------------------- /_Scripts/Weapons/ImpactScript.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class ImpactScript : MonoBehaviour 6 | { 7 | public void DestroyAfterAnimation() 8 | { 9 | Destroy(gameObject); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /_Scripts/Interfaces/IHittable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.Events; 5 | 6 | public interface IHittable 7 | { 8 | UnityEvent OnGetHit { get; set; } 9 | void GetHit(int damage, GameObject damageDealer); 10 | } 11 | -------------------------------------------------------------------------------- /_Scripts/AI/AIMovementData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class AIMovementData : MonoBehaviour 6 | { 7 | [field: SerializeField] 8 | public Vector2 Direction { get; set; } 9 | [field: SerializeField] 10 | public Vector2 PointOfInterest { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /_Scripts/Interfaces/IAgentInput.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.Events; 3 | 4 | public interface IAgentInput 5 | { 6 | UnityEvent OnFireButtonPressed { get; set; } 7 | UnityEvent OnFireButtonReleased { get; set; } 8 | UnityEvent OnMovementKeyPressed { get; set; } 9 | UnityEvent OnPointerPositionChange { get; set; } 10 | } -------------------------------------------------------------------------------- /_Scripts/AgentStepAudioPlayer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | 7 | public class AgentStepAudioPlayer : AudioPlayer 8 | { 9 | 10 | [SerializeField] 11 | protected AudioClip stepClip; 12 | 13 | public void PlayStepSound() 14 | { 15 | PlayClipWithVariablePitch(stepClip); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /_Scripts/DataSO/EnemyDataSO.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | [CreateAssetMenu(menuName = "Enemies/EnemyData")] 6 | public class EnemyDataSO : ScriptableObject 7 | { 8 | [field: SerializeField] 9 | public int MaxHealth { get; set; } = 3; 10 | [field: SerializeField] 11 | public int Damage { get; set; } = 1; 12 | } 13 | -------------------------------------------------------------------------------- /_Scripts/DataSO/MovementDataSO.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | [CreateAssetMenu(menuName ="Agent/MovementData")] 6 | public class MovementDataSO : ScriptableObject 7 | { 8 | [Range(1,10)] 9 | public float maxSpeed = 5; 10 | 11 | [Range(0.1f,100)] 12 | public float acceleration = 50, deacceleration = 50; 13 | } 14 | -------------------------------------------------------------------------------- /_Scripts/Feedback/Feedback.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public abstract class Feedback : MonoBehaviour 6 | { 7 | public abstract void CreateFeedback(); 8 | public abstract void CompletePreviousFeedback(); 9 | 10 | protected virtual void OnDestroy() 11 | { 12 | CompletePreviousFeedback(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /_Scripts/AI/AIActionData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class AIActionData : MonoBehaviour 6 | { 7 | [field: SerializeField] 8 | public bool Attack { get; set; } 9 | [field: SerializeField] 10 | public bool TargetSpotted { get; set; } 11 | [field: SerializeField] 12 | public bool Arrived { get; set; } 13 | } 14 | -------------------------------------------------------------------------------- /_Scripts/AI/Actions/IdleAction.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class IdleAction : AIAction 6 | { 7 | public override void TakeAction() 8 | { 9 | aiMovementData.Direction = Vector2.zero; 10 | aiMovementData.PointOfInterest = transform.position; 11 | enemyBrain.Move(aiMovementData.Direction, aiMovementData.PointOfInterest); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /_Scripts/Weapons/Bullet.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public abstract class Bullet : MonoBehaviour 6 | { 7 | [SerializeField] 8 | protected BulletDataSO bulletData; 9 | 10 | public virtual BulletDataSO BulletData 11 | { 12 | get { return bulletData; } 13 | set { bulletData = value; } 14 | } 15 | 16 | //[field: SerializeField] 17 | //public virtual BulletDataSO BulletData { get; set; } 18 | } 19 | -------------------------------------------------------------------------------- /_Scripts/Weapons/WeaponAudio.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class WeaponAudio : AudioPlayer 6 | { 7 | [SerializeField] 8 | private AudioClip shootBulletClip = null, outOfBulletsClip = null; 9 | 10 | public void PlayShootSound() 11 | { 12 | PlayClip(shootBulletClip); 13 | } 14 | 15 | public void PlayNoBulletsSound() 16 | { 17 | PlayClip(outOfBulletsClip); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /_Scripts/Enemies/EnemyMeleeAttack.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class EnemyMeleeAttack : EnemyAttack 6 | { 7 | public override void Attack(int damage) 8 | { 9 | if (waitBeforeNextAttack == false) 10 | { 11 | var hittable = GetTarget().GetComponent(); 12 | hittable?.GetHit(damage, gameObject); 13 | StartCoroutine(WaitBeforeAttackCoroutine()); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /_Scripts/AI/Actions/AttackAction.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class AttackAction : AIAction 6 | { 7 | public override void TakeAction() 8 | { 9 | aiMovementData.Direction = Vector2.zero; 10 | aiMovementData.PointOfInterest = enemyBrain.Target.transform.position; 11 | enemyBrain.Move(aiMovementData.Direction, aiMovementData.PointOfInterest); 12 | aiActionData.Attack = true; 13 | enemyBrain.Attack(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /_Scripts/AI/Actions/ChaseAction.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class ChaseAction : AIAction 6 | { 7 | public override void TakeAction() 8 | { 9 | var direction = enemyBrain.Target.transform.position - transform.position; 10 | aiMovementData.Direction = direction.normalized; 11 | aiMovementData.PointOfInterest = enemyBrain.Target.transform.position; 12 | enemyBrain.Move(aiMovementData.Direction, aiMovementData.PointOfInterest); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /_Scripts/UI/UIAmmo.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using TMPro; 5 | 6 | public class UIAmmo : MonoBehaviour 7 | { 8 | [SerializeField] 9 | private TextMeshProUGUI text = null; 10 | 11 | public void UdpateBulletsText(int bulletCount) 12 | { 13 | if(bulletCount == 0) 14 | { 15 | text.color = Color.red; 16 | } 17 | else 18 | { 19 | text.color = Color.white; 20 | } 21 | text.SetText(bulletCount.ToString()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /_Scripts/AI/AITransition.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class AITransition : MonoBehaviour 6 | { 7 | [field: SerializeField] 8 | public List Decisions { get; set; } 9 | [field: SerializeField] 10 | public AIState PositiveResult { get; set; } 11 | [field: SerializeField] 12 | public AIState NegativeResult { get; set; } 13 | 14 | private void Awake() 15 | { 16 | Decisions.Clear(); 17 | Decisions = new List(GetComponents()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /_Scripts/AgentSounds.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class AgentSounds : AudioPlayer 6 | { 7 | [SerializeField] 8 | private AudioClip hitClip = null, deathClip = null, voiceLineClip = null; 9 | 10 | public void PlayeHitSound() 11 | { 12 | PlayClipWithVariablePitch(hitClip); 13 | } 14 | 15 | public void PlayDeathSound() 16 | { 17 | PlayClip(deathClip); 18 | } 19 | 20 | public void PlayeVoiceSound() 21 | { 22 | PlayClipWithVariablePitch(voiceLineClip); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /_Scripts/DataSO/ResourceDataSO.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | [CreateAssetMenu(menuName ="Items/ResourceData")] 6 | public class ResourceDataSO : ScriptableObject 7 | { 8 | [field: SerializeField] 9 | public ResourceTypeEnum ResourceType { get; set; } 10 | [SerializeField] 11 | private int minAmount = 1, maxAmount = 5; 12 | 13 | public int GetAmount() 14 | { 15 | return Random.Range(minAmount, maxAmount + 1); 16 | } 17 | } 18 | 19 | public enum ResourceTypeEnum 20 | { 21 | None, 22 | Health, 23 | Ammo 24 | } 25 | -------------------------------------------------------------------------------- /_Scripts/AI/AIAction.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public abstract class AIAction : MonoBehaviour 6 | { 7 | protected AIActionData aiActionData; 8 | protected AIMovementData aiMovementData; 9 | protected EnemyAIBrain enemyBrain; 10 | 11 | private void Awake() 12 | { 13 | aiActionData = transform.root.GetComponentInChildren(); 14 | aiMovementData = transform.root.GetComponentInChildren(); 15 | enemyBrain = transform.root.GetComponent(); 16 | } 17 | 18 | public abstract void TakeAction(); 19 | } 20 | -------------------------------------------------------------------------------- /_Scripts/AI/AIDecision.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public abstract class AIDecision : MonoBehaviour 6 | { 7 | protected AIActionData aiActionData; 8 | protected AIMovementData aiMovementData; 9 | protected EnemyAIBrain enemyBrain; 10 | 11 | private void Awake() 12 | { 13 | aiActionData = transform.root.GetComponentInChildren(); 14 | aiMovementData = transform.root.GetComponentInChildren(); 15 | enemyBrain = transform.root.GetComponent(); 16 | } 17 | 18 | public abstract bool MakeADecision(); 19 | } 20 | -------------------------------------------------------------------------------- /_Scripts/Feedback/FeedbackPlayer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | public class FeedbackPlayer : MonoBehaviour 7 | { 8 | [SerializeField] 9 | private List feedbackToPlay = null; 10 | 11 | public void PlayFeedback() 12 | { 13 | FinishFeedback(); 14 | foreach (var feedback in feedbackToPlay) 15 | { 16 | feedback.CreateFeedback(); 17 | } 18 | } 19 | 20 | private void FinishFeedback() 21 | { 22 | foreach (var feedback in feedbackToPlay) 23 | { 24 | feedback.CompletePreviousFeedback(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /_Scripts/Player/PlayerWeapon.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class PlayerWeapon : AgentWeapon 6 | { 7 | [SerializeField] 8 | private UIAmmo uiAmmo = null; 9 | 10 | public bool AmmoFull { get => weapon != null && weapon.AmmoFull;} 11 | 12 | private void Start() 13 | { 14 | if (weapon != null) 15 | { 16 | weapon.OnAmmoChange.AddListener(uiAmmo.UdpateBulletsText); 17 | uiAmmo.UdpateBulletsText(weapon.Ammo); 18 | } 19 | 20 | } 21 | 22 | public void AddAmmo(int amount) 23 | { 24 | if (weapon != null) 25 | weapon.Ammo += amount; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /_Scripts/GameManager.cs: -------------------------------------------------------------------------------- 1 | using DG.Tweening; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using UnityEngine; 6 | using UnityEngine.SceneManagement; 7 | 8 | public class GameManager : MonoBehaviour 9 | { 10 | [SerializeField] 11 | private Texture2D cursorTexture = null; 12 | 13 | private void Start() 14 | { 15 | SetCursorIcon(); 16 | } 17 | 18 | private void SetCursorIcon() 19 | { 20 | Cursor.SetCursor(cursorTexture, new Vector2(cursorTexture.width / 2f, cursorTexture.height / 2f), CursorMode.Auto); 21 | } 22 | 23 | public void RestartLevel() 24 | { 25 | DOTween.KillAll(); 26 | SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /_Scripts/Feedback/TimeFreezeFeedback.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class TimeFreezeFeedback : Feedback 6 | { 7 | [SerializeField] 8 | private float freezeTimeDelay = 0.01f, unfreezeTimeDelay = 0.02f; 9 | [SerializeField] 10 | [Range(0, 1)] 11 | private float timeFreezeValue = 0.2f; 12 | 13 | public override void CompletePreviousFeedback() 14 | { 15 | if (TimeController.instance != null) 16 | TimeController.instance.ResetTimeScale(); 17 | } 18 | 19 | public override void CreateFeedback() 20 | { 21 | TimeController.instance.ModifyTimeScale(timeFreezeValue, freezeTimeDelay, () => TimeController.instance.ModifyTimeScale(1, unfreezeTimeDelay)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /_Scripts/AgentAnimations.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | [RequireComponent(typeof(Animator))] 6 | public class AgentAnimations : MonoBehaviour 7 | { 8 | protected Animator agentAnimator; 9 | 10 | private void Awake() 11 | { 12 | agentAnimator = GetComponent(); 13 | } 14 | 15 | public void SetWalkAnimation(bool val) 16 | { 17 | agentAnimator.SetBool("Walk", val); 18 | } 19 | 20 | public void AnimatePlayer(float velocity) 21 | { 22 | SetWalkAnimation(velocity > 0); 23 | } 24 | 25 | public void PlayDeathAnimation() 26 | { 27 | agentAnimator.SetTrigger("Death"); 28 | } 29 | 30 | public void SetWalkSpeed(int val) 31 | { 32 | agentAnimator.SetFloat("WalkMultiplier", val); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /_Scripts/Feedback/ShakeFeedback.cs: -------------------------------------------------------------------------------- 1 | using DG.Tweening; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | public class ShakeFeedback : Feedback 7 | { 8 | [SerializeField] 9 | private GameObject objectToShake = null; 10 | [SerializeField] 11 | private float duration = 0.2f, strength = 1, randomness = 90; 12 | [SerializeField] 13 | private int vibrato = 10; 14 | [SerializeField] 15 | private bool snapping = false, fadeout = true; 16 | 17 | public override void CompletePreviousFeedback() 18 | { 19 | objectToShake.transform.DOComplete(); 20 | } 21 | 22 | public override void CreateFeedback() 23 | { 24 | CompletePreviousFeedback(); 25 | objectToShake.transform.DOShakePosition(duration, strength, vibrato, randomness, snapping, fadeout); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /_Scripts/Enemies/EnemyAttack.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public abstract class EnemyAttack : MonoBehaviour 6 | { 7 | protected EnemyAIBrain enemyBrain; 8 | 9 | [field: SerializeField] 10 | public float AttackDelay { get; private set; } = 1; 11 | protected bool waitBeforeNextAttack; 12 | 13 | private void Awake() 14 | { 15 | enemyBrain = GetComponent(); 16 | } 17 | 18 | 19 | public abstract void Attack(int damage); 20 | 21 | protected IEnumerator WaitBeforeAttackCoroutine() 22 | { 23 | waitBeforeNextAttack = true; 24 | yield return new WaitForSeconds(AttackDelay); 25 | waitBeforeNextAttack = false; 26 | } 27 | 28 | protected GameObject GetTarget() 29 | { 30 | return enemyBrain.Target; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /_Scripts/Resources/Resource.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | [RequireComponent(typeof(AudioSource))] 6 | public class Resource : MonoBehaviour 7 | { 8 | [field: SerializeField] 9 | public ResourceDataSO ResourceData { get; set; } 10 | 11 | AudioSource audioSource; 12 | 13 | private void Awake() 14 | { 15 | audioSource = GetComponent(); 16 | } 17 | 18 | public void PickUpResource() 19 | { 20 | StartCoroutine(DestroyCoroutine()); 21 | } 22 | 23 | IEnumerator DestroyCoroutine() 24 | { 25 | GetComponent().enabled = false; 26 | GetComponent().enabled = false; 27 | audioSource.Play(); 28 | yield return new WaitForSeconds(audioSource.clip.length); 29 | Destroy(gameObject); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /_Scripts/Weapons/WeaponRenderer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | [RequireComponent(typeof(SpriteRenderer))] 6 | public class WeaponRenderer : MonoBehaviour 7 | { 8 | [SerializeField] 9 | protected int playerSortingOrder = 0; 10 | private SpriteRenderer weaponRenderer = null; 11 | 12 | private void Awake() 13 | { 14 | weaponRenderer = GetComponent(); 15 | } 16 | 17 | public void FlipSprite(bool val) 18 | { 19 | weaponRenderer.flipY = val; 20 | } 21 | 22 | public void RenderBehindHead(bool val) 23 | { 24 | if (val) 25 | { 26 | weaponRenderer.sortingOrder = playerSortingOrder - 1; 27 | } 28 | else 29 | { 30 | weaponRenderer.sortingOrder = playerSortingOrder + 1; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /_Scripts/Feedback/DissolveFeedback.cs: -------------------------------------------------------------------------------- 1 | using DG.Tweening; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using UnityEngine.Events; 6 | 7 | public class DissolveFeedback : Feedback 8 | { 9 | [SerializeField] 10 | private SpriteRenderer spriteRenderer = null; 11 | [SerializeField] 12 | private float duration = 0.05f; 13 | [field: SerializeField] 14 | public UnityEvent DeathCallback { get; set; } 15 | 16 | public override void CompletePreviousFeedback() 17 | { 18 | spriteRenderer.DOComplete(); 19 | spriteRenderer.material.DOComplete(); 20 | } 21 | 22 | public override void CreateFeedback() 23 | { 24 | var sequence = DOTween.Sequence(); 25 | sequence.Append(spriteRenderer.material.DOFloat(0, "_Dissolve", duration)); 26 | if(DeathCallback != null) 27 | { 28 | sequence.AppendCallback(() => DeathCallback.Invoke()); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /_Scripts/AudioPlayer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | [RequireComponent(typeof(AudioSource))] 6 | public abstract class AudioPlayer : MonoBehaviour 7 | { 8 | protected AudioSource audioSource; 9 | [SerializeField] 10 | protected float pitchRandomness = 0.05f; 11 | protected float basePitch; 12 | 13 | private void Awake() 14 | { 15 | audioSource = GetComponent(); 16 | } 17 | 18 | private void Start() 19 | { 20 | basePitch = audioSource.pitch; 21 | } 22 | 23 | protected void PlayClipWithVariablePitch(AudioClip clip) 24 | { 25 | var randomPitch = UnityEngine.Random.Range(-pitchRandomness, pitchRandomness); 26 | audioSource.pitch = basePitch + randomPitch; 27 | PlayClip(clip); 28 | } 29 | 30 | protected void PlayClip(AudioClip clip) 31 | { 32 | audioSource.Stop(); 33 | audioSource.clip = clip; 34 | audioSource.Play(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /_Scripts/AI/Decisions/DistanceDecision.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class DistanceDecision : AIDecision 6 | { 7 | [field: SerializeField] 8 | [field: Range(0.1f, 15)] 9 | public float Distance { get; set; } = 5; 10 | 11 | public override bool MakeADecision() 12 | { 13 | if(Vector3.Distance(enemyBrain.Target.transform.position, transform.position) < Distance) 14 | { 15 | if (aiActionData.TargetSpotted == false) 16 | { 17 | aiActionData.TargetSpotted = true; 18 | } 19 | } 20 | else 21 | { 22 | aiActionData.TargetSpotted = false; 23 | } 24 | return aiActionData.TargetSpotted; 25 | } 26 | 27 | protected void OnDrawGizmos() 28 | { 29 | if(UnityEditor.Selection.activeObject == gameObject) 30 | { 31 | Gizmos.color = Color.green; 32 | Gizmos.DrawWireSphere(transform.position, Distance); 33 | Gizmos.color = Color.white; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /_Scripts/Feedback/FlashLightFeedback.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using UnityEngine.Experimental.Rendering.Universal; 6 | 7 | public class FlashLightFeedback : Feedback 8 | { 9 | [SerializeField] 10 | private Light2D lightTarget = null; 11 | [SerializeField] 12 | private float lightOnDelay = 0.01f, lightOffDelay = 0.01f; 13 | [SerializeField] 14 | private bool defaultState = false; 15 | 16 | public override void CompletePreviousFeedback() 17 | { 18 | StopAllCoroutines(); 19 | lightTarget.enabled = defaultState; 20 | } 21 | 22 | public override void CreateFeedback() 23 | { 24 | StartCoroutine(ToggleLightCoroutine(lightOnDelay, true, () => StartCoroutine(ToggleLightCoroutine(lightOffDelay, false)))); 25 | } 26 | 27 | IEnumerator ToggleLightCoroutine(float time, bool result, Action FinishCallback = null) 28 | { 29 | yield return new WaitForSeconds(time); 30 | lightTarget.enabled = result; 31 | FinishCallback?.Invoke(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /_Scripts/TimeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | public class TimeController : MonoBehaviour 7 | { 8 | public static TimeController instance; 9 | 10 | private void Awake() 11 | { 12 | if(instance == null) 13 | { 14 | instance = this; 15 | }else if (instance != this) 16 | { 17 | Destroy(this); 18 | } 19 | } 20 | 21 | public void ResetTimeScale() 22 | { 23 | StopAllCoroutines(); 24 | Time.timeScale = 1; 25 | } 26 | 27 | public void ModifyTimeScale(float endTimeValue, float timeToWait, Action OnCompleteCallback = null) 28 | { 29 | StartCoroutine(TimeScaleCoroutine(endTimeValue, timeToWait, OnCompleteCallback)); 30 | } 31 | 32 | IEnumerator TimeScaleCoroutine(float endTimeValue, float timeToWait, Action OnCompleteCallback) 33 | { 34 | yield return new WaitForSecondsRealtime(timeToWait); 35 | Time.timeScale = endTimeValue; 36 | OnCompleteCallback?.Invoke(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /_Scripts/Feedback/BulletShellGenerator.cs: -------------------------------------------------------------------------------- 1 | using DG.Tweening; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using UnityEngine; 6 | using Random = UnityEngine.Random; 7 | 8 | public class BulletShellGenerator : ObjectPool 9 | { 10 | [SerializeField] 11 | private float flyDuration = 0.3f; 12 | [SerializeField] 13 | private float flyStrength = 1; 14 | 15 | public void SpawnBulletShell() 16 | { 17 | var shell = SpawnObject(); 18 | MoveShellInRandomDirection(shell); 19 | } 20 | 21 | private void MoveShellInRandomDirection(GameObject shell) 22 | { 23 | shell.transform.DOComplete(); 24 | var randomDirection = Random.insideUnitCircle; 25 | randomDirection = randomDirection.y > 0 ? new Vector2(randomDirection.x, -randomDirection.y) : randomDirection; 26 | 27 | shell.transform.DOMove(((Vector2)transform.position + randomDirection) * flyStrength, flyDuration).OnComplete(() => shell.GetComponent().Play()); 28 | shell.transform.DORotate(new Vector3(0, 0, Random.Range(0f, 360f)), flyDuration); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /_Scripts/DataSO/WeaponDataSO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | [CreateAssetMenu(menuName = "Weapons/WeaponData")] 7 | public class WeaponDataSO : ScriptableObject 8 | { 9 | [field: SerializeField] 10 | public BulletDataSO BulletData { get; set; } 11 | 12 | [field: SerializeField] 13 | [field: Range(0,100)] 14 | public int AmmoCapacity { get; set; } = 100; 15 | 16 | [field: SerializeField] 17 | public bool AutomaticFire { get; set; } = false; 18 | 19 | [field: SerializeField] 20 | [field: Range(0.1f, 2f)] 21 | public float WeaponDelay { get; set; } = .1f; 22 | 23 | [field: SerializeField] 24 | [field: Range(0, 10)] 25 | public float SpreadAngle { get; set; } = 5; 26 | 27 | [SerializeField] 28 | private bool multiBulletShoot = false; 29 | [SerializeField] 30 | [Range(1,10)] 31 | private int bulletCount = 1; 32 | 33 | internal int GetBulletCountToSpawn() 34 | { 35 | if (multiBulletShoot) 36 | { 37 | return bulletCount; 38 | } 39 | return 1; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /_Scripts/ObjectPool.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class ObjectPool : MonoBehaviour 6 | { 7 | [SerializeField] 8 | protected GameObject objectToSpawn; 9 | [SerializeField] 10 | protected int poolSize; 11 | protected int currentSize; 12 | protected Queue objectPool; 13 | 14 | private void Awake() 15 | { 16 | objectPool = new Queue(); 17 | } 18 | 19 | public virtual GameObject SpawnObject(GameObject currentObject = null) 20 | { 21 | if (currentObject == null) 22 | currentObject = objectToSpawn; 23 | 24 | GameObject spawnedObject = null; 25 | 26 | if (currentSize < poolSize) 27 | { 28 | spawnedObject = Instantiate(currentObject, transform.position, Quaternion.identity); 29 | spawnedObject.name = currentObject.name + "_" + currentSize; 30 | currentSize++; 31 | } 32 | else 33 | { 34 | spawnedObject = objectPool.Dequeue(); 35 | spawnedObject.transform.position = transform.position; 36 | spawnedObject.transform.rotation = Quaternion.identity; 37 | } 38 | objectPool.Enqueue(spawnedObject); 39 | return spawnedObject; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /_Scripts/UI/UIHealth.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | 6 | public class UIHealth : MonoBehaviour 7 | { 8 | [SerializeField] 9 | private GameObject heartPrefab = null, healthPanel = null; 10 | [SerializeField] 11 | private Sprite heartFull = null, heartEmpty = null; 12 | 13 | private int heartCount = 0; 14 | 15 | private List hearts = new List(); 16 | 17 | public void Initialize(int livesCount) 18 | { 19 | heartCount = livesCount; 20 | foreach (Transform child in healthPanel.transform) 21 | { 22 | Destroy(child.gameObject); 23 | } 24 | for (int i = 0; i < livesCount; i++) 25 | { 26 | hearts.Add(Instantiate(heartPrefab, healthPanel.transform).GetComponent()); 27 | } 28 | } 29 | 30 | public void UpdateUI(int health) 31 | { 32 | int currentIndex = 0; 33 | for (int i = 0; i < heartCount; i++) 34 | { 35 | if(currentIndex >= health) 36 | { 37 | hearts[i].sprite = heartEmpty; 38 | } 39 | else 40 | { 41 | hearts[i].sprite = heartFull; 42 | } 43 | currentIndex++; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /_Scripts/DataSO/BulletDataSO.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | [CreateAssetMenu(menuName = "Weapons/BulletData")] 6 | public class BulletDataSO : ScriptableObject 7 | { 8 | [field: SerializeField] 9 | public GameObject bulletPrefab { get; set; } 10 | 11 | [field: SerializeField] 12 | [field: Range(1, 100)] 13 | public float BulletSpeed { get; internal set; } = 1; 14 | 15 | [field: SerializeField] 16 | [field: Range(1, 10)] 17 | public int Damage { get; set; } = 1; 18 | 19 | [field: SerializeField] 20 | [field: Range(0, 100)] 21 | public float Friction { get; internal set; } = 0; 22 | 23 | [field: SerializeField] 24 | public bool Bounce { get; set; } = false; 25 | 26 | [field: SerializeField] 27 | public bool GoThroughHittable { get; set; } = false; 28 | 29 | [field: SerializeField] 30 | public bool IsRaycast { get; set; } = false; 31 | 32 | [field: SerializeField] 33 | public GameObject ImpactObstaclePrefab { get; set; } 34 | 35 | [field: SerializeField] 36 | public GameObject ImpactEnemyPrefab { get; set; } 37 | 38 | [field: SerializeField] 39 | [field: Range(1, 20)] 40 | public float KnockBackPower { get; set; } = 5; 41 | 42 | [field: SerializeField] 43 | [field: Range(0.01f, 1f)] 44 | public float KnockBackDelay { get; set; } = 0.1f; 45 | } 46 | -------------------------------------------------------------------------------- /_Scripts/AI/Decisions/LookDecision.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.Events; 5 | 6 | public class LookDecision : AIDecision 7 | { 8 | [SerializeField] 9 | [Range(1,15)] 10 | private float Distance = 15; 11 | [SerializeField] 12 | private LayerMask raycastMask = new LayerMask(); 13 | 14 | [field: SerializeField] 15 | public UnityEvent OnPlayerSpotted { get; set; } 16 | 17 | public override bool MakeADecision() 18 | { 19 | var direction = enemyBrain.Target.transform.position - transform.position; 20 | 21 | RaycastHit2D hit = Physics2D.Raycast(transform.position, direction, Distance, raycastMask); 22 | if (hit.collider != null && hit.collider.gameObject.layer == LayerMask.NameToLayer("Player")) 23 | { 24 | OnPlayerSpotted?.Invoke(); 25 | return true; 26 | } 27 | return false; 28 | } 29 | 30 | private void OnDrawGizmos() 31 | { 32 | if(UnityEditor.Selection.activeObject == gameObject && enemyBrain!= null && enemyBrain.Target != null) 33 | { 34 | Gizmos.color = Color.red; 35 | var direction = enemyBrain.Target.transform.position - transform.position; 36 | Gizmos.DrawRay(transform.position, direction.normalized * Distance); 37 | Gizmos.color = Color.white; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /_Scripts/Feedback/ShakeCinemachineFeedback.cs: -------------------------------------------------------------------------------- 1 | using Cinemachine; 2 | using System.Collections; 3 | using UnityEngine; 4 | 5 | public class ShakeCinemachineFeedback : Feedback 6 | { 7 | [SerializeField] 8 | private CinemachineVirtualCamera cinemachineCamera; 9 | [SerializeField] 10 | [Range(0,5)] 11 | private float amplitude = 1, intensity = 1; 12 | [SerializeField] 13 | [Range(0,1)] 14 | private float duration = 0.1f; 15 | 16 | private CinemachineBasicMultiChannelPerlin noise; 17 | 18 | private void Start() 19 | { 20 | if (cinemachineCamera == null) 21 | cinemachineCamera = FindObjectOfType(); 22 | noise = cinemachineCamera.GetCinemachineComponent(); 23 | } 24 | public override void CompletePreviousFeedback() 25 | { 26 | StopAllCoroutines(); 27 | noise.m_AmplitudeGain = 0; 28 | } 29 | 30 | public override void CreateFeedback() 31 | { 32 | noise.m_AmplitudeGain = amplitude; 33 | noise.m_FrequencyGain = intensity; 34 | StartCoroutine(ShakeCoroutine()); 35 | } 36 | 37 | IEnumerator ShakeCoroutine() 38 | { 39 | for (float i = duration; i > 0; i-=Time.deltaTime) 40 | { 41 | noise.m_AmplitudeGain = Mathf.Lerp(0, amplitude, i / duration); 42 | yield return null; 43 | } 44 | noise.m_AmplitudeGain = 0; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /_Scripts/AgentRenderer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.Events; 5 | 6 | [RequireComponent(typeof(SpriteRenderer))] 7 | public class AgentRenderer : MonoBehaviour 8 | { 9 | protected SpriteRenderer spriteRenderer; 10 | 11 | [field: SerializeField] 12 | public UnityEvent OnBackwardMovement { get; set; } 13 | 14 | private void Awake() 15 | { 16 | spriteRenderer = GetComponent(); 17 | } 18 | 19 | public void FaceDirection(Vector2 pointerinput) 20 | { 21 | var direction = (Vector3)pointerinput - transform.position; 22 | var result = Vector3.Cross(Vector2.up, direction); 23 | if(result.z > 0) 24 | { 25 | spriteRenderer.flipX = true; 26 | }else if(result.z < 0) 27 | { 28 | spriteRenderer.flipX = false; 29 | } 30 | } 31 | 32 | public void CheckIfBackwardMovement(Vector2 movementVector) 33 | { 34 | float angle = 0; 35 | if(spriteRenderer.flipX == true) 36 | { 37 | angle = Vector2.Angle(-transform.right, movementVector); 38 | } 39 | else 40 | { 41 | angle = Vector2.Angle(transform.right, movementVector); 42 | } 43 | Debug.Log(angle); 44 | if (angle > 90) 45 | { 46 | OnBackwardMovement?.Invoke(-1); 47 | } 48 | else 49 | { 50 | OnBackwardMovement?.Invoke(1); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /_Scripts/Feedback/FlashSpriteFeedback.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class FlashSpriteFeedback : Feedback 6 | { 7 | [SerializeField] 8 | private SpriteRenderer spriteRenderer = null; 9 | [SerializeField] 10 | private float flashTime = 0.1f; 11 | [SerializeField] 12 | private Material flashMaterial = null; 13 | 14 | private Shader originalMaterialShader; 15 | 16 | private void Start() 17 | { 18 | originalMaterialShader = spriteRenderer.material.shader; 19 | } 20 | 21 | public override void CompletePreviousFeedback() 22 | { 23 | StopAllCoroutines(); 24 | spriteRenderer.material.shader = originalMaterialShader; 25 | } 26 | 27 | public override void CreateFeedback() 28 | { 29 | if (spriteRenderer.material.HasProperty("_MakeSolidColor") == false) 30 | { 31 | spriteRenderer.material.shader = flashMaterial.shader; 32 | } 33 | spriteRenderer.material.SetInt("_MakeSolidColor", 1); 34 | StartCoroutine(WaitBeforeChangingBack()); 35 | } 36 | 37 | IEnumerator WaitBeforeChangingBack() 38 | { 39 | yield return new WaitForSeconds(flashTime); 40 | if (spriteRenderer.material.HasProperty("_MakeSolidColor")) 41 | { 42 | spriteRenderer.material.SetInt("_MakeSolidColor", 0); 43 | } 44 | else 45 | { 46 | spriteRenderer.material.shader = originalMaterialShader; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /_Scripts/Enemies/EenemySpawner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using Random = UnityEngine.Random; 6 | 7 | public class EenemySpawner : MonoBehaviour 8 | { 9 | [SerializeField] 10 | private GameObject enemyPrefab = null; 11 | [SerializeField] 12 | private List spawnPoints = null; 13 | [SerializeField] 14 | private int count = 20; 15 | [SerializeField] 16 | private float minDelay = 0.8f, maxDelay = 1.5f; 17 | 18 | IEnumerator SpawnCoroutine() 19 | { 20 | while(count > 0) 21 | { 22 | count--; 23 | var randmoIndex = Random.Range(0, spawnPoints.Count); 24 | 25 | var randomOffset = Random.insideUnitCircle; 26 | var spawnPoint = spawnPoints[randmoIndex].transform.position + (Vector3)randomOffset; 27 | 28 | SpawnEnemy(spawnPoint); 29 | 30 | var randmoTime = Random.Range(minDelay, maxDelay); 31 | yield return new WaitForSeconds(randmoTime); 32 | } 33 | } 34 | 35 | private void SpawnEnemy(Vector3 spawnPoint) 36 | { 37 | Instantiate(enemyPrefab, spawnPoint, Quaternion.identity); 38 | } 39 | 40 | private void Start() 41 | { 42 | if (spawnPoints.Count > 0) 43 | { 44 | foreach (var spawnPoint in spawnPoints) 45 | { 46 | SpawnEnemy(spawnPoint.transform.position); 47 | } 48 | } 49 | StartCoroutine(SpawnCoroutine()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /_Scripts/AgentWeapon.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | public class AgentWeapon : MonoBehaviour 7 | { 8 | protected float desiredAngle; 9 | 10 | [SerializeField] 11 | protected WeaponRenderer weaponRenderer; 12 | [SerializeField] 13 | protected Weapon weapon; 14 | 15 | private void Awake() 16 | { 17 | AssignWeapon(); 18 | } 19 | 20 | private void AssignWeapon() 21 | { 22 | weaponRenderer = GetComponentInChildren(); 23 | weapon = GetComponentInChildren(); 24 | } 25 | 26 | public virtual void AimWeapon(Vector2 pointerposition) 27 | { 28 | var aimDirection = (Vector3)pointerposition - transform.position; 29 | desiredAngle = Mathf.Atan2(aimDirection.y, aimDirection.x) * Mathf.Rad2Deg; 30 | AdjustWeaponRendering(); 31 | transform.rotation = Quaternion.AngleAxis(desiredAngle, Vector3.forward); 32 | } 33 | 34 | protected void AdjustWeaponRendering() 35 | { 36 | if(weaponRenderer != null) 37 | { 38 | weaponRenderer.FlipSprite(desiredAngle > 90 || desiredAngle < -90); 39 | weaponRenderer.RenderBehindHead(desiredAngle < 180 && desiredAngle > 0); 40 | } 41 | 42 | } 43 | 44 | public void Shoot() 45 | { 46 | if(weapon!=null) 47 | weapon.TryShooting(); 48 | } 49 | 50 | public void StopShooting() 51 | { 52 | if (weapon != null) 53 | weapon.StopShooting(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /_Scripts/Enemies/EnemyAIBrain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using UnityEngine.Events; 6 | 7 | public class EnemyAIBrain : MonoBehaviour, IAgentInput 8 | { 9 | [field: SerializeField] 10 | public GameObject Target { get; set; } 11 | 12 | [field: SerializeField] 13 | public AIState CurrentState { get; private set; } 14 | 15 | [field: SerializeField] 16 | public UnityEvent OnFireButtonPressed { get; set; } 17 | [field: SerializeField] 18 | public UnityEvent OnFireButtonReleased { get; set; } 19 | [field: SerializeField] 20 | public UnityEvent OnMovementKeyPressed { get; set; } 21 | [field: SerializeField] 22 | public UnityEvent OnPointerPositionChange { get; set; } 23 | 24 | 25 | private void Awake() 26 | { 27 | Target = FindObjectOfType().gameObject; 28 | } 29 | 30 | private void Update() 31 | { 32 | if (Target == null) 33 | { 34 | OnMovementKeyPressed?.Invoke(Vector2.zero); 35 | } 36 | else 37 | { 38 | CurrentState.UpdateState(); 39 | } 40 | 41 | } 42 | 43 | public void Attack() 44 | { 45 | OnFireButtonPressed?.Invoke(); 46 | } 47 | 48 | public void Move(Vector2 movementDirection, Vector2 targetPosition) 49 | { 50 | OnMovementKeyPressed?.Invoke(movementDirection); 51 | OnPointerPositionChange?.Invoke(targetPosition); 52 | } 53 | 54 | internal void ChangeToState(AIState state) 55 | { 56 | CurrentState = state; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /_Scripts/AI/AIState.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class AIState : MonoBehaviour 6 | { 7 | private EnemyAIBrain enemyBrain = null; 8 | [SerializeField] 9 | private List actions = null; 10 | [SerializeField] 11 | private List transitions = null; 12 | 13 | private void Awake() 14 | { 15 | enemyBrain = transform.root.GetComponent(); 16 | } 17 | 18 | public void UpdateState() 19 | { 20 | foreach (var action in actions) 21 | { 22 | action.TakeAction(); 23 | } 24 | foreach (var transition in transitions) 25 | { 26 | //player in range -> True -> Back to Idle 27 | //player visible -> True -> Chase 28 | 29 | bool result = false; 30 | foreach (var decision in transition.Decisions) 31 | { 32 | result = decision.MakeADecision(); 33 | if (result == false) 34 | break; 35 | } 36 | if (result) 37 | { 38 | if (transition.PositiveResult != null) 39 | { 40 | enemyBrain.ChangeToState(transition.PositiveResult); 41 | return; 42 | } 43 | } 44 | else 45 | { 46 | if(transition.NegativeResult != null) 47 | { 48 | enemyBrain.ChangeToState(transition.NegativeResult); 49 | return; 50 | } 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /_Scripts/Enemies/Enemy.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.Events; 5 | 6 | public class Enemy : MonoBehaviour, IHittable, IAgent, IKnockBack 7 | { 8 | [field: SerializeField] 9 | public EnemyDataSO EnemyData { get; set; } 10 | 11 | [field: SerializeField] 12 | public int Health { get; private set; } = 2; 13 | 14 | [field: SerializeField] 15 | public EnemyAttack enemyAttack { get; set; } 16 | 17 | private bool dead = false; 18 | 19 | private AgentMovement agentMovemenet; 20 | 21 | [field: SerializeField] 22 | public UnityEvent OnGetHit { get; set; } 23 | 24 | [field: SerializeField] 25 | public UnityEvent OnDie { get; set; } 26 | 27 | private void Awake() 28 | { 29 | if(enemyAttack == null) 30 | { 31 | enemyAttack = GetComponent(); 32 | } 33 | agentMovemenet = GetComponent(); 34 | } 35 | private void Start() 36 | { 37 | Health = EnemyData.MaxHealth; 38 | } 39 | 40 | public void GetHit(int damage, GameObject damageDealer) 41 | { 42 | if(dead == false) 43 | { 44 | Health--; 45 | OnGetHit?.Invoke(); 46 | if (Health <= 0) 47 | { 48 | dead = true; 49 | OnDie?.Invoke(); 50 | 51 | } 52 | } 53 | 54 | } 55 | 56 | public void Die() 57 | { 58 | Destroy(gameObject); 59 | } 60 | 61 | public void PerformAttack() 62 | { 63 | if (dead == false) 64 | { 65 | enemyAttack.Attack(EnemyData.Damage); 66 | } 67 | } 68 | 69 | public void KnockBack(Vector2 direction, float power, float duration) 70 | { 71 | agentMovemenet.KnockBack(direction, power, duration); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /_Scripts/AgentInput.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using UnityEngine.Events; 6 | 7 | public class AgentInput : MonoBehaviour, IAgentInput 8 | { 9 | private Camera mainCamera; 10 | private bool fireButtonDown = false; 11 | 12 | [field: SerializeField] 13 | public UnityEvent OnMovementKeyPressed { get; set; } 14 | 15 | [field: SerializeField] 16 | public UnityEvent OnPointerPositionChange { get; set; } 17 | 18 | [field: SerializeField] 19 | public UnityEvent OnFireButtonPressed { get; set; } 20 | 21 | [field: SerializeField] 22 | public UnityEvent OnFireButtonReleased { get; set; } 23 | 24 | private void Awake() 25 | { 26 | mainCamera = Camera.main; 27 | } 28 | private void Update() 29 | { 30 | GetMovementInput(); 31 | GetPointerInput(); 32 | GetFireInput(); 33 | } 34 | 35 | private void GetFireInput() 36 | { 37 | if (Input.GetAxisRaw("Fire1") > 0) 38 | { 39 | if (fireButtonDown == false) 40 | { 41 | fireButtonDown = true; 42 | OnFireButtonPressed?.Invoke(); 43 | } 44 | 45 | } 46 | else 47 | { 48 | if (fireButtonDown == true) 49 | { 50 | fireButtonDown = false; 51 | OnFireButtonReleased?.Invoke(); 52 | } 53 | 54 | } 55 | } 56 | 57 | private void GetPointerInput() 58 | { 59 | var mouseInWorldSpace = mainCamera.ScreenToWorldPoint(Input.mousePosition); 60 | OnPointerPositionChange?.Invoke(mouseInWorldSpace); 61 | } 62 | 63 | private void GetMovementInput() 64 | { 65 | OnMovementKeyPressed?.Invoke(new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"))); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /_Scripts/Resources/ItemDropper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | using Random = UnityEngine.Random; 7 | 8 | public class ItemDropper : MonoBehaviour 9 | { 10 | [SerializeField] 11 | private List itemsToDrop = new List(); 12 | float[] itemWeights; 13 | 14 | [SerializeField] 15 | [Range(0,1)] 16 | private float dropChance = 0.5f; 17 | 18 | private void Start() 19 | { 20 | itemWeights = itemsToDrop.Select(item => item.rate).ToArray(); 21 | } 22 | 23 | public void DropItem() 24 | { 25 | var dropVariable = Random.value; 26 | if(dropVariable < dropChance) 27 | { 28 | int index = GetRandomWeightedIndex(itemWeights); 29 | Instantiate(itemsToDrop[index].itemPrefab, transform.position, Quaternion.identity); 30 | } 31 | } 32 | 33 | private int GetRandomWeightedIndex(float[] itemWeights) 34 | { 35 | float sum = 0f; 36 | for (int i = 0; i < itemWeights.Length; i++) 37 | { 38 | sum += itemWeights[i]; 39 | } 40 | float randomValue = Random.Range(0, sum); 41 | float tempSum = 0; 42 | 43 | for (int i = 0; i < itemsToDrop.Count; i++) 44 | { 45 | //0->0+Weight[0] item 1 (0->0.5) 46 | //Weight[0]-> Weight[0]+Weight[1](0.5 -> 0.7) 47 | //tempSum -> tempSu + Weights[N] 48 | if(randomValue >= tempSum && randomValue < tempSum + itemWeights[i]) 49 | { 50 | return i; 51 | } 52 | tempSum += itemWeights[i]; 53 | } 54 | return 0; 55 | } 56 | } 57 | 58 | [Serializable] 59 | public struct ItemSpawnData 60 | { 61 | [Range(0,1)] 62 | public float rate; 63 | public GameObject itemPrefab; 64 | } 65 | -------------------------------------------------------------------------------- /_Scripts/Weapons/RegularBullet.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class RegularBullet : Bullet 6 | { 7 | protected Rigidbody2D rigidBody2d; 8 | private bool isDead = false; 9 | 10 | public override BulletDataSO BulletData 11 | { 12 | get => base.BulletData; 13 | set 14 | { 15 | base.BulletData = value; 16 | rigidBody2d = GetComponent(); 17 | rigidBody2d.drag = BulletData.Friction; 18 | } 19 | } 20 | 21 | private void FixedUpdate() 22 | { 23 | if(rigidBody2d != null && BulletData != null) 24 | { 25 | rigidBody2d.MovePosition(transform.position + BulletData.BulletSpeed * transform.right * Time.fixedDeltaTime); 26 | } 27 | } 28 | 29 | private void OnTriggerEnter2D(Collider2D collision) 30 | { 31 | if (isDead) 32 | return; 33 | isDead = true; 34 | var hittable = collision.GetComponent(); 35 | hittable?.GetHit(bulletData.Damage, gameObject); 36 | if(collision.gameObject.layer == LayerMask.NameToLayer("Obstacle")) 37 | { 38 | HitObstacle(collision); 39 | }else if (collision.gameObject.layer == LayerMask.NameToLayer("Enemy")) 40 | { 41 | HitEnemy(collision); 42 | } 43 | Destroy(gameObject); 44 | } 45 | 46 | private void HitEnemy(Collider2D collision) 47 | { 48 | var knockback = collision.GetComponent(); 49 | knockback?.KnockBack(transform.right, BulletData.KnockBackPower, BulletData.KnockBackDelay); 50 | Vector2 randomOffset = Random.insideUnitCircle * 0.5f; 51 | Instantiate(BulletData.ImpactEnemyPrefab, collision.transform.position + (Vector3)randomOffset, Quaternion.identity); 52 | } 53 | 54 | private void HitObstacle(Collider2D collision) 55 | { 56 | RaycastHit2D hit = Physics2D.Raycast(transform.position, transform.right); 57 | if (hit.collider != null) 58 | { 59 | Instantiate(BulletData.ImpactObstaclePrefab, hit.point, Quaternion.identity); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /_Scripts/AgentMovement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using UnityEngine.Events; 6 | 7 | [RequireComponent(typeof(Rigidbody2D))] 8 | public class AgentMovement : MonoBehaviour 9 | { 10 | protected Rigidbody2D rigidbody2d; 11 | 12 | [field: SerializeField] 13 | public MovementDataSO MovementData { get; set; } 14 | 15 | [SerializeField] 16 | protected float currentVelocity = 3; 17 | protected Vector2 movementDirection; 18 | 19 | protected bool isKnockedBack = false; 20 | 21 | [field: SerializeField] 22 | public UnityEvent OnVelocityChange { get; set; } 23 | 24 | private void Awake() 25 | { 26 | rigidbody2d = GetComponent(); 27 | } 28 | 29 | public void MoveAgent(Vector2 movementInput) 30 | { 31 | movementDirection = movementInput; 32 | currentVelocity = CalculateSpeed(movementInput); 33 | 34 | } 35 | 36 | private float CalculateSpeed(Vector2 movementInput) 37 | { 38 | if(movementInput.magnitude > 0) 39 | { 40 | currentVelocity += MovementData.acceleration * Time.deltaTime; 41 | } 42 | else 43 | { 44 | currentVelocity -= MovementData.deacceleration * Time.deltaTime; 45 | } 46 | return Mathf.Clamp(currentVelocity, 0, MovementData.maxSpeed); 47 | } 48 | 49 | private void FixedUpdate() 50 | { 51 | OnVelocityChange?.Invoke(currentVelocity); 52 | if(isKnockedBack == false) 53 | rigidbody2d.velocity = currentVelocity*movementDirection.normalized; 54 | } 55 | 56 | public void StopImmediatelly() 57 | { 58 | currentVelocity = 0; 59 | rigidbody2d.velocity = Vector2.zero; 60 | } 61 | 62 | public void KnockBack(Vector2 direction, float power, float duration) 63 | { 64 | if(isKnockedBack == false) 65 | { 66 | isKnockedBack = true; 67 | StartCoroutine(KnockBackCoroutine(direction, power, duration)); 68 | } 69 | } 70 | 71 | public void ResetKnockBack() 72 | { 73 | StopAllCoroutines(); 74 | ResetKnockBackParameters(); 75 | } 76 | 77 | IEnumerator KnockBackCoroutine(Vector2 direction, float power, float duration) 78 | { 79 | rigidbody2d.AddForce(direction.normalized * power, ForceMode2D.Impulse); 80 | yield return new WaitForSeconds(duration); 81 | ResetKnockBackParameters(); 82 | } 83 | 84 | private void ResetKnockBackParameters() 85 | { 86 | currentVelocity = 0; 87 | rigidbody2d.velocity = Vector2.zero; 88 | isKnockedBack = false; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /_Scripts/Player/Player.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.Events; 5 | 6 | public class Player : MonoBehaviour, IAgent, IHittable 7 | { 8 | [SerializeField] 9 | private int maxHealth = 2; 10 | 11 | private int health; 12 | public int Health { 13 | get => health; 14 | set 15 | { 16 | health = Mathf.Clamp(value,0,maxHealth); 17 | uiHealth.UpdateUI(health); 18 | } 19 | } 20 | 21 | private bool dead = false; 22 | 23 | private PlayerWeapon playeWeapon; 24 | 25 | [field: SerializeField] 26 | public UIHealth uiHealth { get; set; } 27 | 28 | [field: SerializeField] 29 | public UnityEvent OnDie { get; set; } 30 | [field: SerializeField] 31 | public UnityEvent OnGetHit { get; set; } 32 | 33 | private void Awake() 34 | { 35 | playeWeapon = GetComponentInChildren(); 36 | } 37 | private void Start() 38 | { 39 | Health = maxHealth; 40 | uiHealth.Initialize(Health); 41 | } 42 | public void GetHit(int damage, GameObject damageDealer) 43 | { 44 | if(dead == false) 45 | { 46 | Health--; 47 | OnGetHit?.Invoke(); 48 | if (Health <= 0) 49 | { 50 | OnDie?.Invoke(); 51 | dead = true; 52 | 53 | } 54 | } 55 | 56 | 57 | } 58 | 59 | private void OnTriggerEnter2D(Collider2D collision) 60 | { 61 | if(collision.gameObject.layer == LayerMask.NameToLayer("Resource")) 62 | { 63 | var resource = collision.gameObject.GetComponent(); 64 | if (resource != null) 65 | { 66 | switch (resource.ResourceData.ResourceType) 67 | { 68 | case ResourceTypeEnum.Health: 69 | if(Health >= maxHealth) 70 | { 71 | return; 72 | } 73 | Health += resource.ResourceData.GetAmount(); 74 | resource.PickUpResource(); 75 | break; 76 | case ResourceTypeEnum.Ammo: 77 | if (playeWeapon.AmmoFull) 78 | { 79 | return; 80 | } 81 | playeWeapon.AddAmmo(resource.ResourceData.GetAmount()); 82 | resource.PickUpResource(); 83 | break; 84 | default: 85 | break; 86 | } 87 | } 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /_Scripts/Weapons/Weapon.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using UnityEngine.Events; 6 | using Random = UnityEngine.Random; 7 | 8 | public class Weapon : MonoBehaviour 9 | { 10 | [SerializeField] 11 | protected GameObject muzzle; 12 | 13 | [SerializeField] 14 | protected int ammo = 10; 15 | 16 | [SerializeField] 17 | protected WeaponDataSO weaponData; 18 | 19 | public int Ammo 20 | { 21 | get { return ammo; } 22 | set { 23 | ammo = Mathf.Clamp(value, 0, weaponData.AmmoCapacity); 24 | OnAmmoChange?.Invoke(ammo); 25 | } 26 | } 27 | 28 | public bool AmmoFull { get => Ammo >= weaponData.AmmoCapacity; } 29 | 30 | protected bool isShooting = false; 31 | 32 | [SerializeField] 33 | protected bool reloadCoroutine = false; 34 | 35 | [field: SerializeField] 36 | public UnityEvent OnShoot { get; set; } 37 | 38 | [field: SerializeField] 39 | public UnityEvent OnShootNoAmmo { get; set; } 40 | 41 | [field: SerializeField] 42 | public UnityEvent OnAmmoChange { get; set; } 43 | 44 | private void Start() 45 | { 46 | Ammo = weaponData.AmmoCapacity; 47 | } 48 | 49 | public void TryShooting() 50 | { 51 | isShooting = true; 52 | } 53 | public void StopShooting() 54 | { 55 | isShooting = false; 56 | } 57 | 58 | public void Reload(int ammo) 59 | { 60 | Ammo += ammo; 61 | } 62 | 63 | private void Update() 64 | { 65 | UseWeapon(); 66 | } 67 | 68 | private void UseWeapon() 69 | { 70 | if(isShooting && reloadCoroutine == false) 71 | { 72 | if (Ammo > 0) 73 | { 74 | Ammo--; 75 | OnShoot?.Invoke(); 76 | for (int i = 0; i < weaponData.GetBulletCountToSpawn(); i++) 77 | { 78 | ShootBullet(); 79 | } 80 | } 81 | else 82 | { 83 | isShooting = false; 84 | OnShootNoAmmo?.Invoke(); 85 | return; 86 | } 87 | FinishShooting(); 88 | } 89 | } 90 | 91 | private void FinishShooting() 92 | { 93 | StartCoroutine(DelayNextShootCoroutine()); 94 | if (weaponData.AutomaticFire == false) 95 | { 96 | isShooting = false; 97 | } 98 | } 99 | 100 | protected IEnumerator DelayNextShootCoroutine() 101 | { 102 | reloadCoroutine = true; 103 | yield return new WaitForSeconds(weaponData.WeaponDelay); 104 | reloadCoroutine = false; 105 | } 106 | 107 | private void ShootBullet() 108 | { 109 | SpawnBullet(muzzle.transform.position, CalculateAngle(muzzle)); 110 | } 111 | 112 | private void SpawnBullet(Vector3 position, Quaternion rotation) 113 | { 114 | var bulletPrefab = Instantiate(weaponData.BulletData.bulletPrefab, position, rotation); 115 | bulletPrefab.GetComponent().BulletData = weaponData.BulletData; 116 | } 117 | 118 | private Quaternion CalculateAngle(GameObject muzzle) 119 | { 120 | float spread = Random.Range(-weaponData.SpreadAngle, weaponData.SpreadAngle); 121 | Quaternion bulletSpreadRotation = Quaternion.Euler(new Vector3(0, 0, spread)); 122 | return muzzle.transform.rotation * bulletSpreadRotation; 123 | } 124 | } 125 | --------------------------------------------------------------------------------