![]() |
|
|
|||||||
| Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
![]() |
|
|
Опции темы | Поиск в этой теме | Опции просмотра |
|
#1
|
||||
|
||||
|
Приветствую!
Очень прошу понимающих людей объяснить мне как реализовать поставленную задачу. Определить цвет пикселя (одного) не проблема, цвет программа определяет мгновенно, а вот если мониторить сразу 4 - проблема.. программа часто не срабатывает при нахождение цвета... Я делаю так: Код:
var
bmp: TBitmap;
h1: HWND;
DC: HDC;
dx,dy,fx,fy,jx,jy,kx,ky: Integer;
procedure TForm1.FormCreate(Sender: TObject);
begin
bmp := TBitmap.Create;
bmp.Width := 1; bmp.Height := 1;
bmp.PixelFormat := pf32bit;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
ReleaseDC(h1,DC);
end;
procedure TForm1.btn1Click(Sender: TObject);
begin
h1 := FindWindow(ProgName, nil);
DC := GetDC(FindWindow(ProgName,''));
//Если программа запущена задаем координаты которые будем проверять. Кстати таймеры все стоят на интервале равным = 1
if h1 <> 0 then
begin
dx:=646;
dy:=715;
fx:=724;
fy:=715;
jx:=802;
jy:=715;
kx:=843;
ky:=715;
//Включаем таймеры которые будут это делать (каждый будет смотреть свой пиксель)
tmr1.Enabled:=True;
tmr2.Enabled:=True;
tmr3.Enabled:=True;
tmr4.Enabled:=True;
end else
begin
ShowMessage('Game Not Started');
end;
end;
//Таких таймеров 4 штуки. Код в них похож, только координаты пикселей разные и сравниваемый цвет.
procedure TForm1.tmr1Timer(Sender: TObject);
var
c: TColor;
rr,gg,bb: Integer;
begin
BitBlt(bmp.Canvas.Handle,0,0,1,1,dc,dx,dy,SRCCOPY);
//releasedc(h1,dc);
c := pinteger(bmp.Scanline[0])^;
c := ((c and $FF) shl 16) or (c and $FF00) or ((c shr 16) and $FF);
rr:=GetRValue(c);
gg:=GetGValue(c);
bb:=GetBValue(c);
if (rr>80) and (gg>80) and (bb>100) then EmulateKey(h1, Ord('D'));
end;
...
procedure TForm1.tmr2Timer(Sender: TObject);
...
procedure TForm1.tmr3Timer(Sender: TObject);
...
procedure TForm1.tmr4Timer(Sender: TObject);
...
Если один таймер стоит - все работает. Два - более или менее тоже а вот 4 уже то срабатывает то нет... Как лучше и правильнее сделать? Я буду вам очень благодарен за помощь. Последний раз редактировалось Alloc, 02.10.2015 в 16:27. |
|
#2
|
||||
|
||||
|
Эм... что это за ...фигня? А если тебе понадобится проверять 10 тысяч пикселей, ты сделаешь 10 тысяч таймеров?
Что помешало загнать координаты и цвета в массивы и в одном таймере один раз брать картинку, пробегать в цикле по массиву и проверять сразу все нужные пиксели? Да и не очень понятно, зачем 1 мс в таймере. Человек видит 24 кадра в секунду, т.е. минимальная различаемая человеком задержка - 40 миллисекунд. В хороших играх FPS обычно делается в районе 60 (16 мс), так обеспечивается большая зрительная гладкость, однако для робота это уже лишено смысла. Ну а быстрее вообще никто не рисует, кроме случаев 3Д-изображения для специальных очков. Какой смысл проверять каждую миллисекунду, если игра перерисовывает не чаще, чем раз в 40? Последний раз редактировалось Bargest, 02.10.2015 в 17:22. |
| Этот пользователь сказал Спасибо Bargest за это полезное сообщение: | ||
Alloc (02.10.2015)
| ||
|
#3
|
||||
|
||||
|
Спасибо за ответ. Я понимаю, для Вас этот код полная жесть, но я только в начале пути познания программирования. Я примерно представляю, что Вы имеете в виду, попытался воспроизвести Ваши советы, но не выходит что то.. Я был бы Вам очень признателен если Вы напишите небольшой пример того, как должен выглядеть массив координат и цветов и как пробегать по нему циклом... Если Вам не трудно, буду очень признателен
|
|
#4
|
||||
|
||||
|
Ну простейший вариант (если не использовать структуры) - 3 массива
Код:
var x, y: array of integer; colors: array of Longint; Код:
for i := 0 to pointsCount-1 do begin
c := ColorToRGB(bmp.Canvas.Pixels[x[i], y[i]]);
if abs(GetRValue(c) - GetRValue(colors[i]))< delta and
abs(GetGValue(c) - GetGValue(colors[i]))< delta and
abs(GetBValue(c) - GetBValue(colors[i]))< delta then
EmulateKey(h1, Ord('D'));
end;Последний раз редактировалось Bargest, 02.10.2015 в 21:10. |
| Этот пользователь сказал Спасибо Bargest за это полезное сообщение: | ||
Alloc (02.10.2015)
| ||
|
#5
|
||||
|
||||
|
Bargest, Большое спасибо за помощь, Вы мне очень помогли. Буду пробовать. А как быть насчет ReleaseDC? Я слышал его обязательно нужно ставить так как может быть утечка памяти. Если его все так и необходимо прописывать, то куда? Тоже в таймер или достаточно в событие Form.Destroy?
Да, и переменные i, pointsCount и delta - объявить как Integer? В этом случае почему то компилятор указывая на знак "<" говорит о "Incompatible types" Последний раз редактировалось Alloc, 02.10.2015 в 21:43. |
|
#6
|
||||
|
||||
|
Цитата:
Цитата:
Можно было и справку почитать на этот случай. |
| Этот пользователь сказал Спасибо Bargest за это полезное сообщение: | ||
Alloc (02.10.2015)
| ||
|
#7
|
||||
|
||||
|
Цитата:
Цитата:
а что же делать с: Код:
BitBlt(bmp.Canvas.Handle,0,0,1,1,dc,dx,dy,SRCCOPY); его придется то же поместить в цикл? Код:
for i := 0 to pointsCount-1 do
begin
BitBlt(bmp.Canvas.Handle,0,0,1,1,dc,x[i],y[i],SRCCOPY);
c := ColorToRGB(bmp.Canvas.Pixels[x[i], y[i]]);
if abs(GetRValue(c) - GetRValue(colors[i])) < delta and
abs(GetGValue(c) - GetGValue(colors[i])) < delta and
abs(GetBValue(c) - GetBValue(colors[i])) < delta then
EmulateKey(h1, Ord('D'));
end;Последний раз редактировалось Alloc, 02.10.2015 в 23:45. |
|
#8
|
||||
|
||||
|
Цитата:
Очевидно, удалять надо: 1) Прямо перед созданием, если DC не нуль. То есть инициализировать DC нулем, и перед выделением проверять: если он не нуль - удалить и занулить. Так обеспечится удаление того, что нагенерируется тыканием на кнопку. 2) При закрытии формы для удаления последнего. При этом надо подумать на тему: а что будет, если анализируемое окно закроется во время работы нашей программы? Поэтому наилучший вариант совсем иной - выделять DC при заходе в таймер и освобождать при выходе. А сохранять хендл окна, а не его DC. Цитата:
Или же брать пиксели в цикле прямо с DC, что видится самым логичным решением. Задача ведь требует считать малое число (четыре) отдельных разбросанных пикселей с экрана, а не обрабатывать фотографии. Зачем вся эта фигня с BMP? Последний раз редактировалось Bargest, 03.10.2015 в 17:41. |
| Этот пользователь сказал Спасибо Bargest за это полезное сообщение: | ||
Alloc (03.10.2015)
| ||
|
#9
|
||||
|
||||
|
Цитата:
Цитата:
Цитата:
Цитата:
Вы имеете в виду через GetPixel? Я пробовал так делать, так же в таймере определял цвет каждого пикселя, сравнивал и выполнял соответствующее действие.. что то типо такого: Код:
PixColorA:= GetPixel(DC,dx,dy);
ra:=GetRValue(PixColorA);
ga:=GetGValue(PixColorA);
ba:=GetBValue(PixColorA);
PixColorB:= GetPixel(DC,fx,fy);
rb:=GetRValue(PixColorB);
gb:=GetGValue(PixColorB);
bb:=GetBValue(PixColorB);
PixColorC:= GetPixel(DC,jx,jy);
rc:=GetRValue(PixColorC);
gc:=GetGValue(PixColorC);
bc:=GetBValue(PixColorC);
PixColorD:= GetPixel(DC,kx,ky);
rd:=GetRValue(PixColorD);
gd:=GetGValue(PixColorD);
bd:=GetBValue(PixColorD);
if (ra>80) and (ga>80) and (ba>100) then EmulateKey(h1, Ord('D'));
if (rb>36) and (gb>100) and (bb>130) then EmulateKey(h1, Ord('F'));
if (rc>36) and (gc>100) and (bc>130) then EmulateKey(h1, Ord('J'));
if (rd>80) and (gd>80) and (bd>100) then EmulateKey(h1, Ord('K')); но дело в том, что в игре эти предметы которые я пытаюсь поймать делая эту программу двигаются очень быстро и если я поставлю допустим 100мс или даже 40мс в таймере они не определятся.. (ну это конечно в моем случае). Может есть другой способ для этой задачи? Я уже пробовал и в 4 потока запускать, все так же) Уже не знаю... на Вас вся надеждачтобы Вы понимали о чем я вообще говорю вот скриншот из игры. Объекты падают быстро сверху вниз и нужно успеть нажать кнопку в тот момент когда объект будет внизу: ![]() Последний раз редактировалось Alloc, 03.10.2015 в 19:40. |
|
#10
|
||||
|
||||
|
Цитата:
Цитата:
Тем не менее, копировать 1 пиксель в BMP (т.е. брать его средствами винды из DC), чтобы потом сделать к нему scanline и взять из него пиксель - это по меньшей мере КРАЙНЕ странно. Цитата:
Вот есть в игре объект. Он движется со скоростью 200 пикселей в секунду. Пусть мы перерисовываем 25 раз в секунду. Значит между двумя кадрами объект переместится на 200/25=8 пикселей. Если часть объекта нужного цвета занимает меньше 8 пикселей, то какой-то пиксель так и не засветится нужным цветом. Какая реальная скорость объекта? Если он пролетает монитор в 1920х1080 по горизонтали за секунду, то между кадрами объект телепортируется на 76 пикселей. Так делается анимация. Цитата:
Если уж делать всяких ботов для быстролетящих объектов, то на мой взгляд не логично завязываться на мигание одного конкретного пикселя. Тут надо уже анализировать хоть какую-то небольшую область, в которую объект обязательно должен попасть. EDIT в виду добавления скриншота: Так очевидно, надо мониторить изменение столбиков пикселей нижних областей. Как только хоть в одном пикселе из столбика произошло переключение цвета с черного на какой-то - нажимать. Если даже тут FPS=60 (что было бы странно), то нет смысла ставить таймер меньше, чем на 8-10мс. Последний раз редактировалось Bargest, 03.10.2015 в 19:51. |
| Этот пользователь сказал Спасибо Bargest за это полезное сообщение: | ||
Alloc (03.10.2015)
| ||
|
#11
|
||||
|
||||
|
Я бы вам рекомендовал записать хотя-бы 10 секунд игры в скрины. Для чистоты эксперимента их можно первоначально в массив записать, а уже потом весь массив сохранить на диск. А уже потом проанализировав скрины можно и со скоростью определится и с правильной позицией захвата пикселей.
|
| Этот пользователь сказал Спасибо Страдалецъ за это полезное сообщение: | ||
Alloc (03.10.2015)
| ||
|
#12
|
||||
|
||||
|
Поддерживаю. Можно хоть тем же чудесным таймером на 1 мс. Заодно и реальные метки времени прикопать для кадров.
|
| Этот пользователь сказал Спасибо Bargest за это полезное сообщение: | ||
Alloc (03.10.2015)
| ||
|
#13
|
||||
|
||||
|
Цитата:
Цитата:
т.е. ваш вариант в принципе все еще актуален в данной ситуации? Я про массивы из координат и цветов и т.д.? И может быть лучше брать цвет пикселя через GetPixel? Или Вы имели в виду что то другое? Последний раз редактировалось Alloc, 03.10.2015 в 20:54. |
|
#14
|
||||
|
||||
|
Я говорю о том, что сравнивать, скорее всего, придется не один пиксель для обнаружения объекта, а ловить любые изменения в небольшом ряду по направлению движения. В остальном-то смысл тот же.
Но сначала лучше прикинуть скорость. "записывать" в таймере кусочки картинки в массив несколько секунд и потом сохранять с временными метками. Хоть увидишь, что "видит" твоя программа. |
| Этот пользователь сказал Спасибо Bargest за это полезное сообщение: | ||
Alloc (03.10.2015)
| ||
|
#15
|
||||
|
||||
|
Цитата:
Цитата:
Хочу сделать небольшое пояснение.. Дело в том, что как бы по отдельности все работает, определяется все замечательно, и без разницы какой способ использую, приведенный в начале темы или через GetPixel... Код:
procedure TForm1.tmr1Timer(Sender: TObject);
var
r,g,b: Integer;
begin
PixColorA:= GetPixel(DC,ax,ay);
r:=GetRValue(PixColorA);
g:=GetGValue(PixColorA);
b:=GetBValue(PixColorA);
if (r>80) and (g>80) and (b>100) then EmulateKey(h1, Ord('D'));
end;а стоит добавить еще пару GetPixel(DC,X,Y) уже для проверки других координат то программа начинает вести себя странно.. то определяет - то нет.. это главная проблема |