DEV Community

Stefhany Santos
Stefhany Santos

Posted on

Como consertei a câmera de um port de PC usando a API do Windows (Unity IL2CPP Modding)

Fala, comunidade! 👋

Recentemente, me deparei com um problema clássico no mundo dos games: um jogo mobile (Heartopia) ganhou um port para PC, mas trouxe consigo todos os controles de toque. O resultado? Para girar a câmera, o jogador precisava segurar o clique esquerdo e arrastar o mouse. Uma experiência de UX terrível para quem joga no teclado e mouse.

Como desenvolvedora, eu não ia simplesmente aceitar isso. Decidi criar um mod para transformar essa câmera em um "Free Look 360º" contínuo, nativo de jogos de PC. O que parecia simples virou um desafio de engenharia reversa e manipulação de SO.

Bem-vindos ao Heartopia PC Camera Fix, o primeiro projeto open-source do meu estúdio indie, Lovel Studio.

🚧 O Desafio: Unity IL2CPP e o Input Manager

O jogo foi compilado em IL2CPP, o que já dificulta bastante a engenharia reversa se comparado ao Mono tradicional. A arquitetura original utilizava o New Unity Input System altamente acoplado para mobile (ScriptsRefactory.BaseService.Input.InputManager).

Tentar interceptar o UnityEngine.Input diretamente via Harmony Hooks estava quebrando o jogo. A solução? Fazer um bypass na engine e lidar diretamente com o Windows.

🛠️ A Solução: C# + user32.dll

Criei um plugin em BepInEx 6 e adotei uma abordagem de "Esteira Virtual" usando a API nativa do Windows.

A arquitetura final do mod (v2.5.0) faz o seguinte:

1. O Paradoxo da Esteira (Virtual Treadmill)
Quando o jogador aperta ALT, eu confino o cursor e simulo um MOUSEEVENTF_RIGHTDOWN. Para permitir o giro infinito (360º), eu leio as coordenadas do monitor via ClientToScreen e teletransporto o cursor (SetCursorPos) de volta para o centro sempre que ele atinge uma margem de 100 pixels. É um loop infinito e invisível de arrasto.

2. Segurança de Interface (WndProc Hook)
O maior bug inicial era: enquanto o jogador girava a câmera livremente, o jogo continuava registrando "cliques" invisíveis na interface, fazendo a personagem atacar do nada.
Para resolver isso, criei um Hook direto no WndProc da janela do jogo:

private IntPtr WndProcHook(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
    // Bloqueia cliques esquerdos apenas quando a câmera Lovel está ativa
    if (isLocked && (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONUP || msg == WM_LBUTTONDBLCLK))
        return IntPtr.Zero;

    return (oldWndProc != IntPtr.Zero) ? CallWindowProc(oldWndProc, hWnd, msg, wParam, lParam) : IntPtr.Zero;
}

Enter fullscreen mode Exit fullscreen mode

Isso literalmente cega o jogo para cliques físicos esquerdos enquanto o modo de câmera está ativo. Um bloqueio limpo no nível do sistema operacional.

🐛 Aonde preciso da ajuda de vocês (Open Source)

Apesar da estabilidade excelente, há um Known Issue: a câmera sofre um leve stuttering ocasional.
Isso acontece porque a Main Thread da Unity às vezes dá gargalos pesados (ex: carregando chunks ou texturas) e o jogo "perde" o frame exato em que a esteira virtual reseta o mouse para o centro, quebrando o movimento por uma fração de segundo.

O projeto está sob Licença MIT no GitHub. Se você manda bem em C#, engenharia reversa ou otimização de Unity, adoraria receber seus Pull Requests! O objetivo é encontrar os offsets corretos para um Hook nativo na classe controladora de câmera via Harmony, eliminando a dependência da user32.dll.

🔗 Acesse o repositório do Heartopia Camera Fix aqui

Se você joga o game, a aba de Releases já tem o .zip pronto no modelo Drag & Drop pra facilitar a vida.

Adoraria saber a opinião de vocês sobre outras abordagens para injetar inputs em jogos IL2CPP. Como vocês resolveriam esse stuttering da Main Thread?

Desenvolvido com muita quebra de cabeça por Stefhany (Lovel Studio) 💜

Top comments (0)