- Статус
- Оффлайн
- Регистрация
- 13 Фев 2026
- Сообщения
- 658
- Реакции
- 18
Надоело таскать за собой огромные хедеры с байтами иконок?
Обычно народ не парится и просто пихает в сурс здоровенные массивы данных для каждой иконки агента. Это жирно, неудобно и вообще моветон для нормальных читов. Есть более изящный способ — запрячь сам Unreal Engine.
Логика простая: дергаем GetCharacterIcon через ProcessEvent, получаем объект текстуры и выводим его на канвас. Метод нативный, так что выглядит все аккуратно и не раздувает бинарник.
Как это работает в коде:
Для начала вытягиваем саму функцию из движка и получаем UObject иконки:
Когда текстура у нас на руках, отрисовываем её через K2_DrawTexture. Это требует наличия актуальных K2 оффсетов в вашем SDK.
Технические нюансы:
В плане детекта Vanguard обычно смотрит на аномальные вызовы функций и левые потоки. Если вызовете это из нативного хука рендера самого движка, проблем быть не должно. Главное — следите за актуальностью оффсетов, при обновах сигнатуры функций GetCharacterIcon могут меняться.
Юзайте на здоровье, если лень возиться с внедрением сторонних ассетов под ESP.
Обычно народ не парится и просто пихает в сурс здоровенные массивы данных для каждой иконки агента. Это жирно, неудобно и вообще моветон для нормальных читов. Есть более изящный способ — запрячь сам Unreal Engine.
Логика простая: дергаем GetCharacterIcon через ProcessEvent, получаем объект текстуры и выводим его на канвас. Метод нативный, так что выглядит все аккуратно и не раздувает бинарник.
Код:
if (globals::visuals::esp_agent_icon)
{
const uintptr_t iconObj = AgentIconEsp::TryGetCharacterIconUObject(enemyPawn);
if (iconObj)
(void)AgentIconEsp::DrawOnCanvas(k2Canvas, iconObj, rx, ry);
}
Как это работает в коде:
Для начала вытягиваем саму функцию из движка и получаем UObject иконки:
Код:
uintptr_t TryGetCharacterIconUObject(uintptr_t shooterPawn)
{
if (!shooterPawn)
{
LogThrottled("agenticon_pawn", 4500, "[AgentIconEsp] DEBUG: TryGet pawn=NULL\n");
return 0;
}
if (!g_GetCharacterIconFn)
g_GetCharacterIconFn = uobject::find_object(L"ShooterGame.ShooterCharacter.GetCharacterIcon");
if (!g_GetCharacterIconFn)
{
LogThrottled("agenticon_fn2", 5000,
"[AgentIconEsp] DEBUG: find_object(GetCharacterIcon) -> NULL (nao ha UFunction para PE)\n");
return 0;
}
alignas(16) unsigned char paramBlob[64]{};
std::memset(paramBlob, 0, sizeof(paramBlob));
reinterpret_cast<UObject*>(shooterPawn)->process_event(g_GetCharacterIconFn, paramBlob);
const uintptr_t tex = *reinterpret_cast<uintptr_t*>(paramBlob);
static DWORD s_peLog = 0;
const DWORD t = GetTickCount();
if (t - s_peLog > 3200)
{
s_peLog = t;
const uintptr_t base = Core::GetGameModuleBase();
const bool vtableOk = tex && base && Core::TestRead(tex, sizeof(void*));
Logger::Log(
"[AgentIconEsp] DEBUG: PE GetCharacterIcon pawn=%p fn=%p -> Out=%p (readable vtable guess=%d)\n",
(void*)shooterPawn,
(void*)g_GetCharacterIconFn,
(void*)tex,
vtableOk ? 1 : 0);
}
if (!tex)
LogThrottled("agenticon_out", 4000, "[AgentIconEsp] DEBUG: PE retornou Out=NULL (personagem sem icone / timing)\n");
return tex;
}
Когда текстура у нас на руках, отрисовываем её через K2_DrawTexture. Это требует наличия актуальных K2 оффсетов в вашем SDK.
Код:
bool DrawOnCanvas(void* canvas, uintptr_t textureUObject, float rootScreenX, float rootScreenY)
{
if (!canvas || !textureUObject)
{
LogThrottled("agenticon_draw_arg", 5000,
"[AgentIconEsp] DEBUG: DrawOnCanvas canvas=%p tex=%p (skip)\n", canvas, (void*)textureUObject);
return false;
}
const FLinearColor white{ 1.f, 1.f, 1.f, 1.f };
const FVector2D pos{ rootScreenX - 10.f, rootScreenY + 28.f };
const bool ok = CanvasDraw::K2_DrawTexture(canvas, reinterpret_cast<void*>(textureUObject), pos, FVector2D{ 20.f, 20.f },
FVector2D{ 0.f, 0.f }, FVector2D{ 1.f, 1.f }, white, EBlendMode::Opaque, 0.f, FVector2D{ 0.5f, 0.5f });
static DWORD s_k2Fail = 0;
if (!ok)
{
const DWORD t = GetTickCount();
if (t - s_k2Fail > 4500)
{
s_k2Fail = t;
Logger::Log("[AgentIconEsp] DEBUG: K2_DrawTexture falhou (CanvasDraw resolvido=%d) tex=%p pos=(%.1f,%.1f)\n",
CanvasDraw::IsK2DrawTextureResolved() ? 1 : 0,
(void*)textureUObject,
(double)pos.X,
(double)pos.Y);
}
}
return ok;
}
Технические нюансы:
- Для стабильной работы необходим корректный вызов ProcessEvent. Если ваш интернал криво хукает события, могут быть фризы.
- Метод работает только если вы имеете доступ к инстансу персонажа (shooterPawn) и самому канвасу.
- Это гораздо чище, чем хранить PNG в ресурсах или байт-массивах, так как вы юзаете то, что уже загружено в память игры.
В плане детекта Vanguard обычно смотрит на аномальные вызовы функций и левые потоки. Если вызовете это из нативного хука рендера самого движка, проблем быть не должно. Главное — следите за актуальностью оффсетов, при обновах сигнатуры функций GetCharacterIcon могут меняться.
Юзайте на здоровье, если лень возиться с внедрением сторонних ассетов под ESP.