Díky jedinému mechanizmu, který hledá obrázek v obrázku, lze postavit kompletního robota, který se umí rozhlédnout. Díky vzorkům vypěstovaných a vyzrálých plodin pozná, kde se nacházejí hotové plodiny. Stejným principem lze hledat stromy. Také zvířecí domky, ty mají vždy dudlík, jako symbol, že je chov dokončen.
Jenže co dál. Nyní je potřeba najít způsob, jak simulovat práci s myší. To je už na řadě Win32 programování. Delphi nám nativní podporu dají v tom, že máme přeložené hlavičky k API funkcím a že nám umožní volat Windows Messages. Pokud vám zprávy windows moc neříkají, a máte zájem je poznat blíže, doporučuji pročíst "About Messages and Message Queues" na MSDN.
Když potřebuji nasimulovat sérii zpráv, abych napodobil nějakou činnost, používám k tomu nástroj zvaný Spy++, který je součástí instalace Microsoft Visual Studio. Při programování nástrojů a utilit je to velice užitečný pomocník.
Obrázek vedle tohoto odstavce ukazuje SPY++ v akci. V seznamu oken najdu to okno, keré mě zajímá, v tomto případě jde o okno, které vytvořil prohlížeč. V mém případě to je Google Chrome. Konkrétní okno, do kterého kreslí, má třídu s názvem "Chrome_RenderWidgetHostHWND". Handle na toto okno získáte ve SPY++ buď hledáním v listu oken a nebo hledáním okna pomocí drag and drop. Přesně stejný způsob hledání okna s Farmeramou jsem použil i já. Pomocí drag and drop mi hráč ukáže na okno, ve kterém běží farmerama. Je mi pak uplně jedno, jestli běží ve Firefoxu, Chromu nebo v něčem jiném. Mechanizmus ukazování na okna mi zprostředkuje handle na okno a to je to jediné, co potřebuji, abych mohl oknu odesílat zprávy. Konec konců přes něj i získávám obrázky Farmeramy. O tom si v tomto článku také povíme.
Na zmíněném obrázku jsem označil několik informací. V záhlaví SPY++ je vidět handle okna s Farmeramou. Dále úplně dole je orámovaný uzel v seznamu oken. Uprostřed obrázku je pak okno s výpisem zpráv, které se nad oknem Farmeramy dějí.
Další obrázek u tohoto odstavce Pak ukazuje nastavení filtru zpráv. Tím lze omezit nebo přesně specifikovat, o jaký druh zpráv máme zájem. Kdybychom odebírali všechny zprávy, jen těžko by se v tom dalo hledat co potřebujeme najít. Proto omezím sledovaný výpis zpráv na oblast myši. Jen zprávy týkající se myši se budou zachytávat. V nich lze pak sledovat posloupnost konkrétních zpráv a jejich hodnoty. Uvidíte tak, jaké zprávy probíhají při pohybu myší, jaké při kliknutí. Pokud windows zprávy znáte, je to dál už brnkačka.
Vysledoval jsem, že ke kliknutí na konkrétní pozici je potřeba odeslat sérii následujících zpráv (parametry zpráv projdeme níže):
Nyní projdeme jednolivé zprávy. Nechci zabrousit do detailů, kterým se věnoval dokumentační tým MSDN, ale krátce okomentuji významy parametrů. Každá zpráva nese 2 parametry. LPARAM a WPARAM. Oba musíme správně plnit, aby napodobení bylo realistické. Špatné vyplnění parametrů kromě toho, že cílová aplikace nebude reagovat jak očekáváme, ale můžeme jí nechtěně uškodit, protože aplikace nemusí umět špatné parametry zpracovat. Proto je potřeba dokumentaci dobře a několikrát číst.
1. WM_LBUTTONDOWN - viz MSDN je zpráva, informující přijímající okno, že uživatel stiskl levé tlačítko myši. WPARAM musí obsahovat hodnotu MK_LBUTTON, protože stisknuta byla levá myš. Jinou hodnotu mixovat nebudeme, protože nám jde skutečně jen o levé tlačítko. (Lze zde ale signalizovat, že jsou stisknuta i jiná tlačítka) LPARAM pak obsahuje souřadnice myši. Systém souřadnic je je absolutní pro okno, na nějž zprávu odesíláme. Pro vytvoření LPRAM můžete použít funkci MakeLParam, která přijímá 2 parametry, dolní a horní WORD, z kterého vyrobí jeden DWORD, který vrátí. My ho použijeme jako paramet zprávy. Dolní WORD je pro nás X osa a horní WORD je Y osa.
2. WM_MOUSEMOVE - viz MSDN je zpráva, informující přijimající okno, že se myš pohybuje. WPARAM bude mít ale hodnotu 0, protože je okno potřebuje informaci o pohybu myši před klikem. LPARAM obsahuje souřadnice, které již umíme vyrobit z předchozího zpracování. Nezkoumal jsem proč se zpráva odesílá až po kliku.
3. WM_SETCURSOR - biz MSDN je odesílána, aby oknu sdělila, že se nad ním pohybuje myš. WPARAM je handle na okno, nad kterým se myš pohybuje, proto zde bude stejný handle okana, jako ten, na který zprávu posíláme. LPARAM je pak mix dvou hodnot. První (dolní WORD) je testovací kód, který říká oblast vzhledem k oknu na které je zpráva odeslána. V tomto případě je hodnota HTCLIENT, protože myš je vně okna. Druhý (horní WORD) pak reprezentuje zprávu, ke které se test váže. Proto bude mít hodnotu WM_LBUTTONDOWN. Důležité je propojit si, že zpráva WM_LBUTTONDOWN byla odelána jako první. Kdyby to tak nebylo, nefungovalo by to. K vytvoření parametru lze použít již zmíněnou funkci MakeLParam.
4. WM_LBUTTONUP - viz MSDN má přeně opačnou funkci jako první zpráva kterou jsme poslali a to tu, že se uvolnilo levé tlačítko myši. Parametry zprávy jsou absolutně totožné jako u první zprávy. Před odesláním této zprávy se vyplatí chviličku počkat. Napříkald 25 ms.
5. WM_SETCURSOR - totožná implementace jako v bodě 3.
Impelementace by mohla bát zabalená do funkce nebo procedury a jako vstupní parametr by mohl být handle na okno a například struktura TPoint, která přidrží informaci o souřadníici myši, kde chceme simulovat kliknutí. Vypíchnu zde kus kódu, který se opět váže na Farmeramu. Implementaci nechám na vás. Mám objekt TFarmerama, který zveřejňuje sadu metod k ovládání hry. Jedna z metod je ono kliknutí. Další je napoříklad handle okna. Vlastnost CurrentBitmap, která přidržuje aktuální screenshot hry a další. Zmíněná metoda Click vypadá v mém případětakto:
(stačí lehce přizpůsobit a je plně funkční)
// Simulace kliknutí procedure TFarmerama.Click(APosX, APosY: WORD); var wParam, lParam: Integer; begin // Scenar: // 1 - WM_LBUTTONDOWN - keys: MK_LBUTTON, PosX, PosY // 2 - WM_MOUSEMOVE - PosX, PosY // 3 - WM_SETCURSOR - Hittest: HTCLIENT, MouseMsg: WM_LBUTTONDOWN // 4 - WM_LBUTTONUP - keys: MK_LBUTTON, PosX, PosY // 5 - WM_SETCURSOR - Hittest: HTCLIENT, MouseMsg: WM_MOUSEMOVE // // * 1 * wParam := MK_LBUTTON; // lParam: // The low-order word specifies the x-coordinate of the cursor. The coordinate is relative to the upper-left corner of the client area. // The high-order word specifies the y-coordinate of the cursor. The coordinate is relative to the upper-left corner of the client area. lParam := MakeLParam(APosX + FOffset.X, APosY + FOffset.Y); SendMessage(FHWnd, WM_LBUTTONDOWN, wParam, lParam); // // * 2 * wParam := 0; // lParam: // The low-order word specifies the x-coordinate of the cursor. The coordinate is relative to the upper-left corner of the client area. // The high-order word specifies the y-coordinate of the cursor. The coordinate is relative to the upper-left corner of the client area. lParam := MakeLParam(APosX + FOffset.X, APosY + FOffset.Y); SendMessage(FHWnd, WM_MOUSEMOVE, wParam, lParam); // // * 3* // wParam - A handle to the window that contains the cursor. wParam := FHWnd; // lParam : // The low-order word of lParam specifies the hit-test code. // The high-order word of lParam specifies the identifier of the mouse message. lParam := MakeLParam(HTCLIENT, WM_LBUTTONDOWN); SendMessage(FHWnd, WM_SETCURSOR, wParam, lParam); // // Sleep(25); // // * 4 * wParam := MK_LBUTTON; lParam := MakeLParam(APosX + FOffset.X, APosY + FOffset.Y); SendMessage(FHWnd, WM_LBUTTONUP, wParam, lParam); // // * 5 * // wParam - A handle to the window that contains the cursor. wParam := FHWnd; // lParam : // The low-order word of lParam specifies the hit-test code. // The high-order word of lParam specifies the identifier of the mouse message. lParam := MakeLParam(HTCLIENT, WM_MOUSEMOVE); SendMessage(FHWnd, WM_SETCURSOR, wParam, lParam); end;
Nyní, když na handle okna s Farmeramou zavoláte metodu Click na správné souřadnice, třeba dokončený chov slepic, musí kliknutí vyvolat ve hře reakci ve formě zobrazení dialogu s nabídkou, co lze se slepičárnou dělat. Viz obrázek vedle odstavce.
Nyní by bylo dobré získat nový obrázek hry a začít hedat další fragment obrázku s ikonou sklizení.
Při výrobě fragmentů jsem pokaždé postupoval tak, že jsem si ofotil obrazovku, vystřihnul fragment. Ten jsem pak upravil tak, aby se dobře hledal. No a přidal jsem ho do robota, aby s ním uměl pracovat.
V tuto chvíli by bylo dobré kolem místa kliknutí vytvořit rámec v kterém se pravděpodobně dialog s tlačítky může nacházet. No vida a hend máme parametry pro další zavolání funkce TExtendedBitmap.LookingFor.
Pro získání prvního a pak další a další scren shoty hry je potřeba udělat několik málo volání Windows API. O tom vám napíšu v dalším článku, protože tento by byl již dlouhý a stal by se nepřehledný.