用C#编写游戏脚本
大学宿舍玩游戏的时候,为了简化重复的键鼠动作,有学习过按键精灵和TC脚本开发工具,并做了一些小脚本,基本达到了当时的需求。不知不觉,已经毕业了3年了,无聊之余又玩起了游戏,对于一些无趣的重复行为,于是又想写个脚本来处理下。比如跑任务,自动补血等,没想到现在的游戏对于按键精灵和TC基本上都是封杀。对于我这种小白,过游戏安全检测这种棘手的事,也许花费很多时间,都没有结果。经常测试,发现游戏不会对自己写的C#脚本进行检测,所以决定用C#来写。
研究了几天,突然间又不想玩游戏了,所以把这几天的研究成果分享给大家,希望对后来的人有启发。我玩的是一款QQ的游戏,我想要做的脚本就是 扫货脚本(当有人摆摊价格低于自己预设的价格时,自动购买下来,倒卖)。
经过分析,最难的步骤是怎么识别摊位上的价格,第一感觉,这不就是文字识别吗,于是找了一个.Net 唯一开源的Tesseract-ocr。经过测试,发现Tesseract-ocr只适合白底黑字的文字识别,于是对图片进行了以下处理
- 变灰度图
- 增加亮度100
- 增加对比度100
- 变黑白
- //反向 游戏文字是白色的
/// <summary>
/// 反像
/// </summary>
/// <param name="bitmapImage"></param>
/// <returns></returns>
public static Bitmap ApplyInvert(Bitmap source)
{
//create a blank bitmap the same size as original
Bitmap newBitmap = new Bitmap(source.Width, source.Height); //get a graphics object from the new image
Graphics g = Graphics.FromImage(newBitmap); // create the negative color matrix
ColorMatrix colorMatrix = new ColorMatrix(new float[][]
{
new float[] {-1, 0, 0, 0, 0},
new float[] {0, -1, 0, 0, 0},
new float[] {0, 0, -1, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {1, 1, 1, 0, 1}
}); // create some image attributes
ImageAttributes attributes = new ImageAttributes(); attributes.SetColorMatrix(colorMatrix); g.DrawImage(source, new Rectangle(0, 0, source.Width, source.Height),
0, 0, source.Width, source.Height, GraphicsUnit.Pixel, attributes); //dispose the Graphics object
g.Dispose(); return newBitmap;
} /// <summary>
/// 图片变成灰度
/// </summary>
/// <param name="b"></param>
/// <returns></returns>
public static Bitmap ToGray(Bitmap b)
{
for (int x = 0; x < b.Width; x++)
{
for (int y = 0; y < b.Height; y++)
{
Color c = b.GetPixel(x, y);
int luma = (int)(c.R * 0.3 + c.G * 0.59 + c.B * 0.11);//转换灰度的算法
b.SetPixel(x, y, Color.FromArgb(luma, luma, luma));
}
}
return b;
} /// <summary>
/// 图像变成黑白
/// </summary>
/// <param name="b"></param>
/// <returns></returns>
public static Bitmap ToBlackWhite(Bitmap b)
{
for (int x = 0; x < b.Width; x++)
{
for (int y = 0; y < b.Height; y++)
{
Color c = b.GetPixel(x, y);
if (c.R < (byte)255)
{
b.SetPixel(x, y, Color.FromArgb(0, 0, 0));
}
}
}
return b;
} /// <summary>
/// 图像亮度调整
/// </summary>
/// <param name="b"></param>
/// <param name="degree"></param>
/// <returns></returns>
public static Bitmap KiLighten(Bitmap b, int degree)
{ if (b == null)
{ return null; } if (degree < -255) degree = -255; if (degree > 255) degree = 255; try
{ int width = b.Width; int height = b.Height; int pix = 0; BitmapData data = b.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); unsafe
{
byte* p = (byte*)data.Scan0; int offset = data.Stride - width * 3; for (int y = 0; y < height; y++)
{ for (int x = 0; x < width; x++)
{ // 处理指定位置像素的亮度 for (int i = 0; i < 3; i++)
{ pix = p[i] + degree; if (degree < 0) p[i] = (byte)Math.Max(0, pix); if (degree > 0) p[i] = (byte)Math.Min(255, pix); } // i p += 3; } // x p += offset; } // y } b.UnlockBits(data); return b; } catch
{ return null; } }
/// <summary>
/// 图像对比度调整
/// </summary>
/// <param name="b">原始图</param>
/// <param name="degree">对比度[-100, 100]</param>
/// <returns></returns> public static Bitmap KiContrast(Bitmap b, int degree)
{ if (b == null)
{ return null; } if (degree < -100) degree = -100; if (degree > 100) degree = 100; try
{ double pixel = 0; double contrast = (100.0 + degree) / 100.0; contrast *= contrast; int width = b.Width; int height = b.Height; BitmapData data = b.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); unsafe
{ byte* p = (byte*)data.Scan0; int offset = data.Stride - width * 3; for (int y = 0; y < height; y++)
{ for (int x = 0; x < width; x++)
{ // 处理指定位置像素的对比度 for (int i = 0; i < 3; i++)
{ pixel = ((p[i] / 255.0 - 0.5) * contrast + 0.5) * 255; if (pixel < 0) pixel = 0; if (pixel > 255) pixel = 255; p[i] = (byte)pixel; } // i p += 3; } // x p += offset; } // y
}
b.UnlockBits(data);
return b;
}
catch
{
return null;
}
}
经过以上处理,发现识别率高了很多,可是不知道什么原因对单个价格,如9,6,5 这种无法识别,而且对于3,8,0,很容易混淆,对于这种扫货的脚本来说,价格识别率必须是100%对的。后来又去学习怎么训练字库,花了很多时间,最终得出一个结论,OCR训练识别率的前提是 文字能被识别,但是识别错了,如果连文字都识别不出,那么没有训练的必要了,就这样,放弃了。
当天晚上,看了一篇别人识别网站验证码的文章,又看了国内的脚本开发的文字识别,看到大漠插件的字库,是一个个像素组成的字。灵光一闪,每个价格的笔画不同,位置不同,同样大小的图片,Base64值肯定不一样啊,第二天做了实验,证明自己的想法是对的,哪怕一个像素不对,都是不一样的。于是写了个脚本,把摊位里1-2000的价格都抓下来,然后处理成黑白后分割成小图片。
/// <summary>
/// 图像转Base字符串
/// </summary>
/// <returns></returns>
public static string ToBaseMd5(this Bitmap img)
{
if (img == null)
return string.Empty;
else
return Convert.ToBase64String(ToByte(img));
}
做脚本嘛,最重要的截取指定区域的图片嘛,直接上代码。
Bitmap image = new Bitmap(26, 18);
Graphics imgGraphics = Graphics.FromImage(image);
//设置截屏区域
imgGraphics.CopyFromScreen(X, Y, 0, 0, new Size(26, 18));
image.Save(path, ImageFormat.Tiff);
以上的技术,基本上可以把识别文字的价格问题解决了,当然中途花了很多时间来做重复的事。
接下来有个问题,怎么定位价格啊,各种按钮的位置,因此要找个参照物,简单的说就是,截取一个参考物的图片,然后其他元素的位置相对这个参照物进行设置。转化成技术来说,就是一张小图在另一张大图里面找到位置,并返回相对坐标。尝试了几种方法,最终使用 AForge 这个开源项目来处理,代码如下
/// <summary>
/// 判断图像是否存在
/// </summary>
/// <param name="template"></param>
/// <param name="bmp"></param>
/// <returns></returns>
public static bool ContainsImg(this Bitmap template, Bitmap bmp)
{
// create template matching algorithm's instance // (set similarity threshold to 92.1%)
ExhaustiveTemplateMatching tm = new ExhaustiveTemplateMatching(0.921f); // find all matchings with specified above similarity
TemplateMatch[] matchings = tm.ProcessImage(template, bmp); // highlight found matchings return matchings.Length > 0;
}
/// <summary>
/// 判断图像是否存在另外的图像中,并返回坐标
/// </summary>
/// <param name="template"></param>
/// <param name="bmp"></param>
/// <returns></returns>
public static Point ContainsGetPoint(this Bitmap template, Bitmap bmp)
{
// create template matching algorithm's instance // (set similarity threshold to 92.1%)
ExhaustiveTemplateMatching tm = new ExhaustiveTemplateMatching(0.921f); // find all matchings with specified above similarity
TemplateMatch[] matchings = tm.ProcessImage(template, bmp); // highlight found matchings
BitmapData data = template.LockBits(new Rectangle(0, 0, template.Width, template.Height), ImageLockMode.ReadWrite, template.PixelFormat);
Point p = new Point(); if (matchings.Length > 0)
{
Drawing.Rectangle(data, matchings[0].Rectangle, Color.White);
p = matchings[0].Rectangle.Location;
template.UnlockBits(data);
} return p;
}
现在价格可以识别了,通过找图,界面的各个坐标都确定了,现在就是写模拟鼠标和键盘的操作了。这个网上很多,我的很简单
对于我的游戏来说鼠标操作,就是移动和左击
public class MouseHelper
{
[DllImport("user32.dll")]
private static extern bool SetCursorPos(int X, int Y); [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, UIntPtr dwExtraInfo); /// <summary>
/// 鼠标左击
/// </summary>
public static void LeftClick()
{
mouse_event(0x02, 0, 0, 0, UIntPtr.Zero);
mouse_event(0x04, 0, 0, 0, UIntPtr.Zero);
}
/// <summary>
/// 鼠标移动到指定的位置
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public static void MovePoint(Point p)
{
SetCursorPos(p.X, p.Y);
}
}
键盘可以用C#自带的方法 SendKeys
SendKeys.Send("输入文本");//用于输入文字
SendKeys.SendWait("{ENTER}");用于输入按键命令
基本上就这些了,另外附上一些可能会用到的技能
找到游戏句柄
/// <summary>
/// 获取游戏句柄
/// </summary>
/// <returns></returns>
public static int GetFFoHandle()
{
Process[] processes = Process.GetProcessesByName("进程名称"); var p = processes.FirstOrDefault(); if (p == null)
{
return 0;
}
else
{
return p.MainWindowHandle.ToInt32();
}
}
根据句柄获取游戏的位置
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
根据句柄将游戏窗体移动到某个位置
/// <summary>
/// 根据句柄移动窗体
/// </summary>
/// <param name="hWnd"></param>
/// <param name="hWndInsertAfter"></param>
/// <param name="x"></param>
/// <param name="Y"></param>
/// <param name="cx"></param>
/// <param name="cy"></param>
/// <param name="wFlags"></param>
/// <returns></returns>
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
其他什么快捷键啊,啥的,网上一大堆就不写了。
好了,就这些,通过以上的代码,可以完成大部分简单的前台脚本了,写的比较乱,但是对于正在研究中的人,我想一定省了不少事。
用C#编写游戏脚本的更多相关文章
- 用Python写一个游戏脚本,你会吗?
前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:ivat4u 学习python有一段时间了,由于python语言的强大 ...
- 从游戏脚本语言说起,剖析Mono所搭建的脚本基础
0x00 前言 在日常的工作中,我偶尔能遇到这样的问题:“为何游戏脚本在现在的游戏开发中变得不可或缺?”.那么这周我就写篇文章从游戏脚本聊起,分析一下游戏脚本因何出现,而mono又能提供怎样的脚本基础 ...
- 编写shell脚本遇到的问题
运行shell脚本提示“syntax error near unexpected token for((i=0;i<$length;i++))”: 原因是因为Linux下的换行符是 \n 而你在 ...
- linux 使用文本编辑器编写shell脚本执行权限不够
在linux下,自己编写的脚本需要执行的时候,需要加上执行的权限 解决方式:chmod 777 test.sh
- Gradle 1.12 翻译——第十三章 编写构建脚本
有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...
- LoadRunner利用ODBC编写MySql脚本
最近做了几周的LoadRunner测试,有一些心得,记录下来,以便以后查找. LoadRunner测试数据库是模拟客户端去连接数据库服务器,因此,需要协议(或者说驱动的支持).LoadRunner本身 ...
- 实践作业2:黑盒测试实践——编写自动化脚本并拍摄测试过程视频 Day 6
下午下课之后小组成员一起交流了一下实验过程遇到的一些问题,并汇总了下各个项目完成情况 该实验目前(写博客是时间)基本完成,具体情况如下 (1)分析系统需求 .(done) (2)设计测试用例.(don ...
- 使用Python的requests模块编写请求脚本
requests模块可用来编写请求脚本. 比如,使用requests的post函数可以模拟post请求: resp = requests.post(url, data = content) url即为 ...
- 在windows下编写shell脚本
注意两点: 1.第一行:#!/bin/bash 2.将文档格式转换为unix,因为在windows下编写shell脚本回车符是\n\r,而linux下的回车符是\n,所以在linux下运行脚本的时候, ...
随机推荐
- UIDatePicker自定义背景
selectDatePicker = [[UIDatePicker alloc]init]; selectDatePicker.frame = CGRectMake(0, 10, 280, 21 ...
- 微软为Visual Studio开发助手拓展C++支持
近日,微软宣布了一项 Visual Studio“开发助手”(Developer Assistant)插件的重大更新,其现已支持“基于 C++ 的情境感知 web 解决方案”.开发助手能够嵌入 Vis ...
- 编译在arm板上使用的sqlite3的静动态库
采用的是sqlite-autoconf-3080002.tar.gz 解压 tar xvf sqlite-autoconf-3080002.tar.gz 进入 cd sqlite-autoconf-3 ...
- 【转 】实战手记:让百万级数据瞬间导入SQL Server
想必每个DBA都喜欢挑战数据导入时间,用时越短工作效率越高,也充分的能够证明自己的实力.实际工作中有时候需要把大量数据导入数据库,然后用于各种程序计算,本文将向大家推荐一个挑战4秒极限让百万级数据瞬间 ...
- fork()详解
参照: http://blog.csdn.net/jason314/article/details/5640969 http://coolshell.cn/articles/7965.html
- PLSQL_性能优化系列02_Oracle Join关联
2014-09-25 Created By BaoXinjian
- apache/php 开启 gzip压缩
1.php方式开启 原理: header("Content-Encoding: gzip"); echo gzencode('songjiankang'); 示例1: functi ...
- [实变函数]4.2 Egrov 定理
1 一致收敛很重要, 但可惜的是很多时候不一致收敛. 比如 $$\bex f_n(x)=x^n\to f(x)=\sedd{\ba{ll} 0,&x\in [0,1)\\ 1,&x=1 ...
- 树莓派3上安装Qt5
按照在2上的安装只安装了qt4,实际上qt5已经可以直接apt方式可以获取到树莓派上了. install qt5-default and qtcreator:$ sudo apt-get instal ...
- ubuntu vnc install
windows & ubuntu http://www.jb51.net/os/Ubuntu/104948.html ubuntu & ubuntu https://www.digit ...