-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathTeleportPositionCalculationSystem.cs
More file actions
119 lines (94 loc) · 5.37 KB
/
Copy pathTeleportPositionCalculationSystem.cs
File metadata and controls
119 lines (94 loc) · 5.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
using Arch.Core;
using Arch.System;
using Arch.SystemGroups;
using Arch.SystemGroups.DefaultSystemGroups;
using DCL.CharacterCamera;
using DCL.CharacterMotion.Components;
using DCL.Diagnostics;
using DCL.Ipfs;
using ECS.Abstract;
using ECS.SceneLifeCycle;
using ECS.SceneLifeCycle.Realm;
using UnityEngine;
using Utility;
using System;
using System.Collections.Generic;
namespace DCL.Character.CharacterMotion.Systems
{
[UpdateInGroup(typeof(PostRenderingSystemGroup))]
[LogCategory(ReportCategory.MOTION)]
public partial class TeleportPositionCalculationSystem : BaseUnityLoopSystem
{
private readonly ILandscape landscape;
private SingleInstanceEntity? cameraCached;
private SingleInstanceEntity cameraEntity => cameraCached ??= World.CacheCamera();
public TeleportPositionCalculationSystem(World world, ILandscape landscape) : base(world)
{
this.landscape = landscape;
}
protected override void Update(float t)
{
CalculateTeleportPositionQuery(World);
}
[Query]
private void CalculateTeleportPosition(in Entity playerEntity, ref PlayerTeleportIntent teleportIntent)
{
if (teleportIntent.IsPositionSet) return;
SceneEntityDefinition? sceneDef = teleportIntent.SceneDef;
Vector2Int parcel = teleportIntent.Parcel;
if (sceneDef == null)
{
Vector3 targetWorldPosition = ParcelMathHelper.GetPositionByParcelPosition(parcel).WithErrorCompensation();
teleportIntent.Position = targetWorldPosition.WithTerrainOffset(landscape.GetHeight(targetWorldPosition.x, targetWorldPosition.z));
}
else if (TeleportUtils.IsRoad(sceneDef.metadata.OriginalJson.AsSpan())) { teleportIntent.Position = ParcelMathHelper.GetPositionByParcelPosition(parcel).WithErrorCompensation(); }
else if (teleportIntent.LandOnParcel)
{
// Land at the requested parcel itself rather than the scene's spawn point
// (e.g. jumping into an event located at a specific parcel of a multi-parcel scene).
// Aim at the parcel center: its base corner lies on the parcel boundary, where settling
// tips the avatar into the neighbouring parcel. The exact landing XZ is refined later by
// TeleportCharacterSystem, which probes the parcel for its actual walkable floor.
const float HALF_PARCEL_SIZE = ParcelMathHelper.PARCEL_SIZE / 2f;
Vector3 targetWorldPosition = ParcelMathHelper.GetPositionByParcelPosition(parcel)
+ new Vector3(HALF_PARCEL_SIZE, 0f, HALF_PARCEL_SIZE);
// Keep the landing inside the scene's parcels; if it falls outside, ValidateTeleportPosition
// clamps it to the requested parcel's base position.
ValidateTeleportPosition(ref targetWorldPosition, parcel, sceneDef);
// Anchor the height on the scene's spawn point as a best guess: the exact parcel floor
// isn't knowable yet (the scene's colliders load later), so TeleportCharacterSystem
// snaps the avatar onto the floor once the scene is ready.
(Vector3 spawnTarget, _) = TeleportUtils.PickTargetWithOffset(sceneDef, parcel, teleportIntent.SpawnPointName);
targetWorldPosition.y = spawnTarget.y;
teleportIntent.Position = targetWorldPosition;
}
else
{
(Vector3 targetWorldPosition, Vector3? cameraTarget) = TeleportUtils.PickTargetWithOffset(sceneDef, parcel, teleportIntent.SpawnPointName);
var originalTargetPosition = targetWorldPosition;
if (!ValidateTeleportPosition(ref targetWorldPosition, parcel, sceneDef))
ReportHub.LogError(ReportCategory.SCENE_LOADING, $"Invalid teleport position: {originalTargetPosition}. Adjusted to: {targetWorldPosition}");
teleportIntent.Position = targetWorldPosition;
if (cameraTarget != null)
{
World?.AddOrGet(cameraEntity, new CameraLookAtIntent(cameraTarget.Value, targetWorldPosition));
World?.AddOrGet(playerEntity, new PlayerLookAtIntent(cameraTarget.Value, targetWorldPosition));
}
}
teleportIntent.IsPositionSet = true;
// The component needs to be re-applied to the entity to ensure that changes are properly propagated
// within the ECS structure. Without this, other systems may receive an outdated version of the component.
World!.Set(playerEntity, teleportIntent);
}
private bool ValidateTeleportPosition(ref Vector3 targetPosition, Vector2Int targetParcel, SceneEntityDefinition sceneDefinition)
{
IReadOnlyList<Vector2Int> sceneParcels = sceneDefinition.metadata.scene.DecodedParcels;
foreach (var sceneParcel in sceneParcels)
if (ParcelMathHelper.CalculateCorners(sceneParcel).Contains(targetPosition))
return true;
// Invalid position, use the target parcel to compute a valid one
targetPosition = ParcelMathHelper.GetPositionByParcelPosition(targetParcel).WithErrorCompensation();
return false;
}
}
}