Thirdperson controler questions

I,ve waiting for this for while so give me for jumping on it real quick


  1. is there a way to lock/disable rotation for left and right and backward  movement or will I just be able to use the strafe classes and such already present, I going for Max Payne type navigation



    thanks in advance

Wow, that was quick… I just checked it in last night.  :)  I haven't announced it yet because I want to do more refinement, but now you've spoiled my surprise so… :wink:



First off, this is the camera/control system originally used in Dirt and much inspired by articles in the Programming Gems series.  It's more advanced in several ways (especially in terms of node control) than the solution posted on the forums a year ago by Per.  That said, there are probably a few features we could bring across such as multitargeting.



There are two major parts to the Third Person controller system.  You have the ChaseCamera and the ThirdPersonHandler.  Both of them are actaully InputHandler classes.  The first one takes in mouse inputs and basically positions the camera using spherical coordinates around a target.  It uses a Spring control system, which gives it a fluid feel when following a moving and stopping target.  You have control over the spring parameters and an offset to the target's local translation which is the center point of the camera view (useful for saying the the camera should zoom in on a characters head/neck vs. her behind.



The ThirdPersonHandler is similar to a Node controller, but uses the current camera angle to determine moving direction.  It also points the controlled target in the direction of the movement.  How this pointing is done can be controlled (e.g. you can set it to be done gradually over time or immediate…  the first method has the character move forward and turn in a smooth motion towards the desired angle.)



At the moment there is no strafe action, but I'll look at adding that in.

PS: I've added several new features to jME's ThirdPersonHandler and ChaseCamera.  Over the next week or so I plan to write an editor in the style of RenParticleEditor for playing with this controller system in real time.

Funny you should ask that… :)  Strafe was added this morning…  Use Q and E as defaults.

yeah I noticed 8) that what I,m getting at I played with the settings alot but couldn't seem to achieve camera-aligned strafing without the arc rotation :(  like the firstperson does I'm trying to do a max payne / hitman type thing where all rotation will be handled by the mouse



hope I'm clear and again fantastic work :smiley:




just been playing with your editor and I have to say this stuff is nice…real nice  :smiley: :smiley: one question though are there methods for locking the rot in camera aligned strafing haven't actually looked at the code yet so I suppose its there just thought I'll ask though

Hmm, well if it is camera aligned, the strafe motion will be an arc.  if it's target aligned it will be a straight line.

if it's a big deal I could add a boolean flag for saying that strafe will always be target based regardless

o.k… then what setting do I use to get the camera to stay directly behind the character and looking down the z axis sort of like in the firstpersoncontroller,  again thaks

bumped into u there with that post sorry but in my defense I've never played a game with arc rotating strafe, but my actual gaming experience is pretty damn limited compared to you guys :wink:

You can disable springs and enable stay behind target…  My editor should be done soon, it will be easier to test there.



As for game experience…  You're probably right about most games keeping strafe target aligned, but in the interests of having as many options as possible… :slight_smile:

niiiice real nice 8)



and again thanks :slight_smile:

Well I had an oportunity to test the settings that I sent you and realised that they messed up anyway so I re-did them they work better now but in my test horizontal mouselook is pretty tricky but I don't really want to get rid of the springs,  its okay looking up or down my question is how do I get my character to pivot in sink with horizontal mouselooking so that the stayBehindTarget doesn,t negatively affect mouselooking





here is my test by the way an example of what I,m getting at




package game;

import java.util.HashMap;

import javax.swing.ImageIcon;

import jmetest.terrain.TestTerrain;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.input.ChaseCamera;
import com.jme.input.KeyInput;
import com.jme.input.ThirdPersonHandler;
import com.jme.input.thirdperson.*;
import com.jme.input.*;
import com.jme.light.DirectionalLight;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Box;
import com.jme.scene.state.CullState;
import com.jme.scene.state.FogState;
import com.jme.scene.state.TextureState;
import com.jme.util.LoggingSystem;
import com.jme.util.TextureManager;
import com.jmex.terrain.TerrainPage;
import com.jmex.terrain.util.FaultFractalHeightMap;
import com.jmex.terrain.util.ProceduralTextureGenerator;
/**
 * <code>TestThirdPersonController</code>
 *
 * @author Joshua Slack
 * @version $Revision: 1.14 $
 */
public class TestThirdPersonController extends SimpleGame {

    private Node m_character;

    private ChaseCamera chaser;

    private TerrainPage page;

    /**
     * Entry point for the test,
     *
     * @param args
     */
    public static void main(String[] args) {
        LoggingSystem.getLogger().setLevel(java.util.logging.Level.OFF);
        TestThirdPersonController app = new TestThirdPersonController();
        app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG);
        app.start();
    }

    /**
     * builds the scene.
     *
     * @see com.jme.app.SimpleGame#initGame()
     */
    protected void simpleInitGame() {
        display.setTitle("jME - 3rd person controller test");
       
        setupCharacter();
        setupTerrain();
        setupChaseCamera();
        setupInput();
    }
   
    protected void simpleUpdate() {
        chaser.update(tpf);
        float camMinHeight = page.getHeight(cam.getLocation()) + 2f;
        if (!Float.isInfinite(camMinHeight) && !Float.isNaN(camMinHeight)
                && cam.getLocation().y <= camMinHeight) {
            cam.getLocation().y = camMinHeight;
            cam.update();
        }

        float characterMinHeight = page.getHeight(m_character
                .getLocalTranslation())+((BoundingBox)m_character.getWorldBound()).yExtent;
        if (!Float.isInfinite(characterMinHeight) && !Float.isNaN(characterMinHeight)) {
            m_character.getLocalTranslation().y = characterMinHeight;
        }
    }

    private void setupCharacter() {
        Box b = new Box("box", new Vector3f(), 5,25,5);
        b.setModelBound(new BoundingBox());
        b.updateModelBound();
        m_character = new Node("char node");
        rootNode.attachChild(m_character);
        m_character.attachChild(b);
        m_character.updateWorldBound(); // We do this to allow the camera setup access to the world bound in our setup code.

        TextureState ts = display.getRenderer().createTextureState();
        ts.setEnabled(true);
        ts.setTexture(
            TextureManager.loadTexture(
            TestThirdPersonController.class.getClassLoader().getResource(
            "jmetest/data/images/Monkey.tga"),
            Texture.MM_LINEAR,
            Texture.FM_LINEAR));
        m_character.setRenderState(ts);
    }
   
    private void setupTerrain() {
        rootNode.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
        fpsNode.setRenderQueueMode(Renderer.QUEUE_ORTHO);

        display.getRenderer().setBackgroundColor(
                new ColorRGBA(0.5f, 0.5f, 0.5f, 1));

        DirectionalLight dr = new DirectionalLight();
        dr.setEnabled(true);
        dr.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
        dr.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
        dr.setDirection(new Vector3f(0.5f, -0.5f, 0));

        CullState cs = display.getRenderer().createCullState();
        cs.setCullMode(CullState.CS_BACK);
        cs.setEnabled(true);
        rootNode.setRenderState(cs);

        lightState.detachAll();
        lightState.attach(dr);

        FaultFractalHeightMap heightMap = new FaultFractalHeightMap(257, 32, 0,
                255, 0.75f);
        Vector3f terrainScale = new Vector3f(10, 1, 10);
        heightMap.setHeightScale(0.001f);
        page = new TerrainPage("Terrain", 33, heightMap.getSize(),
                terrainScale, heightMap.getHeightMap(), false);

        page.setDetailTexture(1, 16);
        rootNode.attachChild(page);

        ProceduralTextureGenerator pt = new ProceduralTextureGenerator(
                heightMap);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/grassb.png")), -128, 0, 128);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/dirt.jpg")), 0, 128, 255);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/highest.jpg")), 128, 255,
                384);

        pt.createTexture(512);

        TextureState ts = display.getRenderer().createTextureState();
        ts.setEnabled(true);
        Texture t1 = TextureManager.loadTexture(pt.getImageIcon().getImage(),
                Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR, true);
        ts.setTexture(t1, 0);

        Texture t2 = TextureManager.loadTexture(TestThirdPersonController.class
                .getClassLoader()
                .getResource("jmetest/data/texture/Detail.jpg"),
                Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
        ts.setTexture(t2, 1);
        t2.setWrap(Texture.WM_WRAP_S_WRAP_T);

        t1.setApply(Texture.AM_COMBINE);
        t1.setCombineFuncRGB(Texture.ACF_MODULATE);
        t1.setCombineSrc0RGB(Texture.ACS_TEXTURE);
        t1.setCombineOp0RGB(Texture.ACO_SRC_COLOR);
        t1.setCombineSrc1RGB(Texture.ACS_PRIMARY_COLOR);
        t1.setCombineOp1RGB(Texture.ACO_SRC_COLOR);
        t1.setCombineScaleRGB(1.0f);

        t2.setApply(Texture.AM_COMBINE);
        t2.setCombineFuncRGB(Texture.ACF_ADD_SIGNED);
        t2.setCombineSrc0RGB(Texture.ACS_TEXTURE);
        t2.setCombineOp0RGB(Texture.ACO_SRC_COLOR);
        t2.setCombineSrc1RGB(Texture.ACS_PREVIOUS);
        t2.setCombineOp1RGB(Texture.ACO_SRC_COLOR);
        t2.setCombineScaleRGB(1.0f);
        rootNode.setRenderState(ts);

        FogState fs = display.getRenderer().createFogState();
        fs.setDensity(0.5f);
        fs.setEnabled(true);
        fs.setColor(new ColorRGBA(0.5f, 0.5f, 0.5f, 0.5f));
        fs.setEnd(1000);
        fs.setStart(500);
        fs.setDensityFunction(FogState.DF_LINEAR);
        fs.setApplyFunction(FogState.AF_PER_VERTEX);
        rootNode.setRenderState(fs);
    }

    private void setupChaseCamera() {
      

        HashMap chaserProps = new HashMap();
        chaserProps.put(ChaseCamera.PROP_ENABLESPRING, "true");
        chaserProps.put(ChaseCamera.PROP_DAMPINGK, "20");
        chaserProps.put(ChaseCamera.PROP_SPRINGK, "100.0");
        chaserProps.put(ChaseCamera.PROP_MAXDISTANCE, "120");
        chaserProps.put(ChaseCamera.PROP_MINDISTANCE, "85");
        chaserProps.put(ChaseCamera.PROP_INITIALSPHERECOORDS, new Vector3f(65f, 0f, FastMath.DEG_TO_RAD * 12f));
        chaserProps.put(ChaseCamera.PROP_STAYBEHINDTARGET, "true");
        chaserProps.put(ChaseCamera.PROP_TARGETOFFSET, new Vector3f(0f, ((BoundingBox) m_character.getWorldBound()).yExtent * 1.25f, 0f));
        chaserProps.put(ThirdPersonMouseLook.PROP_ENABLED, "true");
        chaserProps.put(ThirdPersonMouseLook.PROP_MAXASCENT, "" + FastMath.DEG_TO_RAD * 90);
        chaserProps.put(ThirdPersonMouseLook.PROP_INVERTEDY, "false");
        chaserProps.put(ThirdPersonMouseLook.PROP_MINROLLOUT, "20.0");
        chaserProps.put(ThirdPersonMouseLook.PROP_MAXROLLOUT, "240.0");
        chaserProps.put(ThirdPersonMouseLook.PROP_MOUSEXMULT, "2.0");
        chaserProps.put(ThirdPersonMouseLook.PROP_MOUSEYMULT, "30.0");
        chaserProps.put(ThirdPersonMouseLook.PROP_MOUSEROLLMULT, "50.0");
        chaserProps.put(ThirdPersonMouseLook.PROP_LOCKASCENT, "false");
        chaser = new ChaseCamera(cam, m_character, chaserProps);
        chaser.setActionSpeed(1.25f);
    }

    private void setupInput() {
        HashMap handlerProps = new HashMap();
        handlerProps.put(ThirdPersonHandler.PROP_ROTATEONLY, "true");
        handlerProps.put(ThirdPersonHandler.PROP_DOGRADUAL, "true");
        handlerProps.put(ThirdPersonHandler.PROP_TURNSPEED, "3.1415");
        handlerProps.put(ThirdPersonHandler.PROP_LOCKBACKWARDS, "true");
        handlerProps.put(ThirdPersonHandler.PROP_STRAFETARGETALIGN, "true");
        handlerProps.put(ThirdPersonHandler.PROP_CAMERAALIGNEDMOVE, "true");

        handlerProps.put(ThirdPersonHandler.PROP_KEY_FORWARD, ""+KeyInput.KEY_W);
        handlerProps.put(ThirdPersonHandler.PROP_KEY_LEFT, ""+KeyInput.KEY_A);
        handlerProps.put(ThirdPersonHandler.PROP_KEY_BACKWARD, ""+KeyInput.KEY_S);
        handlerProps.put(ThirdPersonHandler.PROP_KEY_RIGHT, ""+KeyInput.KEY_D);
        handlerProps.put(ThirdPersonHandler.PROP_KEY_STRAFELEFT, ""+KeyInput.KEY_Q);
        handlerProps.put(ThirdPersonHandler.PROP_KEY_STRAFERIGHT, ""+KeyInput.KEY_E);
        input = new ThirdPersonHandler(m_character, cam, handlerProps);
        input.setActionSpeed(100f);


    }
}

Interesting question.  It could either be handled by the controller or by your own program.  I'll look into adding the option when I can.

well in most of the tps' I've played my favs being hitman and max oayne rotateleft/right isn't needed as keys in max payn they dont even have them for example my typical fps/tps setup uses the arrow keys to move forward/bacward and strafe and the right mouse button is also set to forward move, jump and such

my guess would be poled the rotate left/right actions with horizontal mouse look does the input system allow for that if where do I look, also at present can actions be set to mouse keys and also does the current system allow for multiple keys to one action and is it still done the same way as the old system. I have a bit of a disability that make these things necessary "haven't been able to play halflife2 because it didn't allow for this."

do you mean mapping the mouse look to say the arrow keys?

I'm trying to write the character pivot by utilizing NodeMouseLook my question is should I extend ThirdPersonMouseLook or MouseInputActions I'm at my office, just finished an audit report will be free for the rest of the day …7 hours

If you are trying to make your character pivot, you can simply turn that on in ThirdPersonHandler  (setRotateOnly)  If you are using node handler, I guess the question isn't really about jme's third person system  :slight_smile:

but it is about the thirdperson as we discussed early I would like to apply the pivot action when using the thirdperson mouselook i.e. have the character rotate left or right based on the mouse movement like NodeMouseLook which applies keyNoderotateleft/right to the movement of the mouse, if I understand the docs correctly so to that end can I


  1. create method utilizing NodeMouseLook in/extending chasecamera or ThirdPersonMouseLook  "which I just knocked up in the office…while my supervisor wasn't looking :D" ( not compiled or tested btw)



    or


  2. should I instead create a class similar to NodeMouseLook that utilize ThirdPersonRotateRight and so forth, TpersonNodeMouseLook or similar





    basically I'm trying to steer the character with the mouse to be precise



    I hope I make some sense now.



    even in english there are Language barriers