Animation
Animating the Wizard
It's a sad fact that, despite how good your game is, unless it looks good, people won't give it the time of day. So, we'll do animation next, since that will make the game look better.
As with a lot of 3D games, the hardest part of doing animation is finding (or creating) a 3D model with some good animations. However, the Mage model included in the repository for this tutorial is pretty good and comes with everything we need.
So, to animate the Wizard/Mage thing, change the WizardModel class so it looks like the following:-
public class WizardModel implements IAvatarModel {
public static final float MODEL_WIDTH = 0.4f;
private static final float MODEL_DEPTH = 0.3f;
public static final float MODEL_HEIGHT = 0.7f;
private AssetManager assetManager;
private Spatial model;
// Anim
private AnimChannel channel;
private int currAnimCode = -1;
public WizardModel(AssetManager _assetManager) {
assetManager = _assetManager;
}
@Override
public Spatial createAndGetModel() {
model = assetManager.loadModel("Models/mage/mage.blend");
JMEModelFunctions.setTextureOnSpatial(assetManager, model, "Models/mage/mage.png");
model.setShadowMode(ShadowMode.CastAndReceive);
JMEModelFunctions.scaleModelToHeight(model, MODEL_HEIGHT);
JMEModelFunctions.moveYOriginTo(model, 0f);
JMEAngleFunctions.rotateToWorldDirection(model, new Vector3f(-1, 0, 1)); // Point model fwds
AnimControl control = JMEModelFunctions.getNodeWithControls(null, (Node)model);
channel = control.createChannel();
setAnim(AbstractAvatar.ANIM_IDLE); // Default
return model;
}
@Override
public float getCameraHeight() {
return MODEL_HEIGHT - 0.2f;
}
@Override
public float getBulletStartHeight() {
return MODEL_HEIGHT - 0.3f;
}
@Override
public Vector3f getCollisionBoxSize() {
return new Vector3f(MODEL_WIDTH, MODEL_HEIGHT, MODEL_DEPTH);
}
@Override
public Spatial getModel() {
return model;
}
@Override
public void setAnim(int animCode) {
if (currAnimCode == animCode) {
return;
}
switch (animCode) {
case AbstractAvatar.ANIM_IDLE:
channel.setLoopMode(LoopMode.Loop);
channel.setAnim("Idle");
break;
case AbstractAvatar.ANIM_WALKING:
case AbstractAvatar.ANIM_RUNNING:
channel.setLoopMode(LoopMode.Loop);
channel.setAnim("Walk");
break;
case AbstractAvatar.ANIM_ATTACK:
channel.setLoopMode(LoopMode.Loop);
channel.setAnim("Attack");
break;
case AbstractAvatar.ANIM_DIED:
channel.setLoopMode(LoopMode.Loop);
channel.setAnim("Die");
break;
}
currAnimCode = animCode;
}
}
A lot of this is the same, but you'll see we've added some references to help us animate the wizard, including an AnimChannel class, which we populate in the createAndGetModel() method. We've also populated the setAnim() method, which starts an animation sequence depending on the animation code that is passed.
We also need to add code to the OtherWizardAvatar class, since this is the entity that will be animated. (No animation is done , and the Make it look like the following:-
public class OtherWizardAvatar extends AbstractOtherPlayersAvatar {
public OtherWizardAvatar(IEntityController game, int eid, float x, float y, float z, byte side, String playerName) {
super(game, MTDClientEntityCreator.AVATAR, eid, x, y, z, new WizardModel(game.getAssetManager()), side, playerName);
}
@Override
public void setAnimCode_ClientSide(int animCode) {
model.setAnim(animCode);
}
}
The main change here is that we've added the setAnimCode_ClientSide(), which gets called when we get a message that tells us that the animation has changed.
And that's it! If you run the server and two instances of the client, you should see that the wizard plays the "Walk" animation when walking, and the "Idle" animation otherwise (albeit it looks more like dancing than being idle).
Comments
Post a Comment