📄 fsm_unit.cpp.svn-base
字号:
/* Copyright (C) Steve Rabin, 2000.
* All rights reserved worldwide.
*
* This software is provided "as is" without express or implied
* warranties. You may freely copy and compile this source into
* applications you distribute provided that the copyright text
* below is included in the resulting source code, for example:
* "Portions Copyright (C) Steve Rabin, 2000"
*/
//////////////////////////////////////////////////////////////
//
// Filename: fsm_drone.cpp
//
// Author: Steve Rabin
// E-mail: stevera@noa.nintendo.com
// From the book "Game Programming Gems"
// From the article "Designing a General Robust AI Engine"
//
// Brief Disclaimer:
// This code is free to use for commercial use and is in the
// public domain. You may distribute, copy, modify, or use as
// is as long as this information is present. This code makes
// no guarantees written or implied and is provided solely as
// an example. Have a nice day!
//
// Purpose:
// This file is an example of a state machine that multiple
// game objects can reference. Note that no state information
// is saved within this file. State information lies solely
// inside each game object. It would be a mistake to save any
// state info within this file.
//
//////////////////////////////////////////////////////////////
#include "fsm_unit.h"
#include "fsm.h"
#include "custom_time.h"
#include "fsmmacros.h"
#include "msgroute.h"
#include "game_object.h"
#include "util.h"
#include "../gamedata/fireball.h"
#include "../gamedata/gamedata.h"
#include "../gamedata/structs.h"
#include "../gamedata/mdlmodel.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "math.h"
bool PlayerSensed(GameObject *u, GameObject *p);
void CalcNewPos(GameObject *go);
bool UnitProcessStateMachine( GameObject* go, unsigned int state, MsgObject* msg )
{
//This is the state machine language.
//To see the keywords, look in the file "fsmmacros.h".
//You can get MS Visual Studio to highlight these words by listing them in
//a text file called "usertype.dat" put in the same directory where Msdev.exe
//is stored (like C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin).
//You'll find the "usertype.dat file in the same directory this file is in.
//Just copy it to the correct directory.
BeginStateMachine
OnEnter
SetState( STATE_Wander ); //一开始,怪物们就巡逻
OnMsg(MSG_Damage)
go->health -= msg->ex.damage;
if (go->health <= 0) {
SetState(STATE_Dead);
}
//----------------------------------------------------------------
State( STATE_Wander )
OnEnter
go->model->SetSeq(WALK);
go->rotY = RandomBetween(0.0f, 179.0f); //随机设置方向
SendDelayedMsgToCurrentState( MSG_Timeout, 10000, go->unique_id, go->unique_id, go ); //设定巡逻的时间
OnMsg( MSG_CalcNewPos )
go->checked = false;
go->collide = false;
CalcNewPos(go);
if (go->newPos.x != go->oldPos.x || go->newPos.z != go->oldPos.z)
go->NeedChecked = true;
else
go->NeedChecked = false;
OnUpdate
if (PlayerSensed(go, &player)) { //如果发现player
SetState(STATE_Rest); //由于SetState()是宏,所以要加{}
}
if (go->checked) {
if (!go->collide) {
go->pos = go->newPos;
}
else {
SetState(STATE_Rest);
}
}else {
assert( !"You must calculate new position before update!" );
}
OnMsg( MSG_Timeout )
if (msg->state == STATE_Wander) {
if( rand()%100 < 50 ) {
SetState( STATE_Wander );
} else {
SetState( STATE_Rest );
}
}
//----------------------------------------------------------------
State( STATE_Pursue )
OnEnter
go->model->SetSeq(WALK);
go->rotY = GetRotY(go->pos, player.pos); //方向朝向player(也就是player和go的连线的方向)
OnMsg( MSG_CalcNewPos )
go->checked = false;
go->collide = false;
CalcNewPos(go);
if (go->newPos.x != go->oldPos.x || go->newPos.z != go->oldPos.z)
go->NeedChecked = true;
else
go->NeedChecked = false;
OnUpdate
//====================================================================================================
//
//在这里面有很多东西可做,可以做一下人工智能方面的东西,不过我只是简单的处理了一下,让怪物朝着player跑
//大概可以搞一个寻路的东西
//现在这里面有很多bug,比如如果怪物碰到了树,还是会攻击,而且是直接朝树攻击,很傻
//
//====================================================================================================
go->rotY = GetRotY(go->pos, player.pos); //方向朝向player(也就是player和go的连线的方向)
if (GetDistance(go->pos, player.pos) <= go->attackDist) { //如果距离小于等于攻击距离,则转为攻击状态
SetState(STATE_Attack);
return true;
}
if (!PlayerSensed(go, &player)) { //如果不再感觉得到player则转为休息状态
//go->rotY = RandomBetween(0.0f, 179.0f);
SetState(STATE_Rest);
return true;
}
if (go->checked) {
if (!go->collide) { //如果没撞,则更新位置
go->pos = go->newPos;
}
else {
//如果撞了则休息
SetState(STATE_Rest);
}
} else {
assert( !"You must calculate new position before update!" );
}
//----------------------------------------------------------------
State(STATE_Rest)
OnEnter
go->model->SetSeq(IDLE);
SendDelayedMsgToCurrentState( MSG_Timeout, 500, go->unique_id, go->unique_id, go );
go->NeedChecked = false;
go->checked = false;
go->collide = false;
OnUpdate
//什么也不做,等到timeout时才进行判断
OnMsg( MSG_Timeout )
if (msg->state == STATE_Rest) { //休息够了则继续巡逻
if (GetDistance(go->pos, player.pos) <= go->attackDist) //在攻击范围内
{
SetState( STATE_Attack );
}
else if (PlayerSensed(go, &player)) //感觉到了但不在攻击范围内
{
SetState( STATE_Pursue );
}
else
{
SetState( STATE_Wander );
}
}
//----------------------------------------------------------------
State( STATE_Attack ) //基本上跟player的attack差不多
OnEnter
go->model->SetSeq(ATTACK);
go->rotY = GetRotY(go->pos, player.pos); //将方向朝向player(也就是player和go的连线的方向)
SendDelayedMsgToCurrentState( MSG_Timeout, go->model->GetSeqTime()-300, go->unique_id, go->unique_id, go );
go->NeedChecked = false;
go->checked = false;
go->collide = false;
Vertex fbPos, fbDest;
////////////!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
fbPos.x = go->pos.x + (go->radius+0.3 + 0.1) * sinf(go->rotY*0.0174533f);
fbPos.z = go->pos.z + (go->radius+0.3 + 0.1) * cosf(go->rotY*0.0174533f);
fbPos.y = go->pos.y + go->lenY * 0.5;
fbDest = player.pos;
fbDest.y += go->lenY * 0.5;
FireBall fb(fbPos, fbDest, go->unique_id, go->aggressivity);
fireBalls.push_back(fb);
OnUpdate
//什么也不做,等待这个动作结束
OnMsg( MSG_Timeout )
if (msg->state == STATE_Attack) {
if (GetDistance(go->pos, player.pos) <= go->attackDist)
{
SetState( STATE_Attack );
}
else if (PlayerSensed(go, &player))
{
SetState( STATE_Pursue );
}
else
{
SetState( STATE_Rest );
}
}
//----------------------------------------------------------------
State( STATE_Dead )
OnEnter
go->model->SetSeq(DIE);
//要么释放内存,要么标志一下它死了
SendDelayedMsgToCurrentState( MSG_Timeout, go->model->GetSeqTime()-400, go->unique_id, go->unique_id, go );
OnMsg( MSG_Timeout )
if (msg->state == STATE_Dead)
go->bMarkedForDeletion = true;
EndStateMachine
}
bool PlayerSensed(GameObject *u, GameObject *p)
{
float pRotYu = abs(GetRotY(u->pos, p->pos) - u->rotY);
float distToP = GetDistance(u->pos, p->pos);
if (pRotYu < 60.0f || pRotYu > 300.0f){ // 如果p在u视野内,即在120度角以内
if (distToP <= u->eyeDist) { //在可以看见的距离以内
return true;
}
} else if (distToP <= u->earDist) { //在可以听见的距离以内,这个距离小于看见的距离
return true;
}
return false;
}
void CalcNewPos(GameObject *go)
{
float incLen = incT * go->moveSpeed;
go->oldPos = go->pos;
go->newPos.x = go->oldPos.x + incLen * sinf(go->rotY * 0.0174533f);
go->newPos.z = go->oldPos.z + incLen * cosf(go->rotY * 0.0174533f);
go->newPos.y = map.getHeight(go->newPos.x, go->newPos.z);
}
//void DroneTranslateStateName( unsigned int state, char* name )
//{
// switch( state )
// {
// case STATE_Global:
// strcpy( name, "Global" );
// break;
//
// case STATE_Idle:
// strcpy( name, "Idle" );
// break;
//
// case STATE_Wander:
// strcpy( name, "Wander" );
// break;
//
// case STATE_Pursue:
// strcpy( name, "Pursue" );
// break;
//
// case STATE_Evade:
// strcpy( name, "Evade" );
// break;
//
// case STATE_Cower:
// strcpy( name, "Cower" );
// break;
//
// case STATE_Hide:
// strcpy( name, "Hide" );
// break;
//
// case STATE_Dead:
// strcpy( name, "Dead As A Doornail" );
// break;
//
// default:
// strcpy( name, "Can't translate state name" );
// }
//
//}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -