程序截图如下所示:
if ( m_pCharacter->pos( ).y( ) > 20.0 )
{
qDebug( "AI go Up." );
m_pCharacter->SetAnimation( Character::_Up_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).x( ) < 608.0 )
{
qDebug( "AI go Right." );
m_pCharacter->SetAnimation( Character::_Right_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).y( ) < 340.0 )
{
qDebug( "AI go Down." );
m_pCharacter->SetAnimation( Character::_Down_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).x( ) > 0.0 )
{
qDebug( "AI go Left." );
m_pCharacter->SetAnimation( Character::_Left_ );
emit TriggerTransition( );
}
这也是我AI的第一个版本,但是正如QtMikuSnake7_ver_1应用程序截图中所示,它并不能达到应有的效果,角色一直在右上角打转。看来第一个版本有问题。问题在哪儿呢?这是由于我们将向上的判定优先于向下的判定,导致了角色在右上角处转至下后又转回了右上角。了解了这个问题之后第一个想法就是为向上判定添加约束条件,使其能够在右上角处正确地转至向下判定而不会折返。下面是AI的第二个版本:
if ( m_pCharacter->pos( ).y( ) > 20.0 &&
m_pCharacter->m_Direction != Character::_Down_ )
{
qDebug( "AI go Up." );
m_pCharacter->SetAnimation( Character::_Up_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).x( ) < 608.0 &&
m_pCharacter->m_Direction != Character::_Left_ )
{
qDebug( "AI go Right." );
m_pCharacter->SetAnimation( Character::_Right_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).y( ) < 340.0 &&
m_pCharacter->m_Direction != Character::_Up_ )
{
qDebug( "AI go Down." );
m_pCharacter->SetAnimation( Character::_Down_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).x( ) > 0.0 &&
m_pCharacter->m_Direction != Character::_Right_ )
{
qDebug( "AI go Left." );
m_pCharacter->SetAnimation( Character::_Left_ );
emit TriggerTransition( );
}
为了保险,按照这种思路,将每一个方向判定都添加了约束条件,即判定当前的方向是何方向。按理说角色要往上走,那么当前的方向就肯定不是往下走,角色要往左走,当前的方向就肯定不是往右走。好了,运行一下,结果发现如QtMikuSnake7_ver_2所示的效果一样,角色在右下角处至左移了一格就往上走了。看来又是一次失误。
if ( m_pCharacter->pos( ).y( ) > 20.0 &&
m_pCharacter->m_Direction != Character::_Down_ &&
m_pCharacter->m_Direction != Character::_Left_ )
{
qDebug( "AI go Up." );
m_pCharacter->SetAnimation( Character::_Up_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).x( ) < 608.0 &&
m_pCharacter->m_Direction != Character::_Left_ )
{
qDebug( "AI go Right." );
m_pCharacter->SetAnimation( Character::_Right_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).y( ) < 340.0 )
{
qDebug( "AI go Down." );
m_pCharacter->SetAnimation( Character::_Down_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).x( ) > 0.0 )
{
qDebug( "AI go Left." );
m_pCharacter->SetAnimation( Character::_Left_ );
emit TriggerTransition( );
}
else if ( m_pCharacter->pos( ).x( ) == 0.0 )// 为防止角色在左下角卡死设立的判断
{
m_pCharacter->SetDirection( Character::_Up_ );
}
注意到最下面一个判断,因为角色走在左下角的时候会因为都满足不了这些判定条件而陷入“卡死”状态,所以我们要进行“解锁”操作——将当前的方向设为向上,这样又可以满足向上的判定了。下面是程序QtMikuSnake7_ver_3的截图。
class Clause: public QObject
{
public:
Clause( Character* pParent = 0 ):
QObject( pParent ), m_pCharacter( pParent ){ }
virtual bool JudgeSentence( void ) = 0;
virtual void Statement( void ) = 0;
protected:
Character* m_pCharacter;
};
随后我设定一个队列,在Qt中有个现成的QQueue。
QQueue m_Clauses;
接着进行语句类的定义,让其继承Clause类:
class DirUpClause: public Clause
{
public:
DirUpClause( Character* pParent = 0 ): Clause( pParent )
{
}
bool JudgeSentence( void )
{
return m_pCharacter->pos( ).y( ) > 20.0;
}
void Statement( void )
{
qDebug( "AI go Up." );
m_pCharacter->SetAnimation( Character::_Up_ );
}
};
class DirDownClause: public Clause
{
public:
DirDownClause( Character* pParent = 0 ): Clause( pParent )
{
}
bool JudgeSentence( void )
{
return m_pCharacter->pos( ).y( ) < 340.0;
}
void Statement( void )
{
qDebug( "AI go Down." );
m_pCharacter->SetAnimation( Character::_Down_ );
}
};
class DirLeftClause: public Clause
{
public:
DirLeftClause( Character* pParent = 0 ): Clause( pParent )
{
}
bool JudgeSentence( void )
{
return m_pCharacter->pos( ).x( ) > 0.0;
}
void Statement( void )
{
qDebug( "AI go Left." );
m_pCharacter->SetAnimation( Character::_Left_ );
}
};
class DirRightClause: public Clause
{
public:
DirRightClause( Character* pParent = 0 ): Clause( pParent )
{
}
bool JudgeSentence( void )
{
return m_pCharacter->pos( ).x( ) < 608.0;
}
void Statement( void )
{
qDebug( "AI go Right." );
m_pCharacter->SetAnimation( Character::_Right_ );
}
};
最后我们在更新对象状态代码中进行一个简单地调用就可以了。
foreach ( Clause* pClause, m_Clauses )
{
if ( pClause->JudgeSentence( ) )
{
pClause->Statement( );
emit TriggerTransition( );
break;
}
else
{
m_Clauses.dequeue( );
m_Clauses.enqueue( pClause );
break;
}
}
上面的代码中,当条件满足的时候进行语句的执行,当条件不满足的时候将该语句从队列的头部移至队列的尾部。这样写虽然代码会比较多,但是思路清晰,对更复杂的状态维护起着重要的作用。执行起来效率也比较高,因为少了一些不必要的判定。下面是程序QtMikuSnake7_ver_4的截图。