OSG小游戏案例-坦克大战移动以及地形跟随

一、框架设计

对于OSG的移动控制我们有两种方案,第一种通过viewer下的setCameraManipulator来自行设计控制脚本。这就需要编写一个类来继承osgGA::CameraManipulator。在这个类中我们可以将坦克的各种属性加入,包括了坦克的初始位置,坦克的旋转角度,相机的初始位置,相机的旋转角度,地形节点(方便以后做地形跟随,碰撞检测)。

二、相机跟随和坦克控制

载入坦克:

osg::MatrixTransform* CreateMT()
{
	osg::MatrixTransform* mt = new osg::MatrixTransform;
	osg::Node* tankBody = osgDB::readNodeFile("../work/tankm1.ive");
	osg::Node* tankTurret = osgDB::readNodeFile("../work/turretm1.ive");
	osg::Node* tankGun = osgDB::readNodeFile("../work/gunm1.flt");

	osg::PositionAttitudeTransform* patbutton = new osg::PositionAttitudeTransform;
	patbutton->setPosition(osg::Vec3(0.0, 0.0, 0));
	patbutton->addChild(tankBody);

	osg::PositionAttitudeTransform* pathead = new osg::PositionAttitudeTransform;
	pathead->setPosition(osg::Vec3(0.0, 0.0, 0.1));
	pathead->addChild(tankTurret);

	osg::PositionAttitudeTransform* patgun = new osg::PositionAttitudeTransform;
	patgun->setPosition(osg::Vec3(0.0, 0.0, 0.2));
	patgun->addChild(tankGun);

	osg::PositionAttitudeTransform* head = new osg::PositionAttitudeTransform;
	head->addChild(pathead);
	head->addChild(patgun);

		
	mt->addChild(patbutton);
	mt->addChild(head);

#pragma region 坦克尾烟粒子
	osgParticle::ParticleSystem *dustParticleSystem = new osgParticle::ParticleSystem;
	// 设置材质,emmisive和光照
	dustParticleSystem->setDefaultAttributes("../work/dust2.rgb", false, false);

	// 粒子系统继承自 Drawable 它可以作为geode的子节点
	osg::Geode *geode = new osg::Geode;
	mt->addChild(geode);
	geode->addDrawable(dustParticleSystem);

	// 加入‘updater’来帮助每一帧的管理
	osgParticle::ParticleSystemUpdater *dustSystemUpdater = new osgParticle::ParticleSystemUpdater;
	// 将updater和例子系统联系起来
	dustSystemUpdater->addParticleSystem(dustParticleSystem);
	// 把updater加入到root中
	mt->addChild(dustSystemUpdater);

	// 创建一个被我们粒子系统使用的粒子 并且定义它的一些属性
	osgParticle::Particle smokeParticle;
	smokeParticle.setSizeRange(osgParticle::rangef(0.01, 10.0)); // meters
	smokeParticle.setLifeTime(2); // seconds
	smokeParticle.setMass(0.01); // 重量 kg
	// 制作我们默认的粒子
	dustParticleSystem->setDefaultParticleTemplate(smokeParticle);


	//创建一个默认的发射器 包括默认counter,placer和shooter
	osgParticle::ModularEmitter *emitter = new osgParticle::ModularEmitter;
	// 把emitter和粒子系统关联
	emitter->setParticleSystem(dustParticleSystem);

	// 获得一个emitter的counter句柄 每一帧将会加入不同数量的粒子
	osgParticle::RandomRateCounter *dustRate = static_cast<osgParticle::RandomRateCounter *>(emitter->getCounter());
	dustRate->setRateRange(2, 3); // generate 5 to 10 particles per second

	// 定义placer ,创建和初始化一个 multi-segment placer 
	osgParticle::MultiSegmentPlacer* lineSegment = new osgParticle::MultiSegmentPlacer();
	// Add vertices to our placer. This defines line seqments along which our particles will
	// originate. (If we co-locate a tank and this emitter, this will result in dust particles
	// originating from a line extending below and behind the tank model.) 
	lineSegment->addVertex(0, 0, -1);
	lineSegment->addVertex(0, -1, -1);
	lineSegment->addVertex(0, -8, 0);
	// Use this placer for our emitter 
	emitter->setPlacer(lineSegment);

	// 定义shooter, 创建和初始化一个 radial shooter 
	osgParticle::RadialShooter* smokeShooter = new osgParticle::RadialShooter();
	// Set properties of this shooter 
	smokeShooter->setThetaRange(0.0, 3.14159 / 2); // radians, relative to Z axis.
	smokeShooter->setInitialSpeedRange(50, 100); // meters/second
	// Use this shooter for our emitter
	emitter->setShooter(smokeShooter);

	// 创建一个program来加入到粒子系统 Create a modular program and attach it to our particle system
	osgParticle::ModularProgram *moveDustInAir = new osgParticle::ModularProgram;
	moveDustInAir->setParticleSystem(dustParticleSystem);

	// 创建一个operator来模拟重力
	osgParticle::AccelOperator *accelUp = new osgParticle::AccelOperator;
	accelUp->setToGravity(-1); // scale factor for normal acceleration due to gravity. 
	moveDustInAir->addOperator(accelUp);

	// 计算空气阻力 Add an operator to our program to calculate the friction of air.
	osgParticle::FluidFrictionOperator *airFriction = new osgParticle::FluidFrictionOperator;
	airFriction->setFluidToAir();
	//airFriction->setFluidDensity(1.2929/*air*/*5.0f);
	moveDustInAir->addOperator(airFriction);

	// Finally, add the program to the scene 
	mt->addChild(moveDustInAir);
#pragma endregion

	mt->addChild(emitter);

		
	mt->setMatrix(osg::Matrix::translate(0, 0, 0));
	return mt;
}

相机的位置m_vPosition每次加上delta步长,Peng函数是碰撞检测的函数后面会详细介绍。height是碰撞检测能够检测到的地形的最低点。

void ChangePosition(osg::Vec3 delta) 
{
	float height = -1000.0f;
	//m_vPosition = osg::Vec3(m_vPosition.x(), m_vPosition.y(), 3);
	if (height = Peng(tankPosition + delta)) {
		m_vPosition += delta;
		m_vPosition = osg::Vec3(m_vPosition.x(), m_vPosition.y(), height + 11);
	}
}

同理,坦克的位置更新也是 

void ChangePositionTank(osg::Vec3 delta) 
{
	osg::Matrix mat;
	float height = -1000.0f;
	mat.makeRotate(tankRotation._v[0], osg::Vec3(1.0f, 0.0f, 0.0f),
		tankRotation._v[1], osg::Vec3(0.0f, 1.0f, 0.0f),
		tankRotation._v[2], osg::Vec3(0.0f, 0.0f, 1.0f));
	if (height = Peng(tankPosition + delta)) {
		//std::cout << "height" << height << std::endl;
		tankPosition += delta;
		tankPosition = osg::Vec3(tankPosition.x(), tankPosition.y(), height+1);
		mt->setMatrix(mat*osg::Matrixd::translate(tankPosition));
	}
		
}

三、地形跟随、碰撞检测

在坦克的中心点放置一条线,上方200m,下方1000m,如图所示。

                                                    

float Peng(osg::Vec3 pos) {
	osg::Vec3 start = osg::Vec3(pos.x(), pos.y(), pos.z() + 200);
	osg::Vec3 end = osg::Vec3(pos.x(), pos.y(), pos.z() - 1000);
	osgUtil::IntersectionVisitor* iv = new osgUtil::IntersectionVisitor;
	osgUtil::LineSegmentIntersector* ls = new osgUtil::LineSegmentIntersector(start, end);


	osgUtil::LineSegmentIntersector::Intersections intersections;
	float height = -1000.0;
	iv->setIntersector(ls);
	TerrainNode->accept(*iv);
	//如果有交点
	if (ls->containsIntersections()) {
		//取出交点
		intersections = ls->getIntersections();
		osgUtil::LineSegmentIntersector::Intersections::iterator iter = intersections.begin();

		height = iter->getWorldIntersectPoint().z();
		for (; iter != intersections.end(); iter++) {
			if (height < iter->getWorldIntersectPoint().z()) {
				height = iter->getWorldIntersectPoint().z();
			}
		}
		return height;
	}
	else {
		//不许走
		std::cout << "不许走" << std::endl;
		return 0;
	}

四、按键检测(handle函数重写)

KEYDOWN表示键盘的按键按下,同理KEYUP表示键盘按键抬起。类似的还有MOVE表示鼠标的移动,PUSH表示鼠标的按下

bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us) {
	switch (ea.getEventType()) {
	case osgGA::GUIEventAdapter::KEYDOWN:
	{
		if (ea.getKey() == 'w' || ea.getKey() == 'W'|| ea.getKey()== 0xFF52)
		{
			ChangePosition(osg::Vec3(m_fMoveSpeed * cosf(osg::PI_2 + m_vRotation[2]), m_fMoveSpeed*sinf(osg::PI_2 + m_vRotation[2]), 0));
			ChangePositionTank(osg::Vec3(m_fMoveSpeed * cosf(osg::PI_2 + m_vRotation[2]), m_fMoveSpeed*sinf(osg::PI_2 + m_vRotation[2]), 0));
		}
		else if (ea.getKey() == 's' || ea.getKey() == 'S'||ea.getKey() == 0xFF54)
		{
			ChangePosition(osg::Vec3(-m_fMoveSpeed * cosf(osg::PI_2 + m_vRotation[2]), -m_fMoveSpeed * sinf(osg::PI_2 + m_vRotation[2]), 0));
			ChangePositionTank(osg::Vec3(-m_fMoveSpeed * cosf(osg::PI_2 + m_vRotation[2]), -m_fMoveSpeed * sinf(osg::PI_2 + m_vRotation[2]), 0));
		}
		else if (ea.getKey() == 'a' || ea.getKey() == 'A' || ea.getKey() == 0xFF51) //左旋转
		{
			float temp = m_vRotation[2];
			m_vRotation[2] += osg::DegreesToRadians(m_fAngle);
			ChangeRotation(m_fAngle);
			ChangePosition(osg::Vec3(m_fRotateSpeed * (sinf(m_vRotation[2]) -  sin(temp)), m_fRotateSpeed * ((1 - cosf(m_vRotation[2])) - (1 - cos(temp))), 0));
		}
		else if (ea.getKey() == 'd' || ea.getKey() == 'D'||ea.getKey() == 0xFF53)  //右旋转
		{
			float temp = m_vRotation[2];
			m_vRotation[2] -= osg::DegreesToRadians(m_fAngle);
			ChangeRotation(-m_fAngle);
			ChangePosition(osg::Vec3(m_fRotateSpeed * (sinf(m_vRotation[2]) -  sin(temp)), m_fRotateSpeed * ((1 - cosf(m_vRotation[2])) - (1 - cos(temp))), 0));
		}
		else {

		}
		return false;
	}

最后总的类函数如下

class Follow: public osgGA::CameraManipulator {
public:
	Follow(osgViewer::Viewer* vv,osg::Node* terrain,osg::Group* root,osg::Camera* mastercam)
	{
		m_fRotateSpeed = 40;
		master = mastercam;
		rootNode = root;
		TerrainNode = terrain;
		m_vPosition = osg::Vec3(0, -40, 21);
		m_vRotation = osg::Vec3(osg::PI_2-0.15f, 0, 0);
		m_fMoveSpeed = 2.0;
		m_fAngle = 2.5;
		tankPosition = osg::Vec3(0, 0, 11);
		tankRotation = osg::Vec3(0, 0, 0);
		mt = CreateMT();
		mt->setMatrix(osg::Matrixd::translate(tankPosition));
		viewer = vv;
		osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface();
		wsi->getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0), windowwidth, windowheight);
		if (viewer) 
		{
			viewer->getSceneData()->asGroup()->addChild(mt);
		}
	}
	virtual void setByMatrix(const osg::Matrix& matrix) {

	}

	virtual void setByInverseMatrix(const osg::Matrix& matrix)
	{

	}
	virtual osg::Matrixd getMatrix() const
	{
		osg::Matrixd mat;
		mat.makeRotate(m_vRotation.x(),osg::Vec3(1,0,0),
			m_vRotation.y(),osg::Vec3(0,1,0),
			m_vRotation.z(),osg::Vec3(0,0,1));
		return mat * osg::Matrixd::translate(m_vPosition);
	}

	virtual osg::Matrixd getInverseMatrix() const
	{
		osg::Matrixd mat;
		mat.makeRotate(m_vRotation.x(), osg::Vec3(1, 0, 0),
			m_vRotation.y(), osg::Vec3(0, 1, 0),
			m_vRotation.z(), osg::Vec3(0, 0, 1));
		return osg::Matrixd::inverse(mat * osg::Matrixd::translate(m_vPosition));
	}
	void ChangePosition(osg::Vec3 delta) 
	{
		float height = -1000.0f;
		//m_vPosition = osg::Vec3(m_vPosition.x(), m_vPosition.y(), 3);
		if (height = Peng(tankPosition + delta)) {
			m_vPosition += delta;
			m_vPosition = osg::Vec3(m_vPosition.x(), m_vPosition.y(), height + 11);
		}
	}
	void ChangeRotation(float angle) {
		tankRotation[2] += osg::DegreesToRadians(angle);
		osg::Matrix mat;
		mat.makeRotate(tankRotation._v[0], osg::Vec3(1.0f, 0.0f, 0.0f),
			tankRotation._v[1], osg::Vec3(0.0f, 1.0f, 0.0f),
			tankRotation._v[2], osg::Vec3(0.0f, 0.0f, 1.0f));
		
		mt->setMatrix(mat*osg::Matrixd::translate(tankPosition));
	}

	void ChangePositionTank(osg::Vec3 delta) 
	{
		osg::Matrix mat;
		float height = -1000.0f;
		mat.makeRotate(tankRotation._v[0], osg::Vec3(1.0f, 0.0f, 0.0f),
			tankRotation._v[1], osg::Vec3(0.0f, 1.0f, 0.0f),
			tankRotation._v[2], osg::Vec3(0.0f, 0.0f, 1.0f));
		if (height = Peng(tankPosition + delta)) {
			//std::cout << "height" << height << std::endl;
			tankPosition += delta;
			tankPosition = osg::Vec3(tankPosition.x(), tankPosition.y(), height+1);
			mt->setMatrix(mat*osg::Matrixd::translate(tankPosition));
		}
		
	}
	//地形碰撞检测 地形跟随
	float Peng(osg::Vec3 pos) {
		osg::Vec3 start = osg::Vec3(pos.x(), pos.y(), pos.z() + 200);
		osg::Vec3 end = osg::Vec3(pos.x(), pos.y(), pos.z() - 1000);
		osgUtil::IntersectionVisitor* iv = new osgUtil::IntersectionVisitor;
		osgUtil::LineSegmentIntersector* ls = new osgUtil::LineSegmentIntersector(start, end);


		osgUtil::LineSegmentIntersector::Intersections intersections;
		float height = -1000.0;
		iv->setIntersector(ls);
		TerrainNode->accept(*iv);
		//如果有交点
		if (ls->containsIntersections()) {
			//取出交点
			intersections = ls->getIntersections();
			osgUtil::LineSegmentIntersector::Intersections::iterator iter = intersections.begin();

			height = iter->getWorldIntersectPoint().z();
			for (; iter != intersections.end(); iter++) {
				if (height < iter->getWorldIntersectPoint().z()) {
					height = iter->getWorldIntersectPoint().z();
				}
			}
			return height;
		}
		else {
			//不许走
			std::cout << "不许走" << std::endl;
			return 0;
		}
	}

	

	bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us) {
		switch (ea.getEventType()) {
		case osgGA::GUIEventAdapter::KEYDOWN:
		{
			if (ea.getKey() == 'w' || ea.getKey() == 'W'|| ea.getKey()== 0xFF52)
			{
				ChangePosition(osg::Vec3(m_fMoveSpeed * cosf(osg::PI_2 + m_vRotation[2]), m_fMoveSpeed*sinf(osg::PI_2 + m_vRotation[2]), 0));
				ChangePositionTank(osg::Vec3(m_fMoveSpeed * cosf(osg::PI_2 + m_vRotation[2]), m_fMoveSpeed*sinf(osg::PI_2 + m_vRotation[2]), 0));
			}
			else if (ea.getKey() == 's' || ea.getKey() == 'S'||ea.getKey() == 0xFF54)
			{
				ChangePosition(osg::Vec3(-m_fMoveSpeed * cosf(osg::PI_2 + m_vRotation[2]), -m_fMoveSpeed * sinf(osg::PI_2 + m_vRotation[2]), 0));
				ChangePositionTank(osg::Vec3(-m_fMoveSpeed * cosf(osg::PI_2 + m_vRotation[2]), -m_fMoveSpeed * sinf(osg::PI_2 + m_vRotation[2]), 0));
			}
			else if (ea.getKey() == 'a' || ea.getKey() == 'A' || ea.getKey() == 0xFF51) //左旋转
			{
				float temp = m_vRotation[2];
				m_vRotation[2] += osg::DegreesToRadians(m_fAngle);
				ChangeRotation(m_fAngle);
				ChangePosition(osg::Vec3(m_fRotateSpeed * (sinf(m_vRotation[2]) -  sin(temp)), m_fRotateSpeed * ((1 - cosf(m_vRotation[2])) - (1 - cos(temp))), 0));
			}
			else if (ea.getKey() == 'd' || ea.getKey() == 'D'||ea.getKey() == 0xFF53)  //右旋转
			{
				float temp = m_vRotation[2];
				m_vRotation[2] -= osg::DegreesToRadians(m_fAngle);
				ChangeRotation(-m_fAngle);
				ChangePosition(osg::Vec3(m_fRotateSpeed * (sinf(m_vRotation[2]) -  sin(temp)), m_fRotateSpeed * ((1 - cosf(m_vRotation[2])) - (1 - cos(temp))), 0));
			}
			else {

			}
			return false;
		}	
	}

	osg::MatrixTransform* CreateMT()
	{
		osg::MatrixTransform* mt = new osg::MatrixTransform;
		osg::Node* tankBody = osgDB::readNodeFile("../work/tankm1.ive");
		osg::Node* tankTurret = osgDB::readNodeFile("../work/turretm1.ive");
		osg::Node* tankGun = osgDB::readNodeFile("../work/gunm1.flt");

		osg::PositionAttitudeTransform* patbutton = new osg::PositionAttitudeTransform;
		patbutton->setPosition(osg::Vec3(0.0, 0.0, 0));
		patbutton->addChild(tankBody);

		osg::PositionAttitudeTransform* pathead = new osg::PositionAttitudeTransform;
		pathead->setPosition(osg::Vec3(0.0, 0.0, 0.1));
		pathead->addChild(tankTurret);

		osg::PositionAttitudeTransform* patgun = new osg::PositionAttitudeTransform;
		patgun->setPosition(osg::Vec3(0.0, 0.0, 0.2));
		patgun->addChild(tankGun);

		osg::PositionAttitudeTransform* head = new osg::PositionAttitudeTransform;
		head->addChild(pathead);
		head->addChild(patgun);

		
		mt->addChild(patbutton);
		mt->addChild(head);

#pragma region 坦克尾烟粒子
		osgParticle::ParticleSystem *dustParticleSystem = new osgParticle::ParticleSystem;
		// 设置材质,emmisive和光照
		dustParticleSystem->setDefaultAttributes("../work/dust2.rgb", false, false);

		// 粒子系统继承自 Drawable 它可以作为geode的子节点
		osg::Geode *geode = new osg::Geode;
		mt->addChild(geode);
		geode->addDrawable(dustParticleSystem);

		// 加入‘updater’来帮助每一帧的管理
		osgParticle::ParticleSystemUpdater *dustSystemUpdater = new osgParticle::ParticleSystemUpdater;
		// 将updater和例子系统联系起来
		dustSystemUpdater->addParticleSystem(dustParticleSystem);
		// 把updater加入到root中
		mt->addChild(dustSystemUpdater);

		// 创建一个被我们粒子系统使用的粒子 并且定义它的一些属性
		osgParticle::Particle smokeParticle;
		smokeParticle.setSizeRange(osgParticle::rangef(0.01, 10.0)); // meters
		smokeParticle.setLifeTime(2); // seconds
		smokeParticle.setMass(0.01); // 重量 kg
		// 制作我们默认的粒子
		dustParticleSystem->setDefaultParticleTemplate(smokeParticle);


		//创建一个默认的发射器 包括默认counter,placer和shooter
		osgParticle::ModularEmitter *emitter = new osgParticle::ModularEmitter;
		// 把emitter和粒子系统关联
		emitter->setParticleSystem(dustParticleSystem);

		// 获得一个emitter的counter句柄 每一帧将会加入不同数量的粒子
		osgParticle::RandomRateCounter *dustRate = static_cast<osgParticle::RandomRateCounter *>(emitter->getCounter());
		dustRate->setRateRange(2, 3); // generate 5 to 10 particles per second

		// 定义placer ,创建和初始化一个 multi-segment placer 
		osgParticle::MultiSegmentPlacer* lineSegment = new osgParticle::MultiSegmentPlacer();
		// Add vertices to our placer. This defines line seqments along which our particles will
		// originate. (If we co-locate a tank and this emitter, this will result in dust particles
		// originating from a line extending below and behind the tank model.) 
		lineSegment->addVertex(0, 0, -1);
		lineSegment->addVertex(0, -1, -1);
		lineSegment->addVertex(0, -8, 0);
		// Use this placer for our emitter 
		emitter->setPlacer(lineSegment);

		// 定义shooter, 创建和初始化一个 radial shooter 
		osgParticle::RadialShooter* smokeShooter = new osgParticle::RadialShooter();
		// Set properties of this shooter 
		smokeShooter->setThetaRange(0.0, 3.14159 / 2); // radians, relative to Z axis.
		smokeShooter->setInitialSpeedRange(50, 100); // meters/second
		// Use this shooter for our emitter
		emitter->setShooter(smokeShooter);

		// 创建一个program来加入到粒子系统 Create a modular program and attach it to our particle system
		osgParticle::ModularProgram *moveDustInAir = new osgParticle::ModularProgram;
		moveDustInAir->setParticleSystem(dustParticleSystem);

		// 创建一个operator来模拟重力
		osgParticle::AccelOperator *accelUp = new osgParticle::AccelOperator;
		accelUp->setToGravity(-1); // scale factor for normal acceleration due to gravity. 
		moveDustInAir->addOperator(accelUp);

		// 计算空气阻力 Add an operator to our program to calculate the friction of air.
		osgParticle::FluidFrictionOperator *airFriction = new osgParticle::FluidFrictionOperator;
		airFriction->setFluidToAir();
		//airFriction->setFluidDensity(1.2929/*air*/*5.0f);
		moveDustInAir->addOperator(airFriction);

		// Finally, add the program to the scene 
		mt->addChild(moveDustInAir);
#pragma endregion

		mt->addChild(emitter);

		
		mt->setMatrix(osg::Matrix::translate(0, 0, 0));
		return mt;
	}

private:
	osg::Vec3 m_vPosition;
	osg::Vec3 m_vRotation;
	float m_fMoveSpeed;
	float m_fRotateSpeed;
	float m_fAngle;
	osg::Node * TerrainNode;
	osg::Group* rootNode;
	osg::Camera * master;


	osg::Vec3 tankPosition;
	osg::Vec3 tankRotation;
	osg::MatrixTransform* mt;
	osgViewer::Viewer* viewer;
	unsigned int windowwidth, windowheight;
};

 

                                                                                

                                                                                   关注:人工智能咖 随时获取动态

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
本人主要从事图形图象工作,空闲之余接触了一些游戏编程,特写一些编程心得,本文 适合没有接触过人物动画编程的初学者,希望游戏制作的大虾们指点,交流。 在以前还有没接触人物动画编程的时候,觉得通过编程让人物动起来一定是一件很麻烦 的事情,尤其是初学者,大都会摸不着头脑,碰到诸如骨骼动画之类,似乎无从下手。但是 当你了解了它们的格式,就会发现其实真正的人物动画的制作并不是在编程阶段,而是在模 型构建阶段,程序员主要做工作的是掌握模型文件的格式,将存储在人物模型中的各种信息, 如顶点,面片,材质,骨骼或顶点运动的关键帧序列等信息读入内存然后用你熟悉的 SDK 绘制出来,再根据时间采用线性或者球形插值对动作序列的关键帧进行插值,不断变换顶点 坐标,从而得到了一系列连续的人物动画,听起来确实不难吧!当然你也可以在程序里自己 控制人物每一帧每一个关节的运动,我想做游戏的很少有人这么做吧。下面我向大家介绍一 下自己是如何编写人物动画程序的。本人从事的图形图象开发主要是基于 OpenGL 和 OSG 因此范例程序将采用 OpenGL 或 OSG。先声明一下,本人的语言表达能力很差,请大家多 多谅解指正。 考虑到没有接触过人物模型的朋友,我首先从人物模型的结构讲起,游戏人物编程主要 采用的人物模型格式有 Quake 里的md2,md3,Half Life 里的 mdl,Doom里的 md5,还有 典型的骨骼动画模型 ms3d…,至于3dmax 的模型,本人觉得太麻烦!在此我说两个有代表 性的 Md3,和 ms3d,其它的模型都大同小异,只要你了解了它们的格式,程序实现都不难。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值