Still making progress at a reasonable rate. I've added facility in the editor to autocreate texture coordinates on the collision shapes and set up the game so that it can load textures and meshes from the level file in an arbitrary way. The actual level being rendered now is a mesh premade in the editor rather than shapes created on the fly from the collision shapes.
I've got the basics of a state machine set up for the player character now. He can run, walk, turn and stand idle now, and it is all structured into separate state classes that are managed by a PcStateMachine class owned by the PC entity instance. It works using a callback system from the animation controller.
This is basically set up so that in Charm, my model editor, I can tag keyframes in the animations with arbitrary data that is exported along with the rest of the model data. The events are shown as the little diamonds on the trackbar below.
In the game, when the animation controller's update() method is called, you pass in a callback which is invoked whenever the animation passes a tagged keyframe, similar to how the ID3DXAnimationController thing works, although mine is all hand-rolled.
The walk and run states for example lock so that you can't change state except during these events. I then tag each frame in those animations when the feet are both on the floor and legs together, so that now the character takes a minimum of a single step if you just tap the keys, which looks much better.
#include "RunState.h"#include "entities/pc/PcData.h"#include "entities/pc/PcStateMachine.h"#include "maths/Quaternion.h"WalkState::WalkState(PcData &data, PcStateMachine &stateMachine) : PcState(data, stateMachine){}void WalkState::begin(PcState::Type previous){ data.ac.transitionTo(data.ids.walk, previous == PcState::Run); iv = getInputVelocity() * 1.25f;}void WalkState::update(Physics &physics, float delta){ Vec3 v = getInputVelocity() * 1.25f; Vec3 ov = v; if(vectorLength(v)) { iv = v; } else { v = iv; } debugFly(v); data.kcc.move(v, physics, delta); if(vectorLength(ov)) { data.rot = FadeValue(data.rot.value(), axisRotationQuaternion(Vec3(0, lookAngle(normalizeVector(ov)), 0)), 0.1f); }}void WalkState::animationCallback(const AnimationCallbackData &callbackData){ if(!vectorLength(getInputVelocity())) { stateMachine.setNextState(PcState::Idle); } else if(!walkControlDown()) { stateMachine.setNextState(PcState::Run); }}
I developed most of these techniques in the 2D game I was doing before so that was a good way to prototype a lot of the structure I'm using here. It might seem a bit overkill to have a PcState interface and implement each state as a separate derived class and its one I've been tempted to replace with a simple switch-based system, but it is actually quite neat and tidy to be able to encapsulate each state in its own class, along with its own state variables. It works very well to stop you gradually developing a monolithic Player entity which has always been something I've suffered from in past games.
Having moved the physics into the kinematic character controller class this time round has meant that the actual code in these State classes is pretty simple and focused on the bits it should be i.e. behaviour rather than implementation of collisions and so on. Seems to work okay.
Need to start looking at jumping soon, but I might have a hiatus into some normal mapping for the level surfaces first.