我的篮球项目是用的自己的一套架构。在项目中是只把U3D当成了渲染API。
关于状态部分大家可以参照
https://www.cnblogs.com/softimagewht/p/3889350.html
http://blog.csdn.net/code_sheen/article/details/45869505
这个文章这里就不多说了。
我的状态机分为两层。第一是AI状态机用来控制非玩家控制的队员的行为。第二层是动画事件的状态,用来播放动画和处理动画事件。
using UnityEngine;
using System.Collections;
using GlobalVariableNames;
publicclass AI_StateBase {
publicGameObject gameObject;
publicGameObject ball;
publicPlayerController pc;
publicEye eye;
publicBallController bc;
publicstring statename;
publicfloat starttime;
publicfloat intervaltime =-1;
publicStateData stateData =null;
publicfloat baseR =10;//基础值
float dist;
publicVector3 shotpos;
publicVector3 limipos;
publicVector3 ball_limipos;
publicAI_StateBase subset_state =null;
publicfloat watitime =-1.0f;
publicfloat lasttime =-1.0f;
public AI_StateBase (PlayerController p, string name) {
pc = p;
gameObject = pc.gameObject;
eye = gameObject.GetComponent<Eye> ();
statename = name;
bc = PublicClass.GetInterface ().GetBallController ();
if (null!= bc) {
ball = bc.gameObject;
}
stateData =newStateData ();
stateData.isRecoverable =false;
}
publicvirtualvoid Start () {
starttime = GameTimeController.g_fNowdelta;
shotpos = GameLoop_new.Initerface.GetTarget ();
shotpos.y =0.0f;
}
publicvirtualvoid Restart () {
intervaltime =0.0f;
}
publicvirtualvoid Exit () {
stateData.Init ();
}
publicvirtualstring Update () {
return"none";
}
publicvirtualbool DoState (int r =-1)//是否可执行
{
returnfalse;
}
.....
}
这个是AI状态的基类当然还有其它代码这里就不列出了。这些是最基本的。
动画的状态和上面的差不多。只是在UPDATE里多了个动画事件处理
publicvirtualvoid Update () {
if (null!= play_ani) {
// Debug.Log("name:" + pa.GetPlayingName() + "n:" + pc.name+"s:"+name);
pa.SetPlayLoop (playloop);
if (fEtime >=0.0f) {
if (false== endevent) {
if (pa.IsEvent (fEtime)) {
EndEvent ();
}
}
}
if (bUpdatePos) {
Vector3 pos = GetCurAniPos (GameTimeController.g_fNowdelta);
gameObject.transform.position = Vector3.SmoothDamp (gameObject.transform.position, pos, ref followVelocity, 0.1f);
ByColliders ();
} else {
MoveTo ();
}
} else {
float time = GameTimeController.g_fNowdelta - fStartTime;
// Debug.Log("tine:" + time + "end:" + fEtime);
if (time > fEtime) {
// Debug.Log("event:" + endevent);
if (false== endevent) {
Setendevent (true);
EndEvent ();
//endevent = true;
}
}
}
}
这个是我状态机的基本结构 。基于这个结构可以容易作出状态机的回放。
只要记录下当前时间下玩家的AI状态就可以了。状态机之机的通信我还有一个StateData类来传递需要的值。这个类可以记录下来。
这样在回放的时候就可以把当前状态机需要的值传回程序中。调试的时候在AI状态机的
Start(初始状态机时执行) Update(每帧执行)DoState(在判断是否可执行当前状态)这三个地方下断点就可以调试当前的游戏逻辑了。
下面是两个状态机的实例
using UnityEngine;
using System.Collections;
using GlobalVariableNames;
using System.Collections.Generic;
publicclass AI_Jumpshot_State : AI_StateBase {
staticint iBase =0;
Vector3 shotpos;
int type =0;
LevelInfo info =null;
float [] percentage =newfloat [3];
Pos_Type [] postype =newPos_Type [3];
float time;
bool shoot =false;
public AI_Jumpshot_State (PlayerController p, string name) : base (p, name) {
}
publicoverridevoid Start () {
if (pc.IsState ("Jumpshot_State") || pc.IsState ("Layup_State") || pc.IsState ("Dunk_State")) {
return;
}
shoot =false;
time =0.0f;
base.Start ();
shotpos = GameLoop_new.Initerface.GetTarget ();
shotpos.y =0.0f;
subset_state = pc.ai.GetState (stateData.statename);
if (null!= subset_state) {
subset_state.Start ();
}
}
publicoverridebool Do () {
base.Do ();
if (false== pc.bACt) {
returntrue;
}
if (false== pc.bIsController_Ball) {
returnfalse;
}
float dist = Vector3.Distance (pc.transform.position, stateData.endpos);
if (dist <0.1) {
returntrue;
}
if (GameTimeController.g_fNowdelta - time > pc.info.levelinfo.stiff) {
time = GameTimeController.g_fNowdelta;
Team_Type t = (pc.team == Team_Type.TEAM_HOME) ? Team_Type.TEAM_AWARY : Team_Type.TEAM_HOME;
// PlayerController p = PublicClass.GetInterface ().GetPlayerInline (pc.gameObject.transform.position, GameLoop_new.Initerface.GetTarget (), pc, pc.GetBodyR () * 2.0f, t);
// PlayerController p = PublicClass.GetInterface ().RinghtInAhead (pc, pc.GetBodyR () * 2.0f, t);
PlayerController p = eye.RinghtInAhead (45, pc.GetBodyR () *2.0f, t);
if (null== p)//前方没有人
{
Debug.Log ("nobady");
if (pc.scoretype ==1) {
returntrue;
} else {
int r = Math_My.GetRandom (0, 100);
if (r < pc.info.levelinfo.tp) {
returntrue;
}
}
}
float d = Vector3.Distance (shotpos, pc.transform.position);
if (d <=3.0f) {
if (null!= p) {
iBase = (int) (iBase *0.5f);
}
int r = Math_My.GetRandom (0, 100);
if (r <= iBase) {
returntrue;
}
}
if (null!= stateData) {
float td = Vector3.Distance (shotpos, stateData.endpos);
if (td > d) {
int r = Math_My.GetRandom (0, 100);
if (r <= iBase) {
returntrue;
}
}
}
}
returnfalse;
}
publicoverridestring Update () {
if (pc.IsState ("Jumpshot_State") || pc.IsState ("Layup_State") || pc.IsState ("Dunk_State")) {
return"none";
}
if (false== pc.bIsController_Ball) {
return"none";
}
if (null!= subset_state) {
if (true== Do ()) {
if (false== shoot) {
shoot =true;
}
}
if (false== shoot) {
if ("none"== subset_state.Update ()) {
subset_state =null;
}
return"run";
}
}
if (shoot) {
if (pc.bShot) {
BallMessage.Interface.SendShoot ();
shoot =false;
subset_state =null;
}
}
return"run";
}
void CreatePool () {
if (null== info) {
if (Space_Player.SPACE_PF == pc.info.space) {
info = PF_AI_LeveController.GetInterface ().GetLevelInfo (pc.ai.lvl);
} elseif (Space_Player.SPACE_PG == pc.info.space) {
info = PG_AI_LeveController.GetInterface ().GetLevelInfo (pc.ai.lvl);
} else {
info = SG_AI_LeveController.GetInterface ().GetLevelInfo (pc.ai.lvl);
}
}
int tp = info.tp;
if (null!= info) {
if (pc.team == Team_Type.TEAM_HOME) {
if ((UI_ROOT.Initerface.away_score - UI_ROOT.Initerface.home_score) >4) {
tp +=5;
} elseif ((UI_ROOT.Initerface.home_score - UI_ROOT.Initerface.away_score) >4) {
tp +=5;
}
}
percentage [0] = info.spi;
postype [0] = Pos_Type.POSYTYPE_NEAR;
percentage [1] = info.spm;
postype [1] = Pos_Type.POSTYPE_MIDDLE;
percentage [2] = tp;
postype [2] = Pos_Type.POSYTYPE_FAR;
float t = percentage [0];
Pos_Type pt = postype [0];
for (int i =0;i <3;i++) {
for (int j = i +1;j <3;j++) {
if (percentage [i] < percentage [j]) {
t = percentage [i];
pt = postype [i];
percentage [i] = percentage [j];
percentage [j] = t;
postype [i] = postype [j];
postype [j] = pt;
}
}
}
}
}
publicoverridebool DoState (int r =-1) {
if (pc.ai.IsState ("AI_Jumpshot_State")) {
returnfalse;
}
if (false== pc.bShot) {
returnfalse;
}
if (GameState.BallClear != GameLoop_new.Initerface.gs) {
if (pc.bIsController_Ball) {
Team_Type t = (pc.team == Team_Type.TEAM_HOME) ? Team_Type.TEAM_AWARY : Team_Type.TEAM_HOME;
// PlayerController p = PublicClass.GetInterface ().RinghtInAhead (pc, pc.GetBodyR () * 2.0f, t);
Vector3 targetpos = GameLoop_new.Initerface.GetTarget ();
targetpos.y =0.0f;
PlayerController p = PublicClass.GetInterface ().GetPlayerInLine (pc, pc.transform.position, targetpos, t);
if (null== p)//前方没有人
{
if (pc.scoretype >1) {
if (pc.info.levelinfo.tp > pc.info.levelinfo.spi) {
stateData.endpos = pc.transform.position;
returntrue;
}
}
stateData.endpos = CoachController.GetInterface ().GetMovePos (pc, Pos_Type.POSYTYPE_NEAR, false);
stateData.vDir = stateData.endpos - pc.transform.position;
stateData.statename ="AI_Run_State";
pc.ai.GetState ("AI_Run_State").SetStateData (stateData);
returntrue;
}
CreatePool ();
bool bshot =false;
if (r < percentage [0]) {
stateData.endpos = CoachController.GetInterface ().GetMovePos (pc, postype [0]);
bshot =true;
} elseif (r < percentage [1]) {
stateData.endpos = CoachController.GetInterface ().GetMovePos (pc, postype [1]);
bshot =true;
} elseif (r < percentage [2]) {
stateData.endpos = CoachController.GetInterface ().GetMovePos (pc, postype [2]);
bshot =true;
}
if (bshot) {
// Do();
if ((pc.bACt) || (GameState.Hurryup != GameLoop_new.Initerface.gs)) {
float dist = Vector3.Distance (pc.transform.position, stateData.endpos);
if (dist <0.1) {
returnfalse;
}
stateData.vDir = stateData.endpos - pc.transform.position;
stateData.statename ="AI_Run_State";
pc.ai.GetState ("AI_Run_State").SetStateData (stateData);
} else {
stateData.statename ="none";
}
returntrue;
}
}
}
returnfalse;
}
}
using UnityEngine;
using System.Collections;
using GlobalVariableNames;
using System.Collections.Generic;
using GlobalVariableNames;
publicclass Jumpshot_State : StateBase {
float statetime;
// AnimatiorInfo.AniEvent teste1;
// AnimatiorInfo.AniEvent teste2;
// float shotime = 0;
public Jumpshot_State (GameObject gameobject, string statename) : base (gameobject, statename) {
name ="Jumpshot_State";
}
publicoverridevoid InitState (StateData statedata) {
base.InitState (statedata);
SetMonopoly (true);
bUpdatePos =true;
pc.bShot =false;
// pc.bACt = false;
pc.SetAct (false);
pc.SetDD (false);
if (Vector3.Dot (pc.transform.forward, GameLoop_new.Initerface.GetTarget ()) >0) {
SetPlayAni ("jumpshot");
float d = pc.info.levelinfo.current_power *0.01f;
pc.UpdatePower (-d);
} else {
float d = pc.info.levelinfo.current_power *0.02f;
pc.UpdatePower (-d);
SetPlayAni ("turnaroundshot");
}
if (0== m_statedata.statename.CompareTo ("Keep_State")) {
float d = pc.info.levelinfo.current_power *0.02f;
pc.UpdatePower (-d);
SetPlayAni ("turnaroundshot");
}
if (pc.scoretype >1) {
pc.me.max_t++;
} else {
pc.me.max_s++;
}
AnimatiorInfo.AniEvent e = play_ani.GetEvent ("ball_out");
fEtime = e.time;
m_statedata.starttime = fEtime + GameTimeController.g_fNowdelta;
m_statedata.startpos = Math_My.LocalToWorldPoint (gameObject.transform.position, pc.fYaxis, e.ballPosition);
pc.Rotation (m_statedata.y);
if (Goal_Type.GOAL_SHOOT == (Goal_Type) m_statedata.type) {
CoachController.GetInterface ().AddGoal (pc.transform.position, pc.info.space, pc.team, pc.scoretype);
}
}
publicoverridevoid Update () {
Vector3 pos = GameLoop_new.Initerface.GetTarget ();
pos.y =0;
pc.LookAtTarget (pos);
base.Update ();
}
publicoverridestring Exit (StateData statedata) {
Vector3 pos = gameObject.transform.position;
pos.y =0;
gameObject.transform.position = pos;
pc.SetDD (false);
pc.SetAct (true);
UI_ROOT.Initerface.shotpower.SetShow (false);
UI_ROOT.Initerface.shotpower.start =false;
pc.bShot =true;
returnbase.Exit (statedata);
}
publicoverridevoid WakeUp (string previous) {
base.WakeUp (previous);
}
publicoverridevoid EndEvent () {
base.EndEvent ();
if (bc.bLoseBall) {
return;
}
m_statedata.startpos = bc.transform.position;
bc.Shoot (m_statedata);
}
}
AI状态机的执行还一个名为『Brain』类的来控制
void Update () {
if (false== pc.rec.play) {
if (null== pc.defend_pc) {
FindDeffensiveTarget ();
}
if (false== GameLoop_new.Initerface.IsLiveBall ()) {
if (null!= ai_currentstate) {
if ((0== ai_currentstate.statename.CompareTo ("AI_TraceBall_State")) ||0== ai_currentstate.statename.CompareTo ("AI_Pickup_State")) {
string t = ai_currentstate.Update ();
if (0== t.CompareTo ("none")) {
ai_currentstate =null;
}
}
}
return;
}
if (null== pc.defend_pc) {
FindDeffensiveTarget ();
}
if (false== bTrain) {
Think ();
} else {
Train ();
}
} else {
if (null!= ai_currentstate) {
nextstate = ai_currentstate.Update ();
}
}
记录和回放的逻辑代码
void Update () {
if (null== GameLoop_new.Initerface) {
return;
}
if (rec) {
RecInfo info =newRecInfo ();
StateBase state =null;
AI_StateBase ai_state =null;
info.statedata =newStateData ();
float time = GameTimeController.g_fNowdelta;
state = cb.GetCurrentState ();
if (null!= cb.ai) {
ai_state = cb.ai.GetCurrent ();
}
if (null!= state) {
info.statename = state.name;
if (null!= state.GetStateData ()) {
info.statedata = (StateData) state.GetStateData ().Clone ();//cb.GetCurrentStateData();
}
info.fstratstatetime = state.fStartTime;
}
if (null!= ai_state) {
info.ainame = ai_state.statename;
if (ai_state.GetStateData () !=null) {
info.statedata = (StateData) ai_state.GetStateData ().Clone ();
}
info.fstratstatetime = ai_state.starttime;
}
info.rectime = time - starttime;
info.gametime = time;
info.controllerball = cb.bIsController_Ball;
info.y = cb.fYaxis;
info.gs = GameLoop_new.Initerface.gs;
if (animator) {
if (null!= cb.pa) {
info.anitime = animator.GetCurrentAnimatorStateInfo (0).normalizedTime;
info.aniname = cb.pa.GetPlayingName ();
}
}
info.bre = PublicClass.GetInterface ().GetBallController ().bRebound;
info.bl = PublicClass.GetInterface ().GetBallController ().bLoseBall;
info.controller_p = PublicClass.GetInterface ().GetBallController ().GetPossessor ();
info.pos = cb.transform.position;
reclist.Add (info);
} elseif (play) {
KeyInput ();
if (index >= reclist.Count) {
cb.ClearState ();
if (null!= cb.ai) {
cb.ai.ai_currentstate =null;
}
if (ball) {
((BallController) cb).SetPossessor (null);
}
index =0;
}
RecInfo info = reclist [index];
PublicClass.GetInterface ().GetBallController ().bRebound = info.bre;
PublicClass.GetInterface ().GetBallController ().bLoseBall = info.bl;
GameLoop_new.Initerface.SetGs (info.gs);
GameTimeController.g_fNowdelta = info.gametime;
cb.Rotation (info.y);
if (0!= info.ainame.CompareTo ("none")) {
cb.ai.GetState (info.ainame).SetStateData (info.statedata);设置当前状态机需要的数据
cb.ai.ChangeState (info.ainame, info.fstratstatetime);//切换到当前的AI状态机
//(Start->UPdate)
} elseif (0!= info.statename.CompareTo ("none")) {
if (null!= cb.ai) {
cb.ai.ai_currentstate =null;
}
if (null!= cb.GetCurrentState ()) {
cb.GetCurrentState ().SetMonopoly (false);
}
cb.PushState (info.statename, info.statedata, info.fstratstatetime);
} else {
cb.ClearState (false);
}
if (ball) {
if (null!= info.controller_p) {
((BallController) cb).SetPossessor (info.controller_p.gameObject);
} else {
((BallController) cb).SetPossessor (null);
cb.transform.position = info.pos;
}
} else {
cb.transform.position = info.pos;
cb.bIsController_Ball = info.controllerball;
}
if (null!= animator) {
if (0!= info.aniname.CompareTo ("none")) {
animator.Play (info.aniname, 0, info.anitime);
}
}
这个就是实现穿点系统的大体方式。目前没有作成通用的。正式成立工作室有时间会作成通用的。目前忙于开发中。
这个也只是一个思路。不知道大家能看懂不。
如果大家不知道这个是什么的话可以看百度上我发的
https://tieba.baidu.com/p/5523322684
演示
编辑器有代码功能,可以试试。
@ayame9joe:什么意思?