Init Homebase

This commit is contained in:
2026-06-02 18:28:23 -07:00
parent 2ee30c01fd
commit dd0064c377
48 changed files with 1934 additions and 12 deletions
+146
View File
@@ -0,0 +1,146 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &3885353946372160549
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3572766465862231365}
- component: {fileID: 3909651526955663392}
- component: {fileID: 3320445911748035220}
- component: {fileID: 9053853372340598254}
- component: {fileID: 6834786618115927220}
- component: {fileID: 1499754650316338861}
m_Layer: 0
m_Name: Storage
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &3572766465862231365
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3885353946372160549}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0.8, y: 0.8, z: 0.8}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &3909651526955663392
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3885353946372160549}
m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &3320445911748035220
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3885353946372160549}
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: 1
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!114 &9053853372340598254
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3885353946372160549}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c16549610bfe4458aa9389201d072bb6, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.Entities.Hybrid::Unity.Entities.Hybrid.Baking.LinkedEntityGroupAuthoring
--- !u!114 &6834786618115927220
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3885353946372160549}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7c79d771cedb4794bf100ce60df5f764, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.NetCode.Authoring.Hybrid::Unity.NetCode.GhostAuthoringComponent
HasOwner: 0
SupportAutoCommandTarget: 1
TrackInterpolationDelay: 0
GhostGroup: 0
UsePreSerialization: 0
UseSingleBaseline: 0
RollbackPredictedSpawnedGhostState: 0
RollbackPredictionOnStructuralChanges: 1
DefaultGhostMode: 0
SupportedGhostModes: 3
OptimizationMode: 0
Importance: 1
MaxSendRate: 0
prefabId:
--- !u!114 &1499754650316338861
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3885353946372160549}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8cc6285a9b8958a47a61a07550b7f792, type: 3}
m_Name:
m_EditorClassIdentifier: ProjectM.Authoring::ProjectM.Authoring.SharedStorageContainerAuthoring
InteractRadius: 2
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 6636e906a2ac46345889f5517658be67
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6928b42621b979b478f0ae236c575df8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,61 @@
using ProjectM.Simulation;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
namespace ProjectM.Authoring
{
/// <summary>
/// Authoring for the baked <see cref="BaseAnchor"/> singleton — the shared home base's fixed anchor
/// and planar build-grid coordinate space. Place once in the gameplay subscene; the GameObject's
/// position is the plot center (and the player spawn-ring center). The baker derives GridOrigin (the
/// min-XZ corner of cell (0,0)) so the plot is centered on the anchor. The entity carries no transform
/// (TransformUsageFlags.None) — the position is baked as flat data. Present identically on client and
/// server (baked, not replicated). Draws the plot footprint as an editor gizmo when selected.
/// </summary>
public class BaseAnchorAuthoring : MonoBehaviour
{
[Min(0.01f)]
[Tooltip("World-unit size of one square build-grid cell.")]
public float CellSize = 1f;
[Min(1)]
[Tooltip("Build-grid extent in cells along X and Z (square plot).")]
public int PlotSize = 32;
private class BaseAnchorBaker : Baker<BaseAnchorAuthoring>
{
public override void Bake(BaseAnchorAuthoring authoring)
{
// Data singleton: no transform on the entity, but we read the GameObject position as data.
var entity = GetEntity(authoring, TransformUsageFlags.None);
float3 anchorPos = authoring.transform.position;
var dims = new int2(authoring.PlotSize, authoring.PlotSize);
float cell = authoring.CellSize;
var gridOrigin = new float3(
anchorPos.x - dims.x * cell * 0.5f,
anchorPos.y,
anchorPos.z - dims.y * cell * 0.5f);
AddComponent(entity, new BaseAnchor
{
AnchorPos = anchorPos,
GridOrigin = gridOrigin,
CellSize = cell,
GridDims = dims,
});
}
}
#if UNITY_EDITOR
// Editor-only footprint gizmo: draws the claimed plot bounds on the base plane when selected.
private void OnDrawGizmosSelected()
{
float extent = CellSize * PlotSize;
Gizmos.color = new Color(0.2f, 0.8f, 1f, 0.9f);
Gizmos.DrawWireCube(transform.position, new Vector3(extent, 0.05f, extent));
}
#endif
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 4b53184727e358b4eb27f68ae25504d8
@@ -0,0 +1,32 @@
using ProjectM.Simulation;
using Unity.Entities;
using UnityEngine;
namespace ProjectM.Authoring
{
/// <summary>
/// Authoring for the shared storage-container ghost prefab: an ownerless INTERPOLATED ghost whose
/// replicated <see cref="StorageEntry"/> buffer is the shared inventory any player deposits into /
/// withdraws from (server-authoritative, applied by StorageOpReceiveSystem). Add a
/// GhostAuthoringComponent (Interpolated) to the prefab so clients see its contents replicate.
/// <c>GetEntity(TransformUsageFlags.Dynamic)</c> gives it a runtime world transform, set at spawn to
/// the base cell center.
/// </summary>
public class SharedStorageContainerAuthoring : MonoBehaviour
{
[Min(0f)]
[Tooltip("Interaction radius (world units) for the deposit/withdraw test; reserved for proximity gating.")]
public float InteractRadius = 2f;
private class SharedStorageContainerBaker : Baker<SharedStorageContainerAuthoring>
{
public override void Bake(SharedStorageContainerAuthoring authoring)
{
var entity = GetEntity(authoring, TransformUsageFlags.Dynamic);
AddComponent<SharedStorageContainer>(entity);
AddComponent(entity, new HitRadius { Value = authoring.InteractRadius });
AddBuffer<StorageEntry>(entity);
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8cc6285a9b8958a47a61a07550b7f792
@@ -0,0 +1,38 @@
using ProjectM.Simulation;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
namespace ProjectM.Authoring
{
/// <summary>
/// Authoring for the baked <see cref="StorageSpawner"/> singleton (mirrors UpgradePickupSpawnerAuthoring).
/// Place once in the gameplay subscene; the server-only SharedStorageSpawnSystem reads it, instantiates
/// the storage-container ghost at the base-grid cell center (BaseGridMath.CellToWorld), then destroys
/// the singleton so it fires exactly once. The entity carries no transform; only the prefab needs one.
/// </summary>
public class StorageSpawnerAuthoring : MonoBehaviour
{
[Tooltip("Storage-container ghost prefab to instantiate. Must carry SharedStorageContainerAuthoring + a GhostAuthoringComponent.")]
public GameObject ContainerPrefab;
[Tooltip("Build-grid cell at which to place the container (cell center, on the base plane).")]
public Vector2Int Cell = new Vector2Int(16, 22);
private class StorageSpawnerBaker : Baker<StorageSpawnerAuthoring>
{
public override void Bake(StorageSpawnerAuthoring authoring)
{
var entity = GetEntity(authoring, TransformUsageFlags.None);
AddComponent(entity, new StorageSpawner
{
Prefab = authoring.ContainerPrefab != null
? GetEntity(authoring.ContainerPrefab, TransformUsageFlags.Dynamic)
: Entity.Null,
Cell = new int2(authoring.Cell.x, authoring.Cell.y),
});
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 968b8c85b6f69ae438e56cb1f19a2450
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1befa5e760ee51b4b8ad625ae55c5024
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,76 @@
using ProjectM.Simulation;
using Unity.Entities;
using Unity.NetCode;
namespace ProjectM.Client
{
/// <summary>
/// Client-only sender for shared-storage deposit/withdraw <see cref="StorageOpRequest"/> RPCs. A
/// one-off action (not per-tick predicted input), so it is an RPC: on an interact key edge (E =
/// deposit, Q = withdraw a default test item) it creates the request entity targeted at the server
/// connection, and the server applies it authoritatively in StorageOpReceiveSystem. Managed
/// SystemBase because it reads the managed Input System. Input System types are fully qualified and
/// <c>using UnityEngine.InputSystem;</c> is intentionally omitted (that namespace defines a
/// PlayerInput type that collides with <see cref="ProjectM.Simulation.PlayerInput"/>). An editor-only
/// static hook (Deposit/Withdraw) drives the same path from execute_code for headless validation
/// without a focused Game view.
/// </summary>
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
public partial class StorageOpSendSystem : SystemBase
{
// Default test item used by the keyboard interact and the parameterless debug hooks.
const ushort DefaultItemId = 1;
const int DefaultCount = 1;
#if UNITY_EDITOR
struct PendingStorageOp { public byte Op; public ushort ItemId; public int Count; }
static readonly System.Collections.Generic.Queue<PendingStorageOp> s_Pending =
new System.Collections.Generic.Queue<PendingStorageOp>();
/// <summary>EDITOR / execute_code hook: queue a deposit of <paramref name="count"/> of <paramref name="itemId"/>.</summary>
public static void Deposit(ushort itemId = DefaultItemId, int count = DefaultCount) =>
s_Pending.Enqueue(new PendingStorageOp { Op = StorageOp.Deposit, ItemId = itemId, Count = count });
/// <summary>EDITOR / execute_code hook: queue a withdraw of <paramref name="count"/> of <paramref name="itemId"/>.</summary>
public static void Withdraw(ushort itemId = DefaultItemId, int count = DefaultCount) =>
s_Pending.Enqueue(new PendingStorageOp { Op = StorageOp.Withdraw, ItemId = itemId, Count = count });
#endif
protected override void OnCreate()
{
RequireForUpdate<NetworkId>();
}
protected override void OnUpdate()
{
// Need the server connection to target the RPC; bail (keeping any queued ops) until connected.
if (!SystemAPI.TryGetSingletonEntity<NetworkId>(out var connection))
return;
var keyboard = UnityEngine.InputSystem.Keyboard.current;
if (keyboard != null)
{
if (keyboard.eKey.wasPressedThisFrame)
Send(connection, StorageOp.Deposit, DefaultItemId, DefaultCount);
if (keyboard.qKey.wasPressedThisFrame)
Send(connection, StorageOp.Withdraw, DefaultItemId, DefaultCount);
}
#if UNITY_EDITOR
while (s_Pending.Count > 0)
{
var op = s_Pending.Dequeue();
Send(connection, op.Op, op.ItemId, op.Count);
}
#endif
}
void Send(Entity connection, byte op, ushort itemId, int count)
{
var request = EntityManager.CreateEntity();
EntityManager.AddComponentData(request, new StorageOpRequest { Op = op, ItemId = itemId, Count = count });
EntityManager.AddComponentData(request, new SendRpcCommandRequest { TargetConnection = connection });
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d59f540925fe24a439bad6f7a77907fe
@@ -34,6 +34,12 @@ namespace ProjectM.Server
public void OnUpdate(ref SystemState state)
{
var spawner = SystemAPI.GetSingleton<PlayerSpawner>();
// M5 home base: re-root the spawn ring on the baked BaseAnchor when present; fall back
// to the spawner's SpawnPoint if the base subscene hasn't streamed in yet.
var center = spawner.SpawnPoint;
if (SystemAPI.TryGetSingleton<BaseAnchor>(out var baseAnchor))
center = BaseGridMath.PlotCenter(baseAnchor);
var ecb = new EntityCommandBuffer(Allocator.Temp);
foreach (var (receive, requestEntity) in
@@ -45,7 +51,7 @@ namespace ProjectM.Server
var networkId = SystemAPI.GetComponent<NetworkId>(connection);
var player = ecb.Instantiate(spawner.PlayerPrefab);
ecb.SetComponent(player, LocalTransform.FromPosition(spawner.SpawnPoint + PlayerSpawnMath.SpawnOffset(networkId.Value, spawner.SpawnRingRadius, spawner.RingSlots)));
ecb.SetComponent(player, LocalTransform.FromPosition(center + PlayerSpawnMath.SpawnOffset(networkId.Value, spawner.SpawnRingRadius, spawner.RingSlots)));
ecb.SetComponent(player, new GhostOwner { NetworkId = networkId.Value });
// Auto-despawn the player when its owning connection is removed.
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9b28010c047f72f4b8d242ee1f4351cd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,51 @@
using ProjectM.Simulation;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Transforms;
namespace ProjectM.Server
{
/// <summary>
/// Server-only, one-shot spawner for the shared home-base storage container (mirrors
/// UpgradePickupSpawnSystem). On its first update it reads the baked <see cref="StorageSpawner"/>
/// singleton and the <see cref="BaseAnchor"/>, instantiates the container ghost at the cell center
/// (<see cref="BaseGridMath.CellToWorld"/>), then destroys the spawner singleton so the system idles
/// (spawned exactly once). Runs in the default SimulationSystemGroup (NOT the prediction loop); the
/// container replicates to clients as an ownerless interpolated ghost. The container is intentionally
/// NOT linked to any connection's LinkedEntityGroup, so it persists across player disconnects.
/// </summary>
[BurstCompile]
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
public partial struct SharedStorageSpawnSystem : ISystem
{
[BurstCompile]
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<StorageSpawner>();
state.RequireForUpdate<BaseAnchor>();
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
var spawnerEntity = SystemAPI.GetSingletonEntity<StorageSpawner>();
var spawner = SystemAPI.GetComponent<StorageSpawner>(spawnerEntity);
var anchor = SystemAPI.GetSingleton<BaseAnchor>();
var ecb = new EntityCommandBuffer(Allocator.Temp);
if (spawner.Prefab != Entity.Null)
{
var container = ecb.Instantiate(spawner.Prefab);
var position = BaseGridMath.CellToWorld(anchor, spawner.Cell);
ecb.SetComponent(container, LocalTransform.FromPosition(position));
}
// One-shot: remove the spawner so RequireForUpdate fails and the system idles.
ecb.DestroyEntity(spawnerEntity);
ecb.Playback(state.EntityManager);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c60c2c14e48ea0c45858cf0054c1663f
@@ -0,0 +1,54 @@
using ProjectM.Simulation;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.NetCode;
namespace ProjectM.Server
{
/// <summary>
/// Server-authoritative handler for <see cref="StorageOpRequest"/> RPCs (deposit/withdraw on the
/// shared storage container). Resolves the single <see cref="SharedStorageContainer"/> as a singleton,
/// applies the op to its replicated <see cref="StorageEntry"/> buffer via <see cref="StorageMath"/>,
/// and destroys the request entity. Runs in the default SimulationSystemGroup (NOT the prediction
/// loop), so a server event is applied exactly once (no rollback double-apply). Op is read as a byte
/// (see <see cref="StorageOp"/>); the buffer mutation auto-replicates to all clients via GhostField.
/// </summary>
[BurstCompile]
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
public partial struct StorageOpReceiveSystem : ISystem
{
[BurstCompile]
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<SharedStorageContainer>();
var builder = new EntityQueryBuilder(Allocator.Temp)
.WithAll<StorageOpRequest, ReceiveRpcCommandRequest>();
state.RequireForUpdate(state.GetEntityQuery(builder));
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
var containerEntity = SystemAPI.GetSingletonEntity<SharedStorageContainer>();
var contents = SystemAPI.GetBuffer<StorageEntry>(containerEntity);
var ecb = new EntityCommandBuffer(Allocator.Temp);
foreach (var (request, requestEntity) in
SystemAPI.Query<RefRO<StorageOpRequest>>().WithAll<ReceiveRpcCommandRequest>().WithEntityAccess())
{
var op = request.ValueRO;
if (op.Op == StorageOp.Withdraw)
StorageMath.Withdraw(contents, op.ItemId, op.Count);
else
StorageMath.Deposit(contents, op.ItemId, op.Count);
ecb.DestroyEntity(requestEntity);
}
ecb.Playback(state.EntityManager);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6739144c8fa1bd040ad766919f9535f3
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d26b9ac48ea390e4fa1310800701eb52
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,27 @@
using Unity.Entities;
using Unity.Mathematics;
namespace ProjectM.Simulation
{
/// <summary>
/// Singleton baked into the gameplay subscene describing the shared home base: a fixed anchor
/// position and the planar build-grid coordinate space (origin + cell size + extent) that M6 build
/// placement snaps structures into and M7 production chains tick inside. Flat and blittable (no
/// entity refs) so it stays deterministic across both worlds and serialization-friendly for later
/// persistence. Present identically on client and server (baked, not replicated).
/// </summary>
public struct BaseAnchor : IComponentData
{
/// <summary>World-space center of the plot; also the player spawn-ring center. Equals BaseGridMath.PlotCenter(this).</summary>
public float3 AnchorPos;
/// <summary>World-space min-XZ CORNER of cell (0,0). Y is the base plane. Baked = AnchorPos - (GridDims*CellSize)/2 on XZ.</summary>
public float3 GridOrigin;
/// <summary>Size of one square grid cell in world units.</summary>
public float CellSize;
/// <summary>Grid extent in cells along X and Z; valid cell indices are [0, GridDims).</summary>
public int2 GridDims;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9434d000149a08e438c5a7b876f7e10c
@@ -0,0 +1,54 @@
using Unity.Mathematics;
namespace ProjectM.Simulation
{
/// <summary>
/// Deterministic, side-effect-free math for the home-base build grid. Pure (no RNG, no wall-clock)
/// so server and predicting client agree on cell occupancy. Convention: <see cref="BaseAnchor.GridOrigin"/>
/// is the min-XZ CORNER of cell (0,0); <see cref="CellToWorld"/> returns cell CENTERS; cell bounds are
/// half-open so a point on a cell's lower edge belongs to that cell and the far plot edge is out of plot.
/// World-&gt;cell uses math.floor (not truncation) so negative coordinates snap correctly. Unit-tested in
/// EditMode (no netcode world required); M6's server placement handler calls these directly.
/// </summary>
public static class BaseGridMath
{
/// <summary>Planar (XZ) world position -&gt; integer grid cell. Uses floor, so values below GridOrigin go negative (out of plot).</summary>
public static int2 WorldToCell(in BaseAnchor a, float3 worldPos)
{
float2 local = (worldPos.xz - a.GridOrigin.xz) / a.CellSize;
return (int2)math.floor(local);
}
/// <summary>Grid cell -&gt; world position of the cell CENTER, on the base plane (Y = GridOrigin.y).</summary>
public static float3 CellToWorld(in BaseAnchor a, int2 cell)
{
float2 centerXZ = a.GridOrigin.xz + ((float2)cell + 0.5f) * a.CellSize;
return new float3(centerXZ.x, a.GridOrigin.y, centerXZ.y);
}
/// <summary>True when the cell is inside the plot. Half-open: [0, GridDims), so the far edge is out.</summary>
public static bool IsCellInPlot(in BaseAnchor a, int2 cell)
{
return math.all(cell >= 0) && math.all(cell < a.GridDims);
}
/// <summary>True when the world point falls inside the plot (its cell is in-plot).</summary>
public static bool IsPointInPlot(in BaseAnchor a, float3 worldPos)
{
return IsCellInPlot(a, WorldToCell(a, worldPos));
}
/// <summary>Clamp a cell into the valid [0, GridDims-1] range.</summary>
public static int2 ClampCell(in BaseAnchor a, int2 cell)
{
return math.clamp(cell, new int2(0, 0), a.GridDims - 1);
}
/// <summary>World-space center of the whole plot (== AnchorPos when baked correctly).</summary>
public static float3 PlotCenter(in BaseAnchor a)
{
float2 centerXZ = a.GridOrigin.xz + (float2)a.GridDims * a.CellSize * 0.5f;
return new float3(centerXZ.x, a.GridOrigin.y, centerXZ.y);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 30b3681e3ce98b241a7ceb7af7e62962
@@ -0,0 +1,12 @@
using Unity.Entities;
namespace ProjectM.Simulation
{
/// <summary>
/// Tag marking the shared home-base storage container. All state lives in the entity's
/// <see cref="StorageEntry"/> buffer. In M5 there is exactly one (server-spawned at a fixed base
/// cell), so server systems resolve it as a singleton. Server-authoritative and world-resident, so
/// its contents survive a player disconnect (no disk persistence yet).
/// </summary>
public struct SharedStorageContainer : IComponentData { }
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8de8d91f5d8f0a64c87b0847ae85c564
@@ -0,0 +1,23 @@
using Unity.Entities;
using Unity.NetCode;
namespace ProjectM.Simulation
{
/// <summary>
/// One (item, count) row in a shared storage container's contents. The container's DynamicBuffer of
/// these is the server-authoritative shared inventory; it is a GhostField buffer so the server's
/// mutations replicate to every client. The container is an ownerless INTERPOLATED ghost, so no
/// OwnerSendType / GhostOwner applies (those are for owner-predicted ghosts like the player).
/// ItemId is an opaque id; a richer item model (and the M7 machine input/output generalisation)
/// layers on top without changing replication.
/// </summary>
[InternalBufferCapacity(16)]
public struct StorageEntry : IBufferElementData
{
/// <summary>Opaque item identifier (0 = empty/unused).</summary>
[GhostField] public ushort ItemId;
/// <summary>Quantity of this item in the container.</summary>
[GhostField] public int Count;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f017cb14ca58bc54191ad3f356541ca6
@@ -0,0 +1,62 @@
using Unity.Entities;
namespace ProjectM.Simulation
{
/// <summary>
/// Pure, deterministic merge logic for a shared storage container's <see cref="StorageEntry"/> buffer.
/// No RNG / wall-clock, so server and (future) prediction agree. Deposit merges into an existing row
/// for the same item or appends a new row; Withdraw decrements and drops a row that hits zero, clamping
/// to available. DynamicBuffer is a handle, so mutations apply to the underlying entity buffer.
/// Unit-tested in EditMode via a plain Entities world.
/// </summary>
public static class StorageMath
{
/// <summary>Add <paramref name="count"/> of <paramref name="itemId"/>, merging into an existing row if present. No-op for count &lt;= 0 or itemId 0.</summary>
public static void Deposit(DynamicBuffer<StorageEntry> buffer, ushort itemId, int count)
{
if (count <= 0 || itemId == 0)
return;
for (int i = 0; i < buffer.Length; i++)
{
if (buffer[i].ItemId == itemId)
{
var entry = buffer[i];
entry.Count += count;
buffer[i] = entry;
return;
}
}
buffer.Add(new StorageEntry { ItemId = itemId, Count = count });
}
/// <summary>
/// Remove up to <paramref name="count"/> of <paramref name="itemId"/>, clamped to what is available.
/// Drops the row when it reaches zero. Returns the amount actually withdrawn (0 if none). No-op for
/// count &lt;= 0 or itemId 0.
/// </summary>
public static int Withdraw(DynamicBuffer<StorageEntry> buffer, ushort itemId, int count)
{
if (count <= 0 || itemId == 0)
return 0;
for (int i = 0; i < buffer.Length; i++)
{
if (buffer[i].ItemId == itemId)
{
var entry = buffer[i];
int taken = entry.Count < count ? entry.Count : count;
entry.Count -= taken;
if (entry.Count <= 0)
buffer.RemoveAt(i);
else
buffer[i] = entry;
return taken;
}
}
return 0;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c20f98fb70e66c94193c16e4bfd7f1b4
@@ -0,0 +1,33 @@
using Unity.NetCode;
namespace ProjectM.Simulation
{
/// <summary>
/// Client -&gt; server request to deposit into or withdraw from the shared storage container. A one-off
/// action, so it is an RPC (not a per-tick predicted input). Op is stored as a byte (see
/// <see cref="StorageOp"/>) rather than an enum to keep the generated serializer trivial and avoid the
/// cross-assembly enum-codegen hazard. No target entity is carried: M5 has a single shared container,
/// which the server resolves as a singleton (entity refs are not stable across worlds).
/// </summary>
public struct StorageOpRequest : IRpcCommand
{
/// <summary>Operation code (see <see cref="StorageOp"/>): 0 = deposit, 1 = withdraw.</summary>
public byte Op;
/// <summary>Item to deposit/withdraw.</summary>
public ushort ItemId;
/// <summary>Quantity to deposit/withdraw (server clamps withdraw to available).</summary>
public int Count;
}
/// <summary>Operation codes for <see cref="StorageOpRequest.Op"/> (byte to keep RPC serialization trivial).</summary>
public static class StorageOp
{
/// <summary>Add items to the shared container.</summary>
public const byte Deposit = 0;
/// <summary>Remove items from the shared container.</summary>
public const byte Withdraw = 1;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: dc9ec88867d746e45b9204331b5bab51
@@ -0,0 +1,20 @@
using Unity.Entities;
using Unity.Mathematics;
namespace ProjectM.Simulation
{
/// <summary>
/// Singleton baked into the gameplay subscene, holding the baked storage-container ghost prefab and
/// the base-grid cell to spawn it at. A one-shot server system instantiates the prefab at
/// BaseGridMath.CellToWorld(anchor, Cell) and then destroys this singleton. Mirrors the
/// UpgradePickupSpawner / PlayerSpawner pattern.
/// </summary>
public struct StorageSpawner : IComponentData
{
/// <summary>Baked storage-container ghost prefab to instantiate.</summary>
public Entity Prefab;
/// <summary>Base-grid cell at which to place the container (cell center, on the base plane).</summary>
public int2 Cell;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6a2e4fad83fa03b4890d736b388a9917
+94
View File
@@ -281,6 +281,52 @@ Transform:
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &874705786
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 874705788}
- component: {fileID: 874705787}
m_Layer: 0
m_Name: BaseAnchor
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &874705787
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 874705786}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4b53184727e358b4eb27f68ae25504d8, type: 3}
m_Name:
m_EditorClassIdentifier: ProjectM.Authoring::ProjectM.Authoring.BaseAnchorAuthoring
CellSize: 1
PlotSize: 32
--- !u!4 &874705788
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 874705786}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 1, z: 0}
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!1 &1379903944
GameObject:
m_ObjectHideFlags: 0
@@ -585,6 +631,52 @@ Transform:
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1930969065
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1930969067}
- component: {fileID: 1930969066}
m_Layer: 0
m_Name: StorageSpawner
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1930969066
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1930969065}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 968b8c85b6f69ae438e56cb1f19a2450, type: 3}
m_Name:
m_EditorClassIdentifier: ProjectM.Authoring::ProjectM.Authoring.StorageSpawnerAuthoring
ContainerPrefab: {fileID: 3885353946372160549, guid: 6636e906a2ac46345889f5517658be67, type: 3}
Cell: {x: 19, y: 19}
--- !u!4 &1930969067
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1930969065}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 1, z: 0}
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!1 &1936735558
GameObject:
m_ObjectHideFlags: 0
@@ -758,3 +850,5 @@ SceneRoots:
- {fileID: 1694504175}
- {fileID: 1936735562}
- {fileID: 691660676}
- {fileID: 874705788}
- {fileID: 1930969067}
@@ -0,0 +1,113 @@
using NUnit.Framework;
using ProjectM.Simulation;
using Unity.Mathematics;
namespace ProjectM.Tests
{
/// <summary>
/// Pure-function tests for <see cref="BaseGridMath"/> (no ECS world), mirroring PlayerSpawnRingTests.
/// Pins the locked M5 home-base grid coordinate space: planar 32x32, CellSize 1.0, corner-origin,
/// center-returning, half-open cell bounds, floor-based world-&gt;cell. M6 placement builds on this.
/// </summary>
public class BaseGridMathTests
{
const float Eps = 1e-4f;
// Locked config: center at (0,1,0); 32x32 cells of 1.0u => origin corner at (-16,1,-16).
static BaseAnchor MakeAnchor()
{
var dims = new int2(32, 32);
float cell = 1f;
var anchorPos = new float3(0f, 1f, 0f);
var gridOrigin = new float3(
anchorPos.x - dims.x * cell * 0.5f,
anchorPos.y,
anchorPos.z - dims.y * cell * 0.5f);
return new BaseAnchor { AnchorPos = anchorPos, GridOrigin = gridOrigin, CellSize = cell, GridDims = dims };
}
[Test]
public void CellToWorld_Returns_Cell_Centers()
{
var a = MakeAnchor();
AssertXz(BaseGridMath.CellToWorld(a, new int2(0, 0)), -15.5f, -15.5f);
AssertXz(BaseGridMath.CellToWorld(a, new int2(31, 31)), 15.5f, 15.5f);
AssertXz(BaseGridMath.CellToWorld(a, new int2(16, 16)), 0.5f, 0.5f);
// Centers stay on the base plane.
Assert.AreEqual(1f, BaseGridMath.CellToWorld(a, new int2(5, 9)).y, Eps);
}
[Test]
public void RoundTrip_CellToWorld_Then_WorldToCell()
{
var a = MakeAnchor();
AssertCell(BaseGridMath.WorldToCell(a, BaseGridMath.CellToWorld(a, new int2(0, 0))), 0, 0);
AssertCell(BaseGridMath.WorldToCell(a, BaseGridMath.CellToWorld(a, new int2(31, 31))), 31, 31);
AssertCell(BaseGridMath.WorldToCell(a, BaseGridMath.CellToWorld(a, new int2(16, 16))), 16, 16);
}
[Test]
public void WorldToCell_Uses_Floor_Not_Truncation()
{
var a = MakeAnchor();
// local x = worldX + 16, then floor. z = 0 -> cell.y = 16.
Assert.AreEqual(0, BaseGridMath.WorldToCell(a, new float3(-15.6f, 1f, 0f)).x);
Assert.AreEqual(-1, BaseGridMath.WorldToCell(a, new float3(-16.4f, 1f, 0f)).x);
// A point exactly on a cell's lower edge belongs to the higher cell (half-open).
Assert.AreEqual(1, BaseGridMath.WorldToCell(a, new float3(-15.0f, 1f, 0f)).x);
Assert.AreEqual(16, BaseGridMath.WorldToCell(a, new float3(-15.6f, 1f, 0f)).y);
}
[Test]
public void IsCellInPlot_Is_HalfOpen()
{
var a = MakeAnchor();
Assert.IsTrue(BaseGridMath.IsCellInPlot(a, new int2(0, 0)));
Assert.IsTrue(BaseGridMath.IsCellInPlot(a, new int2(31, 31)));
Assert.IsFalse(BaseGridMath.IsCellInPlot(a, new int2(-1, 0)));
Assert.IsFalse(BaseGridMath.IsCellInPlot(a, new int2(0, 32)));
Assert.IsFalse(BaseGridMath.IsCellInPlot(a, new int2(32, 32)));
}
[Test]
public void IsPointInPlot_Bounds()
{
var a = MakeAnchor();
Assert.IsTrue(BaseGridMath.IsPointInPlot(a, a.AnchorPos)); // center
Assert.IsTrue(BaseGridMath.IsPointInPlot(a, new float3(-16f, 1f, 0f))); // lower corner edge -> cell 0
Assert.IsTrue(BaseGridMath.IsPointInPlot(a, new float3(15.9f, 1f, 15.9f))); // cell (31,31)
Assert.IsFalse(BaseGridMath.IsPointInPlot(a, new float3(-16.1f, 1f, 0f))); // cell (-1,*) out
Assert.IsFalse(BaseGridMath.IsPointInPlot(a, new float3(16f, 1f, 0f))); // far edge -> cell 32 out
}
[Test]
public void ClampCell_Bounds_To_Plot()
{
var a = MakeAnchor();
AssertCell(BaseGridMath.ClampCell(a, new int2(-5, 99)), 0, 31);
AssertCell(BaseGridMath.ClampCell(a, new int2(10, 10)), 10, 10);
}
[Test]
public void PlotCenter_Equals_AnchorPos()
{
var a = MakeAnchor();
var c = BaseGridMath.PlotCenter(a);
Assert.AreEqual(a.AnchorPos.x, c.x, Eps);
Assert.AreEqual(a.AnchorPos.y, c.y, Eps);
Assert.AreEqual(a.AnchorPos.z, c.z, Eps);
}
static void AssertXz(float3 p, float x, float z)
{
Assert.AreEqual(x, p.x, Eps);
Assert.AreEqual(z, p.z, Eps);
}
static void AssertCell(int2 c, int x, int y)
{
Assert.AreEqual(x, c.x);
Assert.AreEqual(y, c.y);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 001a3491cf2ef57468f3e4c61e98d30c
@@ -0,0 +1,141 @@
using NUnit.Framework;
using ProjectM.Simulation;
using Unity.Entities;
namespace ProjectM.Tests
{
/// <summary>
/// Plain-Entities tests for <see cref="StorageMath"/> deposit/withdraw merge logic, using a temp World
/// and a real DynamicBuffer&lt;StorageEntry&gt; (no netcode world). Pins shared-storage semantics:
/// deposits merge by item id, withdraws clamp to available and drop empty rows.
/// </summary>
public class StorageMathTests
{
static (World world, Entity e) MakeWorld()
{
var world = new World("StorageMathTestWorld");
var e = world.EntityManager.CreateEntity(typeof(StorageEntry));
return (world, e);
}
[Test]
public void Deposit_New_Item_Appends_Row()
{
var (world, e) = MakeWorld();
try
{
var buf = world.EntityManager.GetBuffer<StorageEntry>(e);
StorageMath.Deposit(buf, 1, 5);
Assert.AreEqual(1, buf.Length);
Assert.AreEqual((ushort)1, buf[0].ItemId);
Assert.AreEqual(5, buf[0].Count);
}
finally { world.Dispose(); }
}
[Test]
public void Deposit_Same_Item_Merges()
{
var (world, e) = MakeWorld();
try
{
var buf = world.EntityManager.GetBuffer<StorageEntry>(e);
StorageMath.Deposit(buf, 1, 5);
StorageMath.Deposit(buf, 1, 3);
Assert.AreEqual(1, buf.Length);
Assert.AreEqual(8, buf[0].Count);
}
finally { world.Dispose(); }
}
[Test]
public void Deposit_Different_Items_Separate_Rows()
{
var (world, e) = MakeWorld();
try
{
var buf = world.EntityManager.GetBuffer<StorageEntry>(e);
StorageMath.Deposit(buf, 1, 5);
StorageMath.Deposit(buf, 2, 7);
Assert.AreEqual(2, buf.Length);
}
finally { world.Dispose(); }
}
[Test]
public void Withdraw_Partial_Decrements()
{
var (world, e) = MakeWorld();
try
{
var buf = world.EntityManager.GetBuffer<StorageEntry>(e);
StorageMath.Deposit(buf, 1, 5);
int taken = StorageMath.Withdraw(buf, 1, 2);
Assert.AreEqual(2, taken);
Assert.AreEqual(1, buf.Length);
Assert.AreEqual(3, buf[0].Count);
}
finally { world.Dispose(); }
}
[Test]
public void Withdraw_To_Zero_Drops_Row()
{
var (world, e) = MakeWorld();
try
{
var buf = world.EntityManager.GetBuffer<StorageEntry>(e);
StorageMath.Deposit(buf, 1, 5);
int taken = StorageMath.Withdraw(buf, 1, 5);
Assert.AreEqual(5, taken);
Assert.AreEqual(0, buf.Length);
}
finally { world.Dispose(); }
}
[Test]
public void Withdraw_More_Than_Available_Clamps()
{
var (world, e) = MakeWorld();
try
{
var buf = world.EntityManager.GetBuffer<StorageEntry>(e);
StorageMath.Deposit(buf, 1, 3);
int taken = StorageMath.Withdraw(buf, 1, 10);
Assert.AreEqual(3, taken);
Assert.AreEqual(0, buf.Length);
}
finally { world.Dispose(); }
}
[Test]
public void Withdraw_Missing_Item_Is_NoOp()
{
var (world, e) = MakeWorld();
try
{
var buf = world.EntityManager.GetBuffer<StorageEntry>(e);
StorageMath.Deposit(buf, 1, 3);
int taken = StorageMath.Withdraw(buf, 99, 1);
Assert.AreEqual(0, taken);
Assert.AreEqual(1, buf.Length);
}
finally { world.Dispose(); }
}
[Test]
public void Deposit_Ignores_NonPositive_And_ZeroItem()
{
var (world, e) = MakeWorld();
try
{
var buf = world.EntityManager.GetBuffer<StorageEntry>(e);
StorageMath.Deposit(buf, 1, 0);
StorageMath.Deposit(buf, 1, -5);
StorageMath.Deposit(buf, 0, 5);
Assert.AreEqual(0, buf.Length);
}
finally { world.Dispose(); }
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c3f5de6f166b6da45bd20e2884e2f4c0
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7f08a78b99fcf9948987cc3202bfc0b0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+677
View File
@@ -0,0 +1,677 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!29 &1
OcclusionCullingSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_OcclusionBakeSettings:
smallestOccluder: 5
smallestHole: 0.25
backfaceThreshold: 100
m_SceneGUID: 00000000000000000000000000000000
m_OcclusionCullingData: {fileID: 0}
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 10
m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3
m_FogDensity: 0.01
m_LinearFogStart: 0
m_LinearFogEnd: 300
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
m_AmbientIntensity: 1
m_AmbientMode: 0
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
m_HaloStrength: 0.5
m_FlareStrength: 1
m_FlareFadeSpeed: 3
m_HaloTexture: {fileID: 0}
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
m_DefaultReflectionMode: 0
m_DefaultReflectionResolution: 128
m_ReflectionBounces: 1
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 13
m_BakeOnSceneLoad: 0
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
m_IndirectOutputScale: 1
m_AlbedoBoost: 1
m_EnvironmentLightingMode: 0
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 0
m_LightmapEditorSettings:
serializedVersion: 12
m_Resolution: 2
m_BakeResolution: 40
m_AtlasSize: 1024
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAmbientOcclusion: 0
m_Padding: 2
m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1
m_TextureCompression: 1
m_ReflectionCompression: 2
m_MixedBakeMode: 2
m_BakeBackend: 1
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 512
m_PVRBounces: 2
m_PVREnvironmentSampleCount: 256
m_PVREnvironmentReferencePointCount: 2048
m_PVRFilteringMode: 1
m_PVRDenoiserTypeDirect: 1
m_PVRDenoiserTypeIndirect: 1
m_PVRDenoiserTypeAO: 1
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVREnvironmentMIS: 1
m_PVRCulling: 1
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_LightProbeSampleCountMultiplier: 4
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
m_LightingSettings: {fileID: 0}
--- !u!196 &4
NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
serializedVersion: 3
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
agentSlope: 45
agentClimb: 0.4
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
minRegionArea: 2
manualCellSize: 0
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
buildHeightMesh: 0
maxJobWorkers: 0
preserveTilesOutsideBounds: 0
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &121227139
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 121227141}
- component: {fileID: 121227140}
m_Layer: 0
m_Name: NetConnectionUI
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &121227140
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 121227139}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 977a7574cf6ff4898b7e9db6c21368f9, type: 3}
m_Name:
m_EditorClassIdentifier: ProjectM.Client::ProjectM.Client.ConnectionUI
_port: 7979
--- !u!4 &121227141
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 121227139}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
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!1 &330585543
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 330585546}
- component: {fileID: 330585545}
- component: {fileID: 330585544}
- component: {fileID: 330585547}
- component: {fileID: 330585548}
m_Layer: 0
m_Name: Main Camera
m_TagString: MainCamera
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!81 &330585544
AudioListener:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 330585543}
m_Enabled: 1
--- !u!20 &330585545
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 330585543}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 1
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_Iso: 200
m_ShutterSpeed: 0.005
m_Aperture: 16
m_FocusDistance: 10
m_FocalLength: 50
m_BladeCount: 5
m_Curvature: {x: 2, y: 11}
m_BarrelClipping: 0.25
m_Anamorphism: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 0.3
far clip plane: 1000
field of view: 60
orthographic: 0
orthographic size: 5
m_Depth: -1
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingPath: -1
m_TargetTexture: {fileID: 0}
m_TargetDisplay: 0
m_TargetEye: 3
m_HDR: 1
m_AllowMSAA: 1
m_AllowDynamicResolution: 0
m_ForceIntoRT: 0
m_OcclusionCulling: 1
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!4 &330585546
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 330585543}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 1, z: -10}
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!114 &330585547
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 330585543}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
m_Name:
m_EditorClassIdentifier:
m_RenderShadows: 1
m_RequiresDepthTextureOption: 2
m_RequiresOpaqueTextureOption: 2
m_CameraType: 0
m_Cameras: []
m_RendererIndex: -1
m_VolumeLayerMask:
serializedVersion: 2
m_Bits: 1
m_VolumeTrigger: {fileID: 0}
m_VolumeFrameworkUpdateModeOption: 2
m_RenderPostProcessing: 1
m_Antialiasing: 0
m_AntialiasingQuality: 2
m_StopNaN: 0
m_Dithering: 0
m_ClearDepth: 1
m_AllowXRRendering: 1
m_AllowHDROutput: 1
m_UseScreenCoordOverride: 0
m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0}
m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0}
m_RequiresDepthTexture: 0
m_RequiresColorTexture: 0
m_TaaSettings:
m_Quality: 3
m_FrameInfluence: 0.1
m_JitterScale: 1
m_MipBias: 0
m_VarianceClampScale: 0.9
m_ContrastAdaptiveSharpening: 0
m_Version: 2
--- !u!114 &330585548
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 330585543}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3e5890693b64a429789bf3edfae0a6ff, type: 3}
m_Name:
m_EditorClassIdentifier: ProjectM.Client::ProjectM.Client.PrototypeCameraRig
Pitch: 45
Yaw: 0
Distance: 16
TargetHeight: 1
Orthographic: 0
FieldOfView: 55
OrthoSize: 10
FollowSharpness: 8
FallbackTarget: {x: 3, y: 0, z: 4}
--- !u!1 &410087039
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 410087041}
- component: {fileID: 410087040}
- component: {fileID: 410087042}
m_Layer: 0
m_Name: Directional Light
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!108 &410087040
Light:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 410087039}
m_Enabled: 1
serializedVersion: 13
m_Type: 1
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_Intensity: 2
m_Range: 10
m_SpotAngle: 30
m_InnerSpotAngle: 21.80208
m_CookieSize2D: {x: 10, y: 10}
m_Shadows:
m_Type: 2
m_Resolution: -1
m_CustomResolution: -1
m_Strength: 1
m_Bias: 0.05
m_NormalBias: 0.4
m_NearPlane: 0.2
m_CullingMatrixOverride:
e00: 1
e01: 0
e02: 0
e03: 0
e10: 0
e11: 1
e12: 0
e13: 0
e20: 0
e21: 0
e22: 1
e23: 0
e30: 0
e31: 0
e32: 0
e33: 1
m_UseCullingMatrixOverride: 0
m_Cookie: {fileID: 0}
m_DrawHalo: 0
m_Flare: {fileID: 0}
m_RenderMode: 0
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingLayerMask: 1
m_Lightmapping: 4
m_LightShadowCasterMode: 0
m_AreaSize: {x: 1, y: 1}
m_BounceIntensity: 1
m_ColorTemperature: 5000
m_UseColorTemperature: 1
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
m_UseBoundingSphereOverride: 0
m_UseViewFrustumForShadowCasterCull: 1
m_ForceVisible: 0
m_ShapeRadius: 0
m_ShadowAngle: 0
m_LightUnit: 1
m_LuxAtDistance: 1
m_EnableSpotReflector: 1
--- !u!4 &410087041
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 410087039}
serializedVersion: 2
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
m_LocalPosition: {x: 0, y: 3, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
--- !u!114 &410087042
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 410087039}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UsePipelineSettings: 1
m_AdditionalLightsShadowResolutionTier: 2
m_CustomShadowLayers: 0
m_LightCookieSize: {x: 1, y: 1}
m_LightCookieOffset: {x: 0, y: 0}
m_SoftShadowQuality: 1
m_RenderingLayersMask:
serializedVersion: 0
m_Bits: 1
m_ShadowRenderingLayersMask:
serializedVersion: 0
m_Bits: 1
m_Version: 4
m_LightLayerMask: 1
m_ShadowLayerMask: 1
m_RenderingLayers: 1
m_ShadowRenderingLayers: 1
--- !u!1 &832575517
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 832575519}
- component: {fileID: 832575518}
m_Layer: 0
m_Name: Global Volume
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &832575518
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 832575517}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 172515602e62fb746b5d573b38a5fe58, type: 3}
m_Name:
m_EditorClassIdentifier:
m_IsGlobal: 1
priority: 0
blendDistance: 0
weight: 1
sharedProfile: {fileID: 11400000, guid: 10fc4df2da32a41aaa32d77bc913491c, type: 2}
--- !u!4 &832575519
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 832575517}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
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!1 &833091043
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 833091047}
- component: {fileID: 833091046}
- component: {fileID: 833091045}
- component: {fileID: 833091044}
m_Layer: 0
m_Name: Ground
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!23 &833091044
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 833091043}
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: f5ef5fb55f211414595517e5ed7857b9, 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: 1
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!64 &833091045
MeshCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 833091043}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 5
m_Convex: 0
m_CookingOptions: 30
m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
--- !u!33 &833091046
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 833091043}
m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
--- !u!4 &833091047
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 833091043}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 3, y: 0, z: 4}
m_LocalScale: {x: 3, y: 1, z: 3}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1314640898
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1314640900}
- component: {fileID: 1314640899}
m_Layer: 0
m_Name: GameplaySubScene
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1314640899
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1314640898}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 45a335734b1572644a6a5d09d87adc65, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.Scenes::Unity.Scenes.SubScene
_SceneAsset: {fileID: 102900000, guid: 9dc8ce2e486074792932d618c976e312, type: 3}
_HierarchyColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
AutoLoadScene: 1
_SceneGUID:
Value:
x: 3807153369
y: 2538014340
z: 2171413394
w: 557737884
--- !u!4 &1314640900
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1314640898}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
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!1660057539 &9223372036854775807
SceneRoots:
m_ObjectHideFlags: 0
m_Roots:
- {fileID: 330585546}
- {fileID: 410087041}
- {fileID: 832575519}
- {fileID: 1314640900}
- {fileID: 833091047}
- {fileID: 121227141}
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 071c9bf7daa2eb64fb15fde7c69bbe4b
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: