This commit is contained in:
manhduyhoang90
2026-06-06 00:29:37 +07:00
5 changed files with 141 additions and 32 deletions

View File

@@ -14,6 +14,7 @@ GameObject:
- component: {fileID: 6087599376744356948}
- component: {fileID: 3563399533700019190}
- component: {fileID: 681314853465352057}
- component: {fileID: 4042483058127329834}
m_Layer: 0
m_Name: KamikazeAI
m_TagString: Untagged
@@ -155,3 +156,41 @@ MonoBehaviour:
patrolRadius: 12
patrolWaitTime: 2
explosionEffectPrefab: {fileID: 8568474719719117872, guid: 39bf32dcd9299df4ca44fd10a817eda4, type: 3}
--- !u!114 &4042483058127329834
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6425756872251228809}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f56a83ea88140fa4f869bb2f7ffdb184, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::Invector.vHealthController
openCloseEvents: 0
openCloseWindow: 1
selectedToolbar: 0
_isDead: 1
_currentHealth: 0
isImmortal: 0
fillHealthOnStart: 1
_maxHealth: 100
_healthRecovery: 0
_healthRecoveryDelay: 0
checkHealthEvents: []
_onStartReceiveDamage:
m_PersistentCalls:
m_Calls: []
_onReceiveDamage:
m_PersistentCalls:
m_Calls: []
_onDead:
m_PersistentCalls:
m_Calls: []
onChangeHealth:
m_PersistentCalls:
m_Calls: []
onResetHealth:
m_PersistentCalls:
m_Calls: []

View File

@@ -472,6 +472,7 @@ GameObject:
- component: {fileID: 6469822191588635990}
- component: {fileID: 9027690817715396964}
- component: {fileID: 204793640880232070}
- component: {fileID: 2997468006321853334}
m_Layer: 9
m_Name: xNPC
m_TagString: Enemy
@@ -516,19 +517,20 @@ MonoBehaviour:
patrolSpeed: 2
patrolRadius: 5
playerHasArtifact: 0
isAggroedBySound: 0
laserPrefab: {fileID: 3965388737199864462, guid: fbec2b501d70daa4c9cb481ba53fc0b8, type: 3}
firePoint: {fileID: 5863061020199015852}
minShootDelay: 1
maxShootDelay: 5
maxShootDelay: 3
dodgeForce: 8
dodgeDuration: 0.5
dodgeCooldown: 3
minStrafeDuration: 0.5
maxStrafeDuration: 2.2
maxSpreadAngle: 6
maxSpreadAngle: 3
burstInterval: 0.12
approachWeight: 0.35
minCombatDistance: 5
approachWeight: 1
minCombatDistance: 6
npcName: Guard
persona: You are a grumpy guard protecting gold.
talkRange: 5
@@ -621,18 +623,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 35bba55c2a743d042ab1fff35e29db50, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::AnimatorAI
debugMode: 1
debugColor: {r: 1, g: 0, b: 0.21842146, a: 1}
useSimulation: 0
autoCycleSpeed: 0
simVerticalVelocity: 0
simIsSprinting: 0
simIsAiming: 0
simMoveSetID: 0
sprintThreshold: 0.8
dampTime: 0.1
forcePlayState:
showLayerWeights: 0
groundDistanceValue: 0.05
--- !u!136 &9027690817715396964
CapsuleCollider:
m_ObjectHideFlags: 0
@@ -694,6 +687,24 @@ MonoBehaviour:
onResetHealth:
m_PersistentCalls:
m_Calls: []
--- !u!114 &2997468006321853334
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7522161431095319480}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9d3efef3ad62cd548b0f85eb11858ed1, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::Invector.vHitDamageParticle
openCloseEvents: 0
openCloseWindow: 1
selectedToolbar: 0
defaultDamageEffects:
- {fileID: 120346, guid: b8f2fe1ee1a01724cb72f5b0e6bd2eef, type: 3}
customDamageEffects: []
--- !u!1001 &7561534673732472622
PrefabInstance:
m_ObjectHideFlags: 0

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
@@ -20,6 +20,7 @@ public class EnemyAI : MonoBehaviour
private NavMeshAgent agent;
private Rigidbody rb;
private FieldOfView fov;
private Collider mainCollider;
[Header("Movement Settings")]
public float moveSpeed = 3f;
@@ -53,7 +54,7 @@ public class EnemyAI : MonoBehaviour
public float maxStrafeDuration = 2.2f;
public float maxSpreadAngle = 6f;
public float burstInterval = 0.12f;
public float approachWeight = 0.35f;
public float minCombatDistance = 5.0f;
@@ -79,7 +80,7 @@ public class EnemyAI : MonoBehaviour
public float investigationThreshold = 30f;
public float alertNeighborsThreshold = 70f;
public float alertRange = 20f;
public Node rootNode;
void Start()
@@ -88,6 +89,19 @@ public class EnemyAI : MonoBehaviour
rb = GetComponent<Rigidbody>();
fov = GetComponent<FieldOfView>();
chatBubble = GetComponentInChildren<Hallucinate.UI.ChatBubble>(true);
mainCollider = GetComponent<Collider>();
// Tự động gán Layer Enemy nếu chưa có
if (gameObject.layer == LayerMask.NameToLayer("Default"))
{
gameObject.layer = LayerMask.NameToLayer("Enemy");
Debug.Log($"[AI {npcName}] Tự động chuyển Layer sang Enemy để súng có thể bắn trúng.");
}
if (mainCollider == null)
{
Debug.LogError($"[AI {npcName}] THIẾU COLLIDER! NPC này sẽ không thể bị bắn trúng.");
}
rb.isKinematic = true;
rb.freezeRotation = true;
@@ -105,10 +119,10 @@ public class EnemyAI : MonoBehaviour
void InitTree()
{
var dodgeSequence = new Sequence(new List<Node> { new TaskNode(CheckDodgeConditions), new TaskNode(ActionDodge) });
// Đổi hàm CheckHasArtifact thành CheckCombatConditions để dùng chung cho cả âm thanh
var laserSequence = new Sequence(new List<Node> { new TaskNode(CheckCombatConditions), new TaskNode(ActionFocusAndShoot) });
var chaseSequence = new Sequence(new List<Node> { new TaskNode(CheckCanSeePlayer), new TaskNode(ActionChasePlayer) });
var investigateSequence = new Sequence(new List<Node> { new TaskNode(CheckHasInvestigateTarget), new TaskNode(ActionInvestigate) });
var talkSequence = new Sequence(new List<Node> { new TaskNode(CheckCanTalkToNPC), new TaskNode(ActionTalk) });
@@ -129,6 +143,13 @@ public class EnemyAI : MonoBehaviour
{
if (player == null) return;
// Đảm bảo Collider luôn bật
if (mainCollider != null && !mainCollider.enabled)
{
mainCollider.enabled = true;
Debug.LogWarning($"[AI {npcName}] Collider bị tắt trái phép! Đã tự động bật lại.");
}
if (!agent.isOnNavMesh) return;
suspicionLevel = Mathf.Max(0, suspicionLevel - Time.deltaTime * 0.5f);
@@ -202,7 +223,7 @@ public class EnemyAI : MonoBehaviour
foreach (var hit in hitColliders)
{
if (hit.gameObject == gameObject) continue;
EnemyAI other = hit.GetComponentInParent<EnemyAI>();
if (other != null && !other.isTalking && Time.time >= other.lastTalkTime + talkCooldown)
{
@@ -226,8 +247,8 @@ public class EnemyAI : MonoBehaviour
{
suspicionLevel += volume * 15f;
if (fov != null) fov.lastKnownPlayerPosition = location;
// CẬP NHẬT: Nếu AI nghe thấy tiếng động làm mức nghi ngờ vượt mốc điều tra, chuyển sang trạng thái chiến đấu (bắn bồi)
// Nếu AI nghe thấy tiếng động làm mức nghi ngờ vượt mốc điều tra, chuyển sang trạng thái chiến đấu
if (suspicionLevel >= investigationThreshold)
{
isAggroedBySound = true;
@@ -238,6 +259,16 @@ public class EnemyAI : MonoBehaviour
Debug.Log($"<color=orange>[AI {npcName}]</color> Heard noise! Suspicion: {suspicionLevel}");
}
public void TriggerCombatAlert(Vector3 sourceLocation)
{
suspicionLevel = 100f;
isAggroedBySound = true; // Ép vào trạng thái tấn công
if (fov != null) fov.lastKnownPlayerPosition = sourceLocation;
StopConversation();
AlertNeighbors();
Debug.Log($"<color=red>[AI {npcName}] GUNFIRE DETECTED!</color> Entering combat mode.");
}
public void AlertNeighbors()
{
Collider[] hitColliders = Physics.OverlapSphere(transform.position, alertRange);
@@ -277,7 +308,7 @@ public class EnemyAI : MonoBehaviour
{
DialogueResult result = JsonUtility.FromJson<DialogueResult>(json);
if (chatBubble != null) chatBubble.Show(result.text);
moveSpeed += result.speedMod;
suspicionLevel = Mathf.Clamp(suspicionLevel + result.suspicionMod, 0, 100);
lastTalkTime = Time.time;
@@ -334,7 +365,7 @@ public class EnemyAI : MonoBehaviour
agent.isStopped = false;
agent.speed = moveSpeed * 0.7f;
agent.SetDestination(fov.lastKnownPlayerPosition);
if (Vector3.Distance(transform.position, fov.lastKnownPlayerPosition) < 1.5f)
{
currentWaitTime += Time.deltaTime;
@@ -357,12 +388,12 @@ public class EnemyAI : MonoBehaviour
// CẢI TIẾN: Xác định mục tiêu linh hoạt để tránh Wallhack.
Vector3 targetPos = player.position; // Mặc định khóa chặt người chơi
// Nếu không cầm Artifact và cũng chưa bị nhìn thấy, AI chỉ nhắm vào MỐC ÂM THANH
if (!playerHasArtifact && fov != null && !fov.canSeePlayer && fov.lastKnownPlayerPosition != Vector3.zero)
{
targetPos = fov.lastKnownPlayerPosition;
// Nếu AI tiến hành áp sát và xả đạn vào nơi phát ra tiếng mà không thấy ai, ngưng bắn
if (Vector3.Distance(transform.position, targetPos) < 2f)
{
@@ -472,10 +503,16 @@ public class EnemyAI : MonoBehaviour
isDodging = true;
agent.enabled = false;
rb.isKinematic = false;
// GIỮ COLLIDER LUÔN BẬT KHI NÉ
if (mainCollider != null) mainCollider.enabled = true;
Vector3 dir = (player.position - transform.position).normalized;
Vector3 perp = new Vector3(-dir.z, 0, dir.x);
rb.AddForce((Random.value > 0.5f ? perp : -perp) * dodgeForce, ForceMode.Impulse);
yield return new WaitForSeconds(dodgeDuration);
rb.linearVelocity = Vector3.zero;
rb.isKinematic = true;
agent.enabled = true;

View File

@@ -302,6 +302,15 @@ namespace Invector.vShooter
var rotation = Quaternion.LookRotation(dir);
GameObject bulletObject = null;
var velocityChanged = 0f;
if (projectile == null)
{
Debug.LogError($"<color=red>WEAPON ERROR:</color> No Projectile Prefab assigned to {gameObject.name}!");
return;
}
Debug.Log($"<color=white>WEAPON SHOOT:</color> Spawning projectile. HitLayer: {hitLayer.value}");
if (dispersion > 0 && projectile)
{
for (int i = 0; i < projectilesPerShot; i++)
@@ -311,6 +320,12 @@ namespace Invector.vShooter
bulletObject = Instantiate(projectile, startPoint, spreadRotation);
var pCtrl = bulletObject.GetComponent<vProjectileControl>();
if (pCtrl == null)
{
Debug.LogError($"<color=red>PROJECTILE ERROR:</color> {projectile.name} does not have vProjectileControl script!");
continue;
}
if (pCtrl.debugTrajetory && i == 0)
{
startPoint.DebugPoint(Color.red, 10, 0.1f);
@@ -338,6 +353,13 @@ namespace Invector.vShooter
{
bulletObject = Instantiate(projectile, startPoint, rotation);
var pCtrl = bulletObject.GetComponent<vProjectileControl>();
if (pCtrl == null)
{
Debug.LogError($"<color=red>PROJECTILE ERROR:</color> {projectile.name} does not have vProjectileControl script!");
return;
}
if (pCtrl.debugTrajetory)
{
startPoint.DebugPoint(Color.red, 10, 0.1f);