XpLoDWilD
11-03-2011 11:03:28
Hi everyone,
I've recently switched to BuggySwires to have the nice CharacterController. As it turns out, I have now a little issue with Scene recreation.
I'm using Ogre::Terrain, and generating heightfield on the fly when loading them. But the issue is that when I'm changing map, I must delete the whole scene because it's StaticGeometry. Deleting the Scene implies destroying and recreating the whole Critter RenderSystem. But this also clears all the loaded animations, and actor's settings, which I have to reload then, and this might take some time.
Is there a cleaner way to do it rather than having to clear up the whole Physics module and starting it up ?
betajaen
11-03-2011 11:21:58
I suppose; a changeScene function could be added to Critter. That shouldn't be a problem, and it could automatically delete all of the Bodies, KinematicActors, etc. then reseting some of the Scene again, it should behave like the destructor and constructor, so fairly straightforward to write.
Your using Heightfields right? I don't know if you know, but loaded Meshes and HeightFields meshes don't belong to a Scene but to the World. So if you delete the HeightField geometry, it won't delete the HeightField mesh as well. If you do, and your doing things about this - then fair enough.
I do hate this limitation of deleting the Scene to remove the SceneGeometry though. I need an intelligent and efficient solution to do this, perhaps a destroySceneGeometry function is required after all.
XpLoDWilD
11-03-2011 12:20:48
Definately it would be really nice to have a changeScene function, it would save a lot of efforts to everyone I think
. If I remember correctly, not being able to delete sceneGeometry is related to PhysX core, isn't it? So doing a destroySceneGeometry function would imply "internally" clearing the scene and reloading everything but the specified SceneGeometry ?
Also, I searched a little bit the forums, and I haven't found any documentation about the AnimatedCharacter/CharacterController modules, do some exists or do I have to look and some code samples in here and in the source?
betajaen
11-03-2011 13:05:25
It's more of a restriction I placed in place from interpreting the documentation. Although in NxOgre; Actors, SceneGeometries, KinematicActors and Volumes are separate classes, in PhysX it's just one NxActor. Which even if the NxActor has the NX_BF_KINEMATIC flag, or the NxActor has no NxBody, you can still use the functions - even though the PhysX documentation frowns upon.
What I will do is add a "forceDestroySceneGeometry" function into Scene, I'll put it in this weekend for you, instead of the changeScene function (which isn't needed now).
There is the
1201 tutorial in the BuggySwires version of the NxOgre tutorials. But for no actual documentation, no. But the tutorial demostrates everything about it, and I'm sure you'll pick it up quickly.
betajaen
11-03-2011 15:47:02
There you go:
BuggySwires/Sparked
- Added requested forceDestroySceneGeometry function into Scene.
- Fixed compiler warning in NxOgreMemoryResource.cpp
I haven't tested it though, just compiled it.
XpLoDWilD
11-03-2011 19:23:02
So far it seems to be working. I need to do some extended tests though, I'll report back to you if there's any issue.
About CharacterController, I have one little issue: As soon as my character falls a little bit, it stops moving completely, falls down, then continue moving. Is there a way to set a minimum move speed when falling ?
betajaen
11-03-2011 19:24:39
Falls for a bit, then stops and then falls again?
No, it doesn't have a minimum speed. It just uses the Scene's gravity.
XpLoDWilD
11-03-2011 19:54:14
Oops, it was because my gravity was too small. Each time my character would fall just a little bit even on a little bump where the character could simple walk up and down on it, it would totally stop moving (like having X/Z velocity set to 0), and fall down slowly, and continue this way until I finally reach the end of the bump (I still press the Forward key).
betajaen
11-03-2011 20:03:25
Ahh. I see, it's at -9.8 now I assume?
XpLoDWilD
11-03-2011 21:13:44
Well, it's 5.5 times more in fact. I know it's quite a huge number, I'll check if I'm not doing anything bad...
Also, I seems to have a bug with jump with the CharacterController. When I walk in another direction and press my Jump key, it jumps towards the Z axis and not the relative angle. I'm using left/right with input.is_turning = true; to rotate my character (so through CharacterInputHelper), and InputHelper->up(true); to jump. Also, when I quickly press my rotate key, my character goes to "no pose" (like no animation applied) to Idle. There might be some fading issues in there.
Edit:
So I looked up the Critter code watching how to fix my issues, here's what I've found :
- When you jump, the X/Z displacement are calculated based on forward/backward/left/right values. However, while this might be right when is_turning = false, it's wrong when using character rotation. I think you must calculate jump displacement using the character's yaw angle in order to get the right effect.
- When falling down a mountain (without jumping), the X/Z displacement is set to zero, so it's not physically realist (you arrive to the edge of the mountain, start walking forwards, and as soon as your char falls, the displacement is set to 0 and he starts falling. In real world, he would keep his X/Z velocity). (or this might be due to inadapted gravity and no sliding, it collides down each little centimeter so if you keep moving forwards you Move/fall/land/move/fall/land, and air displacement is set to zero each time he lands - but further testing shows that the air displacement is set to zero when you fall. I'll try and search the incriminating code)
- When using is_turning = true, when I rotate left/right, the animation used is the RUN/WALK animation. It would be better to have some LEAN_LEFT/LEAN_RIGHT animation, or use the idle animation. It's not very good looking having your character "walking" when just turning around.
Edit 2:
- When an animation starts while a previous one haven't looped at least one, the new animation fades from "no pose". Repro case : bind a key to rotate your character (is_turning = true), and spam that key. Each time you press the key, if you're fast enough, the animation will fade from "none" to the animation each time you press it.
I'll try and see if I can patch some of the issues I've found.
XpLoDWilD
12-03-2011 11:53:27
I fixed the first two issues. Here's my advancePhysics function (i added a NxVec3 in the header called mLastDisplacement) :
void AnimatedCharacter::advancePhysics(float deltaTime, const NxOgre::Enums::Priority&)
{
// Wait for any animation to finish, then switch to a new one.
if (mAnimWait)
{
if (mNode->getCurrentAnimation(0) != Enums::NO_ANIMATION)
{
if (mNode->getCurrentAnimationEnded(0) == false && mNode->getCurrentAnimationLoops(0) == false)
{
return;
}
}
if (mNode->getCurrentAnimation(1) != Enums::NO_ANIMATION)
{
if (mNode->getCurrentAnimationEnded(1) == false && mNode->getCurrentAnimationLoops(1) == false)
{
return;
}
}
mAnimWait = false;
mNode->setAnimation(0, mAnimWaitNextAnims[0]);
mNode->setAnimation(1, mAnimWaitNextAnims[1]);
}
bool hasUserMovement = mInput.left_right != 0 || mInput.forward_backward != 0;
bool hasUserJumpMovement = mInput.up == 1;
NxOgre::Vec3 displacement;
// Check for falling.
if ( (!hasCollidedDown() && !hasPreviouslyCollidedDown()) && mUsesGravity && !mIsJumping)
{
if (mIsFalling == false)
{
mIsFalling = true;
mFallTime = 0;
playAnimation(Enums::StockAnimationID_Fall, Enums::StockAnimationID_Fall);
// V = gt = 0
}
else
{
mFallTime += deltaTime;
// V = gt
displacement = mScene->getGravity() * mFallTime;
displacement += mLastDisplacement;
move(displacement * deltaTime);
}
return;
}
if (mIsFalling)
{
mIsFalling = false;
mAirUserDirection.zero();
playAnimation(Enums::StockAnimationID_Land, Enums::StockAnimationID_Land, Enums::StockAnimationID_Idle,Enums::StockAnimationID_Idle, false);
return;
}
// Fall if gravity is enabled.
if (mUsesGravity)
displacement += mScene->getGravity();
if (mIsJumping)
{
mJumpTime += deltaTime;
// v = v0 - gt
displacement = mJumpVelocity0 - (-mScene->getGravity()) * mJumpTime;
// if v <= 0 then now is falling, mIsJumping is false.
if (displacement[mJumpDirectionIndex] <= 0.0f)
{
mIsJumping = false;
mIsFalling = true;
mFallTime = 0;
playAnimation(Enums::StockAnimationID_Fall, Enums::StockAnimationID_Fall);
return;
}
displacement += mAirUserDirection;
move(displacement * deltaTime);
return;
}
else if (hasUserJumpMovement && mIsJumping == false)
{
mIsJumping = true;
mJumpTime = 0;
mJumpVelocity0 = mScene->getGravity().used() * mMaxJumpVelocity;
mJumpDirectionIndex = mJumpVelocity0.axis();
Ogre::Vector3 userDirection = Ogre::Vector3::ZERO;
if (mInput.is_turning == false)
userDirection.x = float(mInput.left_right) * ReciprocalOf127 * mMaxGroundSpeed;
userDirection.z = float(mInput.forward_backward) * ReciprocalOf127 * mMaxGroundSpeed;
userDirection = Ogre::Quaternion(mYaw, Ogre::Vector3::UNIT_Y) * userDirection;
mAirUserDirection.x = userDirection.x;
mAirUserDirection.z = userDirection.z;
/*mAirUserDirection.x = (((mYaw.valueDegrees() - 180.0f) / 1.411764) - 127.0f) * ReciprocalOf127 * mMaxGroundSpeed;
mAirUserDirection.z = float(mInput.forward_backward) * ReciprocalOf127 * mMaxGroundSpeed;*/
playAnimation(Enums::StockAnimationID_Jump, Enums::StockAnimationID_Jump, Enums::StockAnimationID_Fall, Enums::StockAnimationID_Fall);
return;
}
// Switch to idle pose if no user movement has been requested and forward animation is playing.
if (hasUserMovement == false)
{
mLastDisplacement.zero();
playAnimation(Enums::StockAnimationID_Idle, Enums::StockAnimationID_Idle);
}
// Switch to forward pose (if idle), calculate new displacement vector from user movement.
else
{
playAnimation(Enums::StockAnimationID_Forward, Enums::StockAnimationID_Forward);
static Ogre::Vector3 userDirection;
userDirection = Ogre::Vector3::ZERO;
if (mInput.is_turning && mInput.left_right != 0)
{
// y += v * 0.013 * dt
mYaw += Ogre::Radian(float(mInput.left_right) * 0.013 * deltaTime); // temp.
userDirection.x = 0;
}
else if (mInput.is_turning == false)
{
// dS_x = v * 127^-1 * v_max
userDirection.x = float(mInput.left_right) * ReciprocalOf127 * mMaxGroundSpeed;
}
// dS_z = v * 127^-1 * v_max
userDirection.z = float(mInput.forward_backward) * ReciprocalOf127 * mMaxGroundSpeed;
userDirection = Ogre::Quaternion(mYaw, Ogre::Vector3::UNIT_Y) * userDirection;
displacement.x += userDirection.x;
displacement.z += userDirection.z;
mLastDisplacement = displacement;
mLastDisplacement.y = 0;
}
// sS = S * dT
move(displacement * deltaTime);
}
}
Now when you fall you keep your initial velocity, and you jump based on character's Yaw.
Edit:
I fixed the animation reset issue by commenting line 499 of CritterNode.cpp (//state->mState->setWeight(0.0f);). So far it doesn't seem to cause any issue.
Edit 2:
A cool highly wanted feature is the ability to put a relative position of the entity inside the CharacterController. I added "setRelativePosition" to CharacterBase in order to do that. You might want to include that in official trunk
A setYaw/setPitch function can be also useful (for third person cameras, and games with "flying mode")