This commit is contained in:
2026-06-14 23:55:20 +07:00
parent d731f962a3
commit 132def45c7
16 changed files with 380 additions and 77 deletions

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@@ -25,6 +25,7 @@ Material:
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- _ENVIRONMENTREFLECTIONS_OFF
- _NORMALMAP
m_InvalidKeywords:
- _GLOSSYREFLECTIONS_OFF

View File

@@ -11,7 +11,8 @@ Material:
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_ValidKeywords:
- _ENVIRONMENTREFLECTIONS_OFF
m_InvalidKeywords:
- _GLOSSYREFLECTIONS_OFF
m_LightmapFlags: 4
@@ -22,7 +23,6 @@ Material:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
- SHADOWCASTER
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3

View File

@@ -3274,6 +3274,7 @@ GameObject:
- component: {fileID: 1505227404007521061}
- component: {fileID: -3829962440237222997}
- component: {fileID: -3602722895466718484}
- component: {fileID: 5784434468645741687}
m_Layer: 8
m_Name: vShooterMelee_Inventory
m_TagString: Player
@@ -5084,6 +5085,24 @@ MonoBehaviour:
m_DefaultActionMap: Player
m_SplitScreenIndex: -1
m_Camera: {fileID: 0}
--- !u!114 &5784434468645741687
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 146720}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c25b53df82df1fd4daabb89c90ce092a, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::OnlyScove.Player.PlayerInteraction
cameraTransform: {fileID: 0}
interactDistance: 3
interactableLayer:
serializedVersion: 2
m_Bits: 0
interactKey: 101
--- !u!1 &149606
GameObject:
m_ObjectHideFlags: 0

View File

@@ -423,6 +423,18 @@ PrefabInstance:
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5784434468645741687, guid: 092f8aa667b2004459042a82a4c9e41d, type: 3}
propertyPath: cameraTransform
value:
objectReference: {fileID: 1153753045}
- target: {fileID: 5784434468645741687, guid: 092f8aa667b2004459042a82a4c9e41d, type: 3}
propertyPath: ignoreLayers.m_Bits
value: 256
objectReference: {fileID: 0}
- target: {fileID: 5784434468645741687, guid: 092f8aa667b2004459042a82a4c9e41d, type: 3}
propertyPath: interactableLayer.m_Bits
value: 524544
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
@@ -439,6 +451,27 @@ Transform:
m_CorrespondingSourceObject: {fileID: 2207112960010484425, guid: fb7874830b9e56341bf88f2a1123c677, type: 3}
m_PrefabInstance: {fileID: 3886963620680427248}
m_PrefabAsset: {fileID: 0}
--- !u!1 &1133189362 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 1534469393933210, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
m_PrefabInstance: {fileID: 1932644107}
m_PrefabAsset: {fileID: 0}
--- !u!114 &1133189369
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1133189362}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d41bcdbf11a6d6c4bb61a32e85d1635f, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::OnlyScove.Scripts.DoorInteractable
interactionData: {fileID: 0}
woodDoorScript: {fileID: 0}
animator: {fileID: 0}
boolParameterName: IsOpen
--- !u!1 &1142785471
GameObject:
m_ObjectHideFlags: 0
@@ -471,6 +504,11 @@ Transform:
- {fileID: 1731534}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!4 &1153753045 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 2877311890657080738, guid: 092f8aa667b2004459042a82a4c9e41d, type: 3}
m_PrefabInstance: {fileID: 401287878}
m_PrefabAsset: {fileID: 0}
--- !u!1 &1185172173
GameObject:
m_ObjectHideFlags: 0
@@ -845,6 +883,82 @@ MonoBehaviour:
m_EditorClassIdentifier: Assembly-CSharp::Hallucinate.GameSetup.Maze.MazeRenderer
visualProfile: {fileID: 11400000, guid: 15b745b0bb979b84ea937c679ee0f1ed, type: 2}
floorHeight: 3.5
--- !u!1001 &1932644107
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 1534469393933210, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
propertyPath: m_Layer
value: 19
objectReference: {fileID: 0}
- target: {fileID: 1831504449878450, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
propertyPath: m_Name
value: Door_1_Black
objectReference: {fileID: 0}
- target: {fileID: 1831504449878450, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
propertyPath: m_Layer
value: 19
objectReference: {fileID: 0}
- target: {fileID: 1875921088058430, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
propertyPath: m_Layer
value: 19
objectReference: {fileID: 0}
- target: {fileID: 1957651719448814, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
propertyPath: m_Layer
value: 19
objectReference: {fileID: 0}
- target: {fileID: 4970741399400872, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
propertyPath: m_LocalPosition.x
value: -13.22
objectReference: {fileID: 0}
- target: {fileID: 4970741399400872, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
propertyPath: m_LocalPosition.y
value: -4.20374
objectReference: {fileID: 0}
- target: {fileID: 4970741399400872, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
propertyPath: m_LocalPosition.z
value: 3.39
objectReference: {fileID: 0}
- target: {fileID: 4970741399400872, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4970741399400872, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4970741399400872, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4970741399400872, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4970741399400872, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4970741399400872, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4970741399400872, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents:
- targetCorrespondingSourceObject: {fileID: 1534469393933210, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
insertIndex: -1
addedObject: {fileID: 1133189369}
m_SourcePrefab: {fileID: 100100000, guid: b5a5a17dc3a6d9e47bd468828a2dec34, type: 3}
--- !u!1 &1997343488
GameObject:
m_ObjectHideFlags: 0
@@ -949,7 +1063,7 @@ PrefabInstance:
m_Modifications:
- target: {fileID: 2207112960010484425, guid: fb7874830b9e56341bf88f2a1123c677, type: 3}
propertyPath: m_LocalPosition.x
value: 3.5546
value: 0.78
objectReference: {fileID: 0}
- target: {fileID: 2207112960010484425, guid: fb7874830b9e56341bf88f2a1123c677, type: 3}
propertyPath: m_LocalPosition.y
@@ -957,7 +1071,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 2207112960010484425, guid: fb7874830b9e56341bf88f2a1123c677, type: 3}
propertyPath: m_LocalPosition.z
value: -5.60193
value: -10.24
objectReference: {fileID: 0}
- target: {fileID: 2207112960010484425, guid: fb7874830b9e56341bf88f2a1123c677, type: 3}
propertyPath: m_LocalRotation.w
@@ -995,6 +1109,10 @@ PrefabInstance:
propertyPath: m_Layer
value: 8
objectReference: {fileID: 0}
- target: {fileID: 2771692228748849855, guid: fb7874830b9e56341bf88f2a1123c677, type: 3}
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3657229949309460766, guid: fb7874830b9e56341bf88f2a1123c677, type: 3}
propertyPath: fpvTarget
value:
@@ -1035,3 +1153,4 @@ SceneRoots:
- {fileID: 1997343489}
- {fileID: 1142785474}
- {fileID: 401287878}
- {fileID: 1932644107}

View File

@@ -4,34 +4,49 @@ namespace OnlyScove.Scripts
{
public abstract class BaseInteractable : MonoBehaviour, IInteractable
{
[Header("Interaction Settings")]
[SerializeField] protected ObjectInteraction interactionData;
private float lastInteractTime;
public virtual string InteractionPrompt => interactionData != null ? interactionData.promptText : "Interact";
public virtual string InteractionPrompt => interactionData != null ? interactionData.promptText : "Tương tác";
// public virtual void OnInteract(PlayerStateMachine player)
// {
// if (Time.time < lastInteractTime + (interactionData != null ? interactionData.interactionCooldown : 0f))
// return;
//
// lastInteractTime = Time.time;
//
// // Play sound if assigned
// if (interactionData != null && interactionData.interactionSound != null)
// {
// AudioSource.PlayClipAtPoint(interactionData.interactionSound, transform.position);
// }
//
// // Spawn VFX if assigned
// if (interactionData != null && interactionData.interactionVFX != null)
// {
// Instantiate(interactionData.interactionVFX, transform.position, Quaternion.identity);
// }
//
// PerformInteraction(player);
// }
//
// protected abstract void PerformInteraction(PlayerStateMachine player);
public virtual void OnInteract(GameObject interactor)
{
// Kiểm tra Cooldown
float cooldown = interactionData != null ? interactionData.interactionCooldown : 0.5f;
if (Time.time < lastInteractTime + cooldown)
return;
lastInteractTime = Time.time;
// Phát âm thanh nếu có
if (interactionData != null && interactionData.interactionSound != null)
{
AudioSource.PlayClipAtPoint(interactionData.interactionSound, transform.position);
}
// Tạo hiệu ứng VFX nếu có
if (interactionData != null && interactionData.interactionVFX != null)
{
Instantiate(interactionData.interactionVFX, transform.position, Quaternion.identity);
}
// Gọi logic tương tác cụ thể của từng loại vật thể
PerformInteraction(interactor);
}
public virtual void OnHoverEnter()
{
// Sẽ thêm logic Highlight ở Giai đoạn 4
}
public virtual void OnHoverExit()
{
// Sẽ xóa logic Highlight ở Giai đoạn 4
}
// Logic cụ thể bắt buộc các lớp con (Cửa, Cần gạt...) phải triển khai
protected abstract void PerformInteraction(GameObject interactor);
}
}
}

View File

@@ -51,7 +51,6 @@ namespace OnlyScove.Scripts
if (animator == null) animator = GetComponentInParent<Animator>();
// 3. TỰ ĐỘNG TẮT SCRIPT XUNG ĐỘT (CameraOpenDoor) nếu nó đang tồn tại trên Camera
// Điều này giúp hệ thống của bạn chiếm quyền điều khiển hoàn toàn
var conflictingScript = Object.FindFirstObjectByType<CameraDoorScript.CameraOpenDoor>();
if (conflictingScript != null)
{
@@ -60,31 +59,24 @@ namespace OnlyScove.Scripts
}
}
// protected override void PerformInteraction(PlayerStateMachine player)
// {
// Debug.Log($"[Interaction] PerformInteraction CALLED on {gameObject.name}!");
//
// // 1. Ưu tiên script của Door Pack (Wood Door Script)
// if (woodDoorScript != null)
// {
// Debug.Log($"[Interaction] Calling woodDoorScript.OpenDoor() on {gameObject.name}. Previous state: {woodDoorScript.open}");
// woodDoorScript.OpenDoor();
// isOpen = woodDoorScript.open;
// Debug.Log($"[Interaction] New state: {woodDoorScript.open}");
// return;
// }
//
// // 2. Nếu không có script Pack mới dùng Animator
// if (animator != null)
// {
// isOpen = !isOpen;
// animator.SetBool(boolParameterName, isOpen);
// Debug.Log($"[Interaction] Triggered Animator: {boolParameterName} = {isOpen}");
// }
// else
// {
// Debug.LogError($"[Interaction] FAILED: No woodDoorScript or animator found on {gameObject.name}");
// }
// }
protected override void PerformInteraction(GameObject interactor)
{
Debug.Log($"[Interaction] PerformInteraction CALLED on {gameObject.name}!");
// 1. Ưu tiên script của Door Pack (Wood Door Script)
if (woodDoorScript != null)
{
woodDoorScript.OpenDoor();
isOpen = woodDoorScript.open;
return;
}
// 2. Nếu không có script Pack mới dùng Animator
if (animator != null)
{
isOpen = !isOpen;
animator.SetBool(boolParameterName, isOpen);
}
}
}
}
}

View File

@@ -44,14 +44,14 @@ namespace OnlyScove.Scripts
UpdateLightState();
}
// protected override void PerformInteraction(PlayerStateMachine player)
// {
// isOn = !isOn;
// UpdateLightState();
//
// // Log cực mạnh để bạn kiểm tra Console
// Debug.LogWarning($"<color=yellow>[Lamp]</color> Đèn đã chuyển sang: {(isOn ? "BẬT" : "TẮT")}");
// }
protected override void PerformInteraction(GameObject interactor)
{
isOn = !isOn;
UpdateLightState();
// Log cực mạnh để bạn kiểm tra Console
Debug.LogWarning($"<color=yellow>[Lamp]</color> Đèn đã chuyển sang: {(isOn ? "BẬT" : "TẮT")}");
}
private void UpdateLightState()
{

View File

@@ -1,8 +1,18 @@
using UnityEngine;
namespace OnlyScove.Scripts
{
public interface IInteractable
{
string InteractionPrompt { get; }
// void OnInteract(PlayerStateMachine player);
// Thực hiện tương tác
void OnInteract(GameObject interactor);
// Xử lý khi người chơi nhìn vào vật thể
void OnHoverEnter();
// Xử lý khi người chơi rời mắt khỏi vật thể
void OnHoverExit();
}
}
}

View File

@@ -0,0 +1,105 @@
using UnityEngine;
using OnlyScove.Scripts;
using Invector.vCamera;
namespace OnlyScove.Player
{
public class PlayerInteraction : MonoBehaviour
{
[Header("Settings")]
[SerializeField] private float interactDistance = 4f;
[SerializeField] private LayerMask interactableLayer;
[SerializeField] private KeyCode interactKey = KeyCode.E;
private Camera mainCam;
private IInteractable currentInteractable;
private void Start()
{
FindCamera();
}
private void FindCamera()
{
// 1. Tìm Invector Camera
if (vThirdPersonCamera.instance != null)
mainCam = vThirdPersonCamera.instance.GetComponent<Camera>();
// 2. Dự phòng Camera.main
if (mainCam == null) mainCam = Camera.main;
if (mainCam != null)
Debug.Log($"<color=cyan>[Interaction]</color> Đã kết nối với Camera: {mainCam.name}");
}
private void Update()
{
if (mainCam == null || !mainCam.isActiveAndEnabled) { FindCamera(); return; }
// Vẽ tia Ray để Debug
Debug.DrawRay(mainCam.transform.position, mainCam.transform.forward * interactDistance, Color.red);
CheckInteraction();
HandleInput();
}
private void CheckInteraction()
{
// Bắn Ray từ tâm Camera (Giống logic CameraOpenDoor của Asset)
Ray ray = mainCam.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0));
RaycastHit hit;
// Bỏ qua Layer Player (8) để không bị chặn bởi lưng nhân vật
int layerMask = ~(1 << 8);
if (Physics.Raycast(ray, out hit, interactDistance, layerMask))
{
// 1. Kiểm tra Interface IInteractable (Hệ thống của chúng ta)
IInteractable interactable = hit.collider.GetComponentInParent<IInteractable>();
// 2. Kiểm tra trực tiếp script Door (Nếu là Door từ Asset Wood Door Pack)
if (interactable == null)
{
var doorScript = hit.collider.GetComponentInParent<DoorScript.Door>();
if (doorScript != null)
{
// Tự động bọc script Door của Asset vào hệ thống của chúng ta nếu nó chưa có
interactable = hit.collider.gameObject.GetComponentInParent<DoorInteractable>();
if (interactable == null)
{
// Nếu trúng Door mà chưa có DoorInteractable, báo để bạn biết
// Debug.LogWarning($"[Interaction] Trúng Door {hit.collider.name} nhưng thiếu script DoorInteractable!");
}
}
}
if (interactable != null)
{
if (currentInteractable != interactable)
{
if (currentInteractable != null) currentInteractable.OnHoverExit();
currentInteractable = interactable;
currentInteractable.OnHoverEnter();
Debug.Log($"<color=green>[Interaction]</color> NHÌN TRÚNG: {hit.collider.gameObject.name}");
}
return;
}
}
if (currentInteractable != null)
{
currentInteractable.OnHoverExit();
currentInteractable = null;
}
}
private void HandleInput()
{
if (currentInteractable != null && Input.GetKeyDown(interactKey))
{
Debug.Log("<color=yellow>[Interaction]</color> THỰC HIỆN TƯƠNG TÁC!");
currentInteractable.OnInteract(gameObject);
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c25b53df82df1fd4daabb89c90ce092a

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5ce3d3ef991b9c34baf5cdc7e782f909
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
1. Hệ thống Core (Dễ)
* Mục tiêu: Xây dựng nền tảng cho mọi vật thể tương tác.
* Các bước:
* Hoàn thiện Interface IInteractable và lớp trừu tượng BaseInteractable.
* Tạo script PlayerInteraction sử dụng Raycast từ Camera để phát hiện vật thể và nhận lệnh nhấn phím (ví dụ: phím E).
2. UI Prompt & Âm thanh (Dễ)
* Mục tiêu: Phản hồi trực quan và âm thanh cho người chơi.
* Các bước:
* Thêm thành phần hiển thị Text vào HUD (sử dụng UIToolkit như các UI hiện tại của bạn).
* Tích hợp âm thanh từ dữ liệu ObjectInteraction (ScriptableObject) vào hàm tương tác cơ bản.
3. Highlight & Tương tác vật lý (Trung bình)
* Mục tiêu: Làm nổi bật vật thể và thực hiện các tương tác đóng/mở.
* Các bước:
* Object Highlight: Sử dụng Outline shader hoặc thay đổi Material khi người chơi nhìn vào vật thể.
* Door Interaction: Hoàn thiện logic mở/đóng cửa (kết nối với Animator hoặc script có sẵn).
* Lever/Switch: Tạo hệ thống cần gạt sử dụng UnityEvent để kích hoạt các sự kiện khác trong màn chơi.
4. Tương tác đặc biệt & Logic (Khó)
* Mục tiêu: Các cơ chế gameplay phức tạp hơn.
* Các bước:
* Fake Wall: Tường giả biến mất hoặc cho phép đi xuyên qua.
* Teleport: Dịch chuyển người chơi đến vị trí chỉ định.
* Exit: Xử lý chuyển Scene hoặc kết thúc màn chơi.
* Mirror: Hiệu ứng gương phản chiếu và logic tương tác riêng.
5. Đồng bộ Online & Kiểm thử (Cực khó)
* Mục tiêu: Đảm bảo hệ thống hoạt động trong môi trường Multiplayer.
* Các bước:
* Sử dụng Photon (RPC) để đồng bộ trạng thái vật thể (ví dụ: khi một người mở cửa, tất cả mọi người đều thấy cửa mở).
Cách thức triển khai: Chúng ta sẽ đi theo mô hình Modular. Tôi sẽ xây dựng hệ thống Core trước, sau đó mỗi loại vật thể (Cửa, Cần gạt, Tường giả...) sẽ là một Module riêng kế thừa từ Core. Điều này giúp code của bạn sạch, dễ
quản lý và dễ mở rộng sau này.

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 54fd09d52f600024291972ed9edfcc52
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -5540,7 +5540,7 @@ MonoBehaviour:
m_BlockingObjects: 0
m_BlockingMask:
serializedVersion: 2
m_Bits: 4294967295
m_Bits: 4161798071
--- !u!114 &1967599012
MonoBehaviour:
m_ObjectHideFlags: 0

View File

@@ -52,7 +52,7 @@ TagManager:
- BlockAIRayCast
- Pushable
- CoverPoint
-
- Interactable
-
-
-