DELPHI程序如何实现与游戏中的NPC对话

龙恩 2011-11-07 01:13:37
一直在玩大航海台服,最近对挂起了兴趣,想做个自动造船,后台模拟鼠标一切正常,但是在模拟键盘时出了点问题,就是在向游戏输入“TAB”或“回车”以及方向键的时候都没反应,无论是用sendmessage还是postmessage都不行。
游戏中直接操作是可以通过TAB键来切换屏幕上的NPC的,切到要对话的NPC按回车就可以对话,我的目的是想通过模拟输入TAB键来找到NPC,再按回车键与NPC对话。

精简后代码如下:
procedure TForm1.Button1Click(Sender: TObject);
var
hw:HWND;
begin
hw:=findwindow(nil,'大航海時代 Online');
if hw<>0 then
begin
sendmessage(hw,WM_LBUTTONDOWN,MK_LBUTTON,makelparam(637,417)); //在游戏屏幕的这个坐标点鼠标左键
sendmessage(hw,WM_LBUTTONUP,MK_LBUTTON,makelparam(637,417));
postmessage(hw,WM_keydown,$1B,0); //输入TAB键
sleep(1000);
postmessage(hw,WM_keydown,$0D,0); //输入回车
end;
end;

代码运行后可以通过,模拟鼠标输入正常,可是点击按钮游戏中没反应,postmessage换成sendmessage也不行,感觉有点像在记事本中少一个寻找子窗口的findwindowex的语句一样,但是用SPY++无法看到游戏中有子窗口,所以没法加findwindowex查找句柄。

请求高手能帮忙怎么样才能找到游戏中的NPC,或者如何能将键盘模拟到游戏中去。
...全文
248 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
龙恩 2011-11-08
  • 打赏
  • 举报
回复
另外13楼的朋友看清楚啊,F1在你那之前就已经解决了,代码在上面。
龙恩 2011-11-08
  • 打赏
  • 举报
回复
然后在主程序中就可以调用sendkey.pas中的函数

//前台进港
SetForegroundWindow(hw);
SetActiveWindow(hw);
sleep(1000);
sendmessage(hw,WM_LBUTTONDOWN,MK_LBUTTON,makelparam(344,66)); //必须要点一下游戏界面,否则在程序间切换
sendmessage(hw,WM_LBUTTONUP,MK_LBUTTON,makelparam(344,66));
SendKeyString(SK_CTRL_DN); //按下CTRL+左方向调整方向
for i:=0 to 25 do
SendKeyString(SK_left);
SendKeyString(SK_CTRL_UP);
sendkeystring(SK_TAB); //输入TAB
sendkeystring(SK_TAB);
sendkeystring(SK_ENTER); //输入回车


事实证明DELPHI没有什么是不可以的,继续努力冲击后台TAB和回车中,此贴可以结了,如果有高手能告知后台方法,感激不尽。
龙恩 2011-11-08
  • 打赏
  • 举报
回复
哈哈,搞定了,不过是前台的,通过sendkey来搞定的,TAB,回车全都可以了,把代码共享出来可以结贴了。

sendkey.pas

{****************************************************}
{ SendKeys Unit for Delphi 32 }
{ Copyright (c) 1999 by Borut Batagelj (Slovenia) }
{ Aleksey Kuznetsov (Ukraine) }
{ Home Page: www.utilmind.com }
{ E-Mail: info@utilmind.com }
{****************************************************}

unit SendKeys;

interface

uses
Windows, SysUtils;

const
SK_BKSP = #8;
SK_TAB = #9;
SK_ENTER = #13;
SK_ESC = #27;
SK_ADD = #107;
SK_SUB = #109;
SK_F1 = #228;
SK_F2 = #229;
SK_F3 = #230;
SK_F4 = #231;
SK_F5 = #232;
SK_F6 = #233;
SK_F7 = #234;
SK_F8 = #235;
SK_F9 = #236;
SK_F10 = #237;
SK_F11 = #238;
SK_F12 = #239;
SK_HOME = #240;
SK_END = #241;
SK_UP = #242;
SK_DOWN = #243;
SK_LEFT = #244;
SK_RIGHT = #245;
SK_PGUP = #246;
SK_PGDN = #247;
SK_INS = #248;
SK_DEL = #249;
SK_SHIFT_DN = #250;
SK_SHIFT_UP = #251;
SK_CTRL_DN = #252;
SK_CTRL_UP = #253;
SK_ALT_DN = #254;
SK_ALT_UP = #255;

procedure SendKeyString(Text: String);
procedure SendKeysToTitle(WindowTitle: String; Text: String);
procedure SendKeysToHandle(WindowHandle: hWnd; Text: String);
procedure MakeWindowActive(wHandle: hWnd);
function GetHandleFromWindowTitle(TitleText: String): hWnd;

implementation

procedure SendKeyString(Text: String);
var
i: Integer;
Shift: Boolean;
vk, ScanCode: Word;
ch: Char;
c, s: Byte;
const
vk_keys: Array[0..9] of Byte =
(VK_HOME, VK_END, VK_UP, VK_DOWN, VK_LEFT,
VK_RIGHT, VK_PRIOR, VK_NEXT, VK_INSERT, VK_DELETE);
vk_shft: Array[0..2] of Byte = (VK_SHIFT, VK_CONTROL, VK_MENU);
flags: Array[False..True] of Integer = (KEYEVENTF_KEYUP, 0);
begin
Shift := False;
for i := 1 to Length(Text) do
begin
ch := Text[i];
if ch >= #250 then
begin
s := Ord(ch) - 250;
Shift := not Odd(s);
c := vk_shft[s shr 1];
ScanCode := MapVirtualKey(c,0);
Keybd_Event(c, Scancode, Flags[shift], 0);
end
else
begin
vk := 0;
if ch >= #240 then
c := vk_keys[Ord(ch) - 240]
else
if ch >= #228 then {228 (F1) => $70 (vk_F1)}
c := Ord(ch) - 116
else
if ch < #110 then
c := Ord(ch)
else
begin
vk := VkKeyScan(ch);
c := LoByte(vk);
end;
ScanCode := MapVirtualKey(c,0);
if not Shift and (Hi(vk) > 0) then { $2A = scancode of VK_SHIFT }
Keybd_Event(VK_SHIFT, $2A, 0, 0);
Keybd_Event(c,scancode, 0, 0);
sleep(100);
Keybd_Event(c,scancode, KEYEVENTF_KEYUP, 0);
if not Shift and (Hi(vk) > 0) then
Keybd_Event(VK_SHIFT, $2A, KEYEVENTF_KEYUP, 0);
end;
end;
end;

procedure MakeWindowActive(wHandle: hWnd);
begin
if IsIconic(wHandle) then
ShowWindow(wHandle, SW_RESTORE)
else
BringWindowToTop(wHandle);
end;

function GetHandleFromWindowTitle(TitleText: String): hWnd;
var
StrBuf: Array[0..$FF] of Char;
begin
Result := FindWindow(PChar(0), StrPCopy(StrBuf, TitleText));
end;

procedure SendKeysToTitle(WindowTitle: String; Text: String);
var
Window: hWnd;
begin
Window := GetHandleFromWindowTitle(WindowTitle);
MakeWindowActive(Window);
SendKeyString(Text);
end;

procedure SendKeysToHandle(WindowHandle: hWnd; Text: String);
begin
MakeWindowActive(WindowHandle);
SendKeyString(Text);
end;

end.



龙恩 2011-11-08
  • 打赏
  • 举报
回复
郁闷了,放弃后台TAB,改用前台TAB也不行啊,按键精灵都可以啊,我的代码是:
 SetForegroundWindow(hw);
SetActiveWindow(hw);  //将游戏窗口激活到前台
sleep(1000);
keybd_event(9, MapVirtualKey(9, 0), 0, 0);
keybd_event(9, MapVirtualKey(9, 0), KEYEVENTF_KEYUP, 0);
仍然没反应啊,回车也一样,其他键都可以,这是什么原因啊?
龙恩 2011-11-08
  • 打赏
  • 举报
回复
有突破啊,F功能键终于可以传递了,只需要把postmessage的lparam参数重新构造一下就可以实现了。现在就只有TAB和回车了,不敢独享,共享一下!
function VKB_param(VirtualKey:Integer;flag:Integer):Integer; //函数名
var
s,Firstbyte,Secondbyte:String;
S_code:Integer;
Begin
if flag=1 then //按下键
begin
Firstbyte :='00'
end
else //弹起键
begin
Firstbyte :='C0'
end;
S_code:= MapVirtualKey(VirtualKey, 0);
Secondbyte:='00'+inttostr(s_code);
Secondbyte:=copy(Secondbyte,Length(Secondbyte)-1,2);
s:='$'+Firstbyte + Secondbyte + '0001';
Result:=strtoint(s);
End;

procedure TForm1.Button7Click(Sender: TObject);
VAR
lparam:integer;
begin
lparam := VKB_param($77, 1); {按下键F8}
postmessage(hw,WM_keydown,$77,lparam);
end;

龙恩 2011-11-08
  • 打赏
  • 举报
回复
谢谢FUNXU,这篇资料我也看过了,不过大部分的资料都是介绍怎么用DINPUT来做游戏,讲的是游戏怎么获得键盘消息,而不是将键盘消息传递到已有的游戏程序中去,但我在一些论坛里看到有些高人用到这个来做,可惜都没有细讲。
9楼的高人的意见我不太同意,发按键消息不行的话那为啥按键精灵却可以呢,我就不信DELPHI还不如按键强大,大部分的功能我都实现了,包括数字字母等的按键传递,现在就差这些功能键了,苍天啊,赐我一名高手师傅吧,嘿嘿
爱蹄子的羊头 2011-11-08
  • 打赏
  • 举报
回复
你先试试 按键精灵是否可以模拟键盘按键


找CALL 是在反汇编中,找到这个地址

(不管你按什么键,最终要执行的是一段代码/函数)

找到这个函数地址调用就行了. 很难找.



另外 USB 的鼠标和键盘 有可能导致失败

//

再另外. 按键精灵据说有驱动级的 钩子 , 可以使用
youyidianmimang 2011-11-08
  • 打赏
  • 举报
回复
keybd_event 肯定都不能的; 我试过
youyidianmimang 2011-11-08
  • 打赏
  • 举报
回复
哈哈 找到 问同个问题的人了;
普通字符 'a' 之类的 可以 postmessage 进去 ;就是 f1 之类的 就不可以了;
等待 楼主解决;
龙恩 2011-11-07
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 funxu 的回复:]
LZ查一下directxinput
[/Quote]

查了很多directxinput的资料,感觉3楼说的靠谱,但是这方面的实例很少,具体使用还有很大难度,麻烦能不能给个简单的实例参考一下?不用鼠标或手柄的,只要有键盘操作的就行。
tataishan 2011-11-07
  • 打赏
  • 举报
回复
发送按键消息是不行的

楼主可以模拟物理按键

但这样游戏必须前台运行
funxu 2011-11-07
  • 打赏
  • 举报
回复
LZ查一下directxinput
龙恩 2011-11-07
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 ghostcsa 的回复:]
od找call!!!
[/Quote]

麻烦能讲解的详细点吗,没看明白呢!
funxu 2011-11-07
  • 打赏
  • 举报
回复
另外这里有个贴子,仅供参考吧
http://www.cnblogs.com/keycode/archive/2010/10/16/1853166.html
funxu 2011-11-07
  • 打赏
  • 举报
回复
我记得有本delphi的关于游戏编程的书里提到过,但是名字忘记了,不行Lz去当当看看
funxu 2011-11-07
  • 打赏
  • 举报
回复
呃........抱歉,因为我以前做过勾子想修改游戏,但是失败了,后来查资料发现是directxinput,因为这方面的代码大都是c的,因为不是工作需要,所以后来也就放弃了,不好意思哦
龙恩 2011-11-07
  • 打赏
  • 举报
回复
最关键的是怎么把TAB键或回车键以及F功能键传送到游戏中去,后台操作最好,按键精灵可以将F功能键传递到游戏中去。
ghostcsa 2011-11-07
  • 打赏
  • 举报
回复
od找call!!!

1,183

社区成员

发帖
与我相关
我的任务
社区描述
Delphi GAME,图形处理/多媒体
社区管理员
  • GAME,图形处理/多媒体社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧