在C++Builder中如何使用游戏操纵杆

作者&投稿:逄霍 (若有异议请与网页底部的电邮联系)
~
在Windows环境下通过编程来操纵鼠标、键盘是一件再简单不过的事了,不过大家有没有想过要尝试一下另一样我们比较常见的输入工具——游戏操纵杆呢?在某些情况下,尤其是象编制一些小型的游戏软件的时候,加入对游戏操纵杆的支持可以给使用者提供更为友好的人机界面,极大的提高游戏软件的可玩性。
C++Builder中没有专门控制操纵杆函数(其实在常见的编程语言中基本上都没有),因此要增加对游戏操纵杆的支持,就要和Windows的MCI API函数打交道,这里我们首先介绍一些在读取操纵杆的属性、状态,位置和按钮信息时要用到的API函数、常量及数据结构。
相关常量:
#define MM_JOY1MOVE 0x3A0 /* 用以传递操纵杆当前状态的一些消息 */
#define MM_JOY2MOVE 0x3A1
#define MM_JOY1ZMOVE 0x3A2
#define MM_JOY2ZMOVE 0x3A3
#define MM_JOY1BUTTONDOWN 0x3B5
#define MM_JOY2BUTTONDOWN 0x3B6
#define MM_JOY1BUTTONUP 0x3B7
#define MM_JOY2BUTTONUP 0x3B8
#define JOY_BUTTON1 0x0001 /* 用以表明当前操纵杆的状态 */
#define JOY_BUTTON2 0x0002
#define JOY_BUTTON3 0x0004
#define JOY_BUTTON4 0x0008
#define JOY_BUTTON1CHG 0x0100
#define JOY_BUTTON2CHG 0x0200
#define JOY_BUTTON3CHG 0x0400
#define JOY_BUTTON4CHG 0x0800
/* 游戏操纵杆错误返回值 */
#define JOYERR_BASE 160
#define JOYERR_NOERROR (0) /* 正常 */
#define JOYERR_ParmS (JOYERR_BASE+5) /* 参数错误 */
#define JOYERR_NOCANDO (JOYERR_BASE+6) /* 无法正常工作 */
#define JOYERR_UNPLUGGED (JOYERR_BASE+7) /* 操纵杆未连接 */
/* 操纵杆标识号 */
#define JOYSTICKID1 0
#define JOYSTICKID2 1
相关函数:
WINMMAPI UINT WINAPI joyGetNumDevs(void);
获取设备标识号。
MMRESULT WINAPI joyGetDevCaps(UINT uJoyID, LPJOYCAPS pjc, UINT cbjc);
获取操纵杆属性信息,以结构体JoyCaps接收。
WINMMAPI MMRESULT WINAPI joyGetPos(UINT uJoyID, LPJOYINFO pji);
获取操纵杆位置和按钮状态,以结构体接收。
WINMMAPI MMRESULT WINAPI joyGetThreshold(UINT uJoyID, LPUINT puThreshold);
读取操纵杆移动阈值。
WINMMAPI MMRESULT WINAPI joyReleaseCapture(UINT uJoyID);
结束对操纵杆信息的接收。
WINMMAPI MMRESULT WINAPI joySetCapture(HWND hwnd, UINT uJoyID, UINT uPeriod,
BOOL fChanged);
设置接收某一操纵杆的信息的窗口以及将何种频度接收。
WINMMAPI MMRESULT WINAPI joySetThreshold(UINT uJoyID, UINT uThreshold);
设置操纵杆移动阈值。
相关结构体: typedef struct joyCaps{
WORD wMid; /* 制造商标识 */
WORD wPid; /* 生产编号 */
char szPname[MAXPNAMELEN]; /* 产品名称 */
UINT wXmin; /* X轴最小值 */
UINT wXmax; /* X轴最大值 */
UINT wYmin; /* Y轴最小值 */
UINT wYmax; /* Y轴最大值 */
UINT wZmin; /* Z轴最小值 */
UINT wZmax; /* Z轴最大值 */
UINT wNumButtons; /* 按钮数 */
UINT wPeriodMin; /* 最小调用间隔时间(单位 毫秒)*/
UINT wPeriodMax; /* 最大调用间隔时间(单位 毫秒)*/
}JOYCAPS, *PJOYCAPS, NEAR *NPJOYCAPS, FAR *LPJOYCAPS;
typedef struct joyInfo{
UINT wXpos; /* x 轴位置 */
UINT wYpos; /* y 轴位置 */
UINT wZpos; /* z 轴位置 */
UINT wButtons; /* 按钮状态 */
} JOYINFO, *PJOYINFO, NEAR *NPJOYINFO, FAR *LPJOYINFO;
以上这些定义存储在mmsystem.h文件中,所以程序要包含这个头文件。
程序需要首先检查游戏操纵杆的存在,这包括了检查驱动程序支持和确认操纵杆已与系统相连的两项工作。joyGetNumDevs调用检查系统是否配置了游戏端口和驱动程序。如果返回值为零,表明不支持操纵杆功能。如果joyGetNumDevs返回值不为零,则说明系统支持游戏操纵杆功能。但joyGetNumDevs并不能确定操纵杆是否已被连接上了,通过调用可以完成这些工作,并检查是否有错误发生。
如果有游戏端口,joyGetNumDevs返回值通常为16.
一旦确认了操纵杆已连上,就可以接受器发来的消息。joySetCapture通知Windows操纵杆消息应发送到哪里机发送的频率如何。
joySetCapture中的第一个参数通知Windows谁将得到消息,第二个参数确定程序将从那个操纵杆接收消息。第三个参数时表示希望以怎样的频度接受JM_MOVE消息(单位为毫秒),无论操纵杆是否移动,都将以这个频度接受JM_MOVE消息。joySetCapture的四个参数允许程序当操纵杆移动一定的距离后才接受消息。该距离由joySetThreshold设置。
joySetCapture被调用后,窗口将接受操纵杆事件。MM_JOYXMOVE(X=操纵杆号)事件已joySetCapture定义的时间间隔发生。只有当操纵杆的按钮被按下时,MM_JOYXBUTTONUP和MM_JOYXBUTTONDOWN事件才发生。操纵杆时间出发句柄,改变相应的标签状态信息。移动消息也同时通知程序在新的位置重画操纵杆标志。调用joyReleaseCapture通知Windows已结束操纵杆的调用。
在实际编制程序时,应首先在Form1的头文件Form1.h中加入对mmsystem.h的引用,再加入一些相关的消息映射即对MM_JOYXMOVE、MM_JOYXBUTTONUP和MM_JOYXBUTTONDOWN事件的响应函数说明。
#include mmsystem.h
//--------------------
class Tform1:public TForm
{
__published:
...
...
private:
...
TPoint Position;//用于存储操纵杆的坐标位置。
...
public:
MESSAGE_HANDLER(MM_JOY1BUTTONDOWN,TMessage,JMButonUpdate)
MESSAGE_HANDLER(MM_JOY1BUTTONUP,TMessage,JMButonUpdate)
MESSAGE_HANDLER(MM_JOY1MOVE,TMessage,JMMove)
END_MESSAGE_MAP(TForm)
};
在Form1的OnCreate事件中加入以下代码用以检测操纵杆。
void __fastcall TForm1::FormCreate(TObject *Sender)
{
DriverCount = joyGetNumDevs();
Connected = false;
MMRESULT JoyResult;
JOYINFO JoyInfo;
//检查系统是否配置了游戏端口和驱动程序。
if(DriverCount != 0)
{
//仍需调用joyGetPos进行检测,如果返回JOYERR_NOERROR则表示操纵杆连接正常。
//测试第一个操纵杆。
JoyResult = joyGetPos(JOYSTICKID1,JoyInfo);
if(JoyResult == JOYERR_NOERROR )
{
Connected = true;
JoystickID = JOYSTICKID1;
}
//如果发生INVALIDPARAM错误,则退出。
else if(JoyResult == MMSYSERR_INVALPARAM)
Application-MessageBox("An error occured while calling joyGetPos",
"Error", MB_OK);
// 如果第一个操纵杆为连接,则检查第二个操纵杆。
else if((JoyResult=joyGetPos(JOYSTICKID2,JoyInfo)) == JOYERR_NOERROR)
{
Connected = true;
JoystickID = JOYSTICKID2;
}
}
}
在确定操纵杆已正确连接之后就可以读取操纵杆的设备信息。
void TForm1::ShowDeviceInfo(void)
{
joyGetDevCaps(JoystickID,JoyCaps, sizeof(JOYCAPS));
Label1-Caption = "Number of joysticks supported by driver = " +
IntToStr(DriverCount);
Label2-Caption = "Current Joystick ID = " +
IntToStr(intJoystickID);
Label3-Caption = "Manufacturer ID = " +
IntToStr(JoyCaps.wMid);
Label4-Caption = "Product ID = " +
IntToStr(JoyCaps.wPid);
Label5-Caption = "Number of buttons = "+
IntToStr(JoyCaps.wNumButtons);
.
.
.
// 设置当前窗口接收操纵杆信息。
if(Connected)
joySetCapture(Handle,JoystickID,2*JoyCaps.wPeriodMin,FALSE);
//计算操纵杆活动范围和屏幕范围的比率,在后面绘制操纵杆标志时会用到。
XDivider = (JoyCaps.wXmax - JoyCaps.wXmin)/ Width;
YDivider = (JoyCaps.wYmax - JoyCaps.wYmin)/ Height;
}
读取操纵杆位置信息和按钮状态:
void TForm1::ShowStatusInfo(void)
{
if(Connected)
{
JOYINFO JoyInfo;
TPoint Position;
joyGetPos(JoystickID,JoyInfo);
Position.x = JoyInfo.wXpos;
Position.y = JoyInfo.wYpos;
//显示操纵杆的X、Y轴位置。
Label6-Caption = "X Position = " + IntToStr(int(JoyInfo.wXpos));
Label7-Caption = "Y Position = " + IntToStr(int(JoyInfo.wYpos));
//判断某按钮是否被按下,这里只是指按钮初始的状态。
if(JoyInfo.wButtons
JOY_BUTTON1)
Label8-Caption = "Button 1 = Pressed";
else
Label8-Caption = "Button 1 = Not Pressed";
}
}
下面可以编写用以响应当初在头文件中定义的事件JMMove、JMButtonUpdate的代码: JMButtonUpdate的代码:
void __fastcall TForm1::JMMove(TMessage msg)
{
/*当操纵杆位置发生变化时会自动调用本函数。
在本函数中经常是根据操纵杆当前的位置来绘制操纵杆在屏幕上显示的标志,并擦 去原来的标志。这里只是简单的改变Image的坐标位置来表示操纵杆为的移动。 */
Position.x = msg.LParamLo;
Position.y = msg.LParamHi;
//计算新的坐标。
ScreenX = (Position.x-JoyCaps.wXmin)/XDivider - ImageList1-Width/2;
ScreenY = (Position.y-JoyCaps.wYmin)/YDivider - ImageList1-Height/2;
//显示新位置的X、Y值。
Label6-Caption = "X Position = " + IntToStr(int(Position.x));
Label7-Caption = "Y Position = " + IntToStr(int(Position.y));
//移动Image的位置。
Image1-Top=ScreenY;
Image1-Left=ScreenX;
}
void __fastcall TForm1::JMButtonUpdate(TMessage msg)
{
//当程序接收到JM_BUTTONDOWN和JM_BUTTONUP消息时,即某一按钮的状态发生改变时,都会调用本函数。
if(msg.WParam
JOY_BUTTON1) //判断按钮1是否被按下
Label8-Caption = "Button 1 = Pressed";
else
Label8-Caption = "Button 1 = Not Pressed";
}
最后在程序退出的时候要记得关闭对操纵杆的调用,即在FormDestroy事件中加入joyReleaseCapture(JoystickID)。
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
if(Connected)
joyReleaseCapture(JoystickID);
}
最后说明一下,本程序在 CBuilder4/PWin98 SE 环境下通过,在 WindowsNT 下用到的API函数将会与本程序中介绍的函数有所不同,详细区别请参阅Windows API函数手册。另外在程序测试中,我仅使用了最一般的接在声卡上的那种普通4键模拟手柄。针对其他的操纵杆及新型USB手柄/操纵杆,还希望有条件的朋友自己去测试一下。



求高达动漫观看顺序
九、C.E. 70、机动战士高达SEED 十、无、SD高达Force 十一、C.E. 73、机动战士高达SEED DESTINY 十二、A.D. 2307(第一季)、机动战士高达00 十三、A.D. 2312(第二季)、机动战士高达00 《机动战士高达》自1979年登场以来,已成为日本动画作品中最著名、最经久不衰、最庞大、也是盈利最高的...

xcode导入工程
第五步:虽然可以编译了,但是在text**中引入SyCo**中的头文件还是报错,说是找不到头文件,这是因为头文件的搜索路径没有加上SyCo**的路径,在targets->builde settings->Search Paths->Header Search Paths中添加上SyCo**对应的目录"$(SRCROOT)\/SyCo**\/SyCo**"就不报错了,但是xcode对于SyCo**...

本人c语言菜鸟,问一下compile和build后的一大堆文件是什么意思_百度知 ...
compile是编译,build是构建。你编写的C程序是不能直接运行的,要先转换成电脑能直接运行的程序,这个过程就是compile和build。compile会把源程序编译成后缀名为.obj的目标文件,这个文件还要经过build变成可执行程序exe。这个exe文件就是可以运行的程序了。

c语言,用vc6.0++写一个程序,为什么编译和组建产生的文件名不同?_百度...
注意:编译生成的 .exe 文件在工程目录下的Debug文件夹内。以上面的工程为例,路径为 E:\\cDemo,打开看到有一个Debug文件夹,进入可以看到 cDemo.exe。在Debug目录中还会看到一个名为 hello.obj 的文件。.obj是VC\/VS生成的目标文件,类似于C-Free下的.o文件。工程文件说明 进入工程目录 E:\\cDemo...

flutter 反编译
>Reading program jar [C:\\Users\\Administrator\\.gradle\\caches\\transforms-1\\files-1.1\\flutter.jar\\ab2691c929f4d2aa57a17fa551e662a6\\jetified-flutter.jar] (filtered) >Reading program jar [F:\\dartSpace\\flutter\\jiddspace\\nfc\\build\\app\\intermediates\\flutter\\release\\libs.jar] (filtered) >Reading progra...

为什么multisim模拟单片机C程序build完闪退
这个问题引起的原因是main.asm文件路径太长引起的8051\/8052编译器不支持太长的路径名,你可以这样解决试试,将仿真相关的文件复制到别的地方,路径名不能太长如:e:\\new1\\project1\\然后再进行编译仿真。注:multisim提示的大概意思是说main.asm路径名长度超出8051\/8052编译器所能接受的长度,对C:\\...

什么版本的QQ适合使用16进制文件编辑器Ultraedit
对于使用QQ2000C Build 1230版的用户,可以查找如下字串:85C05F0F8489000000”,将找到的结果改为:85C05F0F8589000000,然后保存修改结果即可。对于使用QQ2000C Build 0825版QQ的用户,可以用UltraEdit打开QQ的主文件后查找:0F8564010000A1F0,找到后改为:E96501000090A1F0。对于使用QQ2000C Build 0630...

如何才能使CMake生成的可执行程序便于调试
1,在cmake的gui界面中设定生成Debug模式,2,在执行cmake时使用如下命令:cmake -DCMAKE_BUILD_TYPE=Debug\/Release path 关于ccmake的使用,这里简单的做个说明:1.首先在终端启动cmake的gui界面:"ccmake ." 效果如图:2.然后在gui中输入"c",效果如图:3.然后在gui中输入"e",效果如图...

为什么在ios上canvas动画很卡,是写法问题吗
这是js里面的代码,大家帮忙看看,html代码为,安卓上很流畅,背景是可以通过手势旋转的星河图var num = 200;var w = window.innerWidth;var h = window.innerHeight;var max = 100;var _x = 0;var _y = 0;var _z = 150;var dtr = function(d) {return d * Math.PI \/ 180;};var ...

求C语言各关键字的含义。急!!!
auto :声明自动变量 一般不使用 double :声明双精度变量或函数 int: 声明整型变量或函数 struct:声明结构体变量或函数 break:跳出当前循环 else :条件语句否定分支(与 if 连用)long :声明长整型变量或函数 switch :用于开关语句 case:开关语句分支 enum :声明枚举类型 register:声明积存器变量 ...

临高县18285739959: 如何在C++ Builder中使用OpenGL -
尹歪黄连: 第一步,选择一个编译环境 现在Windows系统的主流编译环境有Visual Studio,Broland C++ Builder,Dev-C++等,它们都是支持OpenGL的.但这里我们选择Visual Studio 2005作为学习OpenGL的环境. 第二步,安装GLUT工具包 GLUT不是OpenGL所必须的,...

临高县18285739959: 如何使用c++builder? -
尹歪黄连: 具体步骤: 1. 打开bcb 2. 由于一般默认一个工程,故选择菜单“file”-“close all”关闭默认打开的工程 3. 点击工具栏第一个按钮"New"打开"new item"对话框,在"new"中选择"console wizzard",会弹出一个对话框.在其中选择是C还...

临高县18285739959: 我们想用C++ Builder写一个五子棋小游戏的程序,不知道怎么写,求指教, -
尹歪黄连: 第一步,不考虑AI的情况下,先简单实现 单人 点击落子,点一次换一次黑白,落一次子判断一次输赢.第二步,加入socket通信两个人互落子.实现开局,落子,判断输赢,悔棋,认输,重新开局基本操作.第三步,加入AI,人机对战.

临高县18285739959: c++builder怎么使用dll -
尹歪黄连: 调用DLL 函数,首先通过选择菜单 Project | Add to Project 的方法,把引入库添加到你的 C++Builder 工程里;其次,在需要调用 DLL 函数的 C++ 源文件里为 DLL 头文件插入 #include 声明;最后添加调用 DLL 函数的代码.程序清单 A 和 B 包含...

临高县18285739959: 如何用C++ builder调试程序 -
尹歪黄连: 1.在代码前用鼠标左键点一下会出现一个红点(叫做断点),这样再运行程序时,程序运行到设置到断点处会停下来,按f8可以一句代码一句代码的运行,并且把鼠标指到你定义的变量上可以看到当前该变量的数值.在代码前再用鼠标左键点一下可以取消断点 2.要看数值,一般是在窗体中添加一个“按钮”控键,然后双击该按钮可以进入该按钮响应的函数,在函数内添家代码ShowMessage(str);其中str是字符串格式,这样程序运行时单击该按钮就可以显示str的内容 (如果要显示int型可以用ShowMessage(IntToStr(str));)

临高县18285739959: 如何使用C++Builder工具执行最简单输出? -
尹歪黄连: C++Builder 6.0 File 菜单 New 一项 最下边 Others...出现的窗体中选择 Console Wizard 一项,就是 控制台程序了

临高县18285739959: c++ builder 怎么使用js -
尹歪黄连: C++ Builder是由Borland公司推出的一款可视化集成开发工具.C++ Builder具有快速的可视化开发环境: 只要简单地把控件(Component)拖到窗体(Form)上,定义一下它的属性,设置一下它的外观,就可以快速地建立应用程序界面;C++ Builder内置了

临高县18285739959: SQLite在C++ Builder里怎么用 -
尹歪黄连: C++/C++builder中调用的方法都是一样的不需要任何组件,可以很方便的调用,,1.在C++builder中 你要用implib 吧sqlite3.lib转换成C++builder可以用的库.然后包含sqlite3.h头文件在文件目录下面放进去sqlite3.dll和sqlite3.lib然后在程序中 ...

临高县18285739959: 怎样用C++ Builder编写扫雷游戏
尹歪黄连: import java.awt.*; import javax.swing.*; import java.util.Random; import java.awt.event.*; class Min extends JPanel //雷的类 { //备注:鼠标的左键 = 1;右键 = 3;中键 = 2 private int flag = 0,statu = 0; //定义雷的属性 0:没有打开 1:打开 2:标示为...

临高县18285739959: c++ builder中怎样调用c -
尹歪黄连: 和调C++的结构体一样,根据声明形式的不同,对应两种调用方式:(注意两者实例化时的不同)/* 结构体声明 */ typedef struct { int a_value; } a_t; struct b_t { int b_value; };/* 结构体调用 */ a_t aa; printf("%d\n" , aa.a_value); struct b_t bb; printf("%d\n", bb.b_value);

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 星空见康网