NPC: Version 1.0.0

This commit is contained in:
manhduyhoang90
2026-06-03 13:42:09 +07:00
parent ff36df26d4
commit 846ddb25ce
9 changed files with 522 additions and 48 deletions

View File

@@ -3,71 +3,171 @@ using UnityEngine;
public class EnemyAI : MonoBehaviour
{
[Header("References")]
public Transform player;
[Header("Detection")]
public float detectRange = 10f;
public float moveSpeed = 3f;
public float rotateSpeed = 50f;
public bool isAttackReady;
[Header("Artifact")]
public bool playerHasArtifact;
[Header("Laser")]
public GameObject laserPrefab;
public Transform firePoint;
public float minShootDelay = 1f;
public float maxShootDelay = 3f;
private float nextShootTime;
public Node behaviorTreeRoot;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
private void Start()
{
nextShootTime = Time.time + Random.Range(minShootDelay, maxShootDelay);
InitBehaviorTree();
}
// Update is called once per frame
void Update()
private void Update()
{
if (behaviorTreeRoot != null)
{
behaviorTreeRoot.Evaluate();
}
behaviorTreeRoot?.Evaluate();
}
void InitBehaviorTree()
private void InitBehaviorTree()
{
// Player có artifact -> focus + shoot
var laserSequence = new Sequence(new List<Node>
{
new TaskNode(CheckHasArtifact),
new TaskNode(ActionFocusAndShoot)
});
// Thấy player -> chạy tới
var chaseSequence = new Sequence(new List<Node>
{
new TaskNode(CheckCanSeePlayer),
new TaskNode(ActionMoveToPlayer)
});
var rotationScanNode = new TaskNode(ActionRotationScan);
// Không thấy ai -> scan
var scanNode = new TaskNode(ActionRotationScan);
behaviorTreeRoot = new Selector(new List<Node>
{
laserSequence,
chaseSequence,
rotationScanNode
scanNode
});
}
private NodeState ActionRotationScan()
{
Debug.Log("ActionRotationScan");
transform.Rotate(Vector3.up, rotateSpeed * Time.deltaTime);
return NodeState.Running;
}
#region CONDITIONS
private NodeState ActionMoveToPlayer()
private NodeState CheckHasArtifact()
{
Debug.Log($"Moving to Player");
Vector3 dir = (player.position - transform.position).normalized;
transform.position += dir * moveSpeed * Time.deltaTime;
return NodeState.Running;
return playerHasArtifact
? NodeState.Success
: NodeState.Failure;
}
private NodeState CheckCanSeePlayer()
{
if (player == null)
{
return NodeState.Failure;
}
float distance = Vector3.Distance(transform.position,player.position);
if (distance < detectRange)
float distance =
Vector3.Distance(transform.position, player.position);
if (distance <= detectRange)
{
Debug.Log($"Player detected!");
return NodeState.Success;
}
return NodeState.Failure;
}
#endregion
#region ACTIONS
private NodeState ActionRotationScan()
{
Debug.Log("Scanning...");
transform.Rotate(
Vector3.up,
rotateSpeed * Time.deltaTime);
return NodeState.Running;
}
private NodeState ActionMoveToPlayer()
{
if (player == null)
return NodeState.Failure;
Debug.Log("Chasing Player");
Vector3 dir =
(player.position - transform.position).normalized;
transform.position +=
dir *
moveSpeed *
Time.deltaTime;
return NodeState.Running;
}
private NodeState ActionFocusAndShoot()
{
if (player == null)
return NodeState.Failure;
// Focus player
Vector3 dir =
player.position - transform.position;
dir.y = 0f;
if (dir != Vector3.zero)
{
Quaternion targetRotation =
Quaternion.LookRotation(dir);
transform.rotation =
Quaternion.Slerp(
transform.rotation,
targetRotation,
5f * Time.deltaTime);
}
// Shoot with random delay
if (Time.time >= nextShootTime)
{
ShootLaser();
nextShootTime =
Time.time +
Random.Range(minShootDelay, maxShootDelay);
}
return NodeState.Running;
}
private void ShootLaser()
{
if (laserPrefab == null || firePoint == null)
return;
Instantiate(
laserPrefab,
firePoint.position,
firePoint.rotation);
Debug.Log("Laser Shot!");
}
#endregion
}

View File

@@ -0,0 +1,30 @@
using UnityEngine;
public class LaserProjectile : MonoBehaviour
{
public float speed = 5f;
public float lifeTime = 5f;
private void Start()
{
Destroy(gameObject, lifeTime);
}
private void Update()
{
transform.position +=
transform.forward *
speed *
Time.deltaTime;
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
Debug.Log("Player Hit");
Destroy(gameObject);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 4e4f602386d4d484ea7a2a3b0c19ac21

View File

@@ -0,0 +1,16 @@
using UnityEngine;
public class RagNPC : MonoBehaviour
{
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 30cdc86fe97fa95428a4b31052cfb22f