Super Smash Bros Camera

Super Smash Bros Camera

This is just a simple modification of the already existing chase camera app state, but I thought I would post it, because it contains code from the actual chase cam that allows for the “smooth movement”.

As the title suggests, it acts like the camera from the game Super Smash Bros, if you’ve ever played that.


Please Note: This is functional but not fully optimised, so it will be somewhat ineffecient until improved


How it works:

The app state creates a new Node called target, which has it’s position set to the same as the spatial/node in your scene that you want the camera to follow. Using linear interpolation, the target node accelerates towards the spatial/node that it’s tracking, bringing the camera with it.

Since the camera is a child to the target node, it always follows it and preserves the same local translation that is initially set. Simply put: the camera doesn’t actually move (or tilt), the only thing that changes in the update loop is the position of target node.

Super Smash Bros essentially operates on 2D axis, within a 3D world, which means that the camera rotation is always locked to face the same direction. You do not need to ever set the camera rotation, in fact you cannot as it will be overwritten since in the update loop the camera always faces the target node.

It has exactly the same methods as the chase camera app state, with four additions taken from the ChaseCamera class in JME:

  • setSmoothMotion();
  • isSmoothMotion();
  • setChasingSensitvity();
  • getChasingSensitivity();

Setup:

First you must declare the new object:

private SmashCameraAppState smashCameraAppState;

You can do this within SimpleInitApp() or Initialize() from any app state, so long as you have access to the state manager.

smashCameraAppState = new SmashCameraAppState();
getStateManager().attach(smashCameraAppState);

//the players mean is just an example of what you could use, it would have it's location 
//set to the mean location of everything that you want to track on each update
smashCameraAppState.setTarget(playersMean);

smashCameraAppState.setSmoothMotion(true);
smashCameraAppState.setChasingSensitivity(3f);

After you create your camera object, you will want to set it’s local position relative to the target node. This will be preserved, unless you decide to change it:

//change these values as desired
cam.setLocation(Vetor3f.ZERO);//this indicates no local translation from the target node

Class:

Here is the code you need to get it working.


I can barely take credit for this, as 90% was written by @nehon, but I hope some of you find this useful.

Right now, it is super basic, but I am planning on expanding and polishing this in the near future.

3 Likes

Thanks for the contribution. My only suggestion is that you maybe post it in a github gist (since it’s just a single class, no need for a whole repository) so that any future updates/requests/etc are a lot easier to deal with.

That’s a good point actually, I’ll get onto that.

If you don’t need rotation or anything then why have it follow a node around? If it’s just a fake node that you force to follow a player then why not just use a Vector3f as the target?

Maybe there is something I’m missing.

That seems like unnecessary indirection.

I could use a Vector3f for the target istead of a node, as it wouldn’t change how it works, the original code that I modified this from uses a node anyway, so I just kept it as it was.

Would I be correct in assuming that using a Vector3f would be faster since it’s a variable, and not an object like the node?


EDIT: Actually yes, target must be a node, since the camera is a child to it. The movement of the node essentially controls the movement of the camera.

No, there must be some code making this happen. A “Camera” in the com.jme3.renderer.Camera sense cannot be a child of a Node because it’s not a spatial.

I haven’t looked at your code so maybe you’ve layered a bunch of things together that are unnecessary instead of just calling camera.setLocation() every now and then. Like a rube-goldberg machine of off-the-shelf objects instead of two or three lines of code.

Sorry, I should have been more spefific, I’m using a camera node:

    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        ...
        target = new Node("ChaseCamTarget");
        camNode.setCamera(app.getCamera());        
        camNode.setControlDir(CameraControl.ControlDirection.SpatialToCamera);
        target.attachChild(camNode);
        ...
}

Yes, which seems unnecessary to me.

This is an example of a Rube-Goldberg machine (in case folks don’t know what that is):

So it seems to me like you have nodes and controls and whatever as a complicated machine to avoid calling:
camera.setLocation(target.subtract(viewDir.mult(veiwDistance)));

I understand what you’re saying, and .setLocation(...) seems like a more effecient approach.

When I wrote it this morning, the proirity was just to get it “working” however possible (bad practice I know), however now that it is working I can begin make optimizations as such.

Yep, always admirable.

…when you posted it as an example, that’s when my “code review” senses started tingling.

JME itself already perpetuates plenty of ‘bad’ examples. Good to clean things up when we can.

True yes, I’ll put a note in the initial post saying so; I currently don’t have time to finish this today.