mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-15 13:04:36 +00:00
feat: add diagonal ascend/descend moves, fix pitch, smooth look angles
- Add MoveDiagonalAscend and MoveDiagonalDescend for "corner" moves: step diagonally around a wall edge while ascending/descending 1 block. Requires at least one intermediate cardinal direction to be passable. - Fix pitch calculation: look toward target's eye level (same height delta as feet delta) instead of subtracting eye height, which caused the player to stare at the ground during flat walks. - Add Yaw/Pitch smoothing via SmoothYaw/SmoothPitch in TemplateHelper. Max 35 deg/tick for yaw, 25 deg/tick for pitch. Prevents instant camera snaps between path segments while still being responsive enough for sprint-jumps and tight maneuvers. - Apply smoothing to all five action templates (Walk, Ascend, Descend, Climb, SprintJump). Made-with: Cursor
This commit is contained in:
parent
399c8cdc79
commit
285c3000c3
9 changed files with 222 additions and 16 deletions
|
|
@ -44,6 +44,16 @@ namespace MinecraftClient.Pathing.Core
|
|||
moves.Add(new MoveDiagonal(-1, 1));
|
||||
moves.Add(new MoveDiagonal(-1, -1));
|
||||
|
||||
// Diagonal ascend/descend: corner jumps and drops
|
||||
foreach (int dx in offsets)
|
||||
{
|
||||
foreach (int dz in offsets)
|
||||
{
|
||||
moves.Add(new MoveDiagonalAscend(dx, dz));
|
||||
moves.Add(new MoveDiagonalDescend(dx, dz));
|
||||
}
|
||||
}
|
||||
|
||||
moves.Add(new MoveClimb(true));
|
||||
moves.Add(new MoveClimb(false));
|
||||
|
||||
|
|
|
|||
|
|
@ -33,8 +33,10 @@ namespace MinecraftClient.Pathing.Execution.Templates
|
|||
double dy = ExpectedEnd.Y - pos.Y;
|
||||
double horizDistSq = dx * dx + dz * dz;
|
||||
|
||||
physics.Yaw = TemplateHelper.CalculateYaw(dx, dz);
|
||||
physics.Pitch = TemplateHelper.CalculatePitch(dx, dy - 1.62, dz);
|
||||
float targetYaw = TemplateHelper.CalculateYaw(dx, dz);
|
||||
float targetPitch = TemplateHelper.CalculatePitch(dx, dy, dz);
|
||||
physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
|
||||
physics.Pitch = TemplateHelper.SmoothPitch(physics.Pitch, targetPitch);
|
||||
input.Forward = true;
|
||||
input.Sprint = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ namespace MinecraftClient.Pathing.Execution.Templates
|
|||
if (_tickCount > 120)
|
||||
return TemplateState.Failed;
|
||||
|
||||
physics.Pitch = _goingUp ? -70f : 70f;
|
||||
float targetPitch = _goingUp ? -70f : 70f;
|
||||
physics.Pitch = TemplateHelper.SmoothPitch(physics.Pitch, targetPitch);
|
||||
|
||||
if (physics.OnClimbable)
|
||||
{
|
||||
|
|
@ -48,7 +49,10 @@ namespace MinecraftClient.Pathing.Execution.Templates
|
|||
input.Jump = true;
|
||||
input.Forward = true;
|
||||
if (horizDistSq > 0.01)
|
||||
physics.Yaw = TemplateHelper.CalculateYaw(dx, dz);
|
||||
{
|
||||
float targetYaw = TemplateHelper.CalculateYaw(dx, dz);
|
||||
physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -58,7 +62,8 @@ namespace MinecraftClient.Pathing.Execution.Templates
|
|||
// Keep centered horizontally by gently steering if drifting.
|
||||
if (horizDistSq > 0.15)
|
||||
{
|
||||
physics.Yaw = TemplateHelper.CalculateYaw(dx, dz);
|
||||
float targetYaw = TemplateHelper.CalculateYaw(dx, dz);
|
||||
physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
|
||||
input.Forward = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -67,7 +72,8 @@ namespace MinecraftClient.Pathing.Execution.Templates
|
|||
{
|
||||
if (horizDistSq > 0.01)
|
||||
{
|
||||
physics.Yaw = TemplateHelper.CalculateYaw(dx, dz);
|
||||
float targetYaw = TemplateHelper.CalculateYaw(dx, dz);
|
||||
physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
|
||||
input.Forward = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,19 +54,21 @@ namespace MinecraftClient.Pathing.Execution.Templates
|
|||
if (_tickCount > 200)
|
||||
return TemplateState.Failed;
|
||||
|
||||
physics.Pitch = TemplateHelper.CalculatePitch(dx, dy - 1.62, dz);
|
||||
float targetYaw = TemplateHelper.CalculateYaw(dx, dz);
|
||||
float targetPitch = TemplateHelper.CalculatePitch(dx, dy, dz);
|
||||
physics.Pitch = TemplateHelper.SmoothPitch(physics.Pitch, targetPitch);
|
||||
|
||||
if (physics.OnClimbable)
|
||||
{
|
||||
if (horizDistSq > 0.25)
|
||||
{
|
||||
physics.Yaw = TemplateHelper.CalculateYaw(dx, dz);
|
||||
physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
|
||||
input.Forward = true;
|
||||
}
|
||||
}
|
||||
else if (horizDistSq > 0.01)
|
||||
{
|
||||
physics.Yaw = TemplateHelper.CalculateYaw(dx, dz);
|
||||
physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
|
||||
input.Forward = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,8 +39,10 @@ namespace MinecraftClient.Pathing.Execution.Templates
|
|||
double dy = ExpectedEnd.Y - pos.Y;
|
||||
double horizDistSq = dx * dx + dz * dz;
|
||||
|
||||
physics.Yaw = TemplateHelper.CalculateYaw(dx, dz);
|
||||
physics.Pitch = TemplateHelper.CalculatePitch(dx, dy - 1.62, dz);
|
||||
float targetYaw = TemplateHelper.CalculateYaw(dx, dz);
|
||||
float targetPitch = TemplateHelper.CalculatePitch(dx, dy, dz);
|
||||
physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
|
||||
physics.Pitch = TemplateHelper.SmoothPitch(physics.Pitch, targetPitch);
|
||||
input.Forward = true;
|
||||
input.Sprint = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ namespace MinecraftClient.Pathing.Execution.Templates
|
|||
{
|
||||
internal static class TemplateHelper
|
||||
{
|
||||
private const double EyeHeight = 1.62;
|
||||
private const float MaxYawStepPerTick = 35f;
|
||||
private const float MaxPitchStepPerTick = 25f;
|
||||
|
||||
internal static float CalculateYaw(double dx, double dz)
|
||||
{
|
||||
float yaw = (float)(-Math.Atan2(dx, dz) / Math.PI * 180.0);
|
||||
|
|
@ -13,17 +17,49 @@ namespace MinecraftClient.Pathing.Execution.Templates
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the pitch angle (in degrees) to look toward a 3D offset.
|
||||
/// Negative = look up, positive = look down. Clamped to [-90, 90].
|
||||
/// The dy is relative to eye height (~1.62 blocks above feet).
|
||||
/// Calculate the pitch angle to look from current eye position toward
|
||||
/// the target's feet-level Y. dy = targetFeetY - playerFeetY.
|
||||
/// </summary>
|
||||
internal static float CalculatePitch(double dx, double dy, double dz)
|
||||
{
|
||||
double horizDist = Math.Sqrt(dx * dx + dz * dz);
|
||||
// Look toward the target's eye level, not feet.
|
||||
// Both player and target are at feet+EyeHeight, so the vertical
|
||||
// difference is just dy (target feet Y - player feet Y).
|
||||
float pitch = (float)(-Math.Atan2(dy, horizDist) / Math.PI * 180.0);
|
||||
return Math.Clamp(pitch, -90f, 90f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Smoothly interpolate yaw toward a target, respecting wrap-around at 0/360.
|
||||
/// </summary>
|
||||
internal static float SmoothYaw(float current, float target, float maxStep = MaxYawStepPerTick)
|
||||
{
|
||||
float delta = target - current;
|
||||
// Normalize to [-180, 180]
|
||||
while (delta > 180f) delta -= 360f;
|
||||
while (delta < -180f) delta += 360f;
|
||||
|
||||
if (Math.Abs(delta) <= maxStep)
|
||||
return target;
|
||||
|
||||
float result = current + Math.Sign(delta) * maxStep;
|
||||
if (result < 0) result += 360f;
|
||||
if (result >= 360f) result -= 360f;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Smoothly interpolate pitch toward a target.
|
||||
/// </summary>
|
||||
internal static float SmoothPitch(float current, float target, float maxStep = MaxPitchStepPerTick)
|
||||
{
|
||||
float delta = target - current;
|
||||
if (Math.Abs(delta) <= maxStep)
|
||||
return target;
|
||||
return current + Math.Sign(delta) * maxStep;
|
||||
}
|
||||
|
||||
internal static double HorizontalDistanceSq(Location a, Location b)
|
||||
{
|
||||
double dx = a.X - b.X;
|
||||
|
|
|
|||
|
|
@ -31,8 +31,10 @@ namespace MinecraftClient.Pathing.Execution.Templates
|
|||
double dx = ExpectedEnd.X - pos.X;
|
||||
double dz = ExpectedEnd.Z - pos.Z;
|
||||
double dy = ExpectedEnd.Y - pos.Y;
|
||||
physics.Yaw = TemplateHelper.CalculateYaw(dx, dz);
|
||||
physics.Pitch = TemplateHelper.CalculatePitch(dx, dy - 1.62, dz);
|
||||
float targetYaw = TemplateHelper.CalculateYaw(dx, dz);
|
||||
float targetPitch = TemplateHelper.CalculatePitch(dx, dy, dz);
|
||||
physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
|
||||
physics.Pitch = TemplateHelper.SmoothPitch(physics.Pitch, targetPitch);
|
||||
input.Forward = true;
|
||||
input.Sprint = true;
|
||||
|
||||
|
|
|
|||
69
MinecraftClient/Pathing/Moves/Impl/MoveDiagonalAscend.cs
Normal file
69
MinecraftClient/Pathing/Moves/Impl/MoveDiagonalAscend.cs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
using MinecraftClient.Pathing.Core;
|
||||
|
||||
namespace MinecraftClient.Pathing.Moves.Impl
|
||||
{
|
||||
/// <summary>
|
||||
/// Jump diagonally (1 block in X and Z) and land 1 block higher.
|
||||
/// Handles the "corner jump" pattern: jump around a wall edge and land
|
||||
/// one block higher on a platform that is diagonally adjacent.
|
||||
/// </summary>
|
||||
public sealed class MoveDiagonalAscend : IMove
|
||||
{
|
||||
public MoveType Type => MoveType.Ascend;
|
||||
public int XOffset { get; }
|
||||
public int ZOffset { get; }
|
||||
public bool DynamicY => false;
|
||||
|
||||
public MoveDiagonalAscend(int xOffset, int zOffset)
|
||||
{
|
||||
XOffset = xOffset;
|
||||
ZOffset = zOffset;
|
||||
}
|
||||
|
||||
public void Calculate(CalculationContext ctx, int x, int y, int z, ref MoveResult result)
|
||||
{
|
||||
int destX = x + XOffset;
|
||||
int destZ = z + ZOffset;
|
||||
int destY = y + 1;
|
||||
|
||||
// Need headroom to jump (y+2 at start)
|
||||
if (!ctx.CanWalkThrough(x, y + 2, z))
|
||||
{
|
||||
result.SetImpossible();
|
||||
return;
|
||||
}
|
||||
|
||||
// Destination: solid ground, body passable, head passable
|
||||
if (!ctx.CanWalkOn(destX, y, destZ))
|
||||
{
|
||||
result.SetImpossible();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ctx.CanWalkThrough(destX, destY, destZ) ||
|
||||
!ctx.CanWalkThrough(destX, destY + 1, destZ))
|
||||
{
|
||||
result.SetImpossible();
|
||||
return;
|
||||
}
|
||||
|
||||
// At least one of the two intermediate cardinal directions must be passable
|
||||
// at both the current and destination height (player sweeps through).
|
||||
bool pathViaX = ctx.CanWalkThrough(x + XOffset, y, z) &&
|
||||
ctx.CanWalkThrough(x + XOffset, y + 1, z) &&
|
||||
ctx.CanWalkThrough(x + XOffset, y + 2, z);
|
||||
bool pathViaZ = ctx.CanWalkThrough(x, y, z + ZOffset) &&
|
||||
ctx.CanWalkThrough(x, y + 1, z + ZOffset) &&
|
||||
ctx.CanWalkThrough(x, y + 2, z + ZOffset);
|
||||
|
||||
if (!pathViaX && !pathViaZ)
|
||||
{
|
||||
result.SetImpossible();
|
||||
return;
|
||||
}
|
||||
|
||||
double cost = ctx.SprintCost * ActionCosts.DiagonalMultiplier + ctx.JumpPenalty;
|
||||
result.Set(destX, destY, destZ, cost);
|
||||
}
|
||||
}
|
||||
}
|
||||
77
MinecraftClient/Pathing/Moves/Impl/MoveDiagonalDescend.cs
Normal file
77
MinecraftClient/Pathing/Moves/Impl/MoveDiagonalDescend.cs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
using MinecraftClient.Mapping;
|
||||
using MinecraftClient.Pathing.Core;
|
||||
|
||||
namespace MinecraftClient.Pathing.Moves.Impl
|
||||
{
|
||||
/// <summary>
|
||||
/// Walk diagonally (1 block in X and Z) and drop 1 block.
|
||||
/// Handles the "corner drop" pattern: step around a wall edge and land
|
||||
/// one block lower on a platform that is diagonally adjacent.
|
||||
/// </summary>
|
||||
public sealed class MoveDiagonalDescend : IMove
|
||||
{
|
||||
public MoveType Type => MoveType.Descend;
|
||||
public int XOffset { get; }
|
||||
public int ZOffset { get; }
|
||||
public bool DynamicY => false;
|
||||
|
||||
public MoveDiagonalDescend(int xOffset, int zOffset)
|
||||
{
|
||||
XOffset = xOffset;
|
||||
ZOffset = zOffset;
|
||||
}
|
||||
|
||||
public void Calculate(CalculationContext ctx, int x, int y, int z, ref MoveResult result)
|
||||
{
|
||||
int destX = x + XOffset;
|
||||
int destZ = z + ZOffset;
|
||||
int destY = y - 1;
|
||||
|
||||
// Destination must have ground, body space, and head space
|
||||
if (!ctx.CanWalkOn(destX, destY - 1, destZ))
|
||||
{
|
||||
result.SetImpossible();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ctx.CanWalkThrough(destX, destY, destZ) ||
|
||||
!ctx.CanWalkThrough(destX, destY + 1, destZ))
|
||||
{
|
||||
result.SetImpossible();
|
||||
return;
|
||||
}
|
||||
|
||||
Material landOn = ctx.GetMaterial(destX, destY - 1, destZ);
|
||||
if (MoveHelper.IsHazardous(landOn))
|
||||
{
|
||||
result.SetImpossible();
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't descend from climbable blocks
|
||||
Material fromDown = ctx.GetMaterial(x, y - 1, z);
|
||||
if (fromDown.CanBeClimbedOn())
|
||||
{
|
||||
result.SetImpossible();
|
||||
return;
|
||||
}
|
||||
|
||||
// At least one of the two intermediate cardinal directions must be passable
|
||||
// (player needs clearance to cut the corner).
|
||||
bool pathViaX = ctx.CanWalkThrough(x + XOffset, y, z) &&
|
||||
ctx.CanWalkThrough(x + XOffset, y + 1, z);
|
||||
bool pathViaZ = ctx.CanWalkThrough(x, y, z + ZOffset) &&
|
||||
ctx.CanWalkThrough(x, y + 1, z + ZOffset);
|
||||
|
||||
if (!pathViaX && !pathViaZ)
|
||||
{
|
||||
result.SetImpossible();
|
||||
return;
|
||||
}
|
||||
|
||||
double cost = ActionCosts.WalkOffBlock * ActionCosts.DiagonalMultiplier
|
||||
+ ActionCosts.FallCost(1);
|
||||
result.Set(destX, destY, destZ, cost);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue