My character controller example

shanefarris

17-12-2010 16:34:30

Here is my CC I based off of the PhysX demo for the CC. It's not perfect, but its a start, and if anyone wants to add anything to it feel free and show us what you did.

Warning this code isn't polished, but you would probably want to modify it anyway if you wanted to use it in your projects.

Header:

#ifndef CHARACTERCONTROL_H
#define CHARACTERCONTROL_H

#include "Defines.h"
#include "NxUserAllocator.h"
#include "NxCharacter\NxController.h"
#include "NxOgre\NxOgreWorld.h"
#include "NxOgre\NxOgreScene.h"

class NxScene;
class NxControllerManager;


namespace Core
{
namespace Physics
{

enum E_GAME_GROUP
{
GROUP_NON_COLLIDABLE,
GROUP_COLLIDABLE_NON_PUSHABLE,
GROUP_COLLIDABLE_PUSHABLE,
};

class ControllerHitReport : public NxUserControllerHitReport
{
public:
virtual NxControllerAction onShapeHit(const NxControllerShapeHit& hit);

virtual NxControllerAction onControllerHit(const NxControllersHit& hit);

};

class CORE_EXPORT CCharacterController
{
public:
CCharacterController(NxOgre::World* world, NxOgre::Scene* scene);
~CCharacterController();

void ReleaseControllerManager();

bool InitCharacterControllers(const reVector3Df& startPos, f32 scale);
void ReleaseCharacterControllers(NxScene& scene);

void UpdateControllers();

u32 MoveCharacter(const reVector3Df& dispVector, f32 elapsedTime, u32 collisionGroups, f32 heightDelta);
const reVector3Df& GetCharacterPos();
NxActor* GetCharacterActor(NxU32 characterIndex);

void ReportSceneChanged(NxU32 characterIndex);

bool ResetCharacterPos(NxU32 index, const NxVec3& pos);
bool UpdateCharacterExtents(bool& increase);

private:

class ControllerAllocator : public NxUserAllocator
{
public:
ControllerAllocator(void) {}
void* mallocDEBUG(size_t size, const char*, int) { return ::malloc(size); }
void* mallocDEBUG(size_t size, const char*, int, const char*, NxMemoryType) { return ::malloc(size); }
void* malloc(size_t size) { return ::malloc(size); }
void* malloc(size_t size, NxMemoryType) { return ::malloc(size); }
void* realloc(void* mem, size_t new_size) { return ::realloc(mem, new_size); }
void free(void* mem) { ::free(mem); }

};

NxF32 SkinWidth;
NxF32 gInitialRadius;
NxF32 gInitialHeight;
NxVec3 gStartPos;
NxVec3 gInitialExtents;

NxCapsuleController* m_Controller;
ControllerAllocator* m_Allocator;
NxOgre::World* m_World;
NxScene* gScene;
NxControllerManager* gManager;
ControllerHitReport* gControllerHitReport;

};

}
}


#endif




Source:

#include "NxPhysics.h"
#include "NxCharacter\NxCapsuleController.h"
#include "NxCharacter\ControllerManager.h"
#include "NxCharacter\NxControllerManager.h"
#include "CharacterControl.h"

using namespace Core::Physics;

#define COLLIDABLE_MASK (1<<GROUP_COLLIDABLE_NON_PUSHABLE) | (1<<GROUP_COLLIDABLE_PUSHABLE)

NxControllerAction ControllerHitReport::onShapeHit(const NxControllerShapeHit& hit)
{
if(hit.shape)
{
NxCollisionGroup group = hit.shape->getGroup();
if(group != GROUP_COLLIDABLE_NON_PUSHABLE)
{
NxActor& actor = hit.shape->getActor();
if(actor.isDynamic())
{
// We only allow horizontal pushes. Vertical pushes when we stand on dynamic objects creates
// useless stress on the solver. It would be possible to enable/disable vertical pushes on
// particular objects, if the gameplay requires it.
if(hit.dir.y==0.0f)
{
NxF32 coeff = actor.getMass() * hit.length * 10.0f;
actor.addForceAtLocalPos(hit.dir*coeff, NxVec3(0,0,0), NX_IMPULSE);
// actor.addForceAtPos(hit.dir*coeff, hit.controller->getPosition(), NX_IMPULSE);
// actor.addForceAtPos(hit.dir*coeff, hit.worldPos, NX_IMPULSE);
}
}
}
}

return NX_ACTION_NONE;
}

NxControllerAction ControllerHitReport::onControllerHit(const NxControllersHit& hit)
{
return NX_ACTION_NONE;
}

CCharacterController::CCharacterController(NxOgre::World* world, NxOgre::Scene* scene)
{
SkinWidth = 0.2f;
gInitialRadius = 0.5f;
gInitialHeight = 2.0f;
gStartPos = NxVec3(0.0f, 10.0f, 0.0f);
gInitialExtents = NxVec3(0.5f, 1.0f, 0.5f);

m_Controller = NULL;
m_World = world;
m_Allocator = new ControllerAllocator();
gManager = NxCreateControllerManager(m_Allocator);
gScene = scene->getScene();
gControllerHitReport = new ControllerHitReport();
}

CCharacterController::~CCharacterController()
{
}

void CCharacterController::ReleaseControllerManager()
{
NxReleaseControllerManager(gManager);
}

void CCharacterController::UpdateControllers()
{
gManager->updateControllers();
}

bool CCharacterController::InitCharacterControllers(const reVector3Df& startPos, NxReal scale)
{
NxCapsuleControllerDesc desc;
desc.position.x = startPos.x;
desc.position.y = startPos.y;
desc.position.z = startPos.z;
desc.radius = gInitialRadius * scale;
desc.height = gInitialHeight * scale;
desc.upDirection = NX_Y;
// desc.slopeLimit = cosf(NxMath::degToRad(45.0f));
desc.slopeLimit = 0;
desc.skinWidth = SkinWidth;
desc.stepOffset = 0.5;
desc.stepOffset = gInitialRadius * 0.5 * scale;
// desc.stepOffset = 0.01f;
// desc.stepOffset = 0; // Fixes some issues
// desc.stepOffset = 10;
desc.callback = gControllerHitReport;
gManager->createController(gScene, desc);

m_Controller = static_cast<NxCapsuleController*>(gManager->getController(0));
return m_Controller ? true : false;
}

void CCharacterController::ReleaseCharacterControllers(NxScene& scene)
{
gManager->purgeControllers();
}

NxU32 CCharacterController::MoveCharacter(const reVector3Df& dispVector, f32 elapsedTime, u32 collisionGroups, f32 heightDelta)
{
// not required if static actors haven't changed
// c->reportSceneChanged();

f32 sharpness = 1.0f;

u32 collisionFlags;
reVector3Df d = dispVector * elapsedTime;
if(heightDelta!=0.0f)
d.y += heightDelta;

const NxVec3 vec = NxVec3(d.x, d.y, d.z);
m_Controller->move(vec, COLLIDABLE_MASK, 0.000001f, collisionFlags, sharpness);
return collisionFlags;
}

const reVector3Df& CCharacterController::GetCharacterPos()
{
NxExtendedVec3 pos = gManager->getController(0)->getFilteredPosition();
return reVector3Df(pos.x, pos.y, pos.z);
}

NxActor* CCharacterController::GetCharacterActor(NxU32 characterIndex)
{
return gManager->getController(characterIndex)->getActor();
}

bool CCharacterController::ResetCharacterPos(NxU32 index, const NxVec3& pos)
{
return gManager->getController(index)->setPosition(NxExtendedVec3(pos.x, pos.y, pos.z));
}

void CCharacterController::ReportSceneChanged(NxU32 index)
{
gManager->getController(index)->reportSceneChanged();
}

bool CCharacterController::UpdateCharacterExtents(bool& increase)
{
NxF32 height = m_Controller->getHeight();
NxF32 radius = m_Controller->getRadius();
NxF32 inc = 1.0f;
reVector3Df pos = GetCharacterPos();
if (increase)
{
height += inc;
pos.y += inc*0.5f;
}
else
{
height -= inc;
pos.y -= inc*0.5f;
}

NxCapsule worldCapsule;
worldCapsule.p0.x = worldCapsule.p1.x = pos.x;
worldCapsule.p0.y = worldCapsule.p1.y = pos.y;
worldCapsule.p0.z = worldCapsule.p1.z = pos.z;
worldCapsule.p0.y -= height*0.5f;
worldCapsule.p1.y += height*0.5f;
worldCapsule.radius = radius;
m_Controller->setCollision(false); // Avoid checking overlap with ourself
bool Status = gScene->checkOverlapCapsule(worldCapsule);
m_Controller->setCollision(true);
if(Status)
{
throw("Can not resize capsule.");
return false;
}

increase = !increase; // Increase or decrease height each time we're called

// WARNING: the SDK currently doesn't check for collisions when changing height, so if you're close
// to a wall you might end up penetrating it. In some cases you might also fall through the level.
// A more advanced implementation will take care of that later.
m_Controller->setPosition(NxExtendedVec3(pos.x, pos.y, pos.z));
return m_Controller->setHeight(height);
}



Oh and I just remembered its still using straight PhysX, I haven't changed it to work with NxOgre yet, but does work with Ogre.

Example usage: passing a "player" object and calling the "InitCharacterControllers" method

ICharacterController* CPhysicsStrategy_PhysX::CreateCharacterController(CPlayer* Player)
{
ICharacterController* CharacterController = new CCharacterController_PhysX(Player);
CharacterController->InitCharacterControllers(Player->GetPlayerNode()->getPosition(), 10.0f);
return CharacterController;
}


Updating: I have a "m_Movement" vector already set by standard keyboard inputs, and that is passed the CC class

void CPlayerZombie::UpdatePhysics(const f32& elapsedTime)
{
if(m_Movement != reVector3Df::ZERO)
{
m_CharacaterController->MoveCharacter(m_Movement, elapsedTime, 0, 0);

// Update the position
m_PlayerEntity->getParentNode()->setPosition(m_CharacaterController->GetCharacterPos());
}
m_CharacaterController->UpdateControllers();
}


Hope this helps, this group has helped me so much I hope this contributes a little bit.

Thanks.