148 lines
4.9 KiB
C#
148 lines
4.9 KiB
C#
|
|
using System.Collections;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using UnityEngine;
|
|||
|
|
|
|||
|
|
public class AiController : MonoBehaviour {
|
|||
|
|
|
|||
|
|
[SerializeField] Strategy strategy;
|
|||
|
|
[SerializeField] public BoxCollider2D closeRangeCollider;
|
|||
|
|
[SerializeField] public BoxCollider2D focusRangeCollider;
|
|||
|
|
[SerializeField] public BoxCollider2D attackRangeCollider;
|
|||
|
|
[SerializeField] public BoxCollider2D playerColldier;
|
|||
|
|
|
|||
|
|
private CharacterController2D playerCharacterController;
|
|||
|
|
|
|||
|
|
[SerializeField] private AudioSource audioSource;
|
|||
|
|
[SerializeField] private AudioClip exclaimAudioClip;
|
|||
|
|
|
|||
|
|
public GameObject exclamation;
|
|||
|
|
public bool isExclaiming = false;
|
|||
|
|
public bool canBeStartled = true;
|
|||
|
|
|
|||
|
|
public bool shouldExclaim = false;
|
|||
|
|
public float exclaimTime = 1.0f;
|
|||
|
|
public float exclaimExpiration = 30.0f;
|
|||
|
|
public float transposeExclamationY = 0.0f;
|
|||
|
|
|
|||
|
|
public bool isCoolingDownAttack = false;
|
|||
|
|
public float attackCooldown = 1.5f;
|
|||
|
|
|
|||
|
|
public Intent intent = Intent.NONE;
|
|||
|
|
public bool isPlayerInRange = false;
|
|||
|
|
public bool isPlayerInAttackRange = false;
|
|||
|
|
|
|||
|
|
public enum Strategy {
|
|||
|
|
// Actively chases the player, attacking when roughly in range.
|
|||
|
|
// Should be annoying.
|
|||
|
|
RUSHDOWN,
|
|||
|
|
|
|||
|
|
// Minds own business, only facing when in range and attacking
|
|||
|
|
// from short range.
|
|||
|
|
// Probably the easiest to handle.
|
|||
|
|
REACTIVE,
|
|||
|
|
|
|||
|
|
// Casually tracks the player, fleeing when approached and
|
|||
|
|
// rushing down some time later.
|
|||
|
|
// Very difficult to defeat.
|
|||
|
|
STRATEGIC,
|
|||
|
|
|
|||
|
|
// Unpredictable.
|
|||
|
|
CRAZY
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public enum Intent {
|
|||
|
|
NONE,
|
|||
|
|
ATTACK,
|
|||
|
|
CHASE,
|
|||
|
|
FLEE
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Start is called before the first frame update
|
|||
|
|
void Awake() {
|
|||
|
|
playerColldier = GameObject.FindGameObjectWithTag("Player").GetComponent<BoxCollider2D>();
|
|||
|
|
playerCharacterController = GameObject.FindGameObjectWithTag("Player").GetComponent<CharacterController2D>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Intent GetStrategicIntent() {
|
|||
|
|
float posX = transform.position.x;
|
|||
|
|
float playerPosX = playerCharacterController.transform.position.x;
|
|||
|
|
float playerVelX = playerCharacterController.m_Rigidbody2D.velocity.x;
|
|||
|
|
// If the player is facing away from me, I'll chase!
|
|||
|
|
if ((playerPosX < posX) != (playerCharacterController.m_FacingRight)) {
|
|||
|
|
return Intent.CHASE;
|
|||
|
|
} else {
|
|||
|
|
// If the player's chasing me, I'll face them!
|
|||
|
|
if ((playerVelX > 0.01 && playerPosX < posX)
|
|||
|
|
|| (playerVelX < -0.01 && playerPosX > posX)) {
|
|||
|
|
return Intent.CHASE;
|
|||
|
|
}
|
|||
|
|
return Intent.FLEE;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Intent GetOnFocusIntent() {
|
|||
|
|
if (isExclaiming) {
|
|||
|
|
return Intent.NONE;
|
|||
|
|
}
|
|||
|
|
switch (strategy) {
|
|||
|
|
case Strategy.RUSHDOWN:
|
|||
|
|
// TODO: Refactor IsTouching to use OnTriggerEnter/Exit events
|
|||
|
|
if (attackRangeCollider.IsTouching(playerColldier) && !isCoolingDownAttack) {
|
|||
|
|
return Intent.ATTACK;
|
|||
|
|
} else {
|
|||
|
|
return Intent.CHASE;
|
|||
|
|
}
|
|||
|
|
case Strategy.REACTIVE:
|
|||
|
|
return Intent.NONE;
|
|||
|
|
case Strategy.STRATEGIC:
|
|||
|
|
return GetStrategicIntent();
|
|||
|
|
case Strategy.CRAZY:
|
|||
|
|
//float choice = Random.Range(0f, 1f);
|
|||
|
|
//if (choice < 0.25f) return Intent.ATTACK;
|
|||
|
|
//if (choice < 0.5f) return Intent.CHASE;
|
|||
|
|
//if (choice < 0.75f) return Intent.FLEE;
|
|||
|
|
return Intent.NONE;
|
|||
|
|
default:
|
|||
|
|
Debug.LogError("Undefined strategy for monster!");
|
|||
|
|
return Intent.NONE;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnTriggerEnter2D(Collider2D collision) {
|
|||
|
|
if (collision.CompareTag("Player")) {
|
|||
|
|
isPlayerInRange = true;
|
|||
|
|
intent = GetOnFocusIntent();
|
|||
|
|
if (shouldExclaim && canBeStartled && !attackRangeCollider.IsTouching(playerColldier)) {
|
|||
|
|
audioSource.PlayOneShot(exclaimAudioClip);
|
|||
|
|
GameObject effect = Instantiate(exclamation);
|
|||
|
|
effect.transform.position = new Vector2(transform.position.x, transform.position.y + transposeExclamationY);
|
|||
|
|
isExclaiming = true;
|
|||
|
|
canBeStartled = false;
|
|||
|
|
Invoke("StopExclaiming", exclaimTime);
|
|||
|
|
Invoke("CanStartle", exclaimExpiration);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void StopExclaiming() {
|
|||
|
|
isExclaiming = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void CanStartle() {
|
|||
|
|
canBeStartled = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnTriggerExit2D(Collider2D collision) {
|
|||
|
|
if (collision.gameObject.CompareTag("Player") &&
|
|||
|
|
!focusRangeCollider.IsTouching(playerColldier)) {
|
|||
|
|
isPlayerInRange = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void FixedUpdate() {
|
|||
|
|
if (isPlayerInRange) {
|
|||
|
|
intent = GetOnFocusIntent();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|