update trap

This commit is contained in:
2026-07-02 08:59:23 +07:00
parent 30ecad1667
commit 00a6bf5669
47 changed files with 3352 additions and 240 deletions

View File

@@ -6,7 +6,10 @@
<component name="ChangeListManager">
<list default="true" id="d308d1cb-09fc-4331-ba20-00f7b43d1576" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/.idea.BABA_YAGA/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.BABA_YAGA/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scenes/Main Scene.unity" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scenes/Main Scene.unity" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Prefabs/Nolan/Nolan.prefab" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Prefabs/Nolan/Nolan.prefab" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scenes/Test.unity" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scenes/Test.unity" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Assets/Scripts/UI/MainMenuController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/UI/MainMenuController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ProjectSettings/TagManager.asset" beforeDir="false" afterPath="$PROJECT_DIR$/ProjectSettings/TagManager.asset" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -21,14 +24,13 @@
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="HighlightingSettingsPerFile">
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/345a537a18334b5cae14bbe9fdbc34944000/43/87a9ecbe/SchedulerBase.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/dc764b59c049482b93bebc78a7e33a04212600/ae/fc8a801d/KeyCode.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/.gemini-pool.json" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Assets/Scripts/Game/EloData.cs.meta" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Assets/Scripts/Game/MatchEloManager.cs.meta" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Assets/Scripts/GameSetup/Maze/MazeManager.cs.meta" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Assets/Third Parties/Photon/Fusion/Editor/Fusion.Unity.Editor.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Library/PackageCache/com.unity.inputsystem@a74895ba9436/InputSystem/Plugins/PlayerInput/PlayerInput.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Library/PackageCache/com.unity.render-pipelines.universal@37e0d4fc2503/Shaders/Lit.shader" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Library/PackageCache/com.unity.render-pipelines.universal@37e0d4fc2503/Shaders/Terrain/TerrainLit.shader" root0="SKIP_HIGHLIGHTING" />
</component>
<component name="McpProjectServerCommands">
<commands />
@@ -45,26 +47,27 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;RunOnceActivity.MCP Project settings loaded&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;RunOnceActivity.typescript.service.memoryLimit.init&quot;: &quot;true&quot;,
&quot;com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultModelSelectionForGA.v1&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;main&quot;,
&quot;junie.onboarding.icon.badge.shown&quot;: &quot;true&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;to.speed.mode.migration.done&quot;: &quot;true&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"ModuleVcsDetector.initialDetectionPerformed": "true",
"RunOnceActivity.MCP Project settings loaded": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.git.unshallow": "true",
"RunOnceActivity.typescript.service.memoryLimit.init": "true",
"com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultModelSelectionForGA.v1": "true",
"git-widget-placeholder": "main",
"junie.onboarding.icon.badge.shown": "true",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "database.query.execution",
"to.speed.mode.migration.done": "true",
"vue.rearranger.settings.migration": "true"
}
}</component>
}]]></component>
<component name="RunManager" selected="Attach to Unity Editor.Attach to Unity Editor">
<configuration name="Start Unity" type="RunUnityExe" factoryName="Unity Executable">
<option name="EXE_PATH" value="C:\Program Files\Unity\Hub\Editor\6000.3.10f1\Editor\Unity.exe" />
@@ -122,6 +125,13 @@
<workItem from="1781727437862" duration="1257000" />
<workItem from="1781765548760" duration="8210000" />
<workItem from="1782445122703" duration="1948000" />
<workItem from="1782578762753" duration="1271000" />
<workItem from="1782580354534" duration="3422000" />
<workItem from="1782609730952" duration="5919000" />
<workItem from="1782784487035" duration="289000" />
<workItem from="1782784801953" duration="6169000" />
<workItem from="1782876039153" duration="6582000" />
<workItem from="1782954269855" duration="1973000" />
</task>
<servers />
</component>

View File

@@ -13345,7 +13345,7 @@ GameObject:
- component: {fileID: 8796553624329906391}
m_Layer: 31
m_Name: Nolan
m_TagString: Untagged
m_TagString: Player
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,46 @@
using System.Collections;
using Fusion;
using UnityEngine;
public class AlarmTrap : TrapBase
{
[Header("Alarm Visual Prefab")]
[SerializeField] private GameObject pingIndicatorPrefab; // Prefab representing the warning icon visible through walls
protected override void OnTriggered(GameObject victim)
{
// Server: Notify the Trapper who placed this trap
if (Owner != PlayerRef.None)
{
RPC_NotifyAlarmPing(Owner, victim.transform.position, trapData.EffectDuration);
}
// Play alarm loop sound or VFX for a duration, then despawn
StartCoroutine(AlarmDurationRoutine());
}
[Rpc(RpcSources.StateAuthority, RpcTargets.InputAuthority)]
private void RPC_NotifyAlarmPing([RpcTarget] PlayerRef targetPlayer, Vector3 position, float duration)
{
Debug.Log($"[Client] Trapper notified! Seeker tripped alarm at position: {position}");
// Instantiate the warning indicator at the Seeker's position
if (pingIndicatorPrefab != null)
{
GameObject indicator = Instantiate(pingIndicatorPrefab, position + Vector3.up * 1.5f, Quaternion.identity);
Destroy(indicator, duration);
}
else
{
// Fallback: log warning or use debug line draw
Debug.DrawLine(position, position + Vector3.up * 5f, Color.red, duration);
}
}
private IEnumerator AlarmDurationRoutine()
{
// Stay active to play audio alarm for a bit
yield return new WaitForSeconds(trapData.EffectDuration);
DespawnTrap();
}
}

View File

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

View File

@@ -0,0 +1,81 @@
using System.Collections;
using Fusion;
using UnityEngine;
using Opsive.UltimateCharacterController.Traits;
using Opsive.UltimateCharacterController.Character;
using Opsive.UltimateCharacterController.Input;
public class BearTrap : TrapBase
{
protected override void OnTriggered(GameObject victim)
{
// 1. Server-side: Apply Damage to Opsive Health
ApplyDamage(victim);
// 2. Server-side: Send RPC to victim client to stun them
var netObj = victim.GetComponent<NetworkObject>();
if (netObj != null)
{
// Send RPC targeted to the victim client using targetPlayer
RPC_ApplyStun(netObj.InputAuthority, netObj.Id, trapData.EffectDuration);
}
// 3. Despawn the trap after a short delay (e.g. 2 seconds for animation/sfx to play)
StartCoroutine(DespawnAfterDelay(2f));
}
private void ApplyDamage(GameObject victim)
{
var health = victim.GetComponent<Health>();
if (health != null && health.IsAlive())
{
// Apply damage using Opsive UCC Health system
health.Damage(trapData.Damage, transform.position, Vector3.zero, 0);
Debug.Log($"[Server] BearTrap applied {trapData.Damage} damage to {victim.name}");
}
}
[Rpc(RpcSources.StateAuthority, RpcTargets.InputAuthority)]
private void RPC_ApplyStun([RpcTarget] PlayerRef targetPlayer, NetworkId victimId, float duration)
{
// Find the victim GameObject on the client using NetworkId
var victimNetObj = Runner.FindObject(victimId);
if (victimNetObj != null)
{
StartCoroutine(StunRoutine(victimNetObj.gameObject, duration));
}
}
private IEnumerator StunRoutine(GameObject target, float duration)
{
Debug.Log($"[Client] Applying root/stun to {target.name} for {duration} seconds");
// Disable UCC inputs and locomotion handling to freeze the player
var locomotionHandler = target.GetComponent<UltimateCharacterLocomotionHandler>();
var playerInput = target.GetComponent<Opsive.UltimateCharacterController.Input.PlayerInput>();
var locomotion = target.GetComponent<UltimateCharacterLocomotion>();
if (locomotionHandler != null) locomotionHandler.enabled = false;
if (playerInput != null) playerInput.enabled = false;
// Stop any current velocity
if (locomotion != null)
{
locomotion.ResetRotationPosition();
}
yield return new WaitForSeconds(duration);
// Re-enable UCC inputs and locomotion handling
if (locomotionHandler != null) locomotionHandler.enabled = true;
if (playerInput != null) playerInput.enabled = true;
Debug.Log($"[Client] Root/stun ended for {target.name}");
}
private IEnumerator DespawnAfterDelay(float delay)
{
yield return new WaitForSeconds(delay);
DespawnTrap();
}
}

View File

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

View File

@@ -0,0 +1,67 @@
using UnityEngine;
using UnityEngine.UI;
public class FlashbangScreenOverlay : MonoBehaviour
{
[SerializeField] private Image flashImage;
private float currentIntensity = 0f;
private float fadeDuration = 3f;
private float elapsedTime = 0f;
private bool isFlashing = false;
void Start()
{
if (flashImage == null)
{
flashImage = GetComponent<Image>();
}
if (flashImage != null)
{
Color c = Color.white;
c.a = 0f;
flashImage.color = c;
flashImage.enabled = false;
}
}
void Update()
{
if (!isFlashing || flashImage == null) return;
elapsedTime += Time.deltaTime;
float progress = elapsedTime / fadeDuration;
if (progress >= 1f)
{
isFlashing = false;
Color c = Color.white;
c.a = 0f;
flashImage.color = c;
flashImage.enabled = false;
}
else
{
// Linear fade out for the white flashbang overlay
Color c = Color.white;
c.a = Mathf.Lerp(currentIntensity, 0f, progress);
flashImage.color = c;
}
}
public void Flash(float duration, float intensity)
{
if (flashImage == null) return;
flashImage.enabled = true;
isFlashing = true;
elapsedTime = 0f;
fadeDuration = duration;
currentIntensity = intensity;
Color c = Color.white;
c.a = intensity;
flashImage.color = c;
}
}

View File

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

View File

@@ -0,0 +1,56 @@
using System.Collections;
using Fusion;
using UnityEngine;
public class FlashbangTrap : TrapBase
{
protected override void OnTriggered(GameObject victim)
{
float radius = trapData.EffectRadius;
float duration = trapData.EffectDuration;
Debug.Log($"[Server] Flashbang Trap triggered at {transform.position}. Radius: {radius}m");
// Blind all Seekers in the explosion radius
Collider[] overlaps = Physics.OverlapSphere(transform.position, radius, LayerMask.GetMask("Player"));
foreach (var col in overlaps)
{
if (IsTargetValid(col.gameObject))
{
var netObj = col.gameObject.GetComponent<NetworkObject>();
if (netObj != null)
{
// Calculate visual falloff based on distance
float distance = Vector3.Distance(transform.position, col.transform.position);
float intensity = Mathf.Clamp01(1f - (distance / radius));
RPC_ApplyFlashbang(netObj.InputAuthority, netObj.Id, duration, intensity);
}
}
}
// Wait for SFX/VFX to play, then despawn
StartCoroutine(DespawnAfterDelay(2f));
}
[Rpc(RpcSources.StateAuthority, RpcTargets.InputAuthority)]
private void RPC_ApplyFlashbang([RpcTarget] PlayerRef targetPlayer, NetworkId victimId, float duration, float intensity)
{
// Resolve the victim GameObject using NetworkId on the client
var victimNetObj = Runner.FindObject(victimId);
if (victimNetObj != null)
{
var overlay = victimNetObj.GetComponentInChildren<FlashbangScreenOverlay>();
if (overlay != null)
{
overlay.Flash(duration, intensity);
}
}
}
private IEnumerator DespawnAfterDelay(float delay)
{
yield return new WaitForSeconds(delay);
DespawnTrap();
}
}

View File

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

View File

@@ -0,0 +1,124 @@
using System.Collections;
using System.Collections.Generic;
using Fusion;
using UnityEngine;
using Opsive.UltimateCharacterController.Traits;
public class PoisonGasTrap : TrapBase
{
private List<GameObject> affectedPlayers = new List<GameObject>();
protected override void OnTriggered(GameObject victim)
{
// Start the server-side poison gas zone routine
StartCoroutine(GasZoneRoutine());
}
private IEnumerator GasZoneRoutine()
{
float elapsed = 0f;
float tickInterval = 1.0f; // Deal damage every 1 second
float duration = trapData.EffectDuration;
float radius = trapData.EffectRadius;
float damagePerTick = trapData.Damage; // Sát thương mỗi tick
Debug.Log($"[Server] Poison Gas Trap activated. Duration: {duration}s, Radius: {radius}m");
while (elapsed < duration)
{
// Find all potential targets in radius
Collider[] overlaps = Physics.OverlapSphere(transform.position, radius, LayerMask.GetMask("Player"));
List<GameObject> currentTicks = new List<GameObject>();
foreach (var col in overlaps)
{
if (IsTargetValid(col.gameObject))
{
currentTicks.Add(col.gameObject);
// Apply damage
var health = col.gameObject.GetComponent<Health>();
if (health != null && health.IsAlive())
{
health.Damage(damagePerTick, transform.position, Vector3.zero, 0);
Debug.Log($"[Server] Poison Gas applied {damagePerTick} damage to {col.gameObject.name}");
}
// If player is newly entered the gas, turn on their UI overlay
if (!affectedPlayers.Contains(col.gameObject))
{
affectedPlayers.Add(col.gameObject);
var netObj = col.gameObject.GetComponent<NetworkObject>();
if (netObj != null)
{
RPC_TogglePoisonOverlay(netObj.InputAuthority, netObj.Id, true);
}
}
}
}
// If player left the gas area, turn off their UI overlay
for (int i = affectedPlayers.Count - 1; i >= 0; --i)
{
var p = affectedPlayers[i];
if (p == null || !currentTicks.Contains(p))
{
if (p != null)
{
var netObj = p.GetComponent<NetworkObject>();
if (netObj != null)
{
RPC_TogglePoisonOverlay(netObj.InputAuthority, netObj.Id, false);
}
}
affectedPlayers.RemoveAt(i);
}
}
yield return new WaitForSeconds(tickInterval);
elapsed += tickInterval;
}
// Clean up: turn off overlay for any remaining players in zone
foreach (var p in affectedPlayers)
{
if (p != null)
{
var netObj = p.GetComponent<NetworkObject>();
if (netObj != null)
{
RPC_TogglePoisonOverlay(netObj.InputAuthority, netObj.Id, false);
}
}
}
affectedPlayers.Clear();
// Despawn the trap
DespawnTrap();
}
[Rpc(RpcSources.StateAuthority, RpcTargets.InputAuthority)]
private void RPC_TogglePoisonOverlay([RpcTarget] PlayerRef targetPlayer, NetworkId victimId, bool show)
{
// Resolve the victim GameObject using NetworkId on the client
var victimNetObj = Runner.FindObject(victimId);
if (victimNetObj != null)
{
var overlay = victimNetObj.GetComponentInChildren<PoisonScreenOverlay>();
if (overlay != null)
{
overlay.SetOverlayActive(show);
}
}
}
// Draw the gas radius in editor for debugging
private void OnDrawGizmosSelected()
{
if (trapData != null)
{
Gizmos.color = new Color(0, 1, 0, 0.3f);
Gizmos.DrawSphere(transform.position, trapData.EffectRadius);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 22d901fce76303a4a9039ba6ac32e7f6

View File

@@ -0,0 +1,47 @@
using UnityEngine;
using UnityEngine.UI;
public class PoisonScreenOverlay : MonoBehaviour
{
[SerializeField] private Image overlayImage;
[SerializeField] private float fadeSpeed = 2f;
private float targetAlpha = 0f;
private float currentAlpha = 0f;
void Start()
{
if (overlayImage == null)
{
overlayImage = GetComponent<Image>();
}
if (overlayImage != null)
{
Color c = overlayImage.color;
c.a = 0f;
overlayImage.color = c;
overlayImage.enabled = false;
}
}
void Update()
{
if (overlayImage == null) return;
if (Mathf.Abs(currentAlpha - targetAlpha) > 0.01f)
{
currentAlpha = Mathf.MoveTowards(currentAlpha, targetAlpha, fadeSpeed * Time.deltaTime);
Color c = overlayImage.color;
c.a = currentAlpha;
overlayImage.color = c;
overlayImage.enabled = currentAlpha > 0.01f;
}
}
public void SetOverlayActive(bool active)
{
targetAlpha = active ? 0.6f : 0f; // maximum 60% opacity green overlay
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8157e011a6f0cfe42be054813cfc0b99

View File

@@ -0,0 +1,575 @@
# KẾ HOẠCH TRIỂN KHAI CHI TIẾT: HỆ THỐNG BẪY (TRAP SYSTEM)
**Dự án:** BABA_YAGA
**Công nghệ:** Unity, Photon Fusion (Shared/Host mode), Opsive Ultimate Character Controller (UCC)
**Tác giả:** Antigravity AI Pair Programmer
**Ngày lập:** 30/06/2026
---
## 📌 1. KIẾN TRÚC TỔNG QUAN (ARCHITECTURE OVERVIEW)
Hệ thống bẫy được thiết kế theo mô hình **Server-Authoritative (Host-authoritative)** kết hợp với **Client-side Prediction/Visualization**. Điều này đảm bảo tính bảo mật (kẻ địch không thể cheat vị trí bẫy hoặc tự ý gỡ bẫy) và trải nghiệm mượt mà của người chơi.
```mermaid
graph TD
Trapper[Trapper Client] -->|1. Di chuyển & Ngắm| Ghost[Ghost Trap Preview - Local]
Trapper -->|2. Click Đặt Bẫy| RPC[RPC_RequestPlaceTrap]
RPC -->|3. Nhận yêu cầu| Host[Host / Server]
Host -->|4. Kiểm tra hợp lệ| Spawn[Runner.Spawn TrapPrefab]
Spawn -->|5. Đồng bộ hóa| AllClients[All Clients Spawned]
AllClients -->|Trapper| ShowFull[Hiển thị rõ + Outline]
AllClients -->|Seeker| HideStealth[Ẩn bẫy/Dùng Stealth Shader]
```
### Các lớp đối tượng cốt lõi (Core Classes)
| Tên File | Loại | Vai trò chính |
| :--- | :--- | :--- |
| `TrapBase.cs` | `NetworkBehaviour` | Lớp cha trừu tượng quản lý vòng đời, trạng thái mạng, va chạm và kích hoạt bẫy. |
| `TrapPlacementController.cs` | `MonoBehaviour` | Xử lý logic hiển thị bóng mờ (Ghost Mesh) và gửi RPC đặt bẫy từ Trapper. |
| `TrapVisibilityHandler.cs` | `MonoBehaviour` | Xử lý việc ẩn/hiện bẫy đối với từng Role (`Seeker` ẩn, `Trapper` hiện). |
| `TrapDataSO.cs` | `ScriptableObject` | Lưu trữ cấu hình bẫy (Thời gian hồi, sát thương, tầm quét, prefab, icon UI). |
---
## 🛠️ 2. CHI TIẾT CÁC GIAI ĐOẠN TRIỂN KHAI
---
### GIAI ĐOẠN 1: XÂY DỰNG CORE ARCHITECTURE & MẠNG (PHOTON FUSION)
*Mục tiêu: Đảm bảo bẫy được sinh ra đồng bộ trên tất cả các Client và có cơ chế quản lý trạng thái mạng.*
#### 1. Định nghĩa enum trạng thái bẫy (`TrapState`)
```csharp
public enum TrapState
{
Arming, // Đang thiết lập (chờ kích hoạt để tránh nổ ngay khi đặt)
Armed, // Đã sẵn sàng hoạt động (đang quét kẻ địch)
Triggered, // Đã bị dẫm trúng và đang chạy hiệu ứng
Despawning // Đang bị hủy / dọn dẹp
}
```
#### 2. Xây dựng lớp Base `TrapBase.cs`
Lớp này sẽ kế thừa từ `NetworkBehaviour` của Fusion.
```csharp
using Fusion;
using UnityEngine;
[RequireComponent(typeof(NetworkObject))]
public abstract class TrapBase : NetworkBehaviour
{
[Header("Trap Config")]
[SerializeField] protected TrapDataSO trapData;
[Networked] public PlayerRef Owner { get; set; }
[Networked, ChangedOnFields(nameof(OnStateChanged))] public TrapState State { get; set; }
[Networked] protected TickTimer ArmingTimer { get; set; }
[Networked] protected TickTimer LifetimeTimer { get; set; }
protected Collider trapCollider;
protected Animator animator;
protected AudioSource audioSource;
public override void Spawned()
{
trapCollider = GetComponent<Collider>();
animator = GetComponentInChildren<Animator>();
audioSource = GetComponent<AudioSource>();
if (Object.HasStateAuthority)
{
State = TrapState.Arming;
ArmingTimer = TickTimer.CreateFromSeconds(Runner, trapData.ArmingDelay);
LifetimeTimer = TickTimer.CreateFromSeconds(Runner, trapData.Lifetime);
}
// Tự động cấu hình hiển thị (Tàng hình với Seeker, Hiện với Trapper)
ConfigureVisibility();
}
public override void FixedUpdateNetwork()
{
if (!Object.HasStateAuthority) return;
// Xử lý đếm ngược Arming
if (State == TrapState.Arming && ArmingTimer.Expired(Runner))
{
State = TrapState.Armed;
}
// Tự hủy nếu hết hạn tồn tại
if (LifetimeTimer.Expired(Runner))
{
DespawnTrap();
}
}
protected virtual void OnTriggerEnter(Collider other)
{
if (!Object.HasStateAuthority) return;
if (State != TrapState.Armed) return;
// Kiểm tra xem đối tượng chạm vào có phải là Seeker không
if (IsTargetValid(other.gameObject))
{
TriggerTrap(other.gameObject);
}
}
protected bool IsTargetValid(GameObject target)
{
// Tích hợp kiểm tra Role của đối tượng va chạm (không tự kích hoạt bẫy của chính mình/đồng đội)
var networkInfo = target.GetComponent<NetworkObject>();
if (networkInfo != null)
{
// Kiểm tra PlayerData của target
var playerData = target.GetComponent<PlayerData>();
if (playerData != null && playerData.PlayerRole == _Role.Seeker)
{
return true;
}
}
return false;
}
protected void TriggerTrap(GameObject victim)
{
State = TrapState.Triggered;
OnTriggered(victim);
}
// Các hàm ảo để các bẫy cụ thể ghi đè (Override)
protected abstract void OnTriggered(GameObject victim);
protected static void OnStateChanged(Changed<TrapBase> changed)
{
changed.Behaviour.HandleStateVisuals();
}
protected virtual void HandleStateVisuals()
{
switch (State)
{
case TrapState.Arming:
// Play đặt bẫy SFX/VFX
break;
case TrapState.Armed:
// Bật đèn tín hiệu/nháy nhẹ (chỉ Trapper thấy rõ)
break;
case TrapState.Triggered:
if (animator != null) animator.SetTrigger("Trigger");
if (audioSource != null && trapData.TriggerSFX != null) audioSource.PlayOneShot(trapData.TriggerSFX);
// Sinh VFX vụ nổ/sập bẫy
break;
}
}
protected void DespawnTrap()
{
State = TrapState.Despawning;
Runner.Despawn(Object);
}
protected abstract void ConfigureVisibility();
}
```
---
### GIAI ĐOẠN 2: HỆ THỐNG ĐẶT BẪY (PLACEMENT MECHANICS)
*Mục tiêu: Người chơi Trapper có thể rê chuột chọn vị trí (Ghost Preview) và click để Spawn bẫy qua RPC mạng.*
#### 1. ScriptableObject cấu hình bẫy (`TrapDataSO.cs`)
```csharp
using UnityEngine;
[CreateAssetMenu(fileName = "NewTrapData", menuName = "BabaYaga/TrapData")]
public class TrapDataSO : ScriptableObject
{
public string TrapName;
public TrapType Type;
public NetworkPrefabRef TrapPrefab;
public GameObject GhostPrefab; // Mô hình mờ để xem trước
public float Cooldown = 5f;
public float ArmingDelay = 1.5f;
public float Lifetime = 60f;
public float PlacementMaxDistance = 5f;
[Header("Visuals & Audio")]
public Sprite Icon;
public AudioClip PlaceSFX;
public AudioClip TriggerSFX;
public GameObject TriggerVFX;
}
```
#### 2. Xử lý đặt bẫy `TrapPlacementController.cs`
Script này gắn trên nhân vật người chơi (chỉ kích hoạt với `Trapper` có Input Authority).
```csharp
using Fusion;
using UnityEngine;
public class TrapPlacementController : NetworkBehaviour
{
[SerializeField] private TrapDataSO[] availableTraps;
[SerializeField] private LayerMask placementLayerMask;
private int selectedTrapIndex = 0;
private GameObject currentGhostInstance;
private bool isPreviewing = false;
private float lastPlaceTime;
void Update()
{
if (!Object.HasInputAuthority) return;
// Nhấn nút để kích hoạt Preview (ví dụ: Phím Q hoặc số 1,2,3)
if (Input.GetKeyDown(KeyCode.Q))
{
TogglePreview();
}
if (isPreviewing)
{
UpdateGhostPosition();
if (Input.GetMouseButtonDown(0))
{
TryPlaceTrap();
}
}
}
void TogglePreview()
{
isPreviewing = !isPreviewing;
if (isPreviewing)
{
if (currentGhostInstance == null)
{
currentGhostInstance = Instantiate(availableTraps[selectedTrapIndex].GhostPrefab);
}
}
else
{
if (currentGhostInstance != null)
{
Destroy(currentGhostInstance);
}
}
}
void UpdateGhostPosition()
{
Ray ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0)); // Raycast từ tâm màn hình
if (Physics.Raycast(ray, out RaycastHit hit, availableTraps[selectedTrapIndex].PlacementMaxDistance, placementLayerMask))
{
currentGhostInstance.SetActive(true);
currentGhostInstance.transform.position = hit.point;
// Xoay bẫy theo mặt phẳng tiếp xúc (Normal của bề mặt)
currentGhostInstance.transform.rotation = Quaternion.FromToRotation(Vector3.up, hit.normal);
// Kiểm tra xem vị trí có hợp lệ không (ví dụ: không đè tường, không quá dốc)
bool isValid = ValidatePlacement(hit.point);
UpdateGhostVisuals(isValid);
}
else
{
currentGhostInstance.SetActive(false); // Ẩn nếu chỉ vào khoảng không
}
}
bool ValidatePlacement(Vector3 position)
{
// Check overlap sphere để đảm bảo không đè lên bẫy khác hoặc tường
Collider[] colliders = Physics.OverlapSphere(position, 0.5f, LayerMask.GetMask("Walls", "Trap"));
return colliders.Length == 0;
}
void UpdateGhostVisuals(bool isValid)
{
// Thay đổi màu Material của Ghost (Xanh = Đặt được, Đỏ = Không đặt được)
Renderer[] renderers = currentGhostInstance.GetComponentsInChildren<Renderer>();
Color color = isValid ? new Color(0f, 1f, 0f, 0.4f) : new Color(1f, 0f, 0f, 0.4f);
foreach (var r in renderers)
{
r.material.color = color;
}
}
void TryPlaceTrap()
{
if (Time.time - lastPlaceTime < availableTraps[selectedTrapIndex].Cooldown)
{
Debug.Log("Trap is on Cooldown!");
return;
}
Ray ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0));
if (Physics.Raycast(ray, out RaycastHit hit, availableTraps[selectedTrapIndex].PlacementMaxDistance, placementLayerMask))
{
if (ValidatePlacement(hit.point))
{
// Gửi RPC yêu cầu Server spawn bẫy thực sự
RPC_RequestPlaceTrap(hit.point, Quaternion.FromToRotation(Vector3.up, hit.normal), selectedTrapIndex);
lastPlaceTime = Time.time;
TogglePreview(); // Tắt preview sau khi đặt
}
}
}
[Rpc(RpcSources.InputAuthority, RpcTargets.StateAuthority)]
private void RPC_RequestPlaceTrap(Vector3 position, Quaternion rotation, int trapIndex)
{
if (trapIndex < 0 || trapIndex >= availableTraps.Length) return;
TrapDataSO data = availableTraps[trapIndex];
// Host sinh bẫy trên mạng
NetworkObject spawnedTrap = Runner.Spawn(data.TrapPrefab, position, rotation, Object.InputAuthority);
// Thiết lập thuộc tính bẫy
TrapBase trapScript = spawnedTrap.GetComponent<TrapBase>();
if (trapScript != null)
{
trapScript.Owner = Object.InputAuthority;
}
}
}
```
---
### GIAI ĐOẠN 3: CƠ CHẾ TẦM NHÌN & KHÔNG GIAN (STEALTH MECHANICS)
*Mục tiêu: Giữ bẫy tàng hình trước phe Seeker nhưng đồng đội/Trapper vẫn nhìn thấy.*
#### Giải pháp Kỹ thuật: Layer & Camera Culling + Shader
1. **Thiết lập Layers**:
- Tạo Layer: `TrapperOnly` (Ví dụ Layer 10).
- Tạo Layer: `SeekerOnly` (Ví dụ Layer 11).
- Tạo Layer: `StealthTrap` (Ví dụ Layer 12).
2. **Camera Culling**:
- Camera của `Trapper` sẽ render cả Layer `Default``StealthTrap` (để nhìn thấy bẫy của mình).
- Camera của `Seeker` mặc định **sẽ bỏ tích (Cull)** Layer `StealthTrap` trong thuộc tính *Culling Mask*. Điều này giúp bẫy tàng hình tuyệt đối trên màn hình của Seeker mà không cần ẩn GameObject (giúp Collider vẫn hoạt động vật lý).
3. **Cơ chế Reveal (Bị phát hiện)**:
- Khi Seeker ở gần bẫy trong bán kính 3m, hoặc dùng kỹ năng dò quét, script cục bộ trên Seeker sẽ chuyển đổi Layer của bẫy từ `StealthTrap` sang `Default` hoặc bật Shader quét hồng ngoại để làm hiển thị bóng mờ bẫy.
```csharp
// Trong script TrapBase.cs hoặc script bổ trợ TrapVisibilityHandler.cs:
protected override void ConfigureVisibility()
{
var localPlayer = Runner.LocalPlayer;
PlayerDataManager pdm = PlayerDataManager.Instance;
if (pdm != null && pdm.TryGetPlayerMetaData(localPlayer, out var meta))
{
if (meta.Role == _Role.Trapper)
{
// Trapper nhìn thấy rõ
gameObject.layer = LayerMask.NameToLayer("Default");
SetTrapAlpha(0.8f); // Hơi trong suốt để nhận biết là bẫy
}
else
{
// Seeker: Đưa vào layer ẩn
gameObject.layer = LayerMask.NameToLayer("StealthTrap");
}
}
}
private void SetTrapAlpha(float alpha)
{
Renderer[] renderers = GetComponentsInChildren<Renderer>();
foreach (var r in renderers)
{
foreach (var mat in r.materials)
{
// Đảm bảo Shader hỗ trợ chế độ trong suốt (Transparent)
Color col = mat.color;
col.a = alpha;
mat.color = col;
}
}
}
```
---
### GIAI ĐOẠN 4: LIÊN KẾT VỚI OPSIVE UCC (INTEGRATION WITH OPSIVE UCC)
*Mục tiêu: Bẫy tác động trực tiếp vào các chỉ số Máu (Health) và Khống chế (State/Abilities) của Opsive Character.*
Opsive UCC có các lớp built-in rất mạnh mẽ để xử lý sát thương và hiệu ứng:
#### 1. Gây sát thương lên người chơi (Apply Damage)
```csharp
using Opsive.Shared.Inventory;
using Opsive.UltimateCharacterController.Traits; // Namespace chứa Health
protected void ApplyDamageToUCC(GameObject target, float damageAmount)
{
Health victimHealth = target.GetComponent<Health>();
if (victimHealth != null && victimHealth.IsAlive())
{
// Gây sát thương thông qua API của Opsive
victimHealth.Damage(damageAmount, transform.position, Vector3.zero, 0);
}
}
```
#### 2. Trói chân / Khống chế di chuyển (Root / Stun Mechanism)
Có 2 cách tích hợp khống chế với UCC:
* **Cách A (Đơn giản):** Can thiệp trực tiếp vào CharacterLocomotion tốc độ di chuyển.
* **Cách B (Khuyên Dùng):** Kích hoạt/Tắt một custom Ability trong UCC (ví dụ: Ability `Stun` hoặc `Frozen` được cấu hình sẵn trong component *UltimateCharacterLocomotion*).
```csharp
using Opsive.UltimateCharacterController.Character;
using Opsive.UltimateCharacterController.Character.Abilities;
protected void ApplyStunToUCC(GameObject target, float duration)
{
UltimateCharacterLocomotion locomotion = target.GetComponent<UltimateCharacterLocomotion>();
if (locomotion != null)
{
// Tìm ability "Stun" hoặc "Frozen" đã được thiết lập sẵn trong Opsive Editor
Ability stunAbility = locomotion.GetAbility<StunAbility>(); // Cần tạo class StunAbility kế thừa từ Ability
if (stunAbility != null)
{
locomotion.TryStartAbility(stunAbility);
// Lên lịch tắt ability sau duration giây
StartCoroutine(RemoveStunCoroutine(locomotion, stunAbility, duration));
}
}
}
private System.Collections.IEnumerator RemoveStunCoroutine(UltimateCharacterLocomotion loco, Ability ability, float delay)
{
yield return new WaitForSeconds(delay);
if (loco != null && ability.IsActive)
{
loco.TryStopAbility(ability);
}
}
```
---
### GIAI ĐOẠN 5: HIỆN THỰC HÓA CÁC LOẠI BẪY CỤ THỂ (CONCRETE TRAPS)
Dưới đây là thiết kế chi tiết cho 4 bẫy quan trọng nhất để bắt đầu:
#### 1. Bear Trap (Bẫy kẹp sắt) - Sát thương đơn mục tiêu + Trói chân
* **Mô tả:** Khi Seeker chạm phải, kẹp sắt sập lại, gây 30 sát thương và trói chân tại chỗ trong 3 giây.
* **Mã nguồn:**
```csharp
public class BearTrap : TrapBase
{
[Header("Bear Trap Settings")]
[SerializeField] private float damage = 30f;
[SerializeField] private float stunDuration = 3f;
protected override void OnTriggered(GameObject victim)
{
// Gây sát thương Opsive Health
ApplyDamageToUCC(victim, damage);
// Trói chân Opsive Locomotion
ApplyStunToUCC(victim, stunDuration);
// Đợi kết thúc Animation sập bẫy trước khi biến mất
StartCoroutine(DespawnAfterDelay(2f));
}
private System.Collections.IEnumerator DespawnAfterDelay(float delay)
{
yield return new WaitForSeconds(delay);
DespawnTrap();
}
protected override void ConfigureVisibility()
{
// Logic tàng hình mặc định
}
}
```
#### 2. Gas Poison Trap (Bẫy độc hại) - Sát thương theo thời gian (DoT) + Giảm tầm nhìn
* **Mô tả:** Khi nổ, kích hoạt Particle System khí độc diện rộng (bán kính 4m). Bất kỳ Seeker nào đứng bên trong sẽ chịu 5 sát thương mỗi giây và bị che mờ camera bởi UI khói độc.
* **Mã nguồn:**
```csharp
public class PoisonGasTrap : TrapBase
{
[SerializeField] private float damagePerSecond = 5f;
[SerializeField] private float gasDuration = 8f;
[SerializeField] private float gasRadius = 4f;
protected override void OnTriggered(GameObject victim)
{
// Bật Particle System độc (đồng bộ qua State thay đổi hiệu ứng)
// Bắt đầu quét vùng gây sát thương định kỳ trên Server
StartCoroutine(ApplyGasDamageArea());
}
private System.Collections.IEnumerator ApplyGasDamageArea()
{
float elapsed = 0f;
while (elapsed < gasDuration)
{
Collider[] targets = Physics.OverlapSphere(transform.position, gasRadius, LayerMask.GetMask("Player"));
foreach (var col in targets)
{
if (IsTargetValid(col.gameObject))
{
ApplyDamageToUCC(col.gameObject, damagePerSecond);
// Rpc_TriggerPoisonOverlayOnClient(col.gameObject.GetComponent<NetworkObject>().InputAuthority);
}
}
yield return new WaitForSeconds(1f);
elapsed += 1f;
}
DespawnTrap();
}
}
```
#### 3. Alarm Trap (Bẫy còi báo động) - Tiết lộ vị trí Seeker
* **Mô tả:** Khi chạm vào, không gây sát thương nhưng hú còi cực to và ping vị trí Seeker lên bản đồ nhỏ (Minimap) của Trapper trong 5 giây.
#### 4. Flashbang Trap (Bẫy mù) - Làm mù màn hình Seeker
* **Mô tả:** Khi dẫm phải sẽ phát nổ chói lóa. Gửi một RPC làm trắng màn hình UI của nạn nhân, giảm âm lượng âm thanh game về 0 và phục hồi dần trong 4 giây.
---
## 📈 3. KẾ HOẠCH BẮT TAY THỰC HIỆN TỪNG BƯỚC (ROADMAP TO ACTION)
Để bắt tay vào làm ngay một cách khoa học, chúng ta sẽ chia thành các Task nhỏ trong **3 Sprint chính**:
### 🏃 SPRINT 1: CORE INFRASTRUCTURE (Dự kiến: 2-3 ngày)
1. [ ] **Tạo cấu trúc thư mục**: Dựng sẵn các folder `Trap/Base`, `Trap/Concrete`, `Trap/ScriptableObjects`.
2. [ ] **Tạo file cấu hình**: Viết class `TrapDataSO.cs`.
3. [ ] **Hoàn thiện Core Class**: Viết `TrapBase.cs` xử lý trigger va chạm vật lý và vòng đời Fusion NetworkObject.
4. [ ] **Tạo Prefab kiểm thử**: Tạo 1 khối Cube gắn `NetworkObject`, `Collider` (Is Trigger), `NetworkTransform` và script kế thừa từ `TrapBase`.
### 🏃 SPRINT 2: PLACEMENT & VISIBILITY (Dự kiến: 3 ngày)
1. [ ] **Hệ thống Ghost Preview**: Viết `TrapPlacementController.cs` để chiếu tia Raycast từ camera và hiển thị mô hình mờ.
2. [ ] **Đồng bộ đặt bẫy**: Viết hàm RPC gửi tọa độ đặt bẫy lên Host để Host sinh bẫy thật.
3. [ ] **Tàng hình & Phát hiện**: Tạo các Layer `StealthTrap` và cấu hình Camera Culling Mask cho Seeker. Viết logic chuyển đổi shader/layer trên client của Trapper.
### 🏃 SPRINT 3: OPSIVE INTEGRATION & CONCRETE TRAPS (Dự kiến: 3 ngày)
1. [ ] **Kết nối Opsive UCC**: Thực hiện code gây sát thương (Health) và viết custom Ability trói chân (Stun/Freeze).
2. [ ] **Hoàn thiện các bẫy cụ thể**: Viết `BearTrap.cs` (Kẹp sắt) và `PoisonGasTrap.cs` (Bẫy ga độc).
3. [ ] **Kiểm thử Multiplayer**: Build game ra 2 màn hình để test đồng bộ:
- Trapper đặt bẫy -> Có hao tốn Cooldown không? Có sinh bẫy đồng bộ không?
- Seeker đi qua bẫy -> Bẫy có nổ không? Seeker có bị trừ máu và đứng im không?
- Camera của Seeker có nhìn thấy bẫy trước khi nổ không? (Phải tàng hình).
---
## ⚠️ 4. CÁC ĐIỂM CẦN LƯU Ý KHI LẬP TRÌNH (CRITICAL TIPS)
1. **Collider Network**: Hãy đảm bảo bẫy có Collider được cấu hình `Is Trigger = True`. Chỉ có Server mới được thực hiện hàm xử lý sát thương hay khống chế trong `OnTriggerEnter` để chống hack.
2. **Ủy quyền trạng thái (State Authority)**: Mọi biến trạng thái như `TrapState State` hay `TickTimer` đều phải được gắn nhãn `[Networked]`. Khi thay đổi các thuộc tính này trên Server, Fusion sẽ tự động đồng bộ xuống Client và gọi hàm Callback `OnStateChanged` để kích hoạt VFX/SFX cục bộ.
3. **Clean Up**: Luôn kiểm tra việc hủy bẫy (`Runner.Despawn`) để tránh tràn bộ nhớ khi đặt quá nhiều bẫy trong Maze (mê cung).

View File

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

View File

@@ -0,0 +1,113 @@
Giai đoạn 1: Xây dựng Nền tảng (Core Architecture)
Mục tiêu: Tạo bộ khung cơ bản, xử lý các logic chung nhất mà bất kỳ bẫy nào cũng phải có, đặc biệt là đảm bảo tính đồng bộ trên mạng.
Create Base Trap System: Tạo một class cha (ví dụ: TrapBase.cs). Tất cả các bẫy sau này sẽ kế thừa từ class này. Class này nên chứa các biến chung như máu của bẫy, thời gian tồn tại, và các hàm ảo (virtual functions) như OnTrigger(), OnDestroy().
Add Trap Ownership System: Thêm logic xác định người chơi nào đã đặt bẫy (lưu trữ ID của người chơi). Điều này rất quan trọng để tính điểm hạ gục hoặc tránh việc bẫy gây sát thương lên chính đồng đội.
Synchronize Traps Online: Khởi tạo các thành phần đồng bộ mạng (như NetworkObject nếu dùng Photon Fusion). Đảm bảo rằng khi một bẫy được sinh ra (spawn), toàn bộ client trong phòng đều nhận biết được sự tồn tại của nó.
Create Trap Prefabs: Dựng các Prefab cơ bản ban đầu cho bẫy (có gắn Mesh, Collider, và script TrapBase).
Giai đoạn 2: Hệ thống Đặt Bẫy (Placement & UI)
Mục tiêu: Cho phép người chơi tương tác, đặt bẫy xuống môi trường một cách hợp lệ và hiển thị thông tin lên màn hình.
Implement Trap Placement: Xử lý logic bắn Raycast từ camera hoặc người chơi để xác định vị trí hợp lệ trên mặt đất (Terrain/Sàn nhà) và Spawn Prefab tại đó.
Add Trap Placement Limit: Giới hạn số lượng bẫy tối đa mà một người chơi có thể đặt cùng lúc để tối ưu hiệu suất và cân bằng game.
Add Trap Cooldown: Thêm bộ đếm thời gian (timer) để ngăn người chơi spam bẫy liên tục.
Create Trap UI Indicator: Cập nhật UI hiển thị icon bẫy, thời gian hồi chiêu (Cooldown), số lượng bẫy còn lại, hoặc hiển thị bóng mờ (ghost mesh) trước khi người chơi click đặt bẫy.
3. Các bước tiếp theo (Giai đoạn 2-3)
Đăng ký bẫy lên Fusion và gán cho
3a. Gán TrapPrefab vào TrapDataSO Player
Mỗi .asset trong Assets/TEST CUA TUAN/TRAP/ cần: Context
47,412 tokens
- Kéo TrapPrefab tương ứng vào ô TrapPrefab (BearTrapData → BearTrap_Network...) 24% used
- Set GhostPrefab (shadow preview mesh) $0.00 spent
- Set Icon, PlaceSFX, TriggerSFX, TriggerVFX
LSP
3b. Tạo Layer "StealthTrap" trong Tags & Layers LSPs are disabled
Vào Edit > Project Settings > Tags and Layers, tạo User Layer (ví dụ Layer 10) tên StealthTrap. Code ConfigureVisibility() trong TrapBase.cs đã dùng layer này để ẩn bẫy với
Seeker.
3c. Cấu hình Camera Culling
- Camera của Seeker: Trong Culling Mask, bỏ tick layer StealthTrap → bẫy sẽ hoàn toàn tàng hình
- Camera của Trapper: Giữ tick layer Default để thấy bẫy
3d. Tạo UI Trap Selection & Cooldown
Thêm Hotbar UI hiển thị:
- Icon 4 bẫy (đã có sẵn trong TrapDataSO.Icon)
- Cooldown overlay (xám đi khi đang CD)
- Số lượng bẫy còn lại (nếu có limit)
3e. Thêm Trap Placement Limit
Hiện tại TrapPlacementController chưa giới hạn số bẫy. Cần thêm [Networked] int trapsPlaced trong PlayerData và kiểm tra trong TryPlaceTrap().
⬖ Getting started ✕
3f. Kiểm tra đồng bộ mạng
OpenCode includes free models
Build 2 client, test flow: so you can start immediately.
- Trapper nhấn Q → ghost preview hiện → Click Left Mouse → RPC gửi lên Host → Runner.Spawn() → cả 2 client thấy trap Connect from 75+ providers to
- Seeker chạy qua → OnTriggerEnter server-side → gọi OnTriggered() → damage/stun/VFX/SFX đồng bộ
Giai đoạn 3: Logic Kích hoạt & Vòng đời (Trigger & Lifecycle)
Mục tiêu: Xử lý những gì xảy ra khi kẻ địch dẫm phải bẫy và cách bẫy biến mất.
Add Trap Trigger Detection: Xử lý va chạm (ví dụ: OnTriggerEnter). Phân loại rõ đối tượng chạm vào là ai (kẻ địch, đồng đội hay chính mình) để quyết định kích hoạt.
Add Trap Animation & Add Trap Sound Effects: Gắn Animator và AudioSource vào Base System. Kích hoạt hiệu ứng hình ảnh/âm thanh khi bẫy nổ hoặc sập xuống.
Add Trap Interaction Effects: Kích hoạt các Particle System (hiệu ứng hạt) hoặc apply sát thương cơ bản/hiệu ứng vật lý lên người dẫm phải.
Add Trap Despawn Logic: Logic dọn dẹp bẫy. Hủy GameObject sau khi đã kích hoạt xong, hoặc sau một khoảng thời gian tồn tại nhất định để giải phóng bộ nhớ.
Giai đoạn 4: Cơ chế Tầm nhìn (Visibility & Counterplay)
Mục tiêu: Tạo chiều sâu chiến thuật cho game bằng cách cho phép giấu bẫy và dò bẫy.
Add Trap Visibility Logic: Làm cho bẫy tàng hình hoặc mờ đi với phe địch, nhưng vẫn hiển thị rõ với người đặt và đồng đội. Có thể can thiệp qua Layer hoặc thay đổi Shader/Material.
Add Trap Reveal Mechanic: Chức năng cho phép kẻ địch phát hiện bẫy ẩn (ví dụ: lại gần trong bán kính 3m, hoặc dùng một kỹ năng dò đường đặc biệt).
Giai đoạn 5: Phát triển các Bẫy Cụ thể (Concrete Trap Types)
Mục tiêu: Kế thừa class TrapBase để tạo ra các loại bẫy với chức năng riêng biệt. Bạn có thể làm nhóm nào trước tùy theo độ ưu tiên của dự án.
Nhóm Sát thương & Khống chế:
Create Bear Trap Logic (Trói chân, sát thương đơn mục tiêu)
Create Spike Trap Logic (Sát thương vật lý diện rộng)
Create Poison Gas Trap (Sát thương theo thời gian - DoT, giảm tầm nhìn)
Create Net Trap Logic (Khống chế/Bắt giữ)
Nhóm Chiến thuật & Thông tin:
Create Sound Trap Logic (Phát ra âm thanh lớn)
Create Alarm Trap Logic (Ping lên minimap hoặc báo động cho phe mình)
Create Flashbang Trap (Làm mù mắt kẻ địch trong thời gian ngắn)
Create EMP Trap Logic (Vô hiệu hóa kỹ năng/màn hình UI của robot địch)
Nhóm Đánh lừa (Deception):
Create Fake Trap Logic (Bẫy giả không có tác dụng, chỉ để hù dọa)
Create Decoy Trap Logic (Tạo hình nhân/hologram giả mạo)
Create Fake Exit Trap & Add Exit Lock Trap (Bẫy liên quan đến lối thoát hiểm để dụ địch)
Giai đoạn 6: Kiểm thử (Testing & Polish)
Test Multiplayer Trap Sync: Build game ra ít nhất 2 client khác nhau. Kiểm tra xem bẫy của Client A đặt xuống thì Client B có nhìn thấy đúng trạng thái tàng hình không, khi dẫm vào thì âm thanh và sát thương có đồng bộ chính xác trên cả 2 máy không.
Việc đi theo trình tự này giúp bạn có một "cái móng" (Giai đoạn 1, 2, 3) thật chắc. Đến Giai đoạn 5, việc thêm một cái bẫy mới chỉ đơn giản là tạo script mới kế thừa TrapBase và viết thêm đúng logic đặc thù của nó mà không phải code lại phần mạng hay UI.

View File

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

View File

@@ -0,0 +1,205 @@
using Fusion;
using UnityEngine;
public enum TrapState
{
Arming, // Newly placed, arming delay active
Armed, // Active and scanning for targets
Triggered, // Tripped by a target, applying effects
Despawning // Mark for cleanup/destruction
}
[RequireComponent(typeof(NetworkObject))]
public abstract class TrapBase : NetworkBehaviour
{
[Header("Trap Config")]
[SerializeField] protected TrapDataSO trapData;
[Networked] public PlayerRef Owner { get; set; }
[Networked, OnChangedRender(nameof(OnStateChanged))] public TrapState State { get; set; }
[Networked] protected TickTimer ArmingTimer { get; set; }
[Networked] protected TickTimer LifetimeTimer { get; set; }
protected Collider trapCollider;
protected Animator animator;
protected AudioSource audioSource;
public override void Spawned()
{
trapCollider = GetComponent<Collider>();
animator = GetComponentInChildren<Animator>();
audioSource = GetComponent<AudioSource>();
if (Object.HasStateAuthority)
{
State = TrapState.Arming;
ArmingTimer = TickTimer.CreateFromSeconds(Runner, trapData.ArmingDelay);
LifetimeTimer = TickTimer.CreateFromSeconds(Runner, trapData.Lifetime);
}
ConfigureVisibility();
}
public override void FixedUpdateNetwork()
{
if (!Object.HasStateAuthority) return;
// Count down the arming delay
if (State == TrapState.Arming && ArmingTimer.Expired(Runner))
{
State = TrapState.Armed;
}
// Auto-despawn when lifetime timer expires
if (LifetimeTimer.Expired(Runner))
{
DespawnTrap();
}
}
protected virtual void OnTriggerEnter(Collider other)
{
// Physics collision triggers are only processed on Server/Host (State Authority)
if (!Object.HasStateAuthority) return;
if (State != TrapState.Armed) return;
if (IsTargetValid(other.gameObject))
{
TriggerTrap(other.gameObject);
}
}
protected bool IsTargetValid(GameObject target)
{
// Verify target has a NetworkObject and check role through PlayerData
var netObj = target.GetComponent<NetworkObject>();
if (netObj != null)
{
var playerData = target.GetComponent<PlayerData>();
if (playerData != null && playerData.PlayerRole == _Role.Seeker)
{
return true;
}
}
return false;
}
protected void TriggerTrap(GameObject victim)
{
State = TrapState.Triggered;
OnTriggered(victim);
}
// Abstract methods to be implemented by specific traps
protected abstract void OnTriggered(GameObject victim);
protected virtual void ConfigureVisibility()
{
var localPlayer = Runner.LocalPlayer;
PlayerDataManager pdm = PlayerDataManager.Instance;
if (pdm != null && pdm.TryGetPlayerMetaData(localPlayer, out var meta))
{
if (meta.Role == _Role.Trapper)
{
// Trapper sees their traps clearly
gameObject.layer = LayerMask.NameToLayer("Default");
SetTrapAlpha(0.8f);
}
else
{
// Seeker: place in stealth layer if exists, otherwise disable MeshRenderers as fallback
int stealthLayer = LayerMask.NameToLayer("StealthTrap");
if (stealthLayer != -1)
{
SetLayerRecursively(gameObject, stealthLayer);
}
else
{
SetRenderersEnabled(false);
}
}
}
}
private void SetLayerRecursively(GameObject obj, int newLayer)
{
if (obj == null) return;
obj.layer = newLayer;
foreach (Transform child in obj.transform)
{
SetLayerRecursively(child.gameObject, newLayer);
}
}
private void SetTrapAlpha(float alpha)
{
Renderer[] renderers = GetComponentsInChildren<Renderer>();
foreach (var r in renderers)
{
foreach (var mat in r.materials)
{
Color col = mat.color;
col.a = alpha;
mat.color = col;
}
}
}
private void SetRenderersEnabled(bool isEnabled)
{
Renderer[] renderers = GetComponentsInChildren<Renderer>();
foreach (var r in renderers)
{
r.enabled = isEnabled;
}
}
public void OnStateChanged()
{
HandleStateVisuals();
}
protected virtual void HandleStateVisuals()
{
switch (State)
{
case TrapState.Arming:
// Play placement sound effect or visual cue locally
if (audioSource != null && trapData.PlaceSFX != null)
{
audioSource.PlayOneShot(trapData.PlaceSFX);
}
break;
case TrapState.Armed:
// Visual indicators of trap active state (e.g. flashing light)
break;
case TrapState.Triggered:
// When triggered, reveal the trap mesh to everyone
SetRenderersEnabled(true);
if (animator != null)
{
animator.SetTrigger("Trigger");
}
if (audioSource != null && trapData.TriggerSFX != null)
{
audioSource.PlayOneShot(trapData.TriggerSFX);
}
if (trapData.TriggerVFX != null)
{
Instantiate(trapData.TriggerVFX, transform.position, Quaternion.identity);
}
break;
}
}
protected void DespawnTrap()
{
if (Object != null && Object.IsValid && Object.HasStateAuthority)
{
State = TrapState.Despawning;
Runner.Despawn(Object);
}
}
}

View File

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

View File

@@ -0,0 +1,37 @@
using UnityEngine;
using Fusion;
public enum TrapType
{
BearTrap,
PoisonGasTrap,
AlarmTrap,
FlashbangTrap
}
[CreateAssetMenu(fileName = "NewTrapData", menuName = "BabaYaga/TrapData")]
public class TrapDataSO : ScriptableObject
{
[Header("Basic Info")]
public string TrapName;
public TrapType Type;
public NetworkPrefabRef TrapPrefab;
public GameObject GhostPrefab; // Preview mesh for Trapper client
[Header("Gameplay Variables")]
public float Cooldown = 5f;
public float ArmingDelay = 1.5f; // Time before trap becomes active
public float Lifetime = 60f; // Time before trap despawns automatically
public float PlacementMaxDistance = 5f;
[Header("Specific Effects")]
public float Damage = 30f;
public float EffectDuration = 3f; // Stun/Blind/Alarm duration
public float EffectRadius = 4f; // For AoE traps like gas/flashbang
[Header("Audio & Visuals")]
public Sprite Icon;
public AudioClip PlaceSFX;
public AudioClip TriggerSFX;
public GameObject TriggerVFX;
}

View File

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

View File

@@ -0,0 +1,244 @@
using Fusion;
using UnityEngine;
public class TrapPlacementController : NetworkBehaviour
{
[Header("Placement Setup")]
[SerializeField] private TrapDataSO[] availableTraps;
[SerializeField] private LayerMask placementLayerMask;
private PlayerData playerData;
private int selectedTrapIndex = 0;
private GameObject currentGhostInstance;
private bool isPreviewing = false;
private float[] cooldownTimers;
public override void Spawned()
{
playerData = GetComponent<PlayerData>();
// Initialize cooldown tracking for each trap type
if (availableTraps != null)
{
cooldownTimers = new float[availableTraps.Length];
}
}
void Update()
{
// Only run placement logic for the local player who is a Trapper
if (!Object.HasInputAuthority) return;
if (playerData == null || playerData.PlayerRole != _Role.Trapper) return;
// Handle cooldown timers update
UpdateCooldowns();
// 1. Input: Select Trap Type via Alpha Keys (1, 2, 3, 4)
HandleTrapSelectionInput();
// 2. Input: Toggle Preview (Q key)
if (Input.GetKeyDown(KeyCode.Q))
{
TogglePreview();
}
// 3. Update Preview & Handle Placement Click
if (isPreviewing)
{
UpdateGhostPosition();
if (Input.GetMouseButtonDown(0)) // Left Click to Place
{
TryPlaceTrap();
}
else if (Input.GetMouseButtonDown(1)) // Right Click to Cancel
{
CancelPreview();
}
}
}
private void UpdateCooldowns()
{
for (int i = 0; i < cooldownTimers.Length; ++i)
{
if (cooldownTimers[i] > 0)
{
cooldownTimers[i] -= Time.deltaTime;
}
}
}
private void HandleTrapSelectionInput()
{
for (int i = 0; i < availableTraps.Length; ++i)
{
if (Input.GetKeyDown(KeyCode.Alpha1 + i))
{
if (selectedTrapIndex != i)
{
selectedTrapIndex = i;
Debug.Log($"Selected trap: {availableTraps[selectedTrapIndex].TrapName}");
// If already previewing, recreate ghost for the new type
if (isPreviewing)
{
DestroyGhost();
CreateGhost();
}
}
}
}
}
private void TogglePreview()
{
if (isPreviewing)
{
CancelPreview();
}
else
{
CreateGhost();
}
}
private void CreateGhost()
{
if (selectedTrapIndex < 0 || selectedTrapIndex >= availableTraps.Length) return;
var data = availableTraps[selectedTrapIndex];
if (data.GhostPrefab != null)
{
currentGhostInstance = Instantiate(data.GhostPrefab);
isPreviewing = true;
}
}
private void CancelPreview()
{
DestroyGhost();
isPreviewing = false;
}
private void DestroyGhost()
{
if (currentGhostInstance != null)
{
Destroy(currentGhostInstance);
currentGhostInstance = null;
}
}
private void UpdateGhostPosition()
{
if (currentGhostInstance == null) return;
// Perform raycast from viewport center
Ray ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0));
var data = availableTraps[selectedTrapIndex];
if (Physics.Raycast(ray, out RaycastHit hit, data.PlacementMaxDistance, placementLayerMask))
{
currentGhostInstance.SetActive(true);
currentGhostInstance.transform.position = hit.point;
// Align rotation with the surface normal
currentGhostInstance.transform.rotation = Quaternion.FromToRotation(Vector3.up, hit.normal);
// Validate placement (make sure no walls, players, or other traps block it)
bool isValid = ValidatePlacement(hit.point);
UpdateGhostVisuals(isValid);
}
else
{
currentGhostInstance.SetActive(false); // Hide preview when looking in the air/out of bounds
}
}
private bool ValidatePlacement(Vector3 position)
{
// Simple check to ensure we don't stack traps on top of each other or clip through walls
Collider[] overlaps = Physics.OverlapSphere(position, 0.5f, LayerMask.GetMask("Walls", "Trap"));
return overlaps.Length == 0;
}
private void UpdateGhostVisuals(bool isValid)
{
if (currentGhostInstance == null) return;
// Visual indicator: Green transparency for valid, red for invalid
Renderer[] renderers = currentGhostInstance.GetComponentsInChildren<Renderer>();
Color ghostColor = isValid ? new Color(0f, 1f, 0f, 0.4f) : new Color(1f, 0f, 0f, 0.4f);
foreach (var r in renderers)
{
foreach (var mat in r.materials)
{
// Set standard rendering properties to handle transparent coloring
mat.SetFloat("_Mode", 3); // Transparent mode
mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
mat.SetInt("_ZWrite", 0);
mat.DisableKeyword("_ALPHATEST_ON");
mat.EnableKeyword("_ALPHABLEND_ON");
mat.DisableKeyword("_ALPHAPREMULTIPLY_ON");
mat.renderQueue = 3000;
mat.color = ghostColor;
}
}
}
private void TryPlaceTrap()
{
if (selectedTrapIndex < 0 || selectedTrapIndex >= availableTraps.Length) return;
// Check local cooldown
if (cooldownTimers[selectedTrapIndex] > 0)
{
Debug.LogWarning($"{availableTraps[selectedTrapIndex].TrapName} is on cooldown!");
return;
}
var data = availableTraps[selectedTrapIndex];
Ray ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0));
if (Physics.Raycast(ray, out RaycastHit hit, data.PlacementMaxDistance, placementLayerMask))
{
if (ValidatePlacement(hit.point))
{
// Send placement request RPC to host
RPC_RequestPlaceTrap(hit.point, Quaternion.FromToRotation(Vector3.up, hit.normal), selectedTrapIndex);
// Set cooldown timer
cooldownTimers[selectedTrapIndex] = data.Cooldown;
CancelPreview();
}
}
}
[Rpc(RpcSources.InputAuthority, RpcTargets.StateAuthority)]
private void RPC_RequestPlaceTrap(Vector3 position, Quaternion rotation, int trapIndex)
{
if (trapIndex < 0 || trapIndex >= availableTraps.Length) return;
TrapDataSO data = availableTraps[trapIndex];
// Host spawns the networked trap prefab
NetworkObject spawnedTrapObj = Runner.Spawn(data.TrapPrefab, position, rotation, Object.InputAuthority);
// Configure ownership
TrapBase trap = spawnedTrapObj.GetComponent<TrapBase>();
if (trap != null)
{
trap.Owner = Object.InputAuthority;
}
Debug.Log($"[Host] Spawned trap '{data.TrapName}' requested by Player {Object.InputAuthority.PlayerId}");
}
private void OnDestroy()
{
DestroyGhost();
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 517119b8bcd57fd40b9bfa76493b234f

View File

@@ -0,0 +1,14 @@
using UnityEngine;
public class WorldSpacePing : MonoBehaviour
{
void LateUpdate()
{
// Face the main camera
if (Camera.main != null)
{
transform.LookAt(transform.position + Camera.main.transform.rotation * Vector3.forward,
Camera.main.transform.rotation * Vector3.up);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 57851794d79358542a14d9c4e421ecf8

View File

@@ -43,7 +43,6 @@ namespace Hallucinate.UI
// Lắng nghe sự kiện thay đổi kích thước toàn màn hình
root.RegisterCallback<GeometryChangedEvent>(OnScreenResize);
/*_logo.RegisterCallback<ClickEvent>(OnLogoClicked);*/
var settingsBtn = root.Q<Button>("SettingsBtn");

View File

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

View File

@@ -0,0 +1,30 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e0918021c117c7d4a8e8d5ae50f16367, type: 3}
m_Name: AlarmTrapData
m_EditorClassIdentifier: Assembly-CSharp::TrapDataSO
TrapName:
Type: 0
TrapPrefab:
RawGuidValue: 00000000000000000000000000000000
GhostPrefab: {fileID: 0}
Cooldown: 5
ArmingDelay: 0.5
Lifetime: 180
PlacementMaxDistance: 5
Damage: 0
EffectDuration: 5
EffectRadius: 1.5
Icon: {fileID: 0}
PlaceSFX: {fileID: 0}
TriggerSFX: {fileID: 0}
TriggerVFX: {fileID: 0}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 02beb6123d7fe7144bba7780d0ce85fd
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,30 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e0918021c117c7d4a8e8d5ae50f16367, type: 3}
m_Name: BearTrapData
m_EditorClassIdentifier: Assembly-CSharp::TrapDataSO
TrapName:
Type: 0
TrapPrefab:
RawGuidValue: 00000000000000000000000000000000
GhostPrefab: {fileID: 0}
Cooldown: 8
ArmingDelay: 1
Lifetime: 120
PlacementMaxDistance: 4
Damage: 30
EffectDuration: 3
EffectRadius: 0.5
Icon: {fileID: 0}
PlaceSFX: {fileID: 0}
TriggerSFX: {fileID: 0}
TriggerVFX: {fileID: 0}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 050052764582c7847a9dec4afd06b2c7
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,30 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e0918021c117c7d4a8e8d5ae50f16367, type: 3}
m_Name: FlashbangTrapData
m_EditorClassIdentifier: Assembly-CSharp::TrapDataSO
TrapName:
Type: 0
TrapPrefab:
RawGuidValue: 00000000000000000000000000000000
GhostPrefab: {fileID: 0}
Cooldown: 15
ArmingDelay: 1.2
Lifetime: 60
PlacementMaxDistance: 4
Damage: 0
EffectDuration: 4
EffectRadius: 5
Icon: {fileID: 0}
PlaceSFX: {fileID: 0}
TriggerSFX: {fileID: 0}
TriggerVFX: {fileID: 0}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: beaf2dd06351db64a8079a5542897324
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,30 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e0918021c117c7d4a8e8d5ae50f16367, type: 3}
m_Name: PoisonGasTrapData
m_EditorClassIdentifier: Assembly-CSharp::TrapDataSO
TrapName:
Type: 0
TrapPrefab:
RawGuidValue: 00000000000000000000000000000000
GhostPrefab: {fileID: 0}
Cooldown: 12
ArmingDelay: 1.5
Lifetime: 90
PlacementMaxDistance: 5
Damage: 5
EffectDuration: 8
EffectRadius: 4
Icon: {fileID: 0}
PlaceSFX: {fileID: 0}
TriggerSFX: {fileID: 0}
TriggerVFX: {fileID: 0}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 70af2cb7323231d458063d062cacb928
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,150 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &8178333286525172923
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1180331695922625857}
- component: {fileID: 8599734570379226995}
- component: {fileID: 6730854896703472132}
- component: {fileID: 4967946788089166994}
- component: {fileID: 6056742849241993903}
- component: {fileID: 4739573552132571598}
m_Layer: 0
m_Name: AlarmTrap_Network
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1180331695922625857
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8178333286525172923}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 19.6542, y: -4.20374, z: -12.26623}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &8599734570379226995
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8178333286525172923}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &6730854896703472132
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8178333286525172923}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!65 &4967946788089166994
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8178333286525172923}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 1
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!114 &6056742849241993903
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8178333286525172923}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -1552182283, guid: e725a070cec140c4caffb81624c8c787, type: 3}
m_Name:
m_EditorClassIdentifier: Fusion.Runtime.dll::Fusion.NetworkObject
SortKey: 1823467846
ObjectInterest: 1
Flags: 262145
NestedObjects: []
NetworkedBehaviours:
- {fileID: 4739573552132571598}
ForceRemoteRenderTimeframe: 0
--- !u!114 &4739573552132571598
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8178333286525172923}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d3677b0b15c4801418e111e571f9ffdf, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::AlarmTrap
trapData: {fileID: 11400000, guid: 02beb6123d7fe7144bba7780d0ce85fd, type: 2}
_State: 0
pingIndicatorPrefab: {fileID: 0}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 7261c30dedebbed449f8109e36dd1120
labels:
- FusionPrefab
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,149 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &7727981485989415878
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2607197293044943558}
- component: {fileID: 5661704153688503297}
- component: {fileID: 1624544616279796053}
- component: {fileID: 8800518859750973183}
- component: {fileID: 5765057633405249995}
- component: {fileID: 818041720083759598}
m_Layer: 0
m_Name: BearTrap_Network
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &2607197293044943558
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7727981485989415878}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 19.6542, y: -4.20374, z: -12.26623}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &5661704153688503297
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7727981485989415878}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &1624544616279796053
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7727981485989415878}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!65 &8800518859750973183
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7727981485989415878}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 1
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!114 &5765057633405249995
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7727981485989415878}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -1552182283, guid: e725a070cec140c4caffb81624c8c787, type: 3}
m_Name:
m_EditorClassIdentifier: Fusion.Runtime.dll::Fusion.NetworkObject
SortKey: 2275952476
ObjectInterest: 1
Flags: 262145
NestedObjects: []
NetworkedBehaviours:
- {fileID: 818041720083759598}
ForceRemoteRenderTimeframe: 0
--- !u!114 &818041720083759598
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7727981485989415878}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d43e058dc396f994281cb98fdc320766, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::BearTrap
trapData: {fileID: 11400000, guid: 050052764582c7847a9dec4afd06b2c7, type: 2}
_State: 0

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: a94855b3c8e2ecd48b3f1e7ba9f327c6
labels:
- FusionPrefab
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,149 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &8711516940896532447
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4376266374365522899}
- component: {fileID: 7642163283115943957}
- component: {fileID: 9046561271100063322}
- component: {fileID: 249580628419696697}
- component: {fileID: 8706279712047939947}
- component: {fileID: 1841005162439775464}
m_Layer: 0
m_Name: FlashbangTrap_Network
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &4376266374365522899
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8711516940896532447}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 19.6542, y: -4.20374, z: -12.26623}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &7642163283115943957
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8711516940896532447}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &9046561271100063322
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8711516940896532447}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!65 &249580628419696697
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8711516940896532447}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 1
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!114 &8706279712047939947
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8711516940896532447}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -1552182283, guid: e725a070cec140c4caffb81624c8c787, type: 3}
m_Name:
m_EditorClassIdentifier: Fusion.Runtime.dll::Fusion.NetworkObject
SortKey: 3937552294
ObjectInterest: 1
Flags: 262145
NestedObjects: []
NetworkedBehaviours:
- {fileID: 1841005162439775464}
ForceRemoteRenderTimeframe: 0
--- !u!114 &1841005162439775464
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8711516940896532447}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: aaed554f0d45575499a9984c3ea151b8, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::FlashbangTrap
trapData: {fileID: 11400000, guid: beaf2dd06351db64a8079a5542897324, type: 2}
_State: 0

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 041feea13b34d924f8bd6111b3d258ff
labels:
- FusionPrefab
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,149 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &4317555471139344146
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7995397700504855272}
- component: {fileID: 276701688181203791}
- component: {fileID: 7792659011929304859}
- component: {fileID: 4536171354803583898}
- component: {fileID: 508792796663975319}
- component: {fileID: 8539244757698054908}
m_Layer: 0
m_Name: PoisonGasTrap_Network
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &7995397700504855272
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4317555471139344146}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 19.6542, y: -4.20374, z: -12.26623}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &276701688181203791
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4317555471139344146}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &7792659011929304859
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4317555471139344146}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!65 &4536171354803583898
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4317555471139344146}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 1
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!114 &508792796663975319
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4317555471139344146}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -1552182283, guid: e725a070cec140c4caffb81624c8c787, type: 3}
m_Name:
m_EditorClassIdentifier: Fusion.Runtime.dll::Fusion.NetworkObject
SortKey: 2774641208
ObjectInterest: 1
Flags: 262145
NestedObjects: []
NetworkedBehaviours:
- {fileID: 8539244757698054908}
ForceRemoteRenderTimeframe: 0
--- !u!114 &8539244757698054908
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4317555471139344146}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 22d901fce76303a4a9039ba6ac32e7f6, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::PoisonGasTrap
trapData: {fileID: 11400000, guid: 70af2cb7323231d458063d062cacb928, type: 2}
_State: 0

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: fc7b4191eaab8c741822807adcca8394
labels:
- FusionPrefab
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -53,7 +53,7 @@ TagManager:
- Pushable
- CoverPoint
- Interactable
-
- StealthTrap
-
-
-