攻城布阵类游戏实现

这是一款类似 《三国志大战》的攻城布阵类游戏。关于三国志大战游戏的玩法搜集了一下,基本有以下几点:

//每个玩家最多控制8张卡片(队伍),队伍分步兵、骑兵、枪兵、弓兵。

//游戏的时间单位是99count,基本上是2秒一个count,每count加一点士气。
//游戏场景是8x8张卡片的大小
//地形,基本地形是平原,森林,伤害沼泽,火山地,移动速度减慢,自身受到伤害,还会解除伏兵状态。骑兵跑到森林里出不了突击,弓箭兵可以躲在地形障碍后面,

//步兵,在操作上根本不需要有太多的顾虑,几乎所有的步兵都有很特殊的技能
//骑兵速度第一转弯时速度下降直线冲刺c多时间后速度达到最高出现突击效果攻击力很大(于双方武力有关),骑兵主要靠突击来攻击敌人,突击状态下对弓箭防御很高可以掩护枪兵等其他兵种
//枪兵,防御第一迎击-没有处于混战状态下的枪兵正前方会有一排枪头能克制骑兵突击,枪击-同样还是正前方的枪头,在接触到敌人的瞬间,会有一次额外的枪击判定,枪兵主要通过不断的离开接近敌人来引发枪击。
//弓兵,远距离攻击停止或移动后短时间内可以射击范围内敌人别给敌人近身就行了。有栅栏的弓兵,要比武力比他高一点,但是没有栅栏的弓兵更好用
//工程车,几乎没有战斗能力,但是一旦攻城就会变得非常恐怖,极缓慢的移动速度。混战状态下的攻城车拥有神一样的防御力
//象兵,移动一段距离之后,出现吹飞技能。给敌人造成小伤害,使敌人被迫后退,碰撞后状态不会消失的,除非自己停下来(突击撞到人之后就会停下来,吹飞是把对方撞后退,自己并不会停)

//士气,用来发动计略
//兵法,每一盘游戏,只能发动一次!一次成功的使用兵法将关系你是否能获得最后的胜利

按自己的想法重新设计了一下: 
伤害计算精确到每个小兵,小兵必须用空间划分来加速。
混战状态每个小兵会对周围一定距离内的其它队小兵产生基础伤害,同时伤害到敌方队长,此基础伤害只受到属性相克的影响。
队长只对敌方队长产生伤害,此伤害受属性相克、buff、地形等影响。
特殊打击对范围内的其它队小兵产生基础伤害,同时伤害到敌方队长,此基础伤害只受到属性相克的影响。
变换阵型产生也可以产生特殊打击。

对于联机版,伤害计算精确到每个小兵就必须同步小兵的位置和动画。消息量有点大,好在可以拼接和压缩消息,且游戏最大玩家数不大。

小兵的动画使用的关键帧顶点动画,使用骨骼动画会比较慢。小兵的移动使用的是阵型偏移跟随,跟随的是队长运动轨迹上延迟的虚拟点加偏移。偏移跟随算法可以参考《游戏编程中的人工智能》。小兵被击飞后马上回原位,小兵人数和队伍血量无关,队伍被击破时全员小兵被击飞不再回复,否则残兵无法摆阵型。
箭头绘制使用的粒子系统,关于粒子系统之前有介绍过,这里又加了一个使用程序发射粒子的接口,箭头轨迹使用抛物线计算。其它打击特效比较简单3dsmax直接导出即可。

 

  谈谈如何在游戏中快速渲染大量角色:

有很多的方法。首先要分析拖慢速度的瓶颈。一般最耗时的部分都在蒙皮动画的计算上,骨骼矩阵的计算次之。

比如要渲染1000个角色,每个角色使用300顶点的简模,一帧内即将有30万次的蒙皮顶点计算,每次计算都包含若干个点和权重矩阵的相乘,计算量很大。 假如每个简模20根骨骼,一帧内又有1000X20次骨骼矩阵的计算。

要加速角色动画的渲染,一种方法是将蒙皮顶点的计算放到gpu中进行。这种方法一般较适用于角色个数较少的高模,因为角色少骨骼矩阵的计算开销不大,同时动作较长较复杂不利于缓存。这种方法的另一个缺点是要对角色施加其它的shader特效时会变的麻烦。

一种更快的方法是使用公告板面片在3D空间中绘制2D的角色。这种方法很快,但是角色的光影很难和场景完美融合。

另一种方法是将骨骼动画塌陷为关键帧动画,也是本次小兵绘制所选择的方法。

将骨骼动画缓存为关键帧动画后,所有的蒙皮和骨骼计算都不需要了。如果动画较短的话甚至可以缓存成静态的缓冲区。

 游戏代码: 有点冗长,贴一部分主要的,自己都感觉有点乱,状态机有点鸡肋

阵型队伍:

//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/ShapeAttack/ShapeArmy.h
//  @Brief:     ShapeArmy
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================
 

#ifndef  __ShapeArmy__H__
#define  __ShapeArmy__H__

#include "Rpg/GameCharacter.h"
#include "Render/Texture.h"
#include "ShapeCommon.h"
#include "Math/MathLibAdvance.h"

class ShapeSoldier;
class ShapeTeam;
class SoundChannel3D;
namespace RendSys
{
	class MC_Particle;
}
class ShapePlayer;

//攻击状态
enum AttackState
{
	AS_Normal=0 ,//普通
	AS_Tuji   ,//突击
	AS_Yingji ,//迎击
	AS_Qiangji,//枪击
	AS_Jianji ,//箭击
	AS_Chuifei,//吹飞
	AS_Num,
};

#define  MaxSoldier 100
struct ShapeStyle:public MovingEntityStyle
{
public:
	DeclareStyleEnum(ShapeArmyStyle               ,92001);
	virtual bool Load(File& file);
	virtual bool Save(File& file);
	int  m_soldierNum;
	vec3 m_offset       [MaxSoldier];
	int  m_pursuitTarget[MaxSoldier];
	int  m_pursuitDelay [MaxSoldier];//配置比根据距离计算更灵活
	int  m_indexs       [MaxSoldier];//初始位置距离leader从近到远排序
};

//队伍类型
struct ShapeArmyStyle:public CharacterStyle
{
public:
	DeclareStyleEnum(ShapeArmyStyle               ,91001);
	virtual bool Load(File& file);
	virtual bool Save(File& file);

	String  textureName;
	int m_frameNum;

	ShapeOccupationType  m_occupation;

	float m_attackSpeed;
	float m_attackPoint;

	int   m_defendPoint;
	int   m_hpPoint;

};

//队伍
class ShapeArmy:public MovingEntity
{
public:
	ShapeArmy();
	~ShapeArmy();

	virtual bool  SetStyle(Style* style);
	virtual ShapeArmyStyle* GetStyle();

	//动态切换阵型
	bool        SetShapeStyle(ShapeStyle* style);
	ShapeStyle* GetShapeStyle();

	virtual void Free();
	virtual void Render();
	virtual void Update();
	virtual void UpdateCollide();

	void  SetLeader(ShapePlayer* leader);
	void  InitPos  (const vec3& new_pos,const vec3& new_head );

	//补位
	void  UpdateDumyLeader ();

	ShapeTeam* GetTeam() const;
	ShapeTeam* GetAwayTeam() const;
	ShapeOccupationType GetOccupation();

public:
	float         m_relife;
	AttackState   m_attackState; //攻击状态
	bool          m_hideState;   //伏兵状态

	OBB2          m_obbAttack;   //特殊攻击区域

	ShapeSoldier* m_soldiers[MaxSoldier];
	int           m_allSoldierNum;
	int           m_liveSoldierNum;
	int           m_deadSoldierNum;
	int           m_movingSoldierNum;

	ShapePlayer*  m_leader;//real leader

	ShapeStyle*   m_shapeStyle;
	
	//记录1秒内的路径
	static const int TrailNum = 16;
	MovingEntity  m_dumyLeader[TrailNum];
	float         m_lastTrailTime;

	SoundChannel3D*  m_soundFootStep;
	SoundChannel3D*  m_soundDead;

	RendSys::MovieClip* m_movieAttacks[AS_Num];
	RendSys::MC_Particle* m_particleAttacks[AS_Num];
};

#endif

//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/ShapeAttack/ShapeArmy.cpp
//  @Brief:     ShapeArmy
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================
 
#include "General/Pch.h"
#include "General/File.h"
#include "AI/Navigation/SteeringSystem.h"
#include "General/General.h"
#include "General/Regulator.h"
#include "General/StringUtil.h"
#include "General/Timer.h"
#include "Input/InputMgr.h"
#include "Render/Camera.h"
#include "Render/Font.h"
#include "Render/RendDriver.h"
#include "Render/SceneMgr.h"
#include "Rpg/RpgGame.h"
#include "Rpg/Using.h"
#include "Rpg/Weapon.h"
#include "ShapeAttack/ShapeArmy.h"
#include "ShapeAttack/ShapeArmySoldier.h"
#include "ShapeAttack/ShapePlayer.h"
#include "ShapeAttack/MiniGameShape.h"
#include "Sound/SoundListener.h"
#include "Sound/SoundManager.h"
#include "Sound/ChannelSound.h"
#include "Math/MathLibAdvance.h"
#include "Render/Shader.h"
#include "Render/MC_Misc.h"
#include "Render/Particle.h"
#include "General/Pce.h"

#define InWaterDis 1
namespace ShapeGame
{

const char* ShapeOccupationTypeToString(int enumeration)
{
	switch(enumeration)
	{
	case OT_Footman   :return "OT_Footman";		
	case OT_Cavalry   :return "OT_Cavalry";		
	case OT_Bowman    :return "OT_Bowman";		
	case OT_Spearman  :return "OT_Spearman";		
	//case //OT_Spearman:return "CMD_GameOver    ";		
	//case //OT_Spearman:return "CMD_Restart     ";		
	case OT_Airman    :return "OT_Airman";		     		       
	default             :return "unknow";
	}
	return "unknow";
}
ShapeOccupationType StringToShapeOccupationType(const char* dsc)
{
	if(stricmp(dsc,"OT_Footman")==0) return OT_Footman;
	else if(stricmp(dsc,"OT_Cavalry")==0) return OT_Cavalry;
	else if(stricmp(dsc,"OT_Bowman")==0) return OT_Bowman;
	else if(stricmp(dsc,"OT_Spearman")==0) return OT_Spearman;
	//else if(stricmp(dsc,"")==0) return;
	//else if(stricmp(dsc,"")==0) return;
	else if(stricmp(dsc,"OT_Airman")==0) return OT_Airman;
	return OT_Footman;
}
}

float ShapeBalance::TujiSpeed = 0;    //70; //突击速度
float ShapeBalance::BowNear   = 80;
float ShapeBalance::BoeFar    = 400;  //射程范围
float ShapeBalance::BowSpeed  = 1000; //20; //快速移动中无法射箭
float ShapeBalance::BowAng    = 90;   //45; //只射击前方敌人

//特殊打击的伤害
float ShapeBalance::AttackStateDamage[8] = 
{
	50,   //AS_Normal=0 ,//普通
	50,	  //AS_Tuji   ,//突击
	50,	  //AS_Yingji ,//迎击
	50,	  //AS_Qiangji,//枪击
	50,	  //AS_Jianji ,//箭击
	50,	  //AS_Chuifei,//吹飞
	50,	  //AS_Num,
	50
};

//属性克制
float ShapeBalance::AttackWeight[8][8] =
{
	1,1,1,1,1,1,1,1,  //OT_Footman=0,//步兵
	1,1,1,1,1,1,1,1,  //OT_Cavalry  ,//骑兵
	1,1,1,1,1,1,1,1,  //OT_Bowman   ,//弓兵
	1,1,1,1,1,1,1,1,  //OT_Spearman ,//矛兵
	1,1,1,1,1,1,1,1,  //OT_Spearman ,//象兵
	1,1,1,1,1,1,1,1,  //OT_Spearman ,//攻城兵
	1,1,1,1,1,1,1,1,  //OT_Airman   ,//空兵
	1,1,1,1,1,1,1,1,
};  

CheckStyleEnum(ShapeStyle)
bool ShapeStyle::Load(File& file)
{
	name = file.ReadString();
	ID = file.ReadInt();

	//file >> m_soldierStyle;//soldierStyle91001,
	file >> m_soldierNum;
	for (int i=0;i<m_soldierNum;i++)
	{
		file >> m_offset[i].x;
		file >> m_offset[i].z;
	}
	for (int i=0;i<m_soldierNum;i++)
	{
		file >> m_pursuitTarget[i];
	}
	for (int i=0;i<m_soldierNum;i++)
	{
		file >> m_pursuitDelay[i];
	}

	//初始位置距离leader从近到远排序   
	float dist[MaxSoldier];
	for (int i=0;i<m_soldierNum;i++)
	{
		m_indexs[i] = i;
		dist[i] = m_offset[i].LengthSq();
	}
	for (int i=0;i<m_soldierNum-1;i++)
	{
		for (int j=i+1;j<m_soldierNum;j++)
		{
			if (dist[i] > dist[j])
			{
				Swap(dist[i],dist[j]);
				Swap(m_indexs[i],m_indexs[j]);
			}
		}
	}
/*file >> m_hpPoint;
	file >> m_attackSpeed;
	file >> m_attackPoint;
	file >> m_defendPoint;*/

	
	return true;
}

bool ShapeStyle::Save(File& file)
{
	//file.Fprintf("hpPoint");
	//file.WriteFloat(m_hpPoint,space);
	//file.Fprintf("atSpeed");
	//file.WriteFloat(m_attackSpeed,space);
	//file.Fprintf("atPoint");
	//file.WriteFloat(m_attackPoint,space);
	//file.Fprintf("defPoint");
	//file.WriteFloat(m_defendPoint,space);
	//file.Fprintf("\n");
	return true;
}


CheckStyleEnum(ShapeArmyStyle)
bool ShapeArmyStyle::Load( File& file )
{
	name = file.ReadString();
	m_occupation = StringToShapeOccupationType(file.ReadString());
	ID = file.ReadInt();

	file.ReadString(modelName,128);
	file.ReadString(boneStyle,128);
	UnifyPath(modelName);

	file >> m_hpPoint;
	file >> m_attackSpeed;
	file >> m_attackPoint;
	file >> m_defendPoint;

	file >> radius >> scale;
	file >> maxSpeed >> maxTurnSpeed >> alarmDistance;

	textureName = file.ReadString();
	m_frameNum=file.ReadInt();

#ifdef _DEBUG
	//maxSpeed *= 10;
#endif
	return true;
}

bool ShapeArmyStyle::Save( File& file )
{
	//String head = file.ReadString();
	//int num = file.ReadInt();

	//name = file.ReadString();
	//ID = file.ReadInt();

	//file.ReadString(modelName,128);
	//file.ReadString(boneStyle,128);
	//UnifyPath(modelName);
	//file >> radius >> scale;
	//file >> maxSpeed >> maxTurnSpeed >> alarmDistance;

	return true;
}

static void onShapeArmyParticleDead(Particle* particle)
{
	ShapeCellThrough cellThrough(G_ShapeGame->m_cellPartition);
	ShapeArmy* army = (ShapeArmy*)particle->userData;
	ShapeSoldier* soldier = cellThrough.FirstEntity(vec2(particle->pos.x,particle->pos.z));
	while(soldier)
	{
		if (soldier->m_state!= ShapeSoldier::SS_Dead &&soldier->m_state!= ShapeSoldier::SS_Invalid
			&&soldier->GetTeam()!=army->GetTeam()
			&&(soldier->GetPos()-particle->pos).LengthSq()<70
			)
		{
			float attack = 9;//特殊打击不受buff影响,只受属性克制的影响
			attack *= ShapeBalance::AttackWeight[OT_Bowman][soldier->m_army->GetOccupation()];
			//箭只攻击一个
			soldier->m_hp -= attack;
			soldier->m_leader->ChangeHP(-attack*0.01f);
			break;
		}
		soldier = cellThrough.NextEntity();
	}
}


ShapeArmy::ShapeArmy()
:m_allSoldierNum(0)
,m_liveSoldierNum(0)
,m_deadSoldierNum(0)
,m_movingSoldierNum(0)
,m_leader(NULL)
,m_shapeStyle(NULL)
{
	for (int i=0;i<MaxSoldier;i++)
	{
		m_soldiers[i] = NULL;
	}
	m_soundFootStep = new SoundChannel3D;
	m_soundDead = new SoundChannel3D;

	for(int i=AS_Normal;i<AS_Num;++i)
	{
		m_movieAttacks[i] = NULL;
		m_particleAttacks[i] = NULL;
	}
}

ShapeArmy::~ShapeArmy()
{
	Free();
	SafeDelete(m_soundFootStep);
	SafeDelete(m_soundDead);
}
void ShapeArmy::Free()
{
	for (int i=0;i<MaxSoldier;i++)
	{
		SafeDelete(m_soldiers[i]);
	}


	for(int i=AS_Normal;i<AS_Num;++i)
	{
		if(m_movieAttacks[i])
		{
			m_movieAttacks[i]->FreeMovie();
			SafeDelete(m_movieAttacks[i]);
		}
	}
}
bool ShapeArmy::SetStyle(Style* style)
{
	if(m_entityStyle == style) 
		return true;

	ShapeArmyStyle* newstyle = dynamic_cast<ShapeArmyStyle*>(style);
	if (newstyle==NULL)
	{
		return false;
	}
	ShapeArmyStyle* oldstyle = dynamic_cast<ShapeArmyStyle*>(m_entityStyle);
	if (oldstyle!=newstyle)
	{
		Free();
		for (int i=0;i<MaxSoldier;i++)
		{
			//m_soldiers[i] = new ShapeSoldier8Board;
			m_soldiers[i] = new ShapeSoldierKeyMovie;
		}
	}

	MovingEntity::SetStyle(style);

	m_allSoldierNum  = MaxSoldier;
	m_liveSoldierNum = MaxSoldier;
	m_deadSoldierNum = 0;
	m_movingSoldierNum = 0;

	//区分队伍颜色
	for (int i=0;i<MaxSoldier;i++)
	{
		m_soldiers[i]->SetStyle(style);
		m_soldiers[i]->m_army = this;
	}

	//
	SetLeader(m_leader);

	m_relife = 0;

	m_soundFootStep->PlaySound__("data/sound/obj_armywalk.wav",true);

	//to cfg
	//攻击特效不分队伍颜色
	LoadConfig loader(LoadConfig::GenDonotReShrinkBound,true,true);
	switch (GetOccupation())
	{
	case OT_Cavalry:
		if(m_movieAttacks[AS_Tuji]==NULL)
		{
			m_movieAttacks[AS_Tuji] = new RendSys::MovieClip;
			m_movieAttacks[AS_Tuji]->LoadFromFile("data/minigame/shapeattack/attack/tuji.movie",&loader);
		}
		break;
	case OT_Spearman:
		if(m_movieAttacks[AS_Yingji]==NULL)
		{
			m_movieAttacks[AS_Yingji] = new RendSys::MovieClip;
			m_movieAttacks[AS_Yingji]->LoadFromFile("data/minigame/shapeattack/attack/yingji.movie",&loader);
		}
		if (m_movieAttacks[AS_Qiangji]==NULL)
		{
			m_movieAttacks[AS_Qiangji] = new RendSys::MovieClip;
			m_movieAttacks[AS_Qiangji]->LoadFromFile("data/minigame/shapeattack/attack/qiangji.movie",&loader);
		}
		break;
	case OT_Bowman:
		if(m_movieAttacks[AS_Jianji]==NULL)
		{
			m_movieAttacks[AS_Jianji] = new RendSys::MovieClip;
			m_movieAttacks[AS_Jianji]->LoadFromFile("data/minigame/shapeattack/attack/jianji.movie",&loader);
			m_particleAttacks[AS_Jianji] = dynamic_cast<MC_Particle*>(m_movieAttacks[AS_Jianji]->GetMovieClip("jian"));
			m_particleAttacks[AS_Jianji]->SetProgramDensity(0);
			m_particleAttacks[AS_Jianji]->GetEmitter()->SetCallBack(onShapeArmyParticleDead);
		}
		break;
	}
	
	for(int i=AS_Normal;i<AS_Num;++i)
	{
		if(m_movieAttacks[i])
			m_movieAttacks[i]->Advance();
	}


	m_attackState = AS_Normal;
	return true;
}
bool ShapeArmy::SetShapeStyle(ShapeStyle* style)
{
	if (style==NULL)
	{
		return false;
	}

	m_shapeStyle = style;
	m_allSoldierNum  = style->m_soldierNum;
	if(m_liveSoldierNum > m_allSoldierNum)
		m_liveSoldierNum = m_allSoldierNum;

	if(m_deadSoldierNum > m_allSoldierNum)
		m_deadSoldierNum = m_allSoldierNum;

	if(m_movingSoldierNum > m_allSoldierNum)
		m_movingSoldierNum = m_allSoldierNum;
	
	return true;
}


ShapeArmyStyle* ShapeArmy::GetStyle()
{
	return dynamic_cast<ShapeArmyStyle*>(m_entityStyle);
}

ShapeStyle* ShapeArmy::GetShapeStyle()
{
	return m_shapeStyle;
}

void ShapeArmy::SetLeader(ShapePlayer* leader)
{
	m_leader = leader;
	if (m_leader && m_shapeStyle)
	{
		m_allSoldierNum  = m_shapeStyle->m_soldierNum;
		m_liveSoldierNum = m_shapeStyle->m_soldierNum;
		m_deadSoldierNum = 0;
		m_movingSoldierNum = 0;
		
		for (int i=0;i<m_shapeStyle->m_soldierNum;i++)
		{
			m_soldiers[i]->SetPos(m_leader->GetPos());
			m_soldiers[i]->m_leader = leader;

			ShapeSoldier8Board* soldier8Board = dynamic_cast<ShapeSoldier8Board*>(m_soldiers[i]);
			if(soldier8Board)
				soldier8Board->m_frame = RandRange(0.0f,(float)soldier8Board->m_frameNum);
		}
		UpdateDumyLeader();
	}
}

void ShapeArmy::Render()
{
	ShapeSoldier** soldier = m_soldiers;
	for (int i=0;i<m_allSoldierNum;i++,soldier++)
	{
		if((*soldier)->m_state!=ShapeSoldier::SS_Invalid)
		{
			(*soldier)->Render();
		}
	}

	//绘制攻击特效
	for(int i=AS_Normal;i<AS_Num;++i)
	{
		if(m_attackState==i && m_movieAttacks[i])
			m_movieAttacks[i]->RendClip();
	}
	
	//debug阵型
	if (0)
	{
		if (
			( G_RendDriver->RendPassStepFlag==NormalPass|| G_RendDriver->RendPassStepFlag==PreRenderMrt)
			)
		{
			if(G_ShaderMgr)G_ShaderMgr->PushShader();
			G_RendDriver->Color4f(1.0f, 1.0f, 1.0f, 1.0f);
			G_RendDriver->DisableRendState(RS_TEXTURE_2D);

			switch (m_attackState)
			{
			case AS_Tuji:
			case AS_Yingji:
			case AS_Qiangji:
				{
					vec3 lt = m_obbAttack.c - m_obbAttack.u[0]*m_obbAttack.e[0] -  m_obbAttack.u[2]*m_obbAttack.e[2];
					vec3 lb = m_obbAttack.c - m_obbAttack.u[0]*m_obbAttack.e[0] +  m_obbAttack.u[2]*m_obbAttack.e[2];
					vec3 rt = m_obbAttack.c + m_obbAttack.u[0]*m_obbAttack.e[0] -  m_obbAttack.u[2]*m_obbAttack.e[2];
					vec3 rb = m_obbAttack.c + m_obbAttack.u[0]*m_obbAttack.e[0] +  m_obbAttack.u[2]*m_obbAttack.e[2];
					G_RendDriver->Line(lt,lb);
					G_RendDriver->Line(lt,rt);
					G_RendDriver->Line(rb,lb);
					G_RendDriver->Line(rb,rt);
				}
				break;
			}
			if(G_ShaderMgr)G_ShaderMgr->PopShader();
		}
	}
	
}

void ShapeArmy::Update()
{
	switch (GetOccupation())
	{
	case OT_Cavalry://骑兵
		{
			if (m_speed.Length()>ShapeBalance::TujiSpeed)
			{
				m_attackState = AS_Tuji;// 速度足够大 出突击
			}
			else
			{
				m_attackState = AS_Normal;
			}
		}
		break;
	case OT_Spearman:
		{
			if (1)
			{
				m_attackState = AS_Yingji;
			}
			else
			{
				//混战后消失?
				m_attackState = AS_Normal;
			}
		}
		break;
	case OT_Bowman:
		{
			vec3  tar = m_leader->GetOppositeMan()->GetPos();
			float dis = (m_pos-tar).Length();
			if (dis>ShapeBalance::BowNear && dis<ShapeBalance::BoeFar  //射程范围
				&&m_speed.Length()<ShapeBalance::BowSpeed              //快速移动中无法射箭
				&&m_heading.Dot(tar-m_pos) > dis*cos(DEG2RAD*ShapeBalance::BowAng) //只射击前方敌人
				)
			{
				m_attackState = AS_Jianji;
			}
			else
			{
				m_attackState = AS_Normal;
			}
		}
		break;
	}

	//更新声音
	if (m_deadSoldierNum>0)
	{
		if (m_soundDead->IsPlaying()==false)
		{
			m_soundDead->PlaySound__("data/sound/event_dead.wav",false);
		}
		//m_sound->SetVolume(float(m_deadSoldierNum)/m_allSoldierNum);
		//m_soundFootStep->SetVolume(1);
		m_soundDead->SetSoundPos(m_leader->GetPos());
		m_soundDead->SetSoundVel(m_leader->GetSpeed());
	}

	if (m_movingSoldierNum>0)
	{
		m_soundFootStep->SetVolume(float(m_movingSoldierNum)/m_allSoldierNum);
		m_soundFootStep->SetSoundPos(m_leader->GetPos());
		m_soundFootStep->SetSoundVel(m_leader->GetSpeed());
	}

	//更新虚拟跟随队长  dumyleader必须足够密集 否则会卡顿 
	m_lastTrailTime+=G_Timer->GetStepTimeLimited();
	//if (m_lastTrailTime>0.15f)
	if (m_lastTrailTime>0.05f)
	{
		m_lastTrailTime = 0;
		for (int i=TrailNum-1;i>0;i--)
		{
			m_dumyLeader[i].SetPos(m_dumyLeader[i-1].GetPos());
			m_dumyLeader[i].SetHeading(m_dumyLeader[i-1].GetHeading());
		}
		m_dumyLeader[0].SetPos(m_leader->GetPos());
		m_dumyLeader[0].SetHeading(m_leader->GetHeading());
	}

	m_pos = m_leader->GetPos();
	m_speed = m_leader->GetSpeed();
	m_heading = m_leader->GetHeading();

	//更新士兵运动
	ShapeSoldier** ppsoldier = m_soldiers;
	ShapeSoldier*  soldier;

	for (int i=0;i<m_allSoldierNum;i++,ppsoldier++)
	{
		soldier = *ppsoldier;
		if(soldier->m_state!=ShapeSoldier::SS_Invalid)
		{
			soldier->Update();
		}
	}


	//小兵被击飞后马上回原位 小兵人数和部队血量无关 部队被击破时全员小兵被击飞不再回复 否则残兵无法摆阵型 
	int invalidNum = m_allSoldierNum - m_liveSoldierNum;
	if(m_relife<invalidNum)
		m_relife += G_Timer->GetStepTimeLimited()*10;//0.8f;
	
	if(m_relife>invalidNum)
		m_relife = invalidNum;

	if (m_relife>1)
	{
		//回血重生 一帧最多重生一个  多个延迟到下一帧
		m_relife -= 1;

		ppsoldier = m_soldiers;
		for (int i=0;i<m_allSoldierNum;i++,ppsoldier++)
		{
			soldier = *ppsoldier;
			if(soldier->m_state==ShapeSoldier::SS_Invalid)
			{
				soldier->m_state = ShapeSoldier::SS_Walk;
				soldier->SetPos(soldier->m_leader->GetPos()+soldier->GetSteering()->GetOffset());
				soldier->m_hp = 100;
				m_liveSoldierNum++;
				break;
			}
		}
	}

	//补位


	//攻击状态特效
	for(int i=AS_Normal;i<AS_Num;++i)
	{
		MovieClip* movie = m_movieAttacks[i];
		if(m_attackState==i && movie)
		{
			Frame* frame = movie->GetProgramFrame();
			frame->m_pos = m_pos;
			frame->m_rot.y = (RAD2DEG*atan2f(m_heading.x,m_heading.z));

			frame->CalQuatMatrix();
			movie->SetProgramFrame(frame);
			movie->Advance();
		}
	}

	//箭击
	if (m_attackState==AS_Jianji && m_particleAttacks[AS_Jianji])
	{
		ppsoldier = m_soldiers;
		//vec3  tar = m_pos+m_heading*100;
		vec3  tar = m_leader->GetOppositeMan()->GetPos();
		float dis = (m_pos-tar).Length();
		float height = dis*0.3f;
		vec3  startH(0,(*ppsoldier)->GetRadius()*1.5f,0);
		for (int i=0;i<m_allSoldierNum;i++,ppsoldier++)
		{
			soldier = *ppsoldier;
			if(soldier->m_state!=ShapeSoldier::SS_Invalid 
				&& soldier->m_attackTime>0.5f
				)
			{
				soldier->m_attackTime = RandRange(0.0f,0.1f);
				Particle* partile = m_particleAttacks[AS_Jianji]->GetEmitter()->PEmitParabolaParticle(soldier->GetPos()+startH,
					                                          tar+soldier->GetSteering()->GetOffset().Mult(vec3(RandRange(0.3f,0.5f),1,RandRange(0.3f,0.5f))),
															  height*RandRange(0.9f,1.1f),500);//980);
				if (partile)
				{
					partile->userData = this;//
				}
			}  
		}
	}

}

void ShapeArmy::UpdateCollide()
{
	if (m_leader->GetState()==GS_Dead)
	{
		return;
	}
	//阵型对士兵的伤害
	{
		m_obbAttack.u[0] = Cross(vec3(0,1,0),m_heading);
		m_obbAttack.u[1] = vec3(0,1,0);
		m_obbAttack.u[2] = m_heading;
		m_obbAttack.e.x = 36;
		m_obbAttack.e.y = 1;
		float attack = ShapeBalance::AttackStateDamage[m_attackState]; //特殊打击不受buff影响,只受属性克制的影响
		switch (m_attackState)
		{
		case AS_Tuji:
			{
				m_obbAttack.c = m_pos;// + ;
				m_obbAttack.e.z = 15;
			}
			break;
		case AS_Yingji:
			{
				m_obbAttack.c = m_pos + m_heading*22;
				m_obbAttack.e.z = 25;
			}
			break;
		case AS_Qiangji:
			{
				m_obbAttack.c = m_pos + m_heading*22;
				m_obbAttack.e.z = 25;
			}
			break;
		}

		//遍历多个格子 3*3九宫格足够覆盖区域了
		float time = G_Timer->GetStepTimeLimited();
		ShapeCellThrough cellThrough(G_ShapeGame->m_cellPartition);
		ShapeSoldier* soldier = cellThrough.FirstEntity(vec2(m_obbAttack.c.x,m_obbAttack.c.z));
		while(soldier)
		{
			if (soldier->m_state!= ShapeSoldier::SS_Dead &&soldier->m_state!= ShapeSoldier::SS_Invalid
				&&soldier->GetTeam()!=this->GetTeam()
				&&TestPointOBB(soldier->GetPos(),m_obbAttack)
				)
			{
				//特殊打击不受buff影响,只受属性克制的影响
				attack *= ShapeBalance::AttackWeight[GetOccupation()][soldier->m_army->GetOccupation()];
				soldier->m_hp -= time*attack;
				soldier->m_leader->ChangeHP(time*attack*-0.01f);
			}
			soldier = cellThrough.NextEntity();
		}
	}
	


	//士兵对士兵伤害
	ShapeSoldier** soldier = m_soldiers;
	m_liveSoldierNum = 0;
	m_deadSoldierNum = 0;
	m_movingSoldierNum = 0;
	for (int i=0;i<m_allSoldierNum;i++,soldier++)
	{
		if((*soldier)->m_state==ShapeSoldier::SS_Dead)
		{
			m_deadSoldierNum++;
		}
		else if((*soldier)->m_state!=ShapeSoldier::SS_Invalid)
		{
			if ((*soldier)->GetSpeed().LengthSq()>1)
			{
				m_movingSoldierNum++;
			}
			(*soldier)->UpdateCollide();
			m_liveSoldierNum++;
		}
	}
}

void ShapeArmy::InitPos(const vec3& new_pos,const vec3& new_head)
{
	m_lastTrailTime = 0;
	for (int i=0;i<TrailNum;i++)
	{
		m_dumyLeader[i].SetPos(new_pos);
		//m_dumyLeader[i].SetHeading(new_head);
	}
	for (int i=0;i<m_allSoldierNum;i++)
	{
		m_soldiers[i]->SetPos(new_pos);
	}
}


void ShapeArmy::UpdateDumyLeader()
{
	//不补位
	for (int i=0;i<m_allSoldierNum;i++)
	{
		int INDEX = m_shapeStyle->m_indexs[i];

		//跟随延迟leader
		int delay = m_shapeStyle->m_pursuitDelay[INDEX];
		{
			if (delay<0)
			{
				m_soldiers[i]->GetSteering()->SetTargetEntity(m_leader);
			}
			else
			{ 
				//距离远的士兵延迟较大
				m_soldiers[i]->GetSteering()->SetTargetEntity(&m_dumyLeader[delay]);
			}	
			m_soldiers[i]->GetSteering()->SetOffset(m_shapeStyle->m_offset[INDEX],true);
		}
	}
}

ShapeTeam* ShapeArmy::GetTeam() const
{
	if (m_leader)
	{
		return m_leader->GetTeam();
	}
	return NULL;
}

ShapeTeam* ShapeArmy::GetAwayTeam() const
{
	if (m_leader)
	{
		return m_leader->GetAwayTeam();
	}
	return NULL;
}

ShapeGame::ShapeOccupationType ShapeArmy::GetOccupation()  
{
	if (GetStyle())
	{
		return GetStyle()->m_occupation;
	}
	return OT_Footman;
}

玩家基类:

//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/ShapeCharacter/ShapeCharacter.h
//  @Brief:     ShapeCharacter
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================
 

#ifndef  __ShapeCharacter__H__
#define  __ShapeCharacter__H__

#include "AI/Entity/LogicCharacter.h"
#include "ShapeAttack/ShapeCommon.h"
#include "Render/Texture.h"
#include "Rpg/MiniGame.h"

class ShapePlayer;
//3D头牌
class ShapePlayerBanner3D:public Banner3D
{
public:
	ShapePlayerBanner3D();
	virtual ~ShapePlayerBanner3D(){}
	virtual void Init();
	virtual void Render3D();
	void SetOwner(ShapePlayer* owner);
	ShapePlayer* m_owner;
	TexturePtr m_textureOccupation;
};
//2D头牌
class ShapePlayerBannerUI:public BannerUI
{
public:
	ShapePlayerBannerUI();
	virtual ~ShapePlayerBannerUI(){}
	virtual void Init();
	virtual void RenderUI();
	void SetOwner(ShapePlayer* owner);
	ShapePlayer* m_owner;
	TexturePtr m_textureOccupation;
};

class ShapeTeam;
class ShapeCharacterItem;
class ShapeArmy;

//一个角色控制多张卡片,实际是多登入了几个npc到房间,但是有别于其它小游戏的是每个玩家都为自己的npc创建role,这样可以切换控制多个队伍
class ShapePlayer: public LogicCharacter, public MiniPlayer//,public StateClass<ShapeCharacter,GS_MaxNum>
{
public:
	ShapePlayer();
	virtual void Free();
	virtual void Update();
	virtual void UpdateCollide();
	virtual void Render();

	virtual bool SetStyle( Style* style );
	virtual int  OnAnimCallBack(const char* frameName);
	virtual void UpdateMovement();

	void  UpdateCommonPre();
	void  UpdateCommonAfter();

	void  SetDebugStr(const char* str,const Color& color);

	void  InitPos( const vec3& new_pos );
	<<状态
	DeclareStateFun(GS_Stand           );      
	DeclareStateFun(GS_Walk            );       
	DeclareStateFun(GS_Show            ); 
	DeclareStateFun(GS_Dead            );
	//typedef StateClass<ShapeCharacter,GS_MaxNum> ThisStateClass;
	typedef bool (ShapePlayer::*StateFun)();
	static StateFun m_stateFun[GS_MaxNum][StateFun_Num];
	virtual bool  RouteCallStateFun(int state,StateFunType fun);
	virtual bool  TryChangeState(ShapeCharacterState state);
	bool   m_stateSwitch[GS_MaxNum];
	>>

	void  SetOppositeMan(ShapePlayer* oppositeMan);	
	ShapePlayer* GetOppositeMan();

	void  SetTeam(ShapeTeam* team);
	ShapeTeam* GetTeam() const;
	ShapeTeam* GetAwayTeam() const;

	void  CollideOtherMan(ShapePlayer* man);

	//int  GetStat(StatType stat) const { return m_stat[stat]; }
	//void SetStat(StatType stat,int val) { m_stat[stat] = val; }

	//职业
	ShapeOccupationType GetOccupation() const;
	void SetOccupation(ShapeGame::ShapeOccupationType val);
	void OnChangeHP(float change,float current);
	int  ChangeHP(float hp);
	//void AddRecord(RecordType type,int value);

	int EquipWeapon(int entityID);
	int UnEquipWeapon(int entityID);

	//!着陆
	virtual bool      GetHeightAt     (vec3& pos,float top=500,float bot=500) const;
//protected:
	ShowAnim         m_showAnim;

//protected:
	SASkillStyle* m_superSkillStyle;

	float         m_forwardEnergy;

	ShapeOccupationType  m_occupation;
	ShapeTeam*        m_team;
	ShapeTeam*        m_awayTeam;
	ShapePlayer*      m_oppositeMan;
	ShapePlayer*      m_teamLeader;

//	int             m_record[RecordNum];
//	int             m_stat[StatNum];

	//方位
	float m_distToTeamLeader;
	float m_distToOppMan;

	int   m_maxHP;
	float m_hp;
	int   m_maxMP;
	float m_mp;
	int   m_ap;
	int   m_dp;

	float m_baseHpRecover;
	float m_baseMpRecover;
	//无敌
	bool  m_noHurt;

	float  m_animSpeed;

	ShapeArmy* m_army;
};


#endif


//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/ShapeCharacter/ShapeCharacter.cpp
//  @Brief:     ShapeCharacter
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================
 
#include "General/Pch.h"
#include "General/File.h"
#include "General/General.h"
#include "General/Timer.h"
#include "General/StringUtil.h"
#include "AI/Navigation/SteeringSystem.h"
#include "ShapeAttack/ShapePlayer.h"
#include "ShapeAttack/ShapePlayerRobot.h"
#include "ShapeAttack/MiniGameShape.h"
#include "ShapeAttack/ShapeArmy.h"
#include "Render/RendDriver.h"
#include "Render/Font.h"
#include "Render/MC_Misc.h"
#include "Sound/ChannelSound.h"
#include "Rpg/Weapon.h"
#include "Rpg/GameUtil.h"
#include "Rpg/SyncGameInfo.h"
#include "General/Pce.h"

namespace ShapeGame
{
	const char* ShapeCharacterStateToString(int enumeration)
	{
		const char* stateName[] =
		{
			"Stand",           //站立
			"Walk",            //移动
			"Show",            //秀
			"Dead",            //
			"GS_MaxNum",
		};
		if (enumeration<=GS_MaxNum && enumeration>=0)
		{
			return stateName[enumeration];
		}
		return "error state";
	}


	const char* KeyEventToString(int enumeration)
	{
		const char* enumName[K_NUM] =
		{
			"K_UP",//0,
			"K_DOWN",//1,
			"K_LEFT",//2,
			"K_RIGHT",//3,
			"K_SQUAT",//4,
			"K_JUMP",//5,
			"K_PUNCHA",//6,
			"K_PUNCHB",//7,
			"K_KICKA",//8,
			"K_KICKB",//9,
			"K_ENERGY",//10,
			"K_DEFEND",//11,
			"K_RAPID",
		};
		if (enumeration<=K_NUM && enumeration>=0)
		{
			return enumName[enumeration];
		}
		return "error enum";
	}



	const char* ShowAnimToString(int enumeration)
	{
		const char* enumName[SA_Num] =
		{
			"Celebrate",//-1,
			"Annoyed",//0,
			"Defiant",//1,
			"PassReq",//1,
		};
		if (enumeration<=SA_Num && enumeration>=0)
		{
			return enumName[enumeration];
		}
		return "error enum";
	}
};



#define InWaterDis 1
#define UndeadTime 2


CheckStyleEnum(SASkillStyle)
bool SASkillStyle::Load(File& file)
{
	ID = file.ReadInt();
	name = file.ReadString();
	m_animTargetName = file.ReadString();
	m_effectTargetName = file.ReadString();
	m_soundName = file.ReadString();

	return true;
}

bool SASkillStyle::Save(File& file)
{
	//attackDis30,  attackAng60,  defendDis30,  defendAng60, 
	return true;
}


ShapePlayerBanner3D::ShapePlayerBanner3D()
:m_owner(NULL)
{
	m_bubbleColor.r = 1;
	m_bubbleColor.g = 0.2f;
	m_bubbleColor.b = 0;
	m_bubbleColor.a = 1;
}
void ShapePlayerBanner3D::Init()
{
}

void ShapePlayerBanner3D::Render3D()
{
	G_RendDriver->SetRenderStateEnable(RS_DEPTH_TEST,false);
	G_RendDriver->SetRenderStateEnable(RS_TEXTURE_2D,true);
	G_RendDriver->Color4f(1.0f, 1.0f, 1.0f,1); 

	for (BubbleNumList::iterator it = m_bubbleNums.begin();it!=m_bubbleNums.end();++it)
	{
		(*it).Render();
	}
	G_RendDriver->SetRenderStateEnable(RS_DEPTH_TEST,true);
	return;
}

void ShapePlayerBanner3D::SetOwner( ShapePlayer* owner )
{
	m_owner = owner;
	SetOwnerRenderObject(owner->GetRenderCharacter());
}


ShapePlayerBannerUI::ShapePlayerBannerUI()
:BannerUI(10)
,m_owner(NULL)
{
	
}

void ShapePlayerBannerUI::Init()
{
	Banner::Init();

	if (m_owner)
	{
		char buf[128];
		const char* str = m_owner->GetStyle()->name.c_str();
		sprintf_s(buf,"%s-lv%d",str,18);
		GenNameFastWords(buf,Color(1.0f,1.0f,1.0f,1.0f));
	}

	ShapePlayer* role = dynamic_cast<ShapePlayer*> (m_owner);
	if (role)
	{		
		if (role->GetTeam()&&role->GetTeam()->GetSide()==TeamRed)
		{
			switch(role->GetOccupation())
			{
			case OT_Cavalry:
				G_TextureMgr->AddTexture(m_textureOccupation,"data/gui/minigame/ShapeAttack/pos_qi_red.png");
				break;
			case OT_Bowman:
				G_TextureMgr->AddTexture(m_textureOccupation,"data/gui/minigame/ShapeAttack/pos_gong_red.png");
				break;
			case OT_Spearman:
				G_TextureMgr->AddTexture(m_textureOccupation,"data/gui/minigame/ShapeAttack/pos_qiang_red.png");
				break;
			case OT_Airman:
				G_TextureMgr->AddTexture(m_textureOccupation,"data/gui/minigame/ShapeAttack/pos_foot_red.png");
				break;
			default:
				G_TextureMgr->AddTexture(m_textureOccupation,"data/gui/minigame/ShapeAttack/pos_foot_red.png");
				break;
			}
		}
		else
		{
			switch(role->GetOccupation())
			{
			case OT_Cavalry:
				G_TextureMgr->AddTexture(m_textureOccupation,"data/gui/minigame/ShapeAttack/pos_qi_blue.png");
				break;
			case OT_Bowman:
				G_TextureMgr->AddTexture(m_textureOccupation,"data/gui/minigame/ShapeAttack/pos_gong_blue.png");
				break;
			case OT_Spearman:
				G_TextureMgr->AddTexture(m_textureOccupation,"data/gui/minigame/ShapeAttack/pos_qiang_blue.png");
				break;
			case OT_Airman:
				G_TextureMgr->AddTexture(m_textureOccupation,"data/gui/minigame/ShapeAttack/pos_foot_blue.png");
				break;
			default:
				G_TextureMgr->AddTexture(m_textureOccupation,"data/gui/minigame/ShapeAttack/pos_foot_blue.png");
				break;
			} 
		}
	}

	//
	m_texBack = NULL;
}

void ShapePlayerBannerUI::RenderUI()
{
	vec3 screenPos = m_screenPos;

		if (screenPos.z < 1)
		{
			vec2 pos(screenPos.x,screenPos.y-16);
			//{
			//	const char* stateName = ManStateToString(m_curState);
			//	pos.x -= G_FontMgr->GetTextWidth(stateName)/2.0;
			//	G_FontMgr->TextAtPos(pos,stateName);
			//}
	
			ShapePlayer* role = dynamic_cast<ShapePlayer*> (m_owner);
			//
			if (m_texBack==NULL && role && role->GetTeam())
			{
				if (role->GetTeam()->GetSide()==TeamRed)
				{
					G_TextureMgr->AddTexture(m_texBack,"data/gui/minigame/ShapeAttack/banner_red.png");
				}
				else
				{
					G_TextureMgr->AddTexture(m_texBack,"data/gui/minigame/ShapeAttack/banner_blue.png");
				}
			}
	
			//Banner::Render();
			G_RendDriver->SetRenderStateEnable(RS_DEPTH_TEST,false);
			G_RendDriver->SetRenderStateEnable(RS_TEXTURE_2D,true);
			G_RendDriver->Color4f(1.0f, 1.0f, 1.0f,0.5f); 
	
			//G_RendDriver->PushMatrix();
			//G_RendDriver->Translatef(screenPos.x-64,screenPos.y,0);
			//绘制back
			if(m_texBack)
			{
				m_texBack->Bind();
				G_RendDriver->DrawTextureRect(RectF(screenPos.x-m_texBack->GetWidth()/2,screenPos.y-m_texBack->GetHeight(),m_texBack->GetWidth(),m_texBack->GetHeight()));
			}
	
			//3d y正向上
			if (m_texBloodRed && m_texBloodGreen)
			{
				float y = -1.5f;
				//绘制血条 红
				m_texBloodRed->Bind();
				G_RendDriver->DrawTextureRect(RectF(screenPos.x-30,screenPos.y-22,90,6));
	
				//绘制血条 绿
				m_texBloodGreen->Bind();
				G_RendDriver->DrawTextureRect(RectF(screenPos.x-30,screenPos.y-22,90*m_hpPersent,6));
			}
	
			//绘制职业
			if(m_textureOccupation)
			{
				int y = -1;
				m_textureOccupation->Bind();
				G_RendDriver->DrawTextureRect(RectF(screenPos.x-72,screenPos.y-55,45,45));
			}
	
			//绘制名字
			if (m_fastWordsName
				&&m_fastWordsName->m_texture)
			{
				G_FontMgr->SetColor(Color(1,1,1,1));
				m_fastWordsName->m_texture->Bind();
				int width = m_fastWordsName->m_rect.width;
				G_RendDriver->DrawTextureRect(RectF(screenPos.x-27,screenPos.y-45,m_fastWordsName->m_rect.width,16),RectF(0,-1,1,-1));
			}
	
			//G_RendDriver->PopMatrix();
		}
}

void ShapePlayerBannerUI::SetOwner( ShapePlayer* owner )
{
	m_owner = owner;
	SetOwnerRenderObject(owner->GetRenderCharacter());
}

static bool G_ChangeStandOnAnimEnd[GS_MaxNum];
ShapePlayer::StateFun ShapePlayer::m_stateFun[GS_MaxNum][StateFun_Num]={};

ShapePlayer::ShapePlayer()
:m_occupation(OT_Cavalry)
,m_hp(100)
,m_maxHP(100)
,m_mp(0)
,m_maxMP(100)
,m_oppositeMan(NULL)
,m_team(NULL)
,m_awayTeam(NULL)
,m_showAnim(SA_Celebrate)
{

	for (int i=0;i<GS_MaxNum;i++)
	{
		G_ChangeStandOnAnimEnd[i] = false;
	}

	G_ChangeStandOnAnimEnd[GS_Show] = true;
	G_ChangeStandOnAnimEnd[GS_Dead] = true;

	RegisterAllStateFun(ShapePlayer);

	m_curState = GS_Stand;
	m_stateAccumTime = 0;

	m_army = new ShapeArmy;

	//预测提前量 因为目标转向太快 跟随者可能会振动
	m_steering->SetLookAheadWeight(0);
}
bool ShapePlayer::SetStyle( Style* style )
{
	LogicCharacter::SetStyle(style);
	m_steering->SteeringOff(SteeringSystem::Separation);
	m_brakingRate = 0.5f;

	//todo 不更新顶点来提速
	m_renderCharacter->SetFrustumSkipEnable(false);

	m_curState = GS_Stand;

	//状态开关
	for (int i=0;i<GS_MaxNum;i++)
	{
		m_stateSwitch[i] = 1;
	}

 
	static int test = 0;
	ShapeStyle* shapeStyle = G_StyleMgr->GetStyle<ShapeStyle>(92001+test%4);
	ShapeArmyStyle* armyStyle = G_StyleMgr->GetStyle<ShapeArmyStyle>(91001+test%4);
	test++;	
	m_occupation = armyStyle->m_occupation;
	m_army->SetStyle(armyStyle);
	m_army->SetShapeStyle(shapeStyle);
	m_army->SetLeader(this);

	//头牌
	//if(dynamic_cast<ShapePlayerRobot*>(this)==NULL)
	{
		ShapePlayerBanner3D* banner3D = new ShapePlayerBanner3D;
		banner3D->m_drawBloodBar = true;

		if(GetTeam()->m_teamSide==TeamRed)
		{
			banner3D->m_bubbleColor.r = 1.0f;
			banner3D->m_bubbleColor.g = 0.4f;
			banner3D->m_bubbleColor.b = 0.3f;
			banner3D->m_bubbleColor.a = 1;
		}
		else
		{
			banner3D->m_bubbleColor.r = 0.4f;
			banner3D->m_bubbleColor.g = 0.8f;
			banner3D->m_bubbleColor.b = 0.3f;
			banner3D->m_bubbleColor.a = 1;
		}

		banner3D->SetOwner(this);
		banner3D->Init();
		GetRenderCharacter()->MountBanner(Slot_HeadBanner,banner3D);

	}

	{
		ShapePlayerBannerUI* bannerUI = new ShapePlayerBannerUI();
		bannerUI->SetOwner(this);
		bannerUI->Init();
		GetRenderCharacter()->MountBanner(Slot_HeadBanner2,bannerUI);
	}

	return true;
}

void ShapePlayer::Free()
{
	LogicCharacter::Free();

	SafeDelete(m_army);

}

void ShapePlayer::UpdateCommonPre()
{
	if (m_oppositeMan)
	{
		vec3 dif = m_oppositeMan->GetPos()-m_pos;
		m_distToOppMan = dif.Length();
		{
			UpdateHeadBySpeed();
		}
	}
	else
	{
		m_distToOppMan = 9999;
		UpdateHeadBySpeed();
	}

	m_distToTeamLeader = (m_pos-m_teamLeader->GetPos()).Length();

	for (int i=0;i<MaxTeamPlayer;i++)
	{
		CollideOtherMan(m_awayTeam->m_mans[i]);
		CollideOtherMan(m_team->m_mans[i]);
	}
}
//碰撞处理
void ShapePlayer::CollideOtherMan(ShapePlayer* man)
{
	if (man==NULL || man==this)
	{
		return;
	}

	//对方也是站立状态 挤退
	vec3 dir = man->GetPos()-m_pos;
	float distance = dir.Length();
	dir.Normalize();

	//突进状态可以穿透
	//if (IsInState(GS_Forward)||man->IsInState(GS_Forward))
	//{
	//	return;
	//}

	if(distance<10)
	{
		vec3 pos;
		distance = 10-distance;

		{
			//各退一半 
			//pos = GetPos()-distance/2*dir;

			//这个距离一次可能退出不完全 但多次后基本退出完毕
			//dirnew 和speed平行和dir同向
			vec3 dirnew= m_speed*(m_speed.Dot(dir));
			dirnew.Normalize();
			dirnew+= dir*0.8f;//稍微偏向以便绕过 0.8越大越容易绕过
			//dirnew = dir; //不是引起振动的原因,但是会很快绕过无法发送推挤
			dirnew.Normalize();
			pos = GetPos()-distance/2*dirnew;
			GetHeightAt(pos,20,3000);
			this->SetPos(pos);


			//pos = enemy->GetPos()+distance/2*dir;
			dirnew= m_oppositeMan->m_speed*(m_oppositeMan->m_speed.Dot(dir));
			dirnew.Normalize();
			dirnew+= dir*0.8f;
			//dirnew = dir;
			dirnew.Normalize();
			pos = m_oppositeMan->GetPos()+distance/2*dirnew;
			GetHeightAt(pos,20,3000);
			m_oppositeMan->SetPos(pos);
		}
	}

	if(distance<50)
	{

		if (0)
		{
			//象兵不退不停 逼退敌阵
		}
		else
		{
			//混战状态减速 不逼退敌阵
			//m_speed.ClampLen(0,GetStyle()->maxSpeed*0.7f);
		}

		//掉血
		if (man->m_team!=m_team)
		{
			float attack = (100-distance)*0.01f;//混战基础攻击受双方buff、地形、属性克制的影响(地形也是通过加buff影响)
			attack *= ShapeBalance::AttackWeight[m_army->GetOccupation()][man->GetOccupation()];
			//ChangeHP(-attack*G_Timer->GetStepTimeLimited());
			man->ChangeHP(-attack*G_Timer->GetStepTimeLimited());
		}
	}
}

void ShapePlayer::Update()
{
	UpdateCommonPre();

	//放在UpdateCommonPre后面,有可能动画回调要用上面更新的一些数据
	//放在StateFun_Update前面,后面要用最新的位置
	LogicCharacter::Update();

	if(GS_Stand<=m_curState&&m_curState<GS_MaxNum)
	{
		RouteCallStateFun(m_curState,StateFun_Update);
		m_stateAccumTime += G_Timer->GetStepTime();
	}

	UpdateCommonAfter();

	if (m_hp < m_maxHP)
	{
		//m_hp += m_baseHpRecover*G_Timer->GetStepTimeLimited();
		m_hp += 4.0f*G_Timer->GetStepTimeLimited();
		if (m_hp > m_maxHP)
		{
			m_hp = m_maxHP;
		}
	}

	if (m_curState != GS_Dead)
		m_army->Update();
}

void ShapePlayer::UpdateCollide()
{
	m_army->UpdateCollide();
}

void ShapePlayer::UpdateCommonAfter()
{
	m_mp+=G_Timer->GetStepTimeLimited()*1.0f;
	if (m_mp>m_maxMP)
	{
		m_mp = m_maxMP;
	}

	{
		Banner* banner = (GetBanner(Slot_HeadBanner2));
		if(banner) 
			banner->SetHp(m_hp,(float)m_hp/m_maxHP);
	}

	{
		Banner* banner = (GetBanner(Slot_HeadBanner));
		if(banner) 
			banner->SetHp(m_hp,(float)m_hp/m_maxHP);

	}
	//LogicCharacter::Update(); 在前面调用,但位置,面向可能已经改变了
	UpdateToRenderChar();

}


bool ShapePlayer::RouteCallStateFun(int state,StateFunType fun)
{
	//stateFun 为虚调用派生方法,
	//改为非虚调用自身方法
	if(m_stateFun[state][fun])
	{
		return (this->*m_stateFun[state][fun])();
	}
	else
	{
		if (fun==StateFun_Check)
		{
			return true;
		}
	}
	return false;
}


void ShapePlayer::Render()
{
	m_renderCharacter->SetPos(m_pos);
	m_renderCharacter->SetDirection(RAD2DEG*atan2f(m_heading.x,m_heading.z));
	m_renderCharacter->Update();

	LogicCharacter::Render();

	if (m_curState != GS_Dead)
		m_army->Render();

}

int  ShapePlayer::OnAnimCallBack( const char* frameName )
{
	LogicCharacter::OnAnimCallBack(frameName);

	if (stricmp(frameName,"ShootOut") == 0)
	{
	}
	else if (stricmp(frameName,"SnFellGround") == 0)
	{
		//击飞后着地
		m_sound->PlaySound__("data/sound/gladiator/fellground_01.mp3");
		//地上加一滩血或坑
	}
	else if (stricmp(frameName,"AnimEnd") == 0 || stricmp(frameName,"end") == 0)
	{
		if (IsInState(GS_Dead))
		{
			m_hp = m_maxHP;
		}

		if (G_ChangeStandOnAnimEnd[m_curState])
		{

			{
				TryChangeState(GS_Stand);
			}	
		}
	}
	return 0;
}

void ShapePlayer::UpdateMovement()
{
	UpdateSpeedPos();
	//转向
	UpdateHeadBySpeed();
	UpdateLanding();
}

bool ShapePlayer::TryChangeState(ShapeCharacterState state)
{
	if (m_curState==state)
	{
		return false;
	}
	if(GS_Stand>state||state>=GS_MaxNum)
	{
		return false;
	}
	//开关
	if(m_stateSwitch[state] == false)
	{
		return false;
	}
	//条件
	if(RouteCallStateFun(state,StateFun_Check)==false)
	{
		return false;
	}
	//
	m_lastState = m_curState;
	m_lastStateType = m_curStateType;

	RouteCallStateFun(m_curState,StateFun_Exit);

	m_stateAccumTime = 0;
	m_curState = state;
	//力度清零
	ClearMoveForce();
	//速度不清零 可以继续减速滑步 除了必杀状态速度要清零

	RouteCallStateFun(m_curState,StateFun_Enter);
	return true;
}

//==================^_^==================^_^==================^_^==================^_^
bool ShapePlayer::GS_StandStateCheck()
{
	return true;
}
bool ShapePlayer::GS_StandStateEnter()
{
	m_renderCharacter->PlayAnim("standing_standby01");
	ClearMoveForce();
	m_speed = vec3(0,0,0);
	return true;
}
bool ShapePlayer::GS_StandStateExit()
{
	return true;
}
bool ShapePlayer::GS_StandStateUpdate()
{
	if (m_hp<=0)
	{
		TryChangeState(GS_Dead);
	}
	const char* anim = "stand";
	if (m_bJumping)
	{
		anim = "jump";
	}
	else if (m_bClimbing)
	{
		anim = "climb";
	}
	m_renderCharacter->PlayAnim(anim);

	return true;
}

//==================^_^==================^_^==================^_^==================^_^
bool ShapePlayer::GS_WalkStateCheck()
{
	return true;
}
bool ShapePlayer::GS_WalkStateEnter()
{
	m_renderCharacter->PlayAnim("walk");
	
	m_sound->PlaySound__("data/sound/event_run.wav",true);
	return true;
}
bool ShapePlayer::GS_WalkStateExit()
{
	m_sound->StopSound();
	return true;
}
bool ShapePlayer::GS_WalkStateUpdate()
{
	m_sound->SetSoundPos(GetPos());
	m_sound->SetSoundVel(GetSpeed());

	//派生类中切换了打击必杀等 但还调用到这里?
	if (IsInState(GS_Walk)==false)
	{
		return false;
	}

	const char* anim = "walk";
	if (m_bJumping)
	{
		anim = "jump";
	}
	else if (m_bClimbing)
	{
		anim = "climb";
	}
	m_renderCharacter->PlayAnim(anim);


	//挤退其它玩家
	vec3 dir = m_oppositeMan->GetPos()-m_pos;
	float distance = dir.Length();
	dir.Normalize();

	//24 帧 两步 40米
	m_animSpeed = m_speed.Length()*0.5f; //*24/40
	if (m_animSpeed<5) //停步时播放速度稍慢而不是无限慢,脚步到位就停
	{
		m_animSpeed = 5;
	}
	else if (m_animSpeed>25)
	{
		m_animSpeed = 25;
	}

	m_renderCharacter->SetAnimSpeed(m_animSpeed);

	vec3 normalSpeed = m_speed;
	normalSpeed.Normalize();

	//if(IsInState(GS_Walk))
	{
		//选择距离较近的做对手
		for (int i=0;i<m_awayTeam->m_manNum;i++)
		{
			ShapePlayer* enemy = m_awayTeam->m_mans[i];
			if (enemy && enemy!=m_oppositeMan)
			{
				float dis1 = (m_pos - enemy->GetPos()).Length();
				if (
					enemy->IsInState(GS_Dead)==false
					&&dis1<m_distToOppMan*0.7f)
				{
					SetOppositeMan(enemy);
				}
			}
		}
		//如果不是队长 还不能离队长太远
	}
	return true;
}


//==================^_^==================^_^==================^_^==================^_^
bool ShapePlayer::GS_ShowStateCheck()
{
	bool state = false;
	if(IsInState(GS_Stand))
		state = true;
	if(IsInState(GS_Walk))
		state = true;


	//state 不满足
	if (state==false)
	{
		return false;
	}

	return true;
}
bool ShapePlayer::GS_ShowStateEnter()
{
	char buf[256];
	sprintf(buf,"Show_%s%02d",ShowAnimToString(m_showAnim),(Rand()%2)+1);
	m_renderCharacter->PlayAnim(buf);
	//m_sound->PlaySound__("data/sound/event_dead.wav");
	return true;
}
bool ShapePlayer::GS_ShowStateExit()
{
	return true;
}
bool ShapePlayer::GS_ShowStateUpdate()
{
	return true;
}


//==================^_^==================^_^==================^_^==================^_^
bool ShapePlayer::GS_DeadStateCheck()
{
	return true;
}
bool ShapePlayer::GS_DeadStateEnter()
{
	//目标应该没有切换
	if (m_oppositeMan)
	{
		//m_oppositeMan->PostState(GS_Show);
		m_oppositeMan->TryChangeState(GS_Show);
	}
	//G_RpgGame->BreakLifeSpellFromCaster(this);
	m_renderCharacter->PlayAnim("dead");
	m_sound->PlaySound__("data/sound/event_dead.wav");
	return true;
}
bool ShapePlayer::GS_DeadStateExit()
{
	return true;
}
bool ShapePlayer::GS_DeadStateUpdate()
{
	if(m_stateAccumTime>4)
	{
		m_hp = m_maxHP;
		TryChangeState(GS_Stand);
	}
	return true;
}


ShapeGame::ShapeOccupationType ShapePlayer::GetOccupation() const
{
	return m_occupation;
}
void ShapePlayer::SetTeam( ShapeTeam* team)
{
	if (team==NULL)
	{
		return;
	}
	m_team = team;
	if (m_team->GetSide()==TeamRed)
	{
		m_awayTeam = G_ShapeGame->GetTeam(TeamBlue);
	}
	else if (m_team->GetSide()==TeamBlue)
	{
		m_awayTeam = G_ShapeGame->GetTeam(TeamRed);
	}
	else
	{
		m_awayTeam=NULL;
		//Assert(0&&"error team type!");
	}
}

ShapeTeam* ShapePlayer::GetTeam() const
{
	return m_team;
}

ShapeTeam* ShapePlayer::GetAwayTeam() const
{
	return m_awayTeam;
}

int  ShapePlayer::ChangeHP(float hp)
{
	//if (m_noHurt && hp<=0)
	//{
	//	return m_hp;
	//}

	m_hp+=hp;
	if(m_hp < 0)
	{
		m_hp = 0;
		//m_goal = DEAD;
		TryChangeState(GS_Dead); //被击打动作播完 站立时死亡
	}

	if(m_hp > m_maxHP)
		m_hp = m_maxHP;

	//OnChangeHP(hp,m_hp);
	return m_hp;
}

void ShapePlayer::OnChangeHP(float change,float current)
{
	if(change < 0)
	{
		Banner* banner = GetBanner(Slot_HeadBanner);
		if(banner)banner->AddBubbleNum(change);
	}
}

void ShapePlayer::SetOppositeMan(ShapePlayer* oppositeMan)
{
	m_oppositeMan = oppositeMan;
	m_steering->SetTargetEntity(m_oppositeMan);
}

ShapePlayer* ShapePlayer::GetOppositeMan()
{
	return m_oppositeMan;
}

int  ShapePlayer::EquipWeapon(int entityID)
{
	int tryCount = 0;
	while (tryCount++ < 1000)
	{
		WeaponStyle* weaponStyle = G_StyleMgr->GetStyle<WeaponStyle>(2001+Rand()%31);
		if(weaponStyle && 
			(weaponStyle->weaponSlot==WS_Lhand 
			||weaponStyle->weaponSlot==WS_Rhand
			||weaponStyle->weaponSlot==WS_Head)
		)
		{
			GameUtil::MountItem(GetRenderCharacter(),weaponStyle);
			break;
		}
	}
	m_sound->PlaySound__("data/sound/event_equip.wav");
	return true;
}

int  ShapePlayer::UnEquipWeapon(int entityID)
{
	GetRenderCharacter()->MountItem(WS_Lhand,NULL);
	GetRenderCharacter()->MountItem(WS_Rhand,NULL);
	GetRenderCharacter()->MountItem(WS_Head,NULL);
	m_sound->PlaySound__("data/sound/event_equip.wav");
	return true;
}

void ShapePlayer::InitPos(const vec3& new_pos)
{
	m_pos = new_pos;
	GetHeightAt(m_pos);
	m_army->InitPos(m_pos,vec3(0,0,-1));
}

//==================^_^==================^_^==================^_^==================^_^
bool ShapePlayer::GetHeightAt(vec3& pos,float top/*=500*/,float bot/*=500*/) const
{
	if (G_ShapeGame) 
	{	
		return G_ShapeGame->GetHeightAt(pos,top,bot);
	}
	return false;
}

z

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值