雷电空战类游戏源代码

12号,感谢水饺,20多度室内有点冷,穿上羽绒服减少能量消耗.....

这是个仿雷电的空战类游戏,写了个开头,还有很多没实现的。一开始由于找不到相关的3D模型资源所以做成了2D的。后来发现做成2D的竟然比做成3D还要啰嗦,就没有继续做下去,主要是引擎对3D支持比较多。

游戏中有几种子弹:制导导弹、闪电、火龙。采集相应的弹夹可以切换子弹。

没做联机功能,早期写的游戏,代码结构不太好,可能需要重构。

 

实体基类:

//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/SpaceWar2D/ScreenEntity.h
//  @Brief:     ScreenEntity
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================
 
#ifndef  __ScreenEntity__H__
#define  __ScreenEntity__H__
#include "General/General.h"
#include "Math/MathLib.h"
#include "Render/Texture.h"
//#include "Rpg/RpgCommon.h"
#include "AI/Entity/BaseEntity.h"
#include "AI/Entity/MovingEntity.h"

class File;


//!attack kick dead run bakrun  run45 run135???
enum SpaceAnimState
{
	SA_Normal = 0,
	SA_Exploide,
	SA_Fire,
	SA_Num,
};

//2d序列
class Serial2D
{
public:
	Serial2D();
	bool Load(File& file);
	void Clear();
	int   maxX;
	int   maxY;
	int   opacityType;
	//每帧的显示时间
	float frameTime; 
	String fileName;
};

//2d序列播放者
class SerialAnimator
{
public:
	SerialAnimator();
	//cenPos drawRect中心
	void Rend(const vec3& cenPos);
	//面向速度 旋转支点在中心cenPos
	void Rend(const vec3& cenPos,const vec3& dir);

	重新规定区域
	//void Rend(const RectF& rect);
	旋转支点在rect中心
	//void Rend(const RectF& rect,const vec3& speed);

	// return is animEnd
	bool Update();

	void SetIndex(int index);

	void PlayAnim(Serial2D* animStyle);
	Serial2D* GetAnim();

	//获得序列裁剪大小
	void GetExtend(int& width,int& height);
	void GetExtend(float& width,float& height);

	float      m_curTime;
	//当前播放格子序号
	int        m_curIndex;
	Serial2D*  m_serial2D;
	TexturePtr m_texture;
};

//!DeclareStyleEnum(eLogicCharacter2DStyle)
class Space2DEntityStyle:public MovingEntityStyle
{
public:
	virtual bool Load(File& file);
	void Clear();

	Serial2D   animStyles[SA_Num];

	RectF collideBox;
	float life;
	int   canDamage;
	float atackness;
	//对于每种敌机这些属性是不变的,这样才能给玩家明确的规则,否则虽然多了复杂性,但是缺少了个性
	//面向速度
	int   faceSpeed;
	//面向玩家
	int   faceRole;
	//出生音乐
	String sound;

	TexturePtr keepAnim[SA_Num];
};


class SoundChannel3D;
//功能简单没必要在库里添加 logiccharacter2d
//? 同时billboard绘制3d面片, 缓冲区优化
//!同样具有跟踪 寻路 重力 物理 动作回调等各个功能 
class Space2DEntity:public MovingEntity
{
public:
	Space2DEntity();
	~Space2DEntity();
	virtual void Update();
	virtual void Render();

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

	bool IsAnimState(SpaceAnimState state);
	bool Intersect(Space2DEntity *pOther);

	bool OutOfClient();
	bool OutOfSight();

	//m_pos 在drawRect的中心
	RectF m_drawRect;
	//更精确的多边形碰撞, 暂不区分不同的动画状态 不同的帧
	RectF m_collideBox;
	float   m_life;
	float   m_atack;
	float   m_liveTime;
	SerialAnimator m_animator;
	SoundChannel3D*  m_sound;

};

//
class SpacePlane2DStyle:public Space2DEntityStyle
{
public:
	virtual bool Load(File& file);
	//void Clear();
	//螺旋桨
	//炮位
#define MaxPropeller 6
	int    propellerNum;
	vec3   propellerPos[MaxPropeller];
	Serial2D   propellerAnimStyles[SA_Num];
	TexturePtr propellerKeepAnim[SA_Num];

	float  fireIntervalRat;

	//子弹类型
	String bulletType;

	//炮位
#define MaxGun 12
	int    gunNum;
	//int    gunFaceRole;
	vec3   gunPos[MaxGun];
	Serial2D   gunAnimStyles[SA_Num];
	TexturePtr gunKeepAnim[SA_Num];
};

struct Space2DBulletStyle;
class SpacePlane2D:public Space2DEntity
{
public:
	SpacePlane2D();
	virtual void Update();
	virtual void Render();

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

	virtual void Fire();

	void SetLastFireTime();
	Space2DBulletStyle* m_bulletStyle;
	SerialAnimator m_propellerAnimator;
	SerialAnimator m_gunAnimator;
	float  m_lastFireTime;
	mat4   m_mat;

};

DeclareEntityGroupEnum(PlayerBulletGroup,100);
DeclareEntityGroupEnum(EnemyBulletGroup,101);
DeclareEntityGroupEnum(ItemGroup,102);
DeclareEntityGroupEnum(EnemyGroup,103);
DeclareEntityGroupEnum(EnvironmentGroup,104);

#endif


//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/SpaceWar2D/ScreenEntity.cpp
//  @Brief:     ScreenEntity
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================
 
#include "General/Pch.h"
#include "General/General.h"
#include "General/File.h"
#include "General/Timer.h"
#include "Render/MC_MovieClip.h"
#include "Render/RendDriver.h"
#include "SpaceWar2D/Space2DEntity.h"
#include "SpaceWar2D/Space2DRole.h"
#include "SpaceWar2D/Space2DItem.h"
#include "SpaceWar2D/SpaceBullet2D.h"
#include "SpaceWar2D/MiniGameSpace2DWar.h"
#include "Sound/ChannelSound.h"
#include "General/Pce.h"

Serial2D::Serial2D()
{
	opacityType = 0;
}

bool Serial2D::Load(File& file)
{
	maxX = file.ReadFloat();
	maxY=file.ReadFloat();
	frameTime =file.ReadFloat();
	fileName=file.ReadString();
	opacityType = file.ReadInt();
	return true;
}

void Serial2D::Clear()
{
	maxX = 0.0f;
	maxY=0.0f;
	frameTime=0.0f;
	fileName="";
}

SerialAnimator::SerialAnimator()
{
	m_curTime = 0;
	//当前播放格子序号
	m_curIndex = 0;
	m_serial2D = NULL;
	m_texture = NULL;
}


void SerialAnimator::PlayAnim(Serial2D* animStyle)
{
	if (m_serial2D==animStyle)
	{
		return;
	}
	m_serial2D = animStyle;
	m_curTime = 0;
	G_TextureMgr->AddTexture(m_texture,animStyle->fileName.c_str());
}

Serial2D* SerialAnimator::GetAnim()
{
	return m_serial2D;
}

void SerialAnimator::GetExtend(int& width,int& height)
{
	width = m_texture->GetWidth()/m_serial2D->maxX;
	height = m_texture->GetHeight()/m_serial2D->maxY;
}

void SerialAnimator::GetExtend(float& width,float& height)
{
	width = m_texture->GetWidth()/m_serial2D->maxX;
	height = m_texture->GetHeight()/m_serial2D->maxY;
}
//cenPos drawRect中心
void SerialAnimator::Rend(const vec3& cenPos)
{
	int maxX = m_serial2D->maxX;
	int maxY = m_serial2D->maxY;

	float xt = (m_curIndex % maxX);
	xt /= maxX;
	float yt = (m_curIndex / maxX);
	yt /= maxY;
	RectF srcRect(xt,yt,1.0f/maxX,1.0f/maxY);

	float tarW = m_texture->GetWidth()/maxX;
	float tarH = m_texture->GetHeight()/maxY;

	G_RendDriver->SetRenderStateEnable(RS_TEXTURE_2D,true);
	G_RendDriver->SetRenderStateEnable(RS_BLEND,true);
	G_RendDriver->BlendFunc((BlendingType)m_serial2D->opacityType);	
	G_RendDriver->Color4f(1,1,1,1);
	m_texture->Bind();
	拖尾
	//for (float f=0.8f;f>=0;f-=0.1f)
	//{
	//	//G_RendDriver->Color4f(1,1,1,(1.0f-f)*0.05);
	//	G_RendDriver->Color4f((1.0f-f)*0.05,(1.0f-f)*0.05,(1.0f-f)*0.05,(1.0f-f)*0.05);
	//	//m_playerAnim[animAction].Rend(pos - m_speed*f*30);
	//	//G_RendDriver->DrawTextureRect(SRect(pos.x,pos.y-f*70,cellW,cellH),srcRect);
	//	float scale = 1/sqrt(f);
	//	G_RendDriver->DrawTextureRect(RectF(pos.x - cellW*(scale-1)/2,pos.y-cellH*(scale-1),cellW*scale,cellH*scale),srcRect);
	//}

	G_RendDriver->Color4f(1,1,1,1);
	G_RendDriver->DrawTextureRect(RectF(cenPos.x-tarW/2,cenPos.y-tarH/2,tarW,tarH),srcRect);


	//G_RendDriver->RendTrigon()
}

//面向速度时 旋转支点在中心cenPos
void SerialAnimator::Rend(const vec3& cenPos,const vec3& dir)
{
	if (dir.LengthSq()<0.001f)
	{
		return Rend(cenPos);
	}

	int maxX = m_serial2D->maxX;
	int maxY = m_serial2D->maxY;

	float xt = (m_curIndex % maxX);
	xt /= maxX;
	float yt = (m_curIndex / maxX);
	yt /= maxY;
	RectF srcRect(xt,yt,1.0f/maxX,1.0f/maxY);

	float tarW = m_texture->GetWidth()/maxX;
	float tarH = m_texture->GetHeight()/maxY;

	G_RendDriver->Color4f(1,1,1,1);
	G_RendDriver->SetRenderStateEnable(RS_BLEND,true);
	G_RendDriver->BlendFunc((BlendingType)m_serial2D->opacityType);
	m_texture->Bind();
	拖尾
	//for (float f=0.8f;f>=0;f-=0.1f)
	//{
	//	//G_RendDriver->Color4f(1,1,1,(1.0f-f)*0.05);
	//	G_RendDriver->Color4f((1.0f-f)*0.05,(1.0f-f)*0.05,(1.0f-f)*0.05,(1.0f-f)*0.05);
	//	//m_playerAnim[animAction].Rend(pos - m_speed*f*30);
	//	//G_RendDriver->DrawTextureRect(SRect(pos.x,pos.y-f*70,cellW,cellH),srcRect);
	//	float scale = 1/sqrt(f);
	//	G_RendDriver->DrawTextureRect(RectF(pos.x - cellW*(scale-1)/2,pos.y-cellH*(scale-1),cellW*scale,cellH*scale),srcRect);
	//}

	float dirRad = atan2(dir.y,dir.x);
	dirRad += HALFPI;
	dirRad*=-1;
	dirRad += _PI;

	G_RendDriver->Color4f(1,1,1,1);
	G_RendDriver->DrawTextureRect(RectF(cenPos.x-tarW/2,cenPos.y-tarH/2,tarW,tarH),srcRect,vec2(tarW*0.5f,tarH*0.5f),dirRad,ET_SCALE);

}

bool SerialAnimator::Update()
{
	if (m_serial2D==NULL)
	{
		return false;
	}
	bool isEnd = false;
	m_curTime += G_Timer->GetStepTime();
	m_curIndex = m_curTime/m_serial2D->frameTime;
	int maxIndex = m_serial2D->maxX*m_serial2D->maxY;
	if(m_curIndex>=maxIndex)
	{
		isEnd = true;
		m_curTime -= m_serial2D->frameTime*maxIndex;
		m_curIndex %= maxIndex;
	}
	return isEnd;
}

void SerialAnimator::SetIndex(int index)
{
	m_curIndex = index;
	if (m_curIndex<0)
	{
		m_curIndex = 0;
	}
	int maxIndex = m_serial2D->maxX*m_serial2D->maxY;
	m_curIndex %= maxIndex;
}



bool Space2DEntityStyle::Load(File& file)
{
	Clear();
	name = file.ReadString();
	description = file.ReadString();
	ID = file.ReadInt();
	assert(ID);
	animStyles[SA_Normal].Load(file);
	animStyles[SA_Exploide].Load(file);

	//collideBox = SRect(file.ReadFloat(),file.ReadFloat(),file.ReadFloat(),file.ReadFloat());
	collideBox.x = file.ReadFloat();
	collideBox.y = file.ReadFloat();
	collideBox.width = file.ReadFloat();
	collideBox.height = file.ReadFloat();

	maxSpeed = file.ReadFloat();
	life = file.ReadFloat();
	canDamage = file.ReadInt();
	atackness = file.ReadFloat();
	faceSpeed = file.ReadInt();
	faceRole = file.ReadInt();
	sound = file.ReadString();


	//keepAnim
	for (int i=0;i<SA_Num;i++)
	{
		G_TextureMgr->AddTexture(keepAnim[i],animStyles[i].fileName.c_str());
	}

	//
	int width, height;
	width = keepAnim[SA_Normal]->GetWidth()/animStyles[SA_Normal].maxX;
	height = keepAnim[SA_Normal]->GetHeight()/animStyles[SA_Normal].maxY;

	//collideBox.x = width*0.1;
	//collideBox.y = height*0.1;
	//collideBox.width = width*0.8;
	//collideBox.height = height*0.8;
	collideBox.x = width*0.0;
	collideBox.y = height*0.0;
	collideBox.width = width*1.0;
	collideBox.height = height*1.0;
	return true;
}


void Space2DEntityStyle::Clear()
{
	for (int i=0;i<SA_Num;i++)
	{
		animStyles[i].Clear();
	}

	//collideBox = SRect(0.0f,0.0f,0.0f,0.0f);
	collideBox.x = 0.0f;
	collideBox.y = 0.0f;
	collideBox.width = 0.0f;
	collideBox.height = 0.0f;

	maxSpeed = 0.0f;
	life = 0.0f;
	canDamage = 0;
	atackness = 0.0f;
	faceSpeed = 0;
}


Space2DEntity::Space2DEntity()
{
	m_drawRect = RectF(0,0,0,0);
	m_collideBox = RectF(0,0,0,0);
	m_life=1;
	m_atack=1;
	m_speed=vec3(0,0,0);
	m_pos = vec3(0,0,0);
	m_sound = new SoundChannel3D;
}
Space2DEntity::~Space2DEntity()
{
	SafeDelete(m_sound);
}
void Space2DEntity::Update()
{
	bool animEnd = m_animator.Update();
	m_liveTime += G_Timer->GetStepTimeLimited();
	Space2DEntityStyle* style = GetStyle();

	if(m_life>0)
	{
		m_pos.x += m_speed.x * G_Timer->GetStepTimeLimited();
		m_pos.y += m_speed.y * G_Timer->GetStepTimeLimited();

		//head
		if (style->faceRole)
		{
			m_heading = G_SpaceWarGame->m_myRolePlayer->GetPos() - m_pos;
			m_heading.Normalize();
		}
		else if (style->faceSpeed)
		{
			m_heading = m_speed;
			m_heading.Normalize();
		}
		else
		{
			m_heading = vec3(0,1,0);
			m_heading.Normalize();
		}
	}
	else
	{
		m_speed = vec3(0,0,0);
		if(animEnd)
		{
			SetToBeRemoved(true);
		}
	}

}
bool Space2DEntity::SetStyle(Style* style_)
{
	MovingEntity::SetStyle(style_);
	Space2DEntityStyle* style = (Space2DEntityStyle*)style_;
	m_speed.y    = style->maxSpeed;
	m_maxSpeed = style->maxSpeed;
	m_life       = style->life;
	m_atack  =  style->atackness;

	m_collideBox = style->collideBox;
	m_liveTime = 0;

	m_animator.PlayAnim(&style->animStyles[SA_Normal]);
	if (!GetStyle()->sound.empty())
	{
		m_sound->PlaySound__(GetStyle()->sound.c_str());
	}
	return true;
}

Space2DEntityStyle* Space2DEntity::GetStyle()
{
	return dynamic_cast<Space2DEntityStyle*>(m_entityStyle);
}

void Space2DEntity::Render()
{
	Space2DEntityStyle* style = GetStyle();
	m_animator.GetExtend(m_drawRect.width,m_drawRect.height);
	m_drawRect.x = m_pos.x -m_drawRect.width / 2;
	m_drawRect.y = m_pos.y -m_drawRect.height / 2;
	G_RendDriver->Color4f(1,1,1,1);
	//m_pos drawRect中心
	m_animator.Rend(m_pos,m_heading);

	//画血条
	if (m_life>0 && style->canDamage)
	{
		G_RendDriver->BlendFunc(RS_SRC_ALPHA, RS_ONE_MINUS_SRC_ALPHA);
		G_RendDriver->Color3f(1.0f, 0.0f, 0.0f); 
		//RectF tar(-5,0,10,1);
		float bloodWidth = m_drawRect.width*0.5f;
		float bloodX = m_drawRect.x + m_drawRect.width*0.25f;
		float bloodY = m_drawRect.y + m_drawRect.height + 5;
		G_RendDriver->DrawRect(RectF(bloodX,bloodY,bloodWidth,5));
		G_RendDriver->Color3f(0.0f, 1.0f, 0.0f); 
		G_RendDriver->DrawRect(RectF(bloodX,bloodY,bloodWidth*m_life/style->life,5));
	}

	G_RendDriver->BlendFunc(RS_SRC_ALPHA, RS_ONE_MINUS_SRC_ALPHA);

}

bool Space2DEntity::IsAnimState(SpaceAnimState state)
{
	return m_animator.GetAnim() == &GetStyle()->animStyles[state];
}

bool Space2DEntity::Intersect(Space2DEntity *pOther)
{
	return m_drawRect.IntersectRect(pOther->m_drawRect);
}


bool Space2DEntity::OutOfClient()
{
	float space = 30;
	return (m_pos.x+m_drawRect.width*0.5f  <-space) 
		|| (m_pos.y+m_drawRect.height*0.5f <-space )
		|| (m_pos.x-m_drawRect.height*0.5f > G_SpaceWarGame->m_width+space)
		|| (m_pos.y-m_drawRect.height*0.5f > G_SpaceWarGame->m_height+space);
}

bool Space2DEntity::OutOfSight()
{
	//m_pos 在图片中心
	return (m_pos.x+m_drawRect.width*0.5f  <0 )
		|| (m_pos.y+m_drawRect.height*0.5f <0 )
		|| (m_pos.x-m_drawRect.height*0.5f >G_SpaceWarGame->m_width )
		|| (m_pos.y-m_drawRect.height*0.5f >G_SpaceWarGame->m_height);
}


bool SpacePlane2DStyle::Load(File& file)
{
	Space2DEntityStyle::Load(file);

	bulletType = file.ReadString();

	/
	propellerNum = file.ReadInt();
	for (int i=0;i<propellerNum;i++)
	{
		propellerPos[i].x = file.ReadFloat();
		propellerPos[i].y = file.ReadFloat();

	}
	if (propellerNum>0)
	{
		//?只有一种类型的propeller
		propellerAnimStyles[SA_Normal].Load(file);
	}
	//keepAnim
	for (int i=0;i<SA_Num;i++)
	{
		G_TextureMgr->AddTexture(propellerKeepAnim[i],propellerAnimStyles[i].fileName.c_str());
	}
	///

	gunNum = file.ReadInt();
	for (int i=0;i<gunNum;i++)
	{
		gunPos[i].x = file.ReadFloat();
		gunPos[i].y = file.ReadFloat();

	}
	if (gunNum>0)
	{
		//?只有一种类型的gun
		gunAnimStyles[SA_Normal].Load(file);
		gunAnimStyles[SA_Exploide].Load(file);
		gunAnimStyles[SA_Fire].Load(file);
	}
	//keepAnim
	for (int i=0;i<SA_Num;i++)
	{
		G_TextureMgr->AddTexture(gunKeepAnim[i],gunAnimStyles[i].fileName.c_str());
	}
	return true;
}


SpacePlane2D::SpacePlane2D()
{

}

void SpacePlane2D::Update()
{
	Space2DEntity::Update();
	SpacePlane2DStyle* style = GetStyle();
	if(m_life>0)
	{
		bool animEnd = m_propellerAnimator.Update();

		animEnd = m_gunAnimator.Update();
		if (animEnd)
		{
			//开火动画不循环
			if (m_gunAnimator.GetAnim() == &style->gunAnimStyles[SA_Fire])
			{
				m_gunAnimator.PlayAnim(&style->gunAnimStyles[SA_Normal]);
			}
		}

		//非循环动画	
		m_animator.SetIndex((1-m_pos.x/G_SpaceWarGame->m_width)*13);

		//ufo 类似循环动画使用螺旋浆绘制
	}
}

void SpacePlane2D::Render()
{
	Space2DEntity::Render();
	SpacePlane2DStyle* style = GetStyle();
	for (int i=0;i<style->gunNum;i++)
	{
		m_gunAnimator.Rend(m_pos+style->gunPos[i],m_heading);
	}
	for (int i=0;i<style->propellerNum;i++)
	{
		m_propellerAnimator.Rend(m_pos+style->propellerPos[i],m_heading);
	}
}

bool SpacePlane2D::SetStyle(Style* style_)
{
	Space2DEntity::SetStyle(style_);
	SpacePlane2DStyle* style = (SpacePlane2DStyle*)style_;
	m_bulletStyle = G_StyleMgr->GetStyle<Space2DBulletStyle>(style->bulletType.c_str());
	if(style->propellerNum>0)
		m_propellerAnimator.PlayAnim(&style->propellerAnimStyles[SA_Normal]);
	if(style->gunNum>0)
		m_gunAnimator.PlayAnim(&style->gunAnimStyles[SA_Normal]);
	SetLastFireTime();
	return true;
}

SpacePlane2DStyle* SpacePlane2D::GetStyle()
{
	return dynamic_cast<SpacePlane2DStyle*>(m_entityStyle);
}

void SpacePlane2D::Fire()
{

}

void SpacePlane2D::SetLastFireTime()
{
	if(m_bulletStyle)
		m_lastFireTime = G_Timer->GetAccumTime() - RandRange(0.f,m_bulletStyle->fireInterval);
}

敌机:

//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/SpaceWar2D/Monster2D.h
//  @Brief:     Monster2D
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================
 
#ifndef  __Monster2D__H__
#define  __Monster2D__H__
#include "Space2DEntity.h"
class Space2DBullet;
class SpaceWar2DGame;

//
struct Space2DEnermyStyle:public SpacePlane2DStyle
{
public:
	enum AIType
	{
		MoveForward = 1,
		MoveLRF,
		MoveSin,
	};
	DeclareStyleEnum(Space2DEnermyStyle       ,101001);
	virtual bool Load(File& file);
	void Clear();
	
	//终极boss 可能有多种子弹

	//掉落物品类型
	String itemType;
	//掉落物品几率
	float  itemChance;
	AIType aiType;
};

class Space2DEnermy:public SpacePlane2D
{
	friend class EnermySpawner;
public:
	Space2DEnermy();
	virtual void Update();
	virtual void Render();
	virtual Space2DEnermyStyle* GetStyle();
	virtual bool SetStyle(Style* style);

	virtual void Fire();

	void UpdateCollision();
};

class Enermy_Ufo:public Space2DEnermy
{

public:
	enum Direction 
	{ 
		DIR_LEFT, 
		DIR_RIGHT, 
		DIR_FORWARD 
	};
	Enermy_Ufo();
	void Update();

	//曲线运动的位置
	float GetPathPosX(float y);
	Direction m_direction;
	vec3  m_onCarrierPos;
	float m_sin1;
	float m_sin2;
};



//struct SpaceCarrier2DStyle:public SpaceEnermy2DStyle
//{
//public:
//	DeclareStyleEnum(SpaceCarrier2DStyle       ,105001);
//	virtual bool Load(File& file);
//	void Clear();
//	int planeNum;
//#define MaxPlane 12
//	String planeName[MaxPlane];
//	vec3   planePos[MaxPlane];
//
//};
//
母舰
//class Enermy_Carrier:public Enermy_Ufo
//{
//public:
//	Enermy_Carrier();
//};


#endif


//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/SpaceWar2D/Monster2D.cpp
//  @Brief:     Monster2D
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================
 
#include "General/Pch.h"
#include "General/Timer.h"
#include "SpaceWar2D/Space2DItem.h"
#include "SpaceWar2D/SpaceBullet2D.h"
#include "SpaceWar2D/Space2DEnermy.h"
#include "SpaceWar2D/Space2DRole.h"
#include "SpaceWar2D/MiniGameSpace2DWar.h"
#include "General/File.h"
#include "Sound/ChannelSound.h"
#include "General/Pce.h"

CheckStyleEnum(Space2DEnermyStyle)
bool Space2DEnermyStyle::Load(File& file)
{
	Clear();
	SpacePlane2DStyle::Load(file);
	itemType = file.ReadString();
	itemChance = file.ReadFloat();
	aiType = (AIType)file.ReadInt();
	//aiType = (AIType)3;

	return true;
}

void Space2DEnermyStyle::Clear()
{
	bulletType = "";
	fireIntervalRat = 1;

	//SpriteStyle::Clear();

}

///
Space2DEnermy::Space2DEnermy()
{
	m_speed.x=0;
	m_lastFireTime=0;
	m_lastFireTime = 0;
//	m_carrierShip = NULL;
}

bool Space2DEnermy::SetStyle(Style* style_)
{
	SpacePlane2D::SetStyle(style_);
	return true;
}

Space2DEnermyStyle* Space2DEnermy::GetStyle()
{
	return dynamic_cast<Space2DEnermyStyle*>(m_entityStyle);
}



void Space2DEnermy::Render()
{
	//SpacePlane2D::Render();
	Space2DEntity::Render();
	SpacePlane2DStyle* style = GetStyle();

	float dirRad = atan2(m_heading.y,m_heading.x);
	dirRad += HALFPI;
	dirRad += _PI;
	//朝向下时mat为单位矩阵
	//dirRad*=-1;
	m_mat.FromRotateZ(dirRad);

	vec3 rolePos = G_SpaceWarGame->m_myRolePlayer->GetPos();
	vec3 pos;
	vec3 dir;
	for (int i=0;i<style->gunNum;i++)
	{
		//旋转后位置会变
		pos = m_pos+m_mat*style->gunPos[i];
		dir = rolePos - pos;
		m_gunAnimator.Rend(pos,dir);
	}

	if (m_life>0)
	{
		//爆炸了就不绘制ufo的螺旋桨
		for (int i=0;i<style->propellerNum;i++)
		{
			pos = m_pos+m_mat*style->propellerPos[i];
			m_propellerAnimator.Rend(pos,m_heading);
		}
	}

}

void Space2DEnermy::Update()
{
	UpdateCollision();

	SpacePlane2D::Update();
	SpacePlane2DStyle* style = GetStyle();
	//
	if(m_life<=0)
	{
		if(IsAnimState(SA_Normal))
		{
			m_sound->PlaySound__("data/sound/event_exploid01.wav",false);

			m_animator.PlayAnim(&style->animStyles[SA_Exploide]);
			m_gunAnimator.PlayAnim(&style->gunAnimStyles[SA_Exploide]);

			G_SpaceWarGame->m_myRolePlayer->m_killEnermy++;
			G_SpaceWarGame->m_myRolePlayer->m_score += GetStyle()->life;
		}
	}
	
	if (IsToBeRemoved())
	{
		if ((Rand()%100)/100.0f < GetStyle()->itemChance)
		{
			//掉落
			Space2DItem *item = new Space2DItem();
			item->SetPos(m_pos);
			Space2DItemStyle* style = G_StyleMgr->GetStyle<Space2DItemStyle>(GetStyle()->itemType.c_str());
			item->SetStyle(style);
			G_EntityMgr->RegisterEntity(item,Enum_EntityGroup(ItemGroup));
		}
	}

	if( OutOfClient() && m_pos.y >= G_SpaceWarGame->m_height )
	{
		SetToBeRemoved(true);
	}
}

void Space2DEnermy::Fire()
{
	if( m_bulletStyle==NULL
		|| G_Timer->GetAccumTime() - m_lastFireTime < m_bulletStyle->fireInterval )
		return;

	Space2DEnermyStyle* style = GetStyle();

	m_gunAnimator.PlayAnim(&style->gunAnimStyles[SA_Fire]);

	//如果飞船面向玩家 需要调整子弹发射角度
	float centerAngle = 270;


	//如果炮台个数大于0
	if (style->gunNum>0)
	{
		//在炮台处发射子弹
		for (int gun=0;gun<style->gunNum;gun++)
		{
			int bulletNum = m_bulletStyle->bulletNum;
			Space2DBullet *pNewBullet;
			vec3 gunPos = m_pos+m_mat*style->gunPos[gun];
			//所有gun 默认facerole
			//if (gun->faceRole)
			{
				vec3 dir = G_SpaceWarGame->m_myRolePlayer->GetPos()-gunPos;
				centerAngle = atan2(dir.y,dir.x)*RAD2DEG;
				//centerAngle += 90;
				centerAngle *= -1;
			}
			float gunW,gunH;
			m_gunAnimator.GetExtend(gunW,gunH);
			//沿着扇弧发射子弹
			float startAng = 0;
			float step = 0;
			if (bulletNum > 1)
			{
				startAng = /*270*/centerAngle - m_bulletStyle->bulletFov/2.0f;
				step = m_bulletStyle->bulletFov/(bulletNum-1);
			}
			else
			{
				startAng = /*270*/centerAngle;
				step = 0;
			}
			vec3 bulletPos,bulletDir;
			for(int i=0; i<bulletNum; i++)
			{
				//use matrix
				float angle = (startAng+i*step)*DEG2RAD;
				bulletDir.x =  cos( angle);
				bulletDir.y =  -sin( angle );

				//推前, 也可能是战舰上的炮位
				bulletPos = gunPos +bulletDir*(gunH*0.5f);

				pNewBullet=new Space2DBullet(style->bulletType,bulletDir);
				pNewBullet->SetPos(bulletPos);
				pNewBullet->SetParentSpeed(m_speed);
				G_EntityMgr->RegisterEntity(pNewBullet,Enum_EntityGroup(EnemyBulletGroup));
			}
		}
	}
	else
	{
		//在飞船前面发射子弹
		int bulletNum = m_bulletStyle->bulletNum;
		Space2DBullet *pNewBullet;
		if (style->faceRole || style->faceSpeed)//? face speed?
		{
			centerAngle = atan2(m_speed.y,m_speed.x)*RAD2DEG;
			//centerAngle += 90;
			centerAngle *= -1;
		}

		//沿着扇弧发射子弹
		float startAng = /*270*/centerAngle - m_bulletStyle->bulletFov/2.0f;
		float step = 0;
		if (bulletNum > 1)
			step = m_bulletStyle->bulletFov/(bulletNum-1);
		vec3 bulletPos,bulletDir;
		for(int i=0; i<bulletNum; i++)
		{
			//use matrix
			float angle = (startAng+i*step)*DEG2RAD;
			bulletDir.x =  cos( angle);
			bulletDir.y =  -sin( angle );

			//推前, 也可能是战舰上的炮位
			bulletPos = m_pos +bulletDir*(m_drawRect.height*0.5f);

			pNewBullet=new Space2DBullet(style->bulletType,bulletDir);
			pNewBullet->SetPos(bulletPos);
			pNewBullet->SetParentSpeed(m_speed);
			G_EntityMgr->RegisterEntity(pNewBullet,Enum_EntityGroup(EnemyBulletGroup));
		}
	}
	

	m_lastFireTime = G_Timer->GetAccumTime();
}
void Space2DEnermy::UpdateCollision()
{
	Space2DRole* pPlayer = G_SpaceWarGame->m_myRolePlayer;
	EntityGroupRef playerBullets(Enum_EntityGroup(PlayerBulletGroup));
	Space2DBullet* pBullet = (Space2DBullet*)(playerBullets.GetFirst());
	// 遍历玩家子弹
	while(pBullet)
	{
		if(m_life <= 0)
			return;
		if(pBullet->m_life > 0)
		{
			if(Intersect(pBullet))
			{
				this->m_life -= pBullet->m_atack;
				//子弹血量
				pBullet->m_life -= this->m_atack;
			}
		}

		pBullet = (Space2DBullet*)playerBullets.GetNext();
	}
}





//void SpaceEnermy2D::SetCarrier(SpaceEnermy2D* carrier)
//{
//	m_carrierShip = carrier;
//}



Enermy_Ufo::Enermy_Ufo()
{
	m_direction=DIR_FORWARD;
}

void Enermy_Ufo::Update()
{
	Space2DEnermy::Update();
	if(m_life<=0) return;

	Space2DEnermyStyle* style = GetStyle();

	if(m_life>0 && m_pos.y>10)
	{
		Fire();
	}


	switch(GetStyle()->aiType)
	{
	case Space2DEnermyStyle::MoveLRF:
		{
			switch( m_direction )
			{
			case DIR_LEFT:
				m_speed.x = -fabs(m_speed.y)*4;
				if( m_pos.x<0 )
				{
					m_speed.x = 0;
					m_direction = DIR_FORWARD;
				}
				break;
			case DIR_RIGHT:
				{
					m_speed.x = fabs(m_speed.y)*4;
					if( m_pos.x > G_SpaceWarGame->m_width )
					{
						m_speed.x = 0;
						m_direction = DIR_FORWARD;
					}
					break;
				}
			default:
				m_speed.x = 0;
				break;

			}

			if( Rand() < 5000*G_Timer->GetStepTimeLimited() )
				m_direction = ( Direction ) ( Rand() % 3 );
		}
		break;

	case Space2DEnermyStyle::MoveSin:
		{
			//WANDER曲线  posx 是以posy为自变量的函数

			if (m_pos.y>200)
			{
				//曲线接近水平的时候 不可移动太快 调整speedY
				float tryY = m_pos.y + (m_maxSpeed-m_speed.y);
				float tryX = GetPathPosX(tryY);
				float trySpeedX = tryX - m_pos.x;
				vec2 normSpeed(trySpeedX,GetStyle()->maxSpeed);
				normSpeed.Normalize();
				m_speed.y = GetStyle()->maxSpeed* normSpeed.y;
			}

			m_pos.x =  GetPathPosX(m_pos.y);
		}
		//break;
	}


	//head
	if (style->faceRole)
	{
		m_heading = G_SpaceWarGame->m_myRolePlayer->GetPos() - m_pos;
		m_heading.Normalize();
	}
	else if (style->faceSpeed)
	{
		m_heading = m_speed;
		m_heading.Normalize();
	}
	else
	{
		m_heading = vec3(0,1,0);
		m_heading.Normalize();
	}
}

float Enermy_Ufo::GetPathPosX(float y)
{
	//WANDER曲线
	float t1 = y/G_SpaceWarGame->m_height; //0~1
	float t2 = t1*2;

	float x =  G_SpaceWarGame->m_width*0.5f
		+G_SpaceWarGame->m_width*0.45f*     (  sin(m_sin1 * t1) * cos(m_sin2 * t1))
		+G_SpaceWarGame->m_width*0.45f*0.45f*(  sin(m_sin1 * t2) * cos(m_sin2 * t2));

	return x;
}


//CheckStyleEnum(SpaceCarrier2DStyle)
//bool SpaceCarrier2DStyle::Load(File& file)
//{
//	Clear();
//	SpaceEnermy2DStyle::Load(file);
//	planeNum = file.ReadInt();
//	for (int i=0;i<planeNum;i++)
//	{
//		planeName[i] = file.ReadString();
//		planePos[i].x = file.ReadFloat();
//		planePos[i].y = file.ReadFloat();
//	}
//	return true;
//}
//
//void SpaceCarrier2DStyle::Clear()
//{
//	SpaceEnermy2DStyle::Clear();
//
//}
//Enermy_Carrier::Enermy_Carrier()
//{
//}

 子弹

//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/SpaceWar2D/Item2D.h
//  @Brief:     Item2D
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================
 
#ifndef  __SpaceBullet2D__H__
#define  __SpaceBullet2D__H__
#include "Space2DEntity.h"
class Space2DRole;

//
struct Space2DBulletStyle:public Space2DEntityStyle
{
public:
	DeclareStyleEnum(Space2DBulletStyle       ,102001);
	virtual bool Load(File& file);
	void Clear();
	//间隔
	float fireInterval;
	//间隔差异率
	float fireIntervalVariation;
	//个数 角度
	float bulletNum;
	float bulletFov;
	//是否瞄准?
	//是否继承发射者速度
	int   addParentSpeed;
	//是否制导
	int   bNavigate;
	//制导发射的密集一些 变成雷电形??
};


// 定义子弹基类
class Space2DBullet:public Space2DEntity
{
public:
	Space2DBullet(String bullettype, const vec3& dir);
	virtual void Update();
	virtual void Render();

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

	void SetParentSpeed(vec3& parentSpeed);
	Space2DEntity* m_navCharacter;
};


#endif


//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/SpaceWar2D/Item2D.cpp
//  @Brief:     Item2D
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================
 
#include "General/Pch.h"
#include "Render/DeferredRender.h"
#include "SpaceWar2D/Space2DItem.h"
#include "SpaceWar2D/SpaceBullet2D.h"
#include "SpaceWar2D/Space2DRole.h"
#include "SpaceWar2D/Space2DEnermy.h"
#include "SpaceWar2D/MiniGameSpace2DWar.h"
#include "General/File.h"
#include "Sound/ChannelSound.h"
#include "General/Pce.h"

CheckStyleEnum(Space2DBulletStyle)
bool Space2DBulletStyle::Load(File& file)
{
	Clear();
	Space2DEntityStyle::Load(file);
	fireInterval = file.ReadFloat();
	fireIntervalVariation = 0.1f;
	bulletNum = file.ReadFloat();
	bulletFov = file.ReadFloat();
	addParentSpeed = file.ReadInt();
	bNavigate = file.ReadInt();
	
	return true;
}
void Space2DBulletStyle::Clear()
{
	name = "";
	//SpriteStyle::Clear();
	fireInterval = 0.0f;
	bulletNum = 0.0f;
	bulletFov = 0.0f;
	faceSpeed = 0;
}

Space2DBullet::Space2DBullet(String type_, const vec3& dir)
:m_navCharacter(NULL)
{
	Space2DBulletStyle* style = G_StyleMgr->GetStyle<Space2DBulletStyle>(type_.c_str());
	SetStyle(style);
	m_speed = dir * style->maxSpeed;
};

bool Space2DBullet::SetStyle(Style* style_)
{
	Space2DEntity::SetStyle(style_);
	Space2DBulletStyle* style = (Space2DBulletStyle*)style_;
	//m_animator.PlayAnim(&style->animStyles[SA_Normal]);
	return true;
}

Space2DBulletStyle* Space2DBullet::GetStyle()
{
	return dynamic_cast<Space2DBulletStyle*>(m_entityStyle);
}

void Space2DBullet::SetParentSpeed(vec3& parentSpeed)
{
	if (GetStyle()->addParentSpeed)
	{
		m_speed += parentSpeed;
	}
}

void Space2DBullet::Update()
{
	if(OutOfClient())
	{
		SetToBeRemoved(true);
	}
 	Space2DEntity::Update();

	//2~3 秒走完
	//attack 每隔 0.5s 减至0.9
	float step = 0.5f;
	if(m_liveTime>step)
	{
		m_atack = GetStyle()->atackness*pow(0.9f,m_liveTime/step);
	}

	if(m_life<=0)
	{
		if(IsAnimState(SA_Normal))
		{
			m_animator.PlayAnim(&GetStyle()->animStyles[SA_Exploide]);
		}
	}

	//制导
	if (GetStyle()->bNavigate)
	{
		// 遍历敌机
		Space2DEnermy* pEnermyTarget = NULL;
		EntityGroupRef enermys(Enum_EntityGroup(EnemyGroup));
		Space2DEnermy* pEnermy = (Space2DEnermy*)enermys.GetFirst();
		//if (m_navCharacter->IsToBeRemoved())
		//{
		//}
		float distSq = 9999999;
		while(pEnermy)
		{
			if(pEnermy->m_life>0)
			{
				float lenSq = (pEnermy->GetPos()-GetPos()).LengthSq();
				if (lenSq<distSq)
				{
					distSq = lenSq;
					pEnermyTarget = pEnermy;
					if (distSq < 10000)
					{
						break;
					}
				}
			}
			pEnermy = (Space2DEnermy*)enermys.GetNext();
		}
		if (pEnermyTarget)
		{
			m_navCharacter = pEnermyTarget;
			vec3 navSpeed = pEnermyTarget->GetPos()-GetPos();
			navSpeed.Normalize();
			navSpeed *= GetStyle()->maxSpeed;
			m_speed = Lerp(m_speed,navSpeed,0.1f);
		}

	}
}
void Space2DBullet::Render()
{
	Space2DEntity::Render();
}


奖子

//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/SpaceWar2D/Item2D.h
//  @Brief:     Item2D
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================
 
#ifndef  __Item2D__H__
#define  __Item2D__H__
#include "Space2DEntity.h"
class Space2DRole;

//奖子
enum ItemType 
{ 
	//弹夹
	caisson_firedragon = 0, //火龙
	caisson_lightning,  //闪电
	caisson_blitzball,  //闪电球
	caisson_frozen,     //冻结枪
	caisson_rocket,     //导弹
	//
	IT_Gold,         //金币  
	IT_Denender,     //防护罩 
	IT_AddMySpeed,   //加减速度  
	IT_AddEnemySpeed,//加减敌速 
	IT_ItemCollector,//奖子收集器
	IT_Fog,          //迷雾 
	ITEM_TYPE_COUNT 
};

const char* ItemTypeToString(int gt);


struct Space2DItemStyle:public Space2DEntityStyle
{
public:
	DeclareStyleEnum(Space2DItemStyle         ,103001);
	virtual bool Load(File& file);
	String bulletType;
};

class Space2DItem:public Space2DEntity
{
public:
	Space2DItem();
	void Update();

	virtual Space2DItemStyle* GetStyle();
	virtual bool SetStyle(Style* style);
	bool  viewUnknow;//显示问号
};

//
struct SpaceEnvironment2DStyle:public Space2DEntityStyle
{
public:
	DeclareStyleEnum(SpaceEnvironment2DStyle         ,104001);
	virtual bool Load(File& file);
};

//装饰物
class SpaceEnvironment2D:public Space2DEntity
{
public:
	SpaceEnvironment2D();
	void Update();

	virtual SpaceEnvironment2DStyle* GetStyle();
	virtual bool SetStyle(Style* style);
};

#endif

//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoClient/SpaceWar2D/Item2D.cpp
//  @Brief:     Item2D
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================
 
#include "General/Pch.h"
#include "Render/DeferredRender.h"
#include "SpaceWar2D/Space2DItem.h"
#include "SpaceWar2D/Space2DRole.h"
#include "SpaceWar2D/Space2DEnermy.h"
#include "SpaceWar2D/MiniGameSpace2DWar.h"
#include "General/File.h"
#include "Sound/ChannelSound.h"
#include "General/Pce.h"

const char* ItemTypeToString(int gt)
{
	switch(gt)
	{
	case caisson_firedragon:
		return "caisson_firedragon";
	case caisson_lightning:
		return "caisson_lightning";
	case caisson_blitzball:
		return "caisson_blitzball";
	case ITEM_TYPE_COUNT:
		return "ITEM_TYPE_COUNT";
	default:
		return "UNKNOWN GOAL TYPE!";
	}
}

CheckStyleEnum(Space2DItemStyle)
bool Space2DItemStyle::Load(File& file)
{
	Space2DEntityStyle::Load(file);
	bulletType = file.ReadString();
	return true;
}


Space2DItem::Space2DItem()
{
	m_speed.x=0;
}

bool Space2DItem::SetStyle(Style* style_)
{
	Space2DEntity::SetStyle(style_);
	Space2DItemStyle* style = (Space2DItemStyle*)style_;
	//m_animator.PlayAnim(&style->animStyles[SA_Normal]);
	return true;
}

Space2DItemStyle* Space2DItem::GetStyle()
{
	return dynamic_cast<Space2DItemStyle*>(m_entityStyle);
}

void Space2DItem::Update()
{
	Space2DRole *pPlayer	= G_SpaceWarGame->m_myRolePlayer;
	Space2DItemStyle* style = GetStyle();
	//if( this->Life <= 0 || pBullet->Life <= 0 ) return;
	if(Intersect(pPlayer))
	{
		pPlayer->m_life += 1;

		//this delete no sound
		pPlayer->m_sound->PlaySound__("data/sound/ui_coin01.wav",false);
		//if (style->bulletType == caisson_firedragon)
		//{
		//	G_AfterEffectMgr->BeginAfterEffect(Effect_Twirl,Layer_Scene);
		//}
		//else if (m_objType == caisson_lightning)
		//{
		//	G_AfterEffectMgr->BeginAfterEffect(Effect_RockPaint,Layer_Scene);
		//}
		//else if (m_objType == caisson_blitzball)
		//{
		//	G_AfterEffectMgr->BeginAfterEffect(Effect_RockPaint,Layer_Scene);
		//}
		SetToBeRemoved(true);
		//pPlayer->bulletCount["player_bullet_lightning"] += 1;
		pPlayer->ChangeWeapon(style->bulletType.c_str());
	}


	Space2DEntity::Update();
	if( OutOfClient() && m_pos.y>=G_SpaceWarGame->m_height )
	{
		SetToBeRemoved(true);
	}
}


//==================^_^==================^_^==================^_^==================^_^
CheckStyleEnum(SpaceEnvironment2DStyle)
bool SpaceEnvironment2DStyle::Load(File& file)
{
	Space2DEntityStyle::Load(file);
	return true;
}

SpaceEnvironment2D::SpaceEnvironment2D()
{
	m_speed.x=0;
}

bool SpaceEnvironment2D::SetStyle(Style* style_)
{
	Space2DEntity::SetStyle(style_);
	SpaceEnvironment2DStyle* style = (SpaceEnvironment2DStyle*)style_;
	m_animator.PlayAnim(&style->animStyles[SA_Normal]);
	return true;
}

SpaceEnvironment2DStyle* SpaceEnvironment2D::GetStyle()
{
	return dynamic_cast<SpaceEnvironment2DStyle*>(m_entityStyle);
}

void SpaceEnvironment2D::Update()
{
	Space2DRole *pPlayer	= G_SpaceWarGame->m_myRolePlayer;
	SpaceEnvironment2DStyle* style = GetStyle();
	//if( this->Life <= 0 || pBullet->Life <= 0 ) return;
	//if(Intersect(pPlayer))
	//{
	//	pPlayer->m_life += 1;

	//	//if (style->bulletType == caisson_firedragon)
	//	//{
	//	//	G_AfterEffectMgr->BeginAfterEffect(Effect_Twirl,Layer_Scene);
	//	//}
	//	//else if (m_objType == caisson_lightning)
	//	//{
	//	//	G_AfterEffectMgr->BeginAfterEffect(Effect_RockPaint,Layer_Scene);
	//	//}
	//	//else if (m_objType == caisson_blitzball)
	//	//{
	//	//	G_AfterEffectMgr->BeginAfterEffect(Effect_RockPaint,Layer_Scene);
	//	//}
	//	SetToBeRemoved(true);
	//	//pPlayer->bulletCount["player_bullet_lightning"] += 1;
	//	pPlayer->ChangeWeapon(style->bulletType.c_str());
	//}


	Space2DEntity::Update();
	if( OutOfClient() && m_pos.y>=G_SpaceWarGame->m_height )
	{
		SetToBeRemoved(true);
	}
}

 

完 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值