게임 실시간 화면을 분석하여 낚시
2022.05.26
C++, OpenCV로 만든 낚시 매크로,
다양한 응용이 가능하나 즉시 사용 가능한 게임은 비공개..
#include <iostream>
#include <ctime>
#include <opencv2/opencv.hpp>
#include <windows.h>
#include <time.h>
#include <string>
using namespace std;
using namespace cv;
#ifdef _DEBUG
#pragma comment(lib, "opencv_world343d.lib")
#else
#pragma comment(lib, "opencv_world343.lib")
#endif
int objWinX, objWinY, objWinW, objWinH;
int scrWidth, scrHeight;
int dispLog;
int LowH, HighH, LowS, HighS, LowV, HighV, castAfterRelease;
int countFishs = 1;
Mat hwnd2mat(HWND hwnd)
{
HDC hwindowDC, hwindowCompatibleDC;
HBITMAP hbwindow;
Mat src;
BITMAPINFOHEADER bi;
hwindowDC = GetDC(hwnd);
hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);
SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);
src.create(objWinH, objWinW, CV_8UC4);
hbwindow = CreateCompatibleBitmap(hwindowDC, objWinW, objWinH);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = objWinW;
bi.biHeight = -objWinH;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
SelectObject(hwindowCompatibleDC, hbwindow);
StretchBlt(hwindowCompatibleDC, 0, 0, objWinW, objWinH, hwindowDC, objWinX, objWinY, objWinW, objWinH, SRCCOPY);
GetDIBits(hwindowCompatibleDC, hbwindow, 0, objWinH, src.data, (BITMAPINFO *)&bi, DIB_RGB_COLORS);
DeleteObject(hbwindow);
DeleteDC(hwindowCompatibleDC);
ReleaseDC(hwnd, hwindowDC);
return src;
}
void keyOutput(int realPosX, int realPosY) {
int castDelay;
castDelay = GetPrivateProfileInt("etcValues", "castDelay", 1000, "./config.ini");
if (dispLog == 1) {
cout << "실행 : 마우스" << realPosX << "/" << realPosY << endl;
}
if (realPosX && realPosY) {
time_t now = time(0);
char* dt = ctime(&now);
cout << dt << " : Get!! " << endl;
SetCursorPos(realPosX, realPosY);
Sleep(100);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Sleep(100);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
Sleep(castDelay * 1000);
time_t now = time(0);
char* dt = ctime(&now);
cout << dt << "[" << countFishs << "] : Cast!! " << endl;
countFishs++;
keybd_event(0x30, 0, 0, 0);
keybd_event(0x30, 0, KEYEVENTF_KEYUP, 0);
Sleep(castAfterRelease * 1000);
}
void keySkill(int useKey) {
int keyCode;
switch (useKey) {
case 1:
keyCode = 0x31;
break;
case 2:
keyCode = 0x32;
break;
case 3:
keyCode = 0x33;
break;
case 4:
keyCode = 0x34;
break;
case 5:
keyCode = 0x35;
break;
case 6:
keyCode = 0x36;
break;
case 7:
keyCode = 0x37;
break;
case 8:
keyCode = 0x38;
break;
case 9:
keyCode = 0x39;
break;
}
keybd_event(keyCode, 0, 0, 0);
keybd_event(keyCode, 0, KEYEVENTF_KEYUP, 0);
Sleep(100);
keybd_event(0x30, 0, 0, 0);
keybd_event(0x30, 0, KEYEVENTF_KEYUP, 0);
Sleep(castAfterRelease * 1000);
}
void setRange(int setR) {
string profile = "profile" + to_string(setR);
LPCSTR _profile = profile.c_str();
LowH = GetPrivateProfileInt(_profile, "LowH", 0, "./config.ini");
HighH = GetPrivateProfileInt(_profile, "HighH", 0, "./config.ini");
LowS = GetPrivateProfileInt(_profile, "LowS", 0, "./config.ini");
HighS = GetPrivateProfileInt(_profile, "HighS", 0, "./config.ini");
LowV = GetPrivateProfileInt(_profile, "LowV", 0, "./config.ini");
HighV = GetPrivateProfileInt(_profile, "HighV", 0, "./config.ini");
}
int main(int argc, char **argv) {
time_t run_start, run_finish;
time(&run_start);
int centerX, centerY;
// 환경변수를 config.ini 에서 불러옴
// etcValues
int posRange, counterPer, counterMax, mouseFocus, startPause;
posRange = GetPrivateProfileInt("etcValues", "posRange", 50, "./config.ini");
dispLog = GetPrivateProfileInt("etcValues", "dispLog", 1, "./config.ini");
counterPer = GetPrivateProfileInt("etcValues", "counterPer", 5, "./config.ini");
counterMax = GetPrivateProfileInt("etcValues", "counterMax", 20, "./config.ini");
mouseFocus = GetPrivateProfileInt("etcValues", "mouseFocus", 0, "./config.ini");
startPause = GetPrivateProfileInt("etcValues", "startPause", 3000, "./config.ini");
castAfterRelease = GetPrivateProfileInt("etcValues", "castAfterRelease", 3, "./config.ini");
int gamePlay = 0;
int output_source = 0;
int key = 0;
int counter = 0;
int prev_pos, this_pos = 0;
int set_range = 1;
int set_range_max = 10;
int freeSet = 0;
int useSkill = 0;
int useSkillKey = 5;
int useSkillKeyMin = 5;
int useSkillKeyMax = 5;
int useSkillKeyTemp = 0;
int useSkillKeyDelay = 300;
time_t skill_start, skill_end;
time(&skill_start);
// 기본 설정값이 없다면 실행
setRange(set_range);
HWND hwndDesktop = GetDesktopWindow();
RECT windowsize; // get the height and width of the screen
GetClientRect(hwndDesktop, &windowsize);
scrWidth = windowsize.right;
scrHeight = windowsize.bottom;
objWinW = scrWidth / 3;
objWinH = scrHeight / 3;
objWinX = scrWidth / 3;
objWinY = scrHeight / 5;
int realPosX = objWinX;
int realPosY = objWinY;
if (scrWidth > 2000) {
namedWindow("설정프로그램");
resizeWindow("설정프로그램", objWinW / 2, objWinH / 2);
moveWindow("설정프로그램", 0, 0);
}
else {
namedWindow("설정프로그램");
resizeWindow("설정프로그램", 400, 800);
moveWindow("설정프로그램", 0, 0);
namedWindow("영상소스");
resizeWindow("영상소스", objWinW / 2, objWinH / 2);
moveWindow("영상소스", 300, 0);
}
while (key != 27)
{
// 프리셋 상태가 아닐경우에만
if (freeSet == 0) {
setRange(set_range);
}
Mat src = hwnd2mat(hwndDesktop);
key = waitKey(60); // you can change wait time
if (counter % counterPer == 0) {
Mat img_all, img_input, img_hsv, img_binary, img_result, img_concat;
/* 이미지 크롭
desktop = src;
Rect rect(objWinX, objWinY, objWinW, objWinH);
img_input = desktop(rect);
*/
img_input = src;
//HSV로 변환
cvtColor(img_input, img_hsv, COLOR_BGR2HSV);
//지정한 HSV 범위를 이용하여 영상을 이진화
inRange(img_hsv, Scalar(LowH, LowS, LowV), Scalar(HighH, HighS, HighV), img_binary);
//morphological opening 작은 점들을 제거
erode(img_binary, img_binary, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
dilate(img_binary, img_binary, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
//morphological closing 영역의 구멍 메우기
dilate(img_binary, img_binary, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
erode(img_binary, img_binary, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
//라벨링
Mat img_labels, stats, centroids;
int numOfLables = connectedComponentsWithStats(img_binary, img_labels, stats, centroids, 8, CV_32S);
//영역박스 그리기
int max = -1, idx = 0;
for (int j = 1; j < numOfLables; j++) {
int area = stats.at<int>(j, CC_STAT_AREA);
if (max < area)
{
max = area;
idx = j;
}
}
int left = stats.at<int>(idx, CC_STAT_LEFT);
int top = stats.at<int>(idx, CC_STAT_TOP);
int width = stats.at<int>(idx, CC_STAT_WIDTH);
int height = stats.at<int>(idx, CC_STAT_HEIGHT);
if (output_source == 0) {
//cvtColor(img_binary, img_result, CV_GRAY2BGR);
img_result = img_binary;
rectangle(img_result, Point(left, top), Point(left + width, top + height), Scalar(255, 255, 255), 1);
}
else {
img_result = img_input;
rectangle(img_result, Point(left, top), Point(left + width, top + height), Scalar(0, 0, 255), 1);
}
//트랙바 생성
cvCreateTrackbar("LowH", "설정프로그램", &LowH, 179); //Hue (0 - 179)
cvCreateTrackbar("HighH", "설정프로그램", &HighH, 179);
cvCreateTrackbar("LowS", "설정프로그램", &LowS, 255); //Saturation (0 - 255)
cvCreateTrackbar("HighS", "설정프로그램", &HighS, 255);
cvCreateTrackbar("LowV", "설정프로그램", &LowV, 255); //Value (0 - 255)
cvCreateTrackbar("HighV", "설정프로그램", &HighV, 255);
cvCreateTrackbar("프리셋", "설정프로그램", &freeSet, 1);
cvCreateTrackbar("LowH", "설정프로그램", &LowH, 179); //Hue (0 - 179)
cvCreateTrackbar("LowS", "설정프로그램", &LowS, 255); //Saturation (0 - 255)
cvCreateTrackbar("LowV", "설정프로그램", &LowV, 255); //Value (0 - 255)
cvCreateTrackbar("HighH", "설정프로그램", &HighH, 179);
cvCreateTrackbar("HighS", "설정프로그램", &HighS, 255);
cvCreateTrackbar("HighV", "설정프로그램", &HighV, 255);
cvCreateTrackbar("동작", "설정프로그램", &gamePlay, 1);
cvCreateTrackbar("영상소스", "설정프로그램", &output_source, 1);
cvCreateTrackbar("로그", "설정프로그램", &dispLog, 1);
cvCreateTrackbar("프로필", "설정프로그램", &set_range, set_range_max);
cvCreateTrackbar("변화높이", "설정프로그램", &posRange, 100);
cvCreateTrackbar("스킵프레임", "설정프로그램", &counterPer, 10);
cvCreateTrackbar("자동캐스팅", "설정프로그램", &counterMax, 20);
cvCreateTrackbar("지연시간", "설정프로그램", &castAfterRelease, 10);
cvCreateTrackbar("포커스", "설정프로그램", &mouseFocus, 1);
cvCreateTrackbar("스킬사용", "설정프로그램", &useSkill, 1);
cvCreateTrackbar("단축시작키", "설정프로그램", &useSkillKeyMin, 9);
cvCreateTrackbar("단축종료키", "설정프로그램", &useSkillKeyMax, 9);
cvCreateTrackbar("간격(초)", "설정프로그램", &useSkillKeyDelay, 600);
if (scrWidth > 2000) {
imshow("설정프로그램", img_result);
}
else {
imshow("영상소스", img_result);
}
// 스킬 키 사용 관련
if (useSkillKeyMax > useSkillKeyMin) {
if(useSkillKeyTemp == 0 || useSkillKeyTemp < useSkillKeyMin || useSkillKeyTemp > useSkillKeyMax) {
useSkillKeyTemp = useSkillKeyMin;
}
useSkillKey = useSkillKeyTemp;
}
else {
useSkillKey = useSkillKeyMin;
}
time(&run_finish);
if (dispLog == 1) {
cout << difftime(run_finish, run_start) << " centerX:" << centerX << " centerY:" << centerY << " prev_pos:" << prev_pos << " this_pos:" << this_pos << endl;
}
if (left > 0 && top > 0) {
centerX = left + width / 2;
centerY = top + height / 2;
realPosX = centerX + objWinX;
realPosY = centerY + objWinY;
this_pos = centerY;
if (gamePlay == 1 && prev_pos != 0 && abs(prev_pos - this_pos) > posRange) { // 이전 픽셀 포지션과 posRange 이상 차이가 난다면
keyOutput(realPosX, realPosY);
// 스킬 키 사용
if (useSkill == 1 && useSkillKey > 0) {
time(&skill_end);
if (difftime(skill_end, skill_start) > useSkillKeyDelay) {
cout << "스킬사용: " << useSkillKey << endl;
keySkill(useSkillKey);
time(&skill_start);
if (useSkillKeyTemp > 0) {
useSkillKeyTemp++;
}
}
}
counter = 1;
prev_pos = 0;
time(&run_start);
}
else {
prev_pos = centerY;
}
if (gamePlay == 1) {
if (mouseFocus == 1) {
SetCursorPos(realPosX, realPosY);
}
if (difftime(run_finish, run_start) > counterMax) {
keyOutput(realPosX, realPosY);
counter = 1;
prev_pos = 0;
time(&run_start);
}
}
}
}
counter++;
}
cvDestroyAllWindows();
}