C#/C# 리니지m/
C#으로 만드는 리니지m 매크로 (4-2)함수 재정렬과 수정
2019. 10. 9.
2019/09/27 - [프로그래밍/C# 리니지m] - C#으로 만드는 리니지m 매크로 (1)이미지서치
2019/10/02 - [프로그래밍/C# 리니지m] - C#으로 만드는 리니지m 매크로 (2-1)hp인식 - tesseract
2019/10/03 - [프로그래밍/C# 리니지m] - C#으로 만드는 리니지m 매크로 (2-2)hp인식 - 픽셀서치
2019/10/07 - [프로그래밍/C# 리니지m] - C#으로 만드는 리니지m 매크로 (3)함수반복과 베르설정
2019/10/09 - [프로그래밍/C# 리니지m] - C#으로 만드는 리니지m 매크로 (4-1)함수 재정렬과 수정
지난 시간 간략한 설명과 Form디자인 수정을 하였고
이번시간에는 함수 수정을 해보겠습니다.
지난시간 수정한 Form디자인을 보고
어떻게 필요한 부분이 어떤것들인지 찾아봅시다.
1) hp_num, hp_per 표시
2) hp_ber, hp_tel 에 적은 수치이하에서 베르or텔 작동
3) 삭제한 button, picturebox 부분 코드 제거or수정
정도로 추려볼수 있겠네요.
그동안 각각의 버튼으로 작동하던 함수들을 하나의 함수로 묶을생각입니다.
우선 button1_click 함수를 봅시다.
기능이 살아있지만 picturebox의 크기가 바뀌었으므로
그부분만 수정해주면되겠네요.
private void Button1_Click(object sender, EventArgs e)
{
string a = searchtext.Text;
IntPtr b = FindWindow(null, a);
IntPtr c = FindWindowEx(b, 0, "RenderWindow", "TheRender");
handle = c;
// 전역 변수 handle에 핸들값 c를저장
Graphics gdata = Graphics.FromHwnd(c);
Rectangle rect = Rectangle.Round(gdata.VisibleClipBounds);
Bitmap bmp = new Bitmap(rect.Width, rect.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
IntPtr hdc = g.GetHdc();
PrintWindow(c, hdc, 0x2);
g.ReleaseHdc(hdc);
}
screen = new Bitmap(bmp, new Size(1280, 720));
pictureBox1.Image = new Bitmap(screen, new Size(320, 180));
// picturebox 크기에 맞게 이미지리사이즈
}
button2_click 함수는 button2(tesseract를 위해 캡처한부분)를 삭제하였으나
button3_click 함수(tesseract 실행)을 하기위해 필요한 부분이었으니
두 함수모두 새로만들 함수에 복사,붙여넣기후 삭제해주고
조금의 수정을 해주겠습니다.
private void function(object sender, EventArgs e)
{
if (searchtext.Text != null || searchtext.Text != " ")
{
searchbutton.PerformClick();
}
Rectangle rect = new Rectangle(130, 13, 85, 20);
// x,y 시작좌표 x,y범위 생성
Bitmap cap = screen.Clone(rect, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
// 캡쳐뜬 이미지에서 위의 Rect 만큼 자르기
cap = new Bitmap(cap, new Size(170, 40));
// 인식률을 높이기위해 두배크기로 리사이즈
for (int i = 0; i < cap.Width; i++)
{
for (int j = 0; j < cap.Height; j++)
{
Color c = cap.GetPixel(i, j);
int binary = (c.R + c.G + c.B) / 3;
if (binary > 190)
cap.SetPixel(i, j, Color.White);
else
cap.SetPixel(i, j, Color.Black);
}
}
// 간단한 for을 통한 Binary(이진화)
//screen = cap; 필요없는부분 삭제
//pictureBox2.Image = screen; 필요없는 부분 삭제
// picturebox2 에 자르고 키우고 이진화한 이미지 띄우기
//Pix pix = PixConverter.ToPix(screen); 함수간소화를 위해 수정
Pix pix = PixConverter.ToPix(cap);
var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.TesseractOnly);
// tesseractengine 생성
string whitelist = "0123456789/";
engine.SetVariable("tessedit_char_whitelist", whitelist);
// 인식률을 높이기위한 숫자와 '/' 만 화이트리스트 적용
var result = engine.Process(pix);
string HP = result.GetText();
HP = HP.Replace(" ", "");
// 공백제거
hp_num.Text = HP;
//string[] split = HP.Split('/'); 필요없는 부분 삭제
// 현재와 맥스수치를 나눠주기 구분점 '/'
//hp_per.Text = "HP : " + (Convert.ToSingle(split[0]) * 100 / Convert.ToSingle(split[1])).ToString("##.##") + "%";
// 필요없는 부분 삭제
}
button4_click 함수는 button4를 삭제하였으나
hp_per label에 띄울 hp%를 구해야하니
필요하겠군요
위의 과정을 반복합니다.
private void function(object sender, EventArgs e)
{
if (searchtext.Text != null || searchtext.Text != " ")
{
searchbutton.PerformClick();
}
Rectangle rect = new Rectangle(130, 13, 85, 20);
// x,y 시작좌표 x,y범위 생성
Bitmap cap = screen.Clone(rect, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
// 캡쳐뜬 이미지에서 위의 Rect 만큼 자르기
cap = new Bitmap(cap, new Size(170, 40));
// 인식률을 높이기위해 두배크기로 리사이즈
for (int i = 0; i < cap.Width; i++)
{
for (int j = 0; j < cap.Height; j++)
{
Color c = cap.GetPixel(i, j);
int binary = (c.R + c.G + c.B) / 3;
if (binary > 190)
cap.SetPixel(i, j, Color.White);
else
cap.SetPixel(i, j, Color.Black);
}
}
// 간단한 for을 통한 Binary(이진화)
//screen = cap; 필요없는부분 삭제
//pictureBox2.Image = screen; 필요없는 부분 삭제
// picturebox2 에 자르고 키우고 이진화한 이미지 띄우기
//Pix pix = PixConverter.ToPix(screen); 함수간소화를 위해 수정
Pix pix = PixConverter.ToPix(cap);
var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.TesseractOnly);
// tesseractengine 생성
string whitelist = "0123456789/";
engine.SetVariable("tessedit_char_whitelist", whitelist);
// 인식률을 높이기위한 숫자와 '/' 만 화이트리스트 적용
var result = engine.Process(pix);
string HP = result.GetText();
HP = HP.Replace(" ", "");
// 공백제거
hp_num.Text = HP;
//string[] split = HP.Split('/'); 필요없는 부분 삭제
// 현재와 맥스수치를 나눠주기 구분점 '/'
//hp_per.Text = "HP : " + (Convert.ToSingle(split[0]) * 100 / Convert.ToSingle(split[1])).ToString("##.##") + "%";
// 필요없는 부분 삭제
Rectangle rect2 = new Rectangle(75, 30, 199, 4);
// rect변수가 겹치므로 rect2로 수정
// x,y 시작좌표 x,y범위 생성 hp바의 맨밑줄만 떠오는것
//Bitmap cap = new Bitmap(pictureBox1.Image); 필요없는 부분 삭제
// 버튼2 클릭 이벤트에서 screen값을 바꾸었기때문에 다시 picturebox1이미지 불러오기
Bitmap cap2 = screen.Clone(rect2, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
// 겹치는 변수 수정 & 클론값 수정
// 캡쳐뜬 이미지에서 위의 Rect 만큼 자르기
cap2 = new Bitmap(cap2, new Size(174, 10));
// 변수 수정
// 보기좋게 picturebox3과 같은 크기로 리사이즈
//pictureBox3.Image = cap; 필요없는 부분 삭제
// picturebox3에 이미지 띄우기
int point = 0;
for (int i = cap2.Width - 1; i > 0; i--)
{
Color c = cap2.GetPixel(i, cap2.Height - 1);
if (c.R > 200)
{
point = i + 1;
break;
}
}
// for 반복문을 통해 뒷 픽셀부터 확인하여 red값이 200이넘는 픽셀위치찾기
hp_per.Text = (100 * point / cap2.Width).ToString("##.##");
//label명 변경
// 찾은 픽셀 위치를 전체 범위에서 몇퍼센트인지 hp_per에 출력
}
이제 마지막으로 실행버튼을 눌렀을 시
작동하던 timer의 이벤트 함수를 새로만든 함수로 수정해주고
function_ber 함수에 있던
기능을 새로운 함수로 옮기면 끝입니다.
우선 timer 수정
public Form1()
{
InitializeComponent();
timer = new Timer();
// 타이머 선언
timer.Interval = 500;
// 타이머 실행간격 500ms(0.5초)
//timer.Tick += new EventHandler(function_ber);
// 타이머 실행시 호출될 함수설정
timer.Tick += new EventHandler(function);
// 새로만든 함수로 timer.tick 수정
}
마지막으로 function_ber 함수를 복붙 수정해주면
private void function(object sender, EventArgs e)
{
if (searchtext.Text != null || searchtext.Text != " ")
{
searchbutton.PerformClick();
}
Rectangle rect = new Rectangle(130, 13, 85, 20);
// x,y 시작좌표 x,y범위 생성
Bitmap cap = screen.Clone(rect, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
// 캡쳐뜬 이미지에서 위의 Rect 만큼 자르기
cap = new Bitmap(cap, new Size(170, 40));
// 인식률을 높이기위해 두배크기로 리사이즈
for (int i = 0; i < cap.Width; i++)
{
for (int j = 0; j < cap.Height; j++)
{
Color c = cap.GetPixel(i, j);
int binary = (c.R + c.G + c.B) / 3;
if (binary > 190)
cap.SetPixel(i, j, Color.White);
else
cap.SetPixel(i, j, Color.Black);
}
}
// 간단한 for을 통한 Binary(이진화)
//screen = cap; 필요없는부분 삭제
//pictureBox2.Image = screen; 필요없는 부분 삭제
// picturebox2 에 자르고 키우고 이진화한 이미지 띄우기
//Pix pix = PixConverter.ToPix(screen); 함수간소화를 위해 수정
Pix pix = PixConverter.ToPix(cap);
var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.TesseractOnly);
// tesseractengine 생성
string whitelist = "0123456789/";
engine.SetVariable("tessedit_char_whitelist", whitelist);
// 인식률을 높이기위한 숫자와 '/' 만 화이트리스트 적용
var result = engine.Process(pix);
string HP = result.GetText();
HP = HP.Replace(" ", "");
// 공백제거
hp_num.Text = HP;
//string[] split = HP.Split('/'); 필요없는 부분 삭제
// 현재와 맥스수치를 나눠주기 구분점 '/'
//hp_per.Text = "HP : " + (Convert.ToSingle(split[0]) * 100 / Convert.ToSingle(split[1])).ToString("##.##") + "%";
// 필요없는 부분 삭제
Rectangle rect2 = new Rectangle(75, 30, 199, 4);
// rect변수가 겹치므로 rect2로 수정
// x,y 시작좌표 x,y범위 생성 hp바의 맨밑줄만 떠오는것
//Bitmap cap = new Bitmap(pictureBox1.Image); 필요없는 부분 삭제
// 버튼2 클릭 이벤트에서 screen값을 바꾸었기때문에 다시 picturebox1이미지 불러오기
Bitmap cap2 = screen.Clone(rect2, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
// 겹치는 변수 수정 & 클론값 수정
// 캡쳐뜬 이미지에서 위의 Rect 만큼 자르기
cap2 = new Bitmap(cap2, new Size(174, 10));
// 변수 수정
// 보기좋게 picturebox3과 같은 크기로 리사이즈
//pictureBox3.Image = cap; 필요없는 부분 삭제
// picturebox3에 이미지 띄우기
int point = 0;
for (int i = cap2.Width - 1; i > 0; i--)
{
Color c = cap2.GetPixel(i, cap2.Height - 1);
if (c.R > 200)
{
point = i + 1;
break;
}
}
// for 반복문을 통해 뒷 픽셀부터 확인하여 red값이 200이넘는 픽셀위치찾기
hp_per.Text = (100 * point / cap2.Width).ToString("##.##");
//label명 변경
// 찾은 픽셀 위치를 전체 범위에서 몇퍼센트인지 hp_per에 출력
string setberhp = hp_ber.Text;
// 변수수정
// 입력한 값을 setberhp에 저장
string settelhp = hp_tel.Text;
// 추가한 기능을 위해 추가
float berhp = Convert.ToSingle(setberhp);
// 변수 수정
// 크기비교를 위해 setberhp값을 float형으로 변환
float telhp = Convert.ToSingle(settelhp);
// 추가한 기능을 위해 추가
// 크기비교를 위해 settelhp값을 float형으로 변환
float currenthp = Convert.ToSingle(hp_per.Text);
// 변수 수정
// 크기비교를 위해 픽셀서치로 찾은 HP%를 float형으로 변환 획득
if (berhp >= currenthp && bercheck == false) // 설정한 berhp 가 현재hp 보다 작아지면 & 베르를 한적이없으면
{
IntPtr xy = new IntPtr(1205 | (630 << 16));
// 8번 슬롯의 좌표값 (1280 x 720 해상도기준)
SendMessage(handle, 0x0201, IntPtr.Zero, xy);
SendMessage(handle, 0x0202, IntPtr.Zero, xy);
// 지정된 핸들에서 xy좌표에 대해 마우스좌측 누르기, 때기
// SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam)
// uint값에 넣어준 0x0201은 마우스좌버튼누르기, 0x0202는 마우스좌버튼때기
bercheck = true;
// 베르 하였으므로 체크변수 true
}
// tel하기위해 추가하는부분
if (telcount > 0)
{
if (telcount == 10)
telcount = 0;
else
telcount++;
}
// 연속tel을 방지하기위한 코드입니다.
if (telhp >= currenthp && telcount == 0) // 설정한 telhp 가 현재hp 보다 작아지면
{
IntPtr xy = new IntPtr(1125 | (630 << 16));
// 7번 슬롯의 좌표값 (1280 x 720 해상도기준)
SendMessage(handle, 0x0201, IntPtr.Zero, xy);
SendMessage(handle, 0x0202, IntPtr.Zero, xy);
// 지정된 핸들에서 xy좌표에 대해 마우스좌측 누르기, 때기
// SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam)
// uint값에 넣어준 0x0201은 마우스좌버튼누르기, 0x0202는 마우스좌버튼때기
telcount++;
}
}
테스트결과는 지난 시간과 다를바가 없으므로 딱히 추가하지않겠습니다.
큰 틀이 하나 끝났으니 지금까지의 결과물 첨부하겠습니다.
※강의에 따라 결과물은 ldplayer(舊 모모)에서만 작동을 보장합니다.
※플레이어 해상도는 1280 * 720 기준으로 만들었으므로 해당해상도에서만 작동을 보장합니다.
※본 프로그램을 씀으로서 불이익은 사용자에게 있습니다.
전체 코드 & 파일링크
https://drive.google.com/open?id=1CLQLDNPJuZ24Rjt8DAsAhZlMcBnzMd3t
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Tesseract;
namespace imagesearch
{
public partial class Form1 : Form
{
[DllImport("User32", EntryPoint = "FindWindow")]
private static extern IntPtr FindWindow(string IpClassName, string IpWindowName);
[DllImport("user32")]
private static extern IntPtr FindWindowEx(IntPtr hWnd1, int hWnd2, string lp1, string lp2);
[DllImport("user32.dll")]
internal static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcblt, int nFlags);
[DllImport("user32.dll")]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern bool SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
Bitmap screen;
IntPtr handle;
Timer timer;
bool bercheck = false;
int telcount = 0;
public Form1()
{
InitializeComponent();
timer = new Timer();
// 타이머 선언
timer.Interval = 500;
// 타이머 실행간격 500ms(0.5초)
//timer.Tick += new EventHandler(function_ber);
// 타이머 실행시 호출될 함수설정
timer.Tick += new EventHandler(function);
// 새로만든 함수로 timer.tick 수정
}
private void Button1_Click(object sender, EventArgs e)
{
string a = searchtext.Text;
IntPtr b = FindWindow(null, a);
IntPtr c = FindWindowEx(b, 0, "RenderWindow", "TheRender");
handle = c;
// 전역 변수 handle에 핸들값 c를저장
Graphics gdata = Graphics.FromHwnd(c);
Rectangle rect = Rectangle.Round(gdata.VisibleClipBounds);
Bitmap bmp = new Bitmap(rect.Width, rect.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
IntPtr hdc = g.GetHdc();
PrintWindow(c, hdc, 0x2);
g.ReleaseHdc(hdc);
}
screen = new Bitmap(bmp, new Size(1280, 720));
pictureBox1.Image = new Bitmap(screen, new Size(320, 180));
// picturebox 크기에 맞게 이미지리사이즈
}
private void Button5_Click(object sender, EventArgs e)
{
if(timer.Enabled == false)
{
timer.Start();
}
else if(timer.Enabled == true)
{
timer.Stop();
}
}
private void function(object sender, EventArgs e)
{
if (searchtext.Text != null || searchtext.Text != " ")
{
searchbutton.PerformClick();
}
Rectangle rect = new Rectangle(130, 13, 85, 20);
// x,y 시작좌표 x,y범위 생성
Bitmap cap = screen.Clone(rect, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
// 캡쳐뜬 이미지에서 위의 Rect 만큼 자르기
cap = new Bitmap(cap, new Size(170, 40));
// 인식률을 높이기위해 두배크기로 리사이즈
for (int i = 0; i < cap.Width; i++)
{
for (int j = 0; j < cap.Height; j++)
{
Color c = cap.GetPixel(i, j);
int binary = (c.R + c.G + c.B) / 3;
if (binary > 190)
cap.SetPixel(i, j, Color.White);
else
cap.SetPixel(i, j, Color.Black);
}
}
// 간단한 for을 통한 Binary(이진화)
//screen = cap; 필요없는부분 삭제
//pictureBox2.Image = screen; 필요없는 부분 삭제
// picturebox2 에 자르고 키우고 이진화한 이미지 띄우기
//Pix pix = PixConverter.ToPix(screen); 함수간소화를 위해 수정
Pix pix = PixConverter.ToPix(cap);
var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.TesseractOnly);
// tesseractengine 생성
string whitelist = "0123456789/";
engine.SetVariable("tessedit_char_whitelist", whitelist);
// 인식률을 높이기위한 숫자와 '/' 만 화이트리스트 적용
var result = engine.Process(pix);
string HP = result.GetText();
HP = HP.Replace(" ", "");
// 공백제거
hp_num.Text = HP;
//string[] split = HP.Split('/'); 필요없는 부분 삭제
// 현재와 맥스수치를 나눠주기 구분점 '/'
//hp_per.Text = "HP : " + (Convert.ToSingle(split[0]) * 100 / Convert.ToSingle(split[1])).ToString("##.##") + "%";
// 필요없는 부분 삭제
Rectangle rect2 = new Rectangle(75, 30, 199, 4);
// rect변수가 겹치므로 rect2로 수정
// x,y 시작좌표 x,y범위 생성 hp바의 맨밑줄만 떠오는것
//Bitmap cap = new Bitmap(pictureBox1.Image); 필요없는 부분 삭제
// 버튼2 클릭 이벤트에서 screen값을 바꾸었기때문에 다시 picturebox1이미지 불러오기
Bitmap cap2 = screen.Clone(rect2, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
// 겹치는 변수 수정 & 클론값 수정
// 캡쳐뜬 이미지에서 위의 Rect 만큼 자르기
cap2 = new Bitmap(cap2, new Size(174, 10));
// 변수 수정
// 보기좋게 picturebox3과 같은 크기로 리사이즈
//pictureBox3.Image = cap; 필요없는 부분 삭제
// picturebox3에 이미지 띄우기
int point = 0;
for (int i = cap2.Width - 1; i > 0; i--)
{
Color c = cap2.GetPixel(i, cap2.Height - 1);
if (c.R > 200)
{
point = i + 1;
break;
}
}
// for 반복문을 통해 뒷 픽셀부터 확인하여 red값이 200이넘는 픽셀위치찾기
hp_per.Text = (100 * point / cap2.Width).ToString("##.##");
//label명 변경
// 찾은 픽셀 위치를 전체 범위에서 몇퍼센트인지 hp_per에 출력
string setberhp = hp_ber.Text;
// 변수수정
// 입력한 값을 setberhp에 저장
string settelhp = hp_tel.Text;
// 추가한 기능을 위해 추가
float berhp = Convert.ToSingle(setberhp);
// 변수 수정
// 크기비교를 위해 setberhp값을 float형으로 변환
float telhp = Convert.ToSingle(settelhp);
// 추가한 기능을 위해 추가
// 크기비교를 위해 settelhp값을 float형으로 변환
float currenthp = Convert.ToSingle(hp_per.Text);
// 변수 수정
// 크기비교를 위해 픽셀서치로 찾은 HP%를 float형으로 변환 획득
if (berhp >= currenthp && bercheck == false) // 설정한 berhp 가 현재hp 보다 작아지면 & 베르를 한적이없으면
{
IntPtr xy = new IntPtr(1205 | (630 << 16));
// 8번 슬롯의 좌표값 (1280 x 720 해상도기준)
SendMessage(handle, 0x0201, IntPtr.Zero, xy);
SendMessage(handle, 0x0202, IntPtr.Zero, xy);
// 지정된 핸들에서 xy좌표에 대해 마우스좌측 누르기, 때기
// SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam)
// uint값에 넣어준 0x0201은 마우스좌버튼누르기, 0x0202는 마우스좌버튼때기
bercheck = true;
// 베르 하였으므로 체크변수 true
}
// tel하기위해 추가하는부분
if (telcount > 0)
{
if (telcount == 10)
telcount = 0;
else
telcount++;
}
// 연속tel을 방지하기위한 코드입니다.
if (telhp >= currenthp && telcount == 0) // 설정한 telhp 가 현재hp 보다 작아지면
{
IntPtr xy = new IntPtr(1125 | (630 << 16));
// 7번 슬롯의 좌표값 (1280 x 720 해상도기준)
SendMessage(handle, 0x0201, IntPtr.Zero, xy);
SendMessage(handle, 0x0202, IntPtr.Zero, xy);
// 지정된 핸들에서 xy좌표에 대해 마우스좌측 누르기, 때기
// SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam)
// uint값에 넣어준 0x0201은 마우스좌버튼누르기, 0x0202는 마우스좌버튼때기
telcount++;
}
}
}
}