本人正在尝试用QT开发一个STG游戏,遇到了一个难题:如何才能使用键盘控制物体在2D平面上自由移动?遍查网上的文档和博客,大多只能实现物体上下左右移动,达不到我需要的标准(比如斜方向移动)。最近终于想出了一个解决方案。(最终效果在文章最后)
假如我声明定义了一个自机(可操作角色)如下,并创建了一个对象作为主界面类的子对象:
//自机类
//声明
#include <QPixmap>
#include <QRect>
class Hero
{
public:
Hero();
void setPosition(int x,int y);//设置自机的位置
QPixmap Plane;
int X;
int Y;
QRect PlaneRect;
};
//定义略
如果需要键盘控制,需要重写游戏主界面类(我这里叫Widget)的keypressevent事件。显然,在事件中使用if...else if...else...这样的结构是绝对达不到我的标准的(因为这样只能一次识别一个键)。
为了解决问题,我增加了一个“移动向量”,声明定义了一个MoveVector类:
//声明
#include <QtMath>
#include <QString>
class MoveVector
{
public:
MoveVector();
void toZeroVector();
void GenerateVector();
void AddVx(qreal deltax);
void AddVy(qreal deltay);
QString StateofMoveKeys[5];//记录键盘相关键的状态
qreal Vx;
qreal Vy;
};
//定义
#include "movevector.h"
MoveVector::MoveVector()
{
for(int i=0;i<5;i++)
{
this->StateofMoveKeys[i]=QString("unpressed");
}
this->toZeroVector();
}
void MoveVector::toZeroVector()//归零向量
{
this->Vx=0;
this->Vy=0;
}
void MoveVector::GenerateVector()//根据按键状态生成单位向量
{
this->toZeroVector();
if(this->StateofMoveKeys[0]==QString("pressed"))//左
{
this->AddVx(-1.0);
}
if(this->StateofMoveKeys[1]==QString("pressed"))//上
{
this->AddVy(-1.0);
}
if(this->StateofMoveKeys[2]==QString("pressed"))//下
{
this->AddVy(1.0);
}
if(this->StateofMoveKeys[3]==QString("pressed"))//右
{
this->AddVx(1.0);
}
qreal length=qSqrt(this->Vx*this->Vx+this->Vy*this->Vy);
if(length!=qreal(0.0))//向量归一化
{
this->Vx=this->Vx/length;
this->Vy=this->Vy/length;
}
}
void MoveVector::AddVx(qreal deltax)
{
this->Vx+=deltax;
}
void MoveVector::AddVy(qreal deltay)
{
this->Vy+=deltay;
}
随后在主界面类Widget里添加MoveVector类子对象,最终声明如下:
//声明,include部分略去
class Widget : public QWidget
{
Q_OBJECT
public:
//无关函数略去
void updateAllPosition();//更新各元素位置
void paintEvent(QPaintEvent*event);//重绘场景各元素,Update()时调用
void keyPressEvent(QKeyEvent *event);//按键事件
void keyReleaseEvent(QKeyEvent *event);//松键事件
QTimer my_Timer;//计时器,用于游戏每10ms进行一次数据的更新处理与重新绘图
Map my_map;//背景场景,本文未用到
Hero my_hero;//自机
MoveVector my_vector;//自机移动向量
};
再将按键事件与松键事件重写如下:
//按键事件
void Widget::keyPressEvent(QKeyEvent *event)
{
if(event->key()==Qt::Key_Left)
{
this->my_vector.StateofMoveKeys[0]=QString("pressed");
}
if(event->key()==Qt::Key_Right)
{
this->my_vector.StateofMoveKeys[3]=QString("pressed");
}
if(event->key()==Qt::Key_Up)
{
this->my_vector.StateofMoveKeys[1]=QString("pressed");
}
if(event->key()==Qt::Key_Down)
{
this->my_vector.StateofMoveKeys[2]=QString("pressed");
}
if(event->key()==Qt::Key_Shift)//低速移动键,参照東方project机制
{
this->my_vector.StateofMoveKeys[4]=QString("pressed");
}
}
//松键事件
void Widget::keyReleaseEvent(QKeyEvent *event)
{
if(event->key()==Qt::Key_Left)
{
this->my_vector.StateofMoveKeys[0]=QString("unpressed");
}
if(event->key()==Qt::Key_Right)
{
this->my_vector.StateofMoveKeys[3]=QString("unpressed");
}
if(event->key()==Qt::Key_Up)
{
this->my_vector.StateofMoveKeys[1]=QString("unpressed");
}
if(event->key()==Qt::Key_Down)
{
this->my_vector.StateofMoveKeys[2]=QString("unpressed");
}
if(event->key()==Qt::Key_Shift)//低速移动键,参照東方project机制
{
this->my_vector.StateofMoveKeys[4]=QString("unpressed");
}
}
在updateAllPosition()方法中进行自机位置的更新:
void Widget::updateAllPosition()
{
//已略去与自机位置更新无关的部分
this->my_vector.GenerateVector();
int isShiftPressed=this->my_vector.StateofMoveKeys[4]==QString("pressed")?1:0;
int deltax=qFloor(this->my_vector.Vx*100000.0)/(10000+isShiftPressed*30000);
int deltay=qFloor(this->my_vector.Vy()*100000.0)/(10000+isShiftPressed*30000);
this->my_hero.setPosition(this->my_hero.X+deltax,this->my_hero.Y+deltay);
}
(注:此处由于设置位置时只能使用int,故我把单位向量放大了100000倍再转换为int,以尽可能减小方向误差。实测效果尚可,但如果有人想出了更好的处理方案希望可以告诉我。)
至此就可以实现使用多方向键控制物体自由移动了。效果如下:
(背景的移动是之前就已实现的,与本文内容无关)