Character Moves Towards Mouse Click

Started by AGP, August 26, 2008, 09:17:15 PM

Previous topic - Next topic

AGP

I'm trying to make an RPG with 2D backgrounds and a 3D hero. When the player clicks on the screen, the hero has to move that way. The following's what I did (for translation, rotation is something else). Is it wrong, because it's not working?
SimpleVector heroCenter = Interact2D.project3D2D(theCamera, buffer, hero.getTransformedCenter());
SimpleVector destination = Interact2D.reproject2D3D(theCamera, buffer, mousePoint.x-(int)heroCenter.x, mousePoint.y-(int)heroCenter.y, hero.getTransformedCenter().z);
destination.z = destination.y;
destination.y = 0;
hero.translate(destination);

EgonOlsen

At least it manages to confuse me... ;D What exactly isn't working? What's the outcome?

AGP

Hah, as opposed to my other overly simple questions? : -)

What's not working is that the translation value is wrong (hero goes somewhere else).

EgonOlsen

#3
What puzzles me is the "destination.z = destination.y; destination.y = 0;"-part...but apart from that, you are feeding the deltas into the reproject2D3D-method where i think you should simply give it mouseX and mouseY. The method works with absolute screen coordinates, not with some deltas. The translation should then be the difference vector between the current translation and the projected point in 3D.

AGP

I guess I misunderstood the documentation, because it says it will assume z to be 1. And the hero should move on the x and z planes. So I figured the z value would instead be placed on the y coordinate (some orientation problem someone with more spacial perception than I would understand!). But you're right, of course, about the delta. It should be calculated on the call to transform (between destination and hero.getTransformedCenter()). The following still doesn't work:
SimpleVector heroCenter3D = hero.getTransformedCenter();
SimpleVector destination = Interact2D.reproject2D3D(theCamera, buffer, mousePoint.x, mousePoint.y);
hero.translate(destination.x-heroCenter3D.x, destination.y-heroCenter3D.y, destination.z-heroCenter3D.z);


Note that the hero shouldn't even move in the y axis anyway, but I suppose I would just change the y and z values later (after I at least saw the hero appearing to be in the right place).

EgonOlsen

Maybe setting the picking plane to heroCenter3D.z wasn't that bad. Does it help to add it again? I'm sorry, but i have no real imagination how your screen looks like and how the hero is intended to move in it, which is why i'm a little confused about what is correct in this case.

AGP

I sent you an e-mail with a screenshot. If we find a solution, I will post it on this thread.

As for the picking plane, I expect you mean for me to add destination.z = destination.y; destination.y = 0; again. But if that's not it, please explain. And should I pass any value for z on reproject2D3D() call?

EgonOlsen

#7
Ok, I've got the screen shot. I understand the problem better now. The reproject2D3D is not suitable for this, because it assumes a fixed Z. You don't have a fixed Z, you have a fixed Y. I tried to adjust the formula for reprojection but it didn't work as expected. There has to be an error somewhere, but sadly, i don't have access to the code ATM. I'll look into it tomorrow.
Another solution would be to use a plane in the X-Z-plane and some ray casting... i have a working version of this, but again: No access until tomorrow... :'(

AGP

Cool. I await your solution, pal. Thanks.

EgonOlsen

Here's the code (a small demo to be exact) of the plane-based solution. The drawback is, that you need that plane and have to orientate it correctly. The pro is, that you can do this with any kind of geometry, i.e. you can add ramps and such easier.
The reprojection solution should be doable too, but i got lost in the magic of converting between spaces, got wrong results and ran out of time. I hope that this solution is appropriate for your problem:


import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.threed.jpct.*;
import com.threed.jpct.util.*;

public class MouseFollow extends JFrame implements MouseMotionListener {

    private final static long serialVersionUID=1L;
 
    private Graphics g = null;
    private FrameBuffer fb = null;
    private World world = null;
    private Object3D plane = null;
    private Object3D ramp = null;
    private Object3D cube = null;
    private Object3D cube2 = null;
    private Object3D sphere = null;

    private boolean doloop = true;

    private int x=320;
    private int y=240;

    public MouseFollow() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setSize(640, 480);
        setResizable(false);
        setLocationRelativeTo(null);
        setVisible(true);
        this.addMouseMotionListener(this);
        g = getGraphics();
    }

    public void mouseMoved(MouseEvent m) {
        x=m.getX();
        y=m.getY();
    }

    public void mouseDragged(MouseEvent m) {}

    private void initStuff() {
        fb = new FrameBuffer(640, 480, FrameBuffer.SAMPLINGMODE_NORMAL);
        world = new World();
        fb.enableRenderer(IRenderer.RENDERER_SOFTWARE);

        ramp = Primitives.getCube(20);
        ramp.setAdditionalColor(Color.red);
        plane = Primitives.getPlane(1, 200);
        plane.setAdditionalColor(Color.GREEN);
        sphere=Primitives.getSphere(30);
        sphere.setAdditionalColor(Color.CYAN);
        sphere.translate(-50,10,50);
        cube2=Primitives.getCube(20);
        cube2.setAdditionalColor(Color.ORANGE);
        cube2.translate(60,-20,60);

        plane.rotateX((float) Math.PI / 2f);
        ramp.rotateX((float) Math.PI / 2f);

        cube = Primitives.getCube(2);

        plane.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
        ramp.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
        sphere.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
        cube2.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);

        world.addObject(plane);
        world.addObject(ramp);
        world.addObject(cube);
        world.addObject(sphere);
        world.addObject(cube2);

        cube.translate(-50, -10, -50);

        Light light = new Light(world);
        light.setPosition(new SimpleVector(0, -80, 0));
        light.setIntensity(40, 25, 22);

        world.buildAllObjects();
    }

    private void follow() {
        SimpleVector ray=Interact2D.reproject2D3D(world.getCamera(), fb, x, y);
       
        if (ray!=null) {
            SimpleVector norm=ray.normalize();
            Matrix mat=world.getCamera().getBack();
            mat=mat.invert3x3();
            norm.matMul(mat);

            SimpleVector ws=new SimpleVector(ray);
            ws.matMul(world.getCamera().getBack().invert3x3());
            ws.add(world.getCamera().getPosition());

            float f=world.calcMinDistance(world.getCamera().getPosition(), norm, 1000);
            if (f!=Object3D.COLLISION_NONE) {
                SimpleVector offset=new SimpleVector(norm);
                norm.scalarMul(f);
                norm=norm.calcSub(offset);
                cube.getTranslationMatrix().setIdentity();
                cube.translate(norm);
                cube.translate(world.getCamera().getPosition());
            }
        }
    }

    private void doIt() throws Exception {
        Camera cam = world.getCamera();
        cam.moveCamera(Camera.CAMERA_MOVEOUT, 100);
        cam.moveCamera(Camera.CAMERA_MOVEUP, 160);
        cam.lookAt(plane.getTransformedCenter());

        world.getLights().setOverbrightLighting(Lights.OVERBRIGHT_LIGHTING_DISABLED);

        while(doloop) {
            follow();
            fb.clear();
            world.renderScene(fb);
            world.draw(fb);
            fb.update();
            fb.display(g);
            Thread.sleep(10);
        }
        fb.disableRenderer(IRenderer.RENDERER_OPENGL);
        System.exit(0);
    }

    public static void main(String[] args) throws Exception {
        MouseFollow cd = new MouseFollow();
        cd.initStuff();
        cd.doIt();
    }
}


AGP

Thanks for the solution, my friend. But I don't understand why it is that the collision-detection line makes it work. If I take it out for the plane, it doesn't work on the plane. But if the other objects are still calculating the collision, it works on them. Why is that?

EgonOlsen

Because it casts a ray into the scene that hits either the objects or the plane. If you disable the plane, it can't be hit any longer.

AGP

OK, that'll take me a little while. It's a little humbling discussing this with you, dude!

Here's a question: why does the cube translate to the camera's position? And then, why does the camera see something whose center is the same as its?

EgonOlsen

#13
Good question...i've asked that myself when looking at this code that i wrote a few months ago. It's because the ray is casted from the camera's position but the cube should be positioned in world space after all. So you have to add the camera's position in a last step. It took me some time to figure this method out back then IIRC. No need to feel small, because you don't understand it at first glance. It's not easy. Maybe drawing it on paper will help. It always helps me out.