• entries
    743
  • comments
    1924
  • views
    580031

Character Controller progress

Sign in to follow this  
Aardvajk

909 views

kcc.jpg

Some good progress this morning. The player capsule (red) now correctly glides and slides around the environment, no longer penetrating either the static or dynamic shapes, and we have the floor ray cast set up and working to detect the distance and normal of the current floor, so can start work on gravity for the player next.

I'm doing a discrete check for the character controller rather than a swept test. You accept the risk of tunnelling here (which one can work around) but get a load of stuff simplified in return. For example, sliding along surfaces is handled automatically when doing a discrete check - you GJK the player capsule and the object together, and then correct the player position based on the minimum separating vector and this automatically slides you along a surface, at the correct proportion based on the angle.

It also means that moving, dynamic objects automatically push the player around out of the box, since the check is discrete and run every frame. Later, we can look at examining the mass of the colliding object and using this to ratio off the MSV between the object and the player in some way, so that the player has the ability to push dynamic objects around too, but this is not a great concern for me. I'm not trying to write a heavy physics sim game here. I'm more interested in being able to do a subset of things with the physics engine (rag-dolls, doors, trapdoors etc) but keep tight control of the things I want to (player, NPCs, etc).

This is the current internals of the Kcc::move() method:

void Kcc::move(Physics &physics, const Vec3 &step){ Vec3 result = pos + step; Matrix to = translationMatrix(result); BroadphaseResult br = physics.broadphaseAabb(shape.aabb(to).expanded(2)); int it = 0; bool loop = true; while(it < 5 && loop) { loop = false; for(auto &b: br.bodies) { ConvexResult r = physics.convexIntersection(shape, to, *b); if(r.valid()) { result += r.separatingVector(); to = translationMatrix(result); loop = true; } } ++it; } pos = result; Vec3 base = pos; base.y -= (ht / 2); RayResult rr = physics.rayCast(Ray(base, Vec3(0, -1, 0)), 100); if(rr.valid()) { debugAddLine(base, rr.worldPoint(), makeColor(255, 255, 255)); debugAddLine(rr.worldPoint(), rr.worldPoint() + rr.normal(), makeColor(0, 255, 0)); }}You basically start with the Kcc at the player position, then call move() with the step then read back the final position from the Kcc when updating the character. You can see we use the broadphase to get the potentially colliding objects, then loop over these, checking for penetration and correcting the final position by the relevant amount.

[EDIT: Seems I'm spinning needlessly around if no collisions as well, which needs sorting. Duh! Handy to post code in a journal then read it back sometimes...]

Needs a bit of work to handle sharp, trapping corner shapes as it can wiggle about a bit in there at the moment, but this should be easy enough to sort out if required. Whether it is needed will depend on how the final level geometry possibilities work out and whether I end up with large dynamic environment objects. Not sure if I want this yet.

I'm also then doing a ray cast to find the floor and just handing some of this information off to the debug system. I'm pretty lazy with my debug system since it will ultimately be removed so it is all just global state in an anonymous namespace inside DebugRender.cpp with an interface of free functions, so it is easy to include it anywhere without having to put in proper dependency flows like I do with the real stuff.

So you can just add debug lines anywhere you include DebugRender.h, and they are drawn as part of the GameMode::render() method called by Application. There's also a setDebugScreenMessage() method that uses varadic templates to allow you to pass any parameters that can be passed to std::ostream, which is then drawn each frame in the top left corner of the screen - handy for debug output that would grow enormous if pushing out to QtCreator's Application Output window via OutputDebugString().

There's then another method, addPhysicsToDebugLines(), that runs through and adds lines representing all the physics shapes to the debugLines list, so you can see where the actual physics shapes are and ensure they are correctly synchronised with the Scene nodes representing the solid objects.

I might take a small detour and add in some kind of shadowing system soon, since it makes it a lot easier to be able to tell at a glance where things are in relation to each other. This will mainly consist of just copying code from previous projects, like a lot of things seem to be these days. I have amassed a vast library of failed projects on this HDD now and have quite a large library of my own code to draw on, which is nice smile.png

Thanks, as usual, for stopping by and I hope this is of interest to someone, somewhere.
Sign in to follow this  


0 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now