Further Tests & Progress

This commit is contained in:
2026-06-04 11:35:57 -07:00
parent 5c11ff4fad
commit 51401d2c2b
65 changed files with 2784 additions and 45 deletions
@@ -38,7 +38,10 @@ namespace ProjectM.Client
float3 _lastKbmGroundPoint; // last valid cursor ground point (held when the projection misses)
bool _haveKbmPoint; // true after the first valid KBM ground hit
bool _cursorHidden; // tracks the applied Cursor.visible state (avoid per-frame churn)
bool _cursorTouched; // we changed the OS cursor at least once -> restore on destroy
bool _cursorTouched;
GameObject _tether;
LineRenderer _tetherLine;
Material _tetherMat; // we changed the OS cursor at least once -> restore on destroy
protected override void OnStartRunning()
{
@@ -52,9 +55,12 @@ namespace ProjectM.Client
bool haveTarget = false;
float3 ringPos = default;
float3 lpPos = default;
float2 lpFacing = default;
EntityManager.CompleteDependencyBeforeRO<LocalTransform>();
EntityManager.CompleteDependencyBeforeRO<PlayerFacing>();
EntityManager.CompleteDependencyBeforeRO<Health>();
foreach (var (xform, facing) in
SystemAPI.Query<RefRO<LocalTransform>, RefRO<PlayerFacing>>()
.WithAll<GhostOwnerIsLocal, PlayerTag>())
@@ -87,6 +93,8 @@ namespace ProjectM.Client
}
ringPos.y += ReticleLiftY;
lpPos = playerPos;
lpFacing = facing.ValueRO.Direction;
haveTarget = true;
break;
}
@@ -97,6 +105,43 @@ namespace ProjectM.Client
if (_reticle.activeSelf != haveTarget) _reticle.SetActive(haveTarget);
}
// Lock-on tether (cosmetic aim HINT - the client computes nearest enemy itself; the server's actual
// gamepad auto-target cone may differ, so divergence is acceptable, not a bug).
bool tetherShown = false;
if (_tetherLine != null && haveTarget && FeelConfig.LockOnEnabled
&& (!FeelConfig.LockOnGamepadOnly || scheme == InputSchemeId.Gamepad))
{
float2 fdir = lpFacing;
if (math.lengthsq(fdir) < 1e-6f) fdir = new float2(0f, 1f);
fdir = math.normalize(fdir);
float rangeSq = FeelConfig.LockOnRange * FeelConfig.LockOnRange;
float cone = math.cos(math.radians(FeelConfig.LockOnArcDegrees));
float bestSq = float.MaxValue;
float3 bestPos = default;
bool found = false;
foreach (var (hx, hh) in
SystemAPI.Query<RefRO<LocalTransform>, RefRO<Health>>().WithAll<EnemyTag>())
{
if (hh.ValueRO.Current <= 0f) continue;
float3 hp = hx.ValueRO.Position;
float2 to = hp.xz - lpPos.xz;
float sq = math.lengthsq(to);
if (sq > rangeSq || sq < 1e-6f) continue;
if (math.dot(fdir, math.normalize(to)) < cone) continue;
if (sq < bestSq) { bestSq = sq; bestPos = hp; found = true; }
}
if (found)
{
_tetherLine.startColor = FeelConfig.LockOnLineColor;
_tetherLine.endColor = FeelConfig.LockOnLineColor;
_tetherLine.widthMultiplier = FeelConfig.LockOnLineWidth;
_tetherLine.SetPosition(0, (Vector3)ringPos);
_tetherLine.SetPosition(1, new Vector3(bestPos.x, ringPos.y, bestPos.z));
tetherShown = true;
}
}
if (_tether != null && _tether.activeSelf != tetherShown) _tether.SetActive(tetherShown);
// Hide the OS cursor only while aiming AND focused; restore otherwise (focus loss / pre-spawn) so an
// unfocused editor or a windowed session is never stranded with an invisible pointer.
bool wantHidden = haveTarget && Application.isFocused;
@@ -131,6 +176,8 @@ namespace ProjectM.Client
if (_reticle != null) Object.Destroy(_reticle);
if (_reticleMat != null) Object.Destroy(_reticleMat);
if (_ringTex != null) Object.Destroy(_ringTex);
if (_tether != null) Object.Destroy(_tether);
if (_tetherMat != null) Object.Destroy(_tetherMat);
}
void BuildReticle()
@@ -154,6 +201,25 @@ namespace ProjectM.Client
_reticle.transform.rotation = Quaternion.Euler(90f, 0f, 0f); // lay flat on the ground
_reticle.transform.localScale = new Vector3(ReticleSize, ReticleSize, 1f);
_reticle.SetActive(false);
// Lock-on tether line (persistent; built once, GC-clean). Own material so the ring texture doesn't tint it.
var lineShader = Shader.Find("Sprites/Default");
if (lineShader == null) lineShader = Shader.Find("Universal Render Pipeline/Particles/Unlit");
_tetherMat = new Material(lineShader) { color = Color.white };
_tether = new GameObject("~AimTether");
_tetherLine = _tether.AddComponent<LineRenderer>();
_tetherLine.material = _tetherMat;
_tetherLine.positionCount = 2;
_tetherLine.useWorldSpace = true;
_tetherLine.numCapVertices = 2;
_tetherLine.alignment = LineAlignment.View;
_tetherLine.textureMode = LineTextureMode.Stretch;
_tetherLine.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
_tetherLine.receiveShadows = false;
_tetherLine.startColor = FeelConfig.LockOnLineColor;
_tetherLine.endColor = FeelConfig.LockOnLineColor;
_tetherLine.widthMultiplier = FeelConfig.LockOnLineWidth;
_tether.SetActive(false);
}
// ---- procedural ring texture (asset-free, like HudSystem's code-built uGUI) ----