Skip to content

Instantly share code, notes, and snippets.

@ousttrue
Created March 1, 2010 19:38
Show Gist options
  • Save ousttrue/318726 to your computer and use it in GitHub Desktop.
Save ousttrue/318726 to your computer and use it in GitHub Desktop.
irrlicht tps camera & joy pad test.
#include <irrlicht.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <iostream>
#include <cassert>
#define MEDIA_PATH "E:/vc/irrlicht-1.7.1/media/"
using namespace irr;
const static f32 MAX_PITCH=88.0f;
inline f64 toRadian(f64 deglee)
{
return M_PI * deglee / 180.0;
}
inline f64 toDeglee(f64 radian)
{
return 180.0 * radian / M_PI;
}
enum ID_PICK_TYPE
{
// I use this ISceneNode ID to indicate a scene node that is
// not pickable by getSceneNodeAndCollisionPointFromRay()
ID_IsNotPickable = 0,
// I use this flag in ISceneNode IDs to indicate that the
// scene node can be picked by ray selection.
IDFlag_IsPickable = 1 << 0,
// I use this flag in ISceneNode IDs to indicate that the
// scene node can be highlighted. In this example, the
// homonids can be highlighted, but the level mesh can't.
IDFlag_IsHighlightable = 1 << 1
};
namespace irr {
class TPSController : public IReferenceCounted
{
scene::ISceneNode *person_;
core::vector3df centerOffset_;
scene::IAnimatedMeshSceneNode *animation_;
// 移動距離/msec
f32 velocity_;
// 移動予定
core::vector3df move_;
// personの向き
f32 personHead_;
// 回転速度/msec
f32 personRotationSpeed_;
f32 baseAngle_;
f32 joystickMoveSpeed_;
enum STATUS{
STATUS_STOP,
STATUS_WALK,
STATUS_JUMP,
STATUS_FALL,
STATUS_DASH,
};
STATUS currentStatus_;
STATUS lastStatus_;
f32 jumpMove_;
f32 jumpPower_;
f32 jumpGravity_;
f32 lastPersonY_;
scene::ICameraSceneNode *camera_;
// camera向き
f32 head_;
f32 pitch_;
// personとカメラの距離
f32 distance_;
// mouseでの回転速度係数
f32 mouseRotatinSpeed_;
// joystickの遊び値
s16 joystickDeadzone_;
f32 joystickRotationSpeed_;
public:
TPSController()
: person_(0), animation_(0), velocity_(0.1f), baseAngle_(0),
personHead_(0), personRotationSpeed_(8.0f), joystickMoveSpeed_(0.5f),
currentStatus_(STATUS_STOP), lastStatus_(STATUS_STOP),
jumpMove_(0), jumpPower_(10.0f), jumpGravity_(-0.4f), lastPersonY_(0),
camera_(0), head_(0), pitch_(-30.0f), distance_(10.0f),
mouseRotatinSpeed_(0.5f), joystickDeadzone_(3200),
joystickRotationSpeed_(2.0f)
{
//grab();
}
~TPSController()
{
if(person_)
person_->drop();
if(animation_)
animation_->drop();
if(camera_)
camera_->drop();
}
void setPerson(scene::ISceneNode *node, const core::vector3df &offset){
person_=node;
person_->grab();
centerOffset_=offset;
}
void setAnimationNode(scene::IAnimatedMeshSceneNode *animation)
{
animation_=animation;
animation_->grab();
}
void setCamera(scene::ICameraSceneNode *camera){
camera_=camera;
camera_->grab();
}
void setVelocity(f32 velocity)
{
velocity_=velocity;
}
void setDistance(f32 distance)
{
distance_=distance;
}
// キャラクタがこちら向きになる角度
void setBaseAngle(f32 angle)
{
baseAngle_=angle;
}
void jump()
{
if(currentStatus_==STATUS_JUMP){
return;
}
if(currentStatus_==STATUS_FALL){
return;
}
currentStatus_=STATUS_JUMP;
jumpMove_=jumpPower_;
}
void moveJoystick(f32 xAxis, f32 yAxis)
{
bool update=false;
if(xAxis<joystickDeadzone_ && xAxis>-joystickDeadzone_){
xAxis=0;
}
else{
update=true;
}
if(yAxis<joystickDeadzone_ && yAxis>-joystickDeadzone_){
yAxis=0;
}
else{
update=true;
}
if(update){
move(xAxis/32767.0f*joystickMoveSpeed_,
yAxis/32767.0f*joystickMoveSpeed_);
}
}
void rotateJoystick(f32 xAxis, f32 yAxis)
{
bool update=false;
if(xAxis<joystickDeadzone_ && xAxis>-joystickDeadzone_){
xAxis=0;
}
else{
update=true;
}
if(yAxis<joystickDeadzone_ && yAxis>-joystickDeadzone_){
yAxis=0;
}
else{
update=true;
}
if(update){
rotate(
xAxis/32767.0f*joystickRotationSpeed_,
yAxis/32767.0f*joystickRotationSpeed_);
}
}
// キャラクターの移動(左スティック)
// カメラ方向を基準とする前後左右に移動する
void move(f32 xMove, f32 zMove)
{
if(!person_){
return;
}
if(!camera_){
return;
}
if(xMove==0 && zMove==0){
if(currentStatus_!=STATUS_JUMP && currentStatus_!=STATUS_FALL){
currentStatus_=STATUS_STOP;
}
return;
}
if(currentStatus_!=STATUS_JUMP && currentStatus_!=STATUS_FALL){
currentStatus_=STATUS_WALK;
}
core::vector3df cameraPosition=camera_->getPosition();
core::vector3df cameraTarget=camera_->getTarget();
// カメラの向きに垂直な左方向を計算する
core::vector3df zAxis=cameraTarget-cameraPosition;
zAxis.Y=0;
zAxis.normalize();
core::vector3df xAxis=core::vector3df(0, 1, 0).crossProduct(zAxis);
// 移動ベクトル
move_=zAxis*zMove+xAxis*xMove;
//move_.normalize();
// 向き
f32 angle=static_cast<f32>(toDeglee(atan2(move_.X, move_.Z)));
f32 rotation=angle-personHead_;
if(rotation!=0){
while(rotation<-180.0f){
rotation+=360.0f;
}
while(rotation>180.0f){
rotation-=360.0f;
}
if(rotation>0){
if(rotation<=personRotationSpeed_){
personHead_=angle;
}
else{
personHead_+=personRotationSpeed_;
}
}
else if(rotation<0){
if(rotation>=-personRotationSpeed_){
personHead_=angle;
}
else{
personHead_-=personRotationSpeed_;
}
}
}
}
// 視点の回転(マウス)
void rotateMouse(s32 head, s32 pitch)
{
rotate(head*mouseRotatinSpeed_, pitch*mouseRotatinSpeed_);
}
// 視点の回転
void rotate(f32 head, f32 pitch)
{
if(!person_){
return;
}
if(!camera_){
return;
}
head_+=head;
while(head_<-180.0f){
head_+=360.0f;
}
while(head_>180.0f){
head_-=360.0f;
}
pitch_+=pitch;
if(pitch_<-MAX_PITCH){
pitch_=-MAX_PITCH;
}
else if(pitch_>MAX_PITCH){
pitch_=MAX_PITCH;
}
}
// 移動を反映する
void update(u32 milisec)
{
if(!person_){
return;
}
core::vector3df personPosition=person_->getPosition();
// person jump
if(currentStatus_==STATUS_JUMP){
jumpMove_+=jumpGravity_;
if(jumpMove_<0){
currentStatus_=STATUS_FALL;
}
}
else if(currentStatus_==STATUS_FALL){
jumpMove_+=jumpGravity_;
// 接地判定?
if(personPosition.Y>=lastPersonY_){
jumpMove_=0;
currentStatus_=STATUS_STOP;
}
lastPersonY_=personPosition.Y;
}
// person move
person_->setPosition(personPosition+
core::vector3df(0, jumpMove_, 0)+
move_*static_cast<f32>(milisec)*velocity_);
move_=core::vector3df(0, 0, 0);
// person rotation
person_->setRotation(
core::vector3df(0, personHead_+baseAngle_, 0));
if(!animation_){
return;
}
if(currentStatus_==lastStatus_){
return;
}
switch(currentStatus_)
{
case STATUS_STOP:
animation_->setMD2Animation(scene::EMAT_STAND);
break;
case STATUS_WALK:
animation_->setMD2Animation(scene::EMAT_RUN);
break;
}
lastStatus_=currentStatus_;
}
void updateCamera()
{
if(!camera_){
return;
}
f32 ch=static_cast<f32>(cos(toRadian(head_)));
f32 sh=static_cast<f32>(sin(toRadian(head_)));
f32 cp=static_cast<f32>(cos(toRadian(pitch_)));
f32 sp=static_cast<f32>(sin(toRadian(pitch_)));
core::vector3df cameraDirection(-sh*cp, -sp, -ch*cp);
core::vector3df target=person_->getPosition()+centerOffset_;
camera_->setPosition(target+cameraDirection*distance_);
camera_->setTarget(target);
}
};
class MyEventReceiver : public IEventReceiver
{
bool KeyIsDown[KEY_KEY_CODES_COUNT];
SEvent::SJoystickEvent JoystickState;
public:
MyEventReceiver()
{
for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i){
KeyIsDown[i] = false;
}
}
virtual bool OnEvent(const SEvent& event)
{
if (event.EventType == EET_KEY_INPUT_EVENT){
KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
}
if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT
&& event.JoystickEvent.Joystick == 0) {
JoystickState = event.JoystickEvent;
}
return false;
}
virtual bool IsKeyDown(EKEY_CODE keyCode) const
{
return KeyIsDown[keyCode];
}
s16 getJoystickAxis(u32 joystick, u32 axis)const
{
if(joystick!=0){
return 0;
}
return JoystickState.Axis[axis];
}
bool IsButtonPressed(u32 joystick, u32 button)const
{
if(joystick!=0){
return false;
}
return JoystickState.IsButtonPressed(button);
}
};
namespace scene {
class TPSAnimator : public ISceneNodeAnimator
{
TPSController *controller_;
u32 lastMS;
public:
TPSAnimator(TPSController *controller)
: lastMS(0), controller_(controller)
{}
//! Destructor
virtual ~TPSAnimator() {}
//! Animates a scene node.
virtual void animateNode(ISceneNode* node, u32 timeMs){
u32 interval=timeMs-lastMS;
lastMS=timeMs;
controller_->update(interval);
}
//! Creates a clone of this animator.
virtual ISceneNodeAnimator* createClone(
ISceneNode* node, ISceneManager* newManager=0){
return 0;
}
//! Returns true if this animator receives events.
virtual bool isEventReceiverEnabled() const
{
return false;
}
//! Event receiver,
//! override this function for camera controlling animators
virtual bool OnEvent(const SEvent& event)
{
return true;
}
//! Returns type of the scene node animator
virtual ESCENE_NODE_ANIMATOR_TYPE getType() const
{
return ESNAT_UNKNOWN;
}
};
}
}
int main()
{
// create device
MyEventReceiver receiver;
IrrlichtDevice* device = createDevice(video::EDT_OPENGL,
core::dimension2d<u32>(640, 480), 16, false, false, false, &receiver);
if (device == 0){
return 1; // could not create selected driver.
}
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
// map
device->getFileSystem()->addZipFileArchive(MEDIA_PATH "map-20kdm2.pk3");
scene::IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp");
scene::IMeshSceneNode* q3node = 0;
q3node = smgr->addOctreeSceneNode(q3levelmesh->getMesh(0), 0, IDFlag_IsPickable);
q3node->setPosition(core::vector3df(-1350,-130,-1400));
scene::ITriangleSelector* selector = smgr->createOctreeTriangleSelector(
q3node->getMesh(), q3node, 128);
q3node->setTriangleSelector(selector);
selector->drop();
const f32 scalingFactor=1.2f;
/*
// ninja
scene::IAnimatedMesh *ninjaMesh=smgr->getMesh(MEDIA_PATH "ninja.b3d");
scene::ISceneNode *ninjaNode=smgr->addEmptySceneNode();
scene::IAnimatedMeshSceneNode *ninja =
smgr->addAnimatedMeshSceneNode(ninjaMesh, ninjaNode);
assert(ninja);
ninja->setMaterialFlag(video::EMF_LIGHTING, false);
//ninja->setFrameLoop(0, 13);
ninja->setAnimationSpeed(15);
ninja->setScale(core::vector3df(scalingFactor));
*/
scene::IAnimatedMesh *SydneyMesh = smgr->getMesh(MEDIA_PATH "sydney.md2");
scene::IAnimatedMeshSceneNode *sydney =
smgr->addAnimatedMeshSceneNode( SydneyMesh );
sydney->setMaterialTexture(0,
driver->getTexture(MEDIA_PATH "sydney.bmp") );
sydney->setMaterialFlag(video::EMF_LIGHTING, false);
sydney->setMD2Animation(scene::EMAT_STAND);
sydney->setScale(core::vector3df(scalingFactor));
// camera
scene::ICameraSceneNode *camera=smgr->addCameraSceneNode();
// TPS control
TPSController *controller=new TPSController;
// TPS control animator
{
scene::ISceneNodeAnimator* anim = new scene::TPSAnimator(controller);
sydney->addAnimator(anim);
anim->drop();
}
{
// collision animator
const core::aabbox3df& box=sydney->getBoundingBox();
scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
selector, sydney, scalingFactor*(box.MaxEdge-box.getCenter()),
core::vector3df(0,-10,0), -scalingFactor*(box.getCenter()));
sydney->addAnimator(anim);
anim->drop();
}
// Add a colorful irrlicht logo
{
device->getGUIEnvironment()->addImage(
driver->getTexture(MEDIA_PATH "irrlichtlogoalpha2.tga"),
core::position2d<s32>(10,20));
gui::IGUIStaticText* diagnostics = device->getGUIEnvironment()->addStaticText(
L"", core::rect<s32>(10, 10, 400, 20));
diagnostics->setOverrideColor(video::SColor(255, 255, 255, 0));
}
// controller
controller->setPerson(sydney, scalingFactor*core::vector3df(0, 18.0f, 0));
controller->setDistance(scalingFactor*40.0f);
controller->setAnimationNode(sydney);
controller->setCamera(camera);
controller->setBaseAngle(-90.0f);
controller->setVelocity(0.5f*scalingFactor);
// activate joysticks
core::array<SJoystickInfo> joystickInfo;
device->activateJoysticks(joystickInfo);
gui::ICursorControl *cursor=device->getCursorControl();
cursor->setVisible(false);
core::vector2di lastMouse=cursor->getPosition();
int lastFPS = -1;
u32 then = device->getTimer()->getTime();
const f32 MOVEMENT_SPEED = 5.f;
while(device->run())
{
// update
const u32 now = device->getTimer()->getTime();
const u32 frameDeltaTime = now - then;
then = now;
if(receiver.IsKeyDown(KEY_ESCAPE)){
device->closeDevice();
break;
}
////////////////////////////////////////////////////////////
// keyboard
////////////////////////////////////////////////////////////
// w
// asd move
f32 zMove=0;
f32 xMove=0;
if(receiver.IsKeyDown(KEY_KEY_W)){
zMove=1.0f;
}
else if(receiver.IsKeyDown(KEY_KEY_S)){
zMove=-1.0f;
}
if(receiver.IsKeyDown(KEY_KEY_A)){
xMove=-1.0f;
}
else if(receiver.IsKeyDown(KEY_KEY_D)){
xMove=1.0f;
}
controller->move(xMove, zMove);
// jump
if(receiver.IsKeyDown(KEY_SPACE)){
controller->jump();
}
// rotattion
f32 head=0;
f32 pitch=0;
if(receiver.IsKeyDown(KEY_LEFT)){
head=-1.0f;
}
else if(receiver.IsKeyDown(KEY_RIGHT)){
head=1.0f;
}
if(receiver.IsKeyDown(KEY_DOWN)){
pitch=-1.0f;
}
else if(receiver.IsKeyDown(KEY_UP)){
pitch=1.0f;
}
controller->rotate(head, pitch);
////////////////////////////////////////////////////////////
// mouse
////////////////////////////////////////////////////////////
// mouse rotation
core::vector2di mouse=cursor->getPosition();
controller->rotateMouse(mouse.X-lastMouse.X, lastMouse.Y-mouse.Y);
cursor->setPosition(0.5f, 0.5f);
lastMouse=cursor->getPosition();
////////////////////////////////////////////////////////////
// joy pad
////////////////////////////////////////////////////////////
// left analog stick
controller->moveJoystick(
static_cast<f32>(
receiver.getJoystickAxis(0, SEvent::SJoystickEvent::AXIS_X)),
static_cast<f32>(
-receiver.getJoystickAxis(0, SEvent::SJoystickEvent::AXIS_Y)));
// right analog stick
controller->rotateJoystick(
static_cast<f32>(
receiver.getJoystickAxis(0, SEvent::SJoystickEvent::AXIS_R)),
static_cast<f32>(
-receiver.getJoystickAxis(0, SEvent::SJoystickEvent::AXIS_Z)));
// pad jump
if(receiver.IsButtonPressed(0, 0)){
controller->jump();
}
//controller->update(frameDeltaTime);
controller->updateCamera();
// draw
driver->beginScene(true, true, video::SColor(255,113,113,133));
smgr->drawAll();
device->getGUIEnvironment()->drawAll();
driver->endScene();
// fps
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw tmp(L"Movement Example - Irrlicht Engine [");
tmp += driver->getName();
tmp += L"] fps: ";
tmp += fps;
device->setWindowCaption(tmp.c_str());
lastFPS = fps;
}
}
controller->drop();
device->drop();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment