Page 1 of 2

Character control and animation transition sample

Posted: Sun May 25, 2008 3:13 pm
by doqkhanh
Hi there, this is my class with some hard code to demo new animation feature in Irrlicht 1.4 called: Animation Transitions

http://www.irrlicht3d.org/wiki/index.ph ... tionSystem
How To Use The New Animation System

This tutorial will show you how to use the new animation system in Irrlicht 1.4

Note: this only applies with bone based formats (eg b3d, ms3d, x)
Transitions

Transitions are for blending from one animation into another. eg when a characters stops running, instead of just jumping to an idle animation, if you use a transition it will blend the animations together and make it look natural.

To use it, call:

Node->setTransitionTime( TimeOfBlendInSeconds );

Joint control will automatically be enabled (the blending needs it), so you must also call:

Node->animateJoints();

Before the drawAll
We have 2 files here, named CCharacter.h and CCharacter.cpp, I have been using ninja free model at:

Code: Select all

http://www.psionic3d.co.uk/downloads/ninja.zip
To use these classes:
1. Create a new instance in your main

Code: Select all

	//Main character
	CCharacter* viethero;
2. Init new character

Code: Select all

	//Setup viethero character
	viethero = new CCharacter(game->getDevice(),game->getSceneManager(),game->getVideoDriver(),"Media\\Model\\Ninja\\ninja.b3d");

	viethero->setPosition(irr::core::vector3df(1275.0f, 60.0f, 2675.0f));

3. In render loop, try to call

Code: Select all

	//Control main character
	viethero->move(targetPoint);
4. On any event, such as mouse click on terrain try to set

Code: Select all

targetPoint = collisionPoint;
viethero->walk();
Happy Irrlicht Coding !

Code: Select all

/*
 ***********************************************
 * Character control
 * *********************************************
 * file name: CCharacter.h
 * encoding: UTF-8
 * tab size: 8
 * indentation: 4
 * created on: 8:55 PM 3/17/2008
 * init by: Do Quoc Khanh - doqkhanh
 * created by: FOSP Team
 * copyright: FOS Project
 */

#pragma once
#include <irrlicht.h>

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

class CCharacter : public irr::scene::IAnimationEndCallBack 
{
private:
	float fSpeed;
	scene::IAnimatedMeshSceneNode* node;


public:
	enum eAttacktype{
		MagicAttack,
		PowerAttack,
		DownswipeAttack
	};

	enum eState{
		Run,
		Walk, 
		Attack,
		Idle
	};	
	
	eAttacktype curAttackType;
	eState curState;
	eState oldState;

public:
	CCharacter(IrrlichtDevice* device, irr::scene::ISceneManager *smgr,irr::video::IVideoDriver *videodriver, char* meshPath);
	~CCharacter(void);
	
	void setState(eState newState);
	void setPosition(core::vector3df pos);
	void setRotate(core::vector3df rot);
	
	core::vector3df getPosition();
	core::vector3df getRotate();
	eState getState();
	irr::scene::IAnimatedMeshSceneNode* getMesh();
	irr::scene::IAnimatedMeshSceneNode* getNode();

	void run();
	void walk();
	void attack(eAttacktype);
	void idle();

	void move(core::vector3df pos);
	void update();

	void remove();

	void OnAnimationEnd(IAnimatedMeshSceneNode* node);
};

Code: Select all

/*
 ***********************************************
 * Character control
 * *********************************************
 * file name: CCharacter.h
 * encoding: UTF-8
 * tab size: 8
 * indentation: 4
 * created on: 9:03 PM 3/17/2008
 * init by: Do Quoc Khanh - doqkhanh
 * created by: FOSP Team
 * copyright: FOS Project
 */
#include "CCharacter.h"

#include <irrlicht.h>
#define ANIMATION_SPEED 5
irr::scene::IAnimatedMeshSceneNode* CCharacter::getMesh()
{
	return node;
}

CCharacter::CCharacter(IrrlichtDevice* device, irr::scene::ISceneManager *smgr,irr::video::IVideoDriver *videodriver, char* meshPath)
{
	node = smgr->addAnimatedMeshSceneNode(smgr->getMesh(meshPath));
	node->setMaterialFlag(video::EMF_LIGHTING, false);

	fSpeed = 0.4f;

	curState = CCharacter::eState::Idle;
	oldState = CCharacter::eState::Idle;

	curAttackType = CCharacter::eAttacktype::MagicAttack;

	node->setJointMode(irr::scene::EJUOR_CONTROL);
	node->setTransitionTime(0.5);
	
	idle();
}

CCharacter::~CCharacter(void)
{
	node->remove();
}

void CCharacter::setState(eState newState)
{
	oldState = curState;
	curState = newState;
}

void CCharacter::setPosition(core::vector3df pos)
{
	node->setPosition(pos);
}

void CCharacter::setRotate(core::vector3df rot)
{
	node->setRotation(rot);
}

	
core::vector3df CCharacter::getPosition()
{
	return node->getPosition();
}

core::vector3df CCharacter::getRotate()
{
	return node->getRotation();
}

CCharacter::eState CCharacter::getState()
{
	return curState;
}


void CCharacter::run()
{
	setState(CCharacter::eState::Run);
	
	node->setAnimationSpeed(ANIMATION_SPEED);
	node->setLoopMode(true);
	node->setFrameLoop(1,14);
}

void CCharacter::walk()
{
	setState(CCharacter::eState::Walk);
	
	node->setAnimationSpeed(ANIMATION_SPEED);
	node->setLoopMode(true);
	node->setFrameLoop(1,14);
}

void CCharacter::idle()
{
	setState(CCharacter::eState::Idle);
	
	node->setAnimationSpeed(ANIMATION_SPEED);
	node->setLoopMode(true);
	node->setFrameLoop(206,300);
}


void CCharacter::attack(eAttacktype attackType)
{
	setState(CCharacter::eState::Attack);
	switch (attackType)
	{
		case eAttacktype::PowerAttack :
		{	
			node->setAnimationSpeed(ANIMATION_SPEED);
			node->setLoopMode(false);
			node->setFrameLoop(45,59);
			node->setAnimationEndCallback(this);
			return;
		} 	

		case eAttacktype::DownswipeAttack:
		{	
			node->setAnimationSpeed(ANIMATION_SPEED);
			node->setLoopMode(false);
			node->setFrameLoop(60,68);
			node->setAnimationEndCallback(this);
			return;
		} 

		case eAttacktype::MagicAttack:
		{	
			node->setAnimationSpeed(ANIMATION_SPEED);
			node->setLoopMode(false);
			node->setFrameLoop(69,72);
			node->setAnimationEndCallback(this);
			return;
		} 

	}

}


core::vector3df faceTarget(irr::core::vector3df targetpos, irr::core::vector3df nodepos) {

  core::vector3df posDiff = targetpos - nodepos;
  f32 degree = nodepos.Y; //keep current rotation if nothing to do
  posDiff.normalize();

  if (posDiff.X != 0.0f || posDiff.Z != 0.0f)
    degree = atan2(posDiff.X,posDiff.Z) * core::RADTODEG;

  return core::vector3df(0,degree,0);
} 


void moveto(irr::scene::ISceneNode *node, //node to move
            irr::core::vector3df vel) //velocity vector
{
    irr::core::matrix4 m;
    m.setRotationDegrees(node->getRotation());
    m.transformVect(vel);
    node->setPosition(node->getPosition() + vel);
    node->updateAbsolutePosition();
} 

void CCharacter::move(core::vector3df pos)
{
	// Avoid complex operation in next step
	if(getState() != CCharacter::eState::Walk && getState() != CCharacter::eState::Run)
		return;

	if (node->getPosition().getDistanceFrom(pos) < 4)
	{
		idle();
	}
	else
	{
		node->setRotation(	faceTarget(pos, node->getPosition()) );
		moveto(node, core::vector3df(0,0,fSpeed));
	}

	this->node->animateJoints() ;
}

void CCharacter::update()
{
	//Do nothing
}

void CCharacter::OnAnimationEnd(IAnimatedMeshSceneNode* node)
{
    idle();
} 

void CCharacter::remove()
{
	node->remove();
}

irr::scene::IAnimatedMeshSceneNode* CCharacter::getNode()
{
	return node;	
}

Posted: Mon May 26, 2008 3:06 pm
by trivtn
Nice work! :)

Posted: Mon May 26, 2008 5:44 pm
by Halifax
A compiled example, and possibly some screenshots, would definitely be cool and probably bump up the interest.

Posted: Tue May 27, 2008 6:36 am
by huydotnet
http://fosengine.googlecode.com/files/D ... %20May.rar
here is the binary, move the ninja with right mouse button

Posted: Tue May 27, 2008 1:24 pm
by doqkhanh
And please notice the difference in:
http://code.google.com/p/fosengine/source/detail?r=113

I fixed a error with animation in idle status.

Posted: Thu May 29, 2008 11:17 am
by trivtn
I've tried it . Here is my screneshot, and little demo.Thanks doqkhanh ! This is very userful in my project.
screenshot :
Image
Demo :
http://www.4shared.com/file/49373783/32 ... onWin.html

Posted: Thu May 29, 2008 6:52 pm
by dlangdev
this is cool. an animation clip based character controller.

i haven't implemented the code yet, from the looks of it the class can definitely fit into the fight (taekwondo sparring) match currently under development.

Posted: Fri May 30, 2008 11:23 am
by dlangdev
Hi doqkhanh, quick question.

Will this code work with the camera?

I will be setting up a test code for a cut-scene, in there I will need to control characters and camera.

I'm looking for a sample b3d file having character and camera animation. Let me know if you have done this kind of test or think it is possible to implement.

Thank you.

Posted: Fri May 30, 2008 11:42 am
by doqkhanh
Hi, my class just control the Ninja character only.
You can get Ninja position and rotation to control your camera.
dlangdev wrote:Hi doqkhanh, quick question.

Will this code work with the camera?

I will be setting up a test code for a cut-scene, in there I will need to control characters and camera.

I'm looking for a sample b3d file having character and camera animation. Let me know if you have done this kind of test or think it is possible to implement.

Thank you.

Posted: Fri May 30, 2008 11:47 am
by dlangdev
It looks like it can be done if the code is modified to fit the cut-scene, though.

thanks, I have an idea now how to approach this problem.

Posted: Thu Jun 12, 2008 9:42 am
by wuallen
incredible, I LOVE LOVE this blending.

Posted: Tue Jul 29, 2008 1:30 pm
by pera
Im using this code it is just what I need - and it is great implementation.
Really perfect. Thanks for sharing! (Ill put your link in comment header)

One thing though, I had to move this line
this->node->animateJoints();
before return from function to animate idle state.

You return from move() function if state is other then walk/run, and idle state was just frozen ninja.

EDIT: Ahh I see you changed that! That would have saved my time! Maybe you should edit first post and add changes to the given code?

Posted: Mon Aug 24, 2009 10:49 am
by theOneAwaited
I love your character animation sample and it really helped me learn alot about how the engine works. Thanks for posting it!

I am new to Irrlicht and I know the character animation sample you posted is rather old, but I was wondering:

why does the character not walk level with the ground?

I know this sample isn't supposed to address that issue, but I was wondering if you could help me figure out how to adjust the character's Y position so he is level with the ground when walking.

I tried terrain->getPosition(characterpos.X, characterPos.Z) to get the Y, but when I pass that to the character, he doesn't move forward anymore like he is still collidiing with the terrain.

Thanks in advance!

Posted: Thu Aug 27, 2009 11:05 am
by trivtn
Here my code ( in device->run() loop):
//Control main character
viethero->move(targetPoint);
viethero->setPosition(vector3df(viethero->getPosition().X,
terrain->getHeight(viethero->getPosition().X,viethero->getPosition().Z)
,viethero->getPosition().Z));
//

Posted: Thu Aug 27, 2009 11:26 am
by trivtn