feat: expose aim smooth as 0-100
This commit is contained in:
parent
fcb1f599a4
commit
6fc8d8c16b
|
|
@ -36,6 +36,38 @@ namespace
|
|||
return a;
|
||||
}
|
||||
|
||||
float Clamp01(float value)
|
||||
{
|
||||
if (value < 0.f) return 0.f;
|
||||
if (value > 1.f) return 1.f;
|
||||
return value;
|
||||
}
|
||||
|
||||
float FrameScale()
|
||||
{
|
||||
float deltaTime = ImGui::GetIO().DeltaTime;
|
||||
if (!(deltaTime > 0.f) || deltaTime > 0.2f)
|
||||
deltaTime = 1.f / 60.f;
|
||||
|
||||
float scale = deltaTime * 60.f;
|
||||
if (scale < 0.5f) scale = 0.5f;
|
||||
if (scale > 2.f) scale = 2.f;
|
||||
return scale;
|
||||
}
|
||||
|
||||
// Distance-adaptive response:
|
||||
// far target -> catch up; near target -> settle without a large final snap.
|
||||
float AimResponse(float error, float smooth, float referenceError)
|
||||
{
|
||||
smooth = Clamp01(smooth * 0.01f);
|
||||
const float nearResponse = 0.06f + (1.f - smooth) * 0.10f;
|
||||
const float farResponse = 0.28f + (1.f - smooth) * 0.26f;
|
||||
const float weight = Clamp01(error / (referenceError > 0.001f ? referenceError : 1.f));
|
||||
const float perFrame = nearResponse + (farResponse - nearResponse) * weight;
|
||||
|
||||
return 1.f - powf(1.f - perFrame, FrameScale());
|
||||
}
|
||||
|
||||
// IsKeyDown は Framework/Input/Input.h のグローバル関数を使用
|
||||
// (GetAsyncKeyState はマウスボタンを検知できない環境があるため、WH_MOUSE_LL フックで捕捉)
|
||||
|
||||
|
|
@ -80,7 +112,12 @@ void CFramework::AimAll()
|
|||
|
||||
// ---- 自瞄 ----
|
||||
if (!g.g_Aim)
|
||||
{
|
||||
m_aimLastTarget = 0;
|
||||
m_aimResidualX = 0.f;
|
||||
m_aimResidualY = 0.f;
|
||||
return;
|
||||
}
|
||||
|
||||
// FOV 円はキーを押していなくても表示
|
||||
Vector2 Center = Vector2(GameSize.x * 0.5f, GameSize.y * 0.5f);
|
||||
|
|
@ -91,7 +128,12 @@ void CFramework::AimAll()
|
|||
// ※ GetAsyncKeyState / 低级钩子 在本环境(管理员+RawInput独占)都检测不到按键,
|
||||
// g_AimForce 是绕过按键检测的替代方案
|
||||
if (!g.g_AimForce && !IsKeyDown(g.g_AimKey0) && !IsKeyDown(g.g_AimKey1) && !IsKeyDown(g.g_AimKey2))
|
||||
{
|
||||
m_aimLastTarget = 0;
|
||||
m_aimResidualX = 0.f;
|
||||
m_aimResidualY = 0.f;
|
||||
return;
|
||||
}
|
||||
|
||||
// カメラ位置 (ローカル頭部を優先、無ければ origin)
|
||||
local.UpdateBone();
|
||||
|
|
@ -179,13 +221,31 @@ void CFramework::AimAll()
|
|||
m_diagBestDist = bestDist;
|
||||
|
||||
if (bestAddr == 0)
|
||||
{
|
||||
m_aimLastTarget = 0;
|
||||
m_aimResidualX = 0.f;
|
||||
m_aimResidualY = 0.f;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bestAddr != m_aimLastTarget)
|
||||
{
|
||||
m_aimLastTarget = bestAddr;
|
||||
m_aimResidualX = 0.f;
|
||||
m_aimResidualY = 0.f;
|
||||
}
|
||||
|
||||
// 2. 照準適用
|
||||
if (g.g_AimMethod == 0)
|
||||
{
|
||||
m_aimResidualX = 0.f;
|
||||
m_aimResidualY = 0.f;
|
||||
AimMemory(bestPoint, localPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
AimMouse(bestScreen, Center);
|
||||
}
|
||||
|
||||
// 3. ターゲット線
|
||||
if (g.g_AimDrawTarget)
|
||||
|
|
@ -268,32 +328,54 @@ Vector3 CFramework::GetAimBone(CPlayer* pEntity)
|
|||
// Mouse モード: 画面中心とターゲットの差分をマウス移動量に変換
|
||||
void CFramework::AimMouse(const Vector2& target, const Vector2& center)
|
||||
{
|
||||
float dx = target.x - center.x;
|
||||
float dy = target.y - center.y;
|
||||
const float dx = target.x - center.x;
|
||||
const float dy = target.y - center.y;
|
||||
const float dist = sqrtf(dx * dx + dy * dy);
|
||||
|
||||
float dist = sqrtf(dx * dx + dy * dy);
|
||||
if (dist < 1.f) // 既に当たっている
|
||||
if (dist < 0.5f)
|
||||
{
|
||||
m_aimResidualX = 0.f;
|
||||
m_aimResidualY = 0.f;
|
||||
return;
|
||||
}
|
||||
|
||||
// smooth: 0=即時追従, 1=ゆっくり追従
|
||||
float scale = g.g_AimSens * (1.f - g.g_AimSmooth);
|
||||
float mx = dx * scale;
|
||||
float my = dy * scale;
|
||||
const float referenceError = g.g_AimFov > 32.f ? g.g_AimFov * 0.5f : 32.f;
|
||||
const float response = AimResponse(dist, g.g_AimSmooth, referenceError);
|
||||
|
||||
// 1フレームの最大移動量でクランプ (なめらか + 暴走防止)
|
||||
float maxSpeed = g.g_AimMaxSpeed;
|
||||
float len = sqrtf(mx * mx + my * my);
|
||||
float sensitivity = g.g_AimSens;
|
||||
if (sensitivity < 0.1f) sensitivity = 0.1f;
|
||||
if (sensitivity > 5.f) sensitivity = 5.f;
|
||||
|
||||
// Never allow the smoothing slider to turn a small error into a one-frame snap.
|
||||
float gain = response * sensitivity;
|
||||
if (gain > 0.85f) gain = 0.85f;
|
||||
|
||||
float mx = dx * gain;
|
||||
float my = dy * gain;
|
||||
|
||||
// MaxSpeed is expressed at 60 FPS; keep the behavior stable across frame rates.
|
||||
float maxSpeed = g.g_AimMaxSpeed * FrameScale();
|
||||
if (maxSpeed < 1.f) maxSpeed = 1.f;
|
||||
|
||||
const float len = sqrtf(mx * mx + my * my);
|
||||
if (len > maxSpeed)
|
||||
{
|
||||
mx = mx / len * maxSpeed;
|
||||
my = my / len * maxSpeed;
|
||||
}
|
||||
|
||||
// デッドゾーン (細かいジッター防止)
|
||||
if (fabsf(mx) < 0.5f && fabsf(my) < 0.5f)
|
||||
// Preserve fractional movement instead of truncating every frame to LONG.
|
||||
const float moveX = mx + m_aimResidualX;
|
||||
const float moveY = my + m_aimResidualY;
|
||||
const LONG sendX = static_cast<LONG>(lroundf(moveX));
|
||||
const LONG sendY = static_cast<LONG>(lroundf(moveY));
|
||||
m_aimResidualX = moveX - static_cast<float>(sendX);
|
||||
m_aimResidualY = moveY - static_cast<float>(sendY);
|
||||
|
||||
if (sendX == 0 && sendY == 0)
|
||||
return;
|
||||
|
||||
SendMouseMove((LONG)mx, (LONG)my);
|
||||
SendMouseMove(sendX, sendY);
|
||||
}
|
||||
|
||||
// Memory モード: MovementContext._rotation (Vector2 x=yaw y=pitch) へ書く
|
||||
|
|
@ -319,13 +401,12 @@ void CFramework::AimMemory(const Vector3& target, const Vector3& localPos)
|
|||
yaw = ClampAngle(yaw);
|
||||
pitch = ClampAngle(pitch);
|
||||
|
||||
// 平滑化: 現在値 → 目標値へ補間 (yaw は短い方へ回す)
|
||||
float t = 1.f - g.g_AimSmooth;
|
||||
if (t <= 0.f)
|
||||
return;
|
||||
|
||||
// Distance-adaptive exponential smoothing. It is never 0 or 1, so the
|
||||
// slider cannot produce either a one-frame snap or a permanently lagging aim.
|
||||
float dyaw = ClampAngle(yaw - rot.x);
|
||||
float dpitch = ClampAngle(pitch - rot.y);
|
||||
float angleError = sqrtf(dyaw * dyaw + dpitch * dpitch);
|
||||
float t = AimResponse(angleError, g.g_AimSmooth, 0.75f);
|
||||
|
||||
Vector2 newRot{
|
||||
ClampAngle(rot.x + dyaw * t),
|
||||
|
|
|
|||
|
|
@ -92,6 +92,11 @@ private:
|
|||
uintptr_t m_diagBestAddr = 0; // 最近一帧锁定的目标地址
|
||||
float m_diagBestDist = 0.f;// 最近一帧目标到屏幕中心的距离
|
||||
|
||||
// 鼠标自瞄的亚像素累积,避免小误差因 LONG 截断而永远到不了目标。
|
||||
uintptr_t m_aimLastTarget = 0;
|
||||
float m_aimResidualX = 0.f;
|
||||
float m_aimResidualY = 0.f;
|
||||
|
||||
void DrawLine(ImVec2 a, ImVec2 b, ImColor color, float width)
|
||||
{
|
||||
ImGui::GetBackgroundDrawList()->AddLine(ImVec2((int)a.x, (int)a.y), ImVec2((int)b.x, (int)b.y), color, width);
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ void CFramework::RenderMenu()
|
|||
ImGui::Checkbox("启用", &g.g_Aim);
|
||||
ImGui::Combo("方式", &g.g_AimMethod, AimMethodList, IM_ARRAYSIZE(AimMethodList));
|
||||
ImGui::CustomSliderFloat("锁定范围", "##AimFov", &g.g_AimFov, 10.f, 500.f);
|
||||
ImGui::CustomSliderFloat("平滑", "##AimSmooth", &g.g_AimSmooth, 0.f, 1.f);
|
||||
ImGui::CustomSliderFloat("平滑(0-100)", "##AimSmooth", &g.g_AimSmooth, 0.f, 100.f);
|
||||
ImGui::CustomSliderFloat("最大距离", "##AimDist", &g.g_AimMaxDist, 50.f, 2000.f);
|
||||
ImGui::Combo("瞄准部位", &g.g_AimBone, AimBoneList, IM_ARRAYSIZE(AimBoneList));
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ struct Globals
|
|||
bool g_Aim = false;
|
||||
int g_AimMethod = 1; // 0 = Memory(写旋转), 1 = Mouse(SendInput)
|
||||
float g_AimFov = 150.f; // FOV 半径 [px]
|
||||
float g_AimSmooth = 0.45f; // 0=瞬移 1=最慢
|
||||
float g_AimSmooth = 45.f; // 0-100: 自适应平滑,0=快但有限幅,100=慢但不会瞬移
|
||||
float g_AimSens = 1.0f; // Mouse 模式灵敏度倍率
|
||||
float g_AimMaxSpeed = 80.f; // Mouse 模式单帧最大移动 [px]
|
||||
float g_AimMaxDist = 500.f; // 最大锁定距离 [m]
|
||||
|
|
|
|||
Loading…
Reference in New Issue