0
\$\begingroup\$

I'm currently trying to implement a move-to-drag behaviour in a 2d grid based game. I want to update the camera such that if follows the "head" of the trail being drawn - In this case, the nodes it is made up of some nodes returned from A* pathfinding utility implementation.

AStarPathfind pather = new AStarPathfindAroundWalls(
    LayerMask.GetMask("Walls"),
    Camera.main.ScreenToWorldPoint(Input.mousePosition)
);
List<Vector2> trailPoints = pather.FindPath(new Vector2(this.transform.position.x, this.transform.position.y));
Camera.main.transform.position = new Vector3(
    trailPoints[ArrowTrailPoints.Count - 1].x,
    trailPoints[ArrowTrailPoints.Count - 1].y,
    Camera.main.transform.position.z
);

When I run this code, the camera sucessfully does indeed snap to the head of the path, but unfortunately, the mouse remains where it originally was, and since this code is ran during an update loop, it will simply keep adding to the path until it can no longer do so. Essentially I would like it to go to the next tile, update the camera, reset the mouse position and then await where the next part of the path should be drawn (of course, this is not possible, since I cannot modify the mouse position directly).

I've tried storing the trail points as a property in the object, and then removing the last known position from the calculation, like so

Camera.main.ScreenToWorldPoint(Input.mousePosition) - lastTrailPoints[lastTrailPoints.Count - 1]

so that the input position would be relative to the amount offset by the camera... but with no success, so I'm starting to think I'm way off using this method. If I do something like

Camera.main.ScreenToWorldPoint(Input.mousePosition) - (MovementNodes[MovementNodes.Count - 1] - this.transform.position)

Then things work almost as expected, but the mouse will still move off of the screen, and I want to keep it centered over the actual head of the trail (where the camera is).

current situation

(black cross = current mouse position, green cross = where I want the mouse pointer to be located, camera is centered over the arrow head/green cross)

Has anyone implemented something similar before and know of a solution?

\$\endgroup\$
  • 2
    \$\begingroup\$ "(of course, this is not possible, since I cannot modify the mouse position directly)" I think you should consider separating your notion of "mouse" (the input device controlled by the driver) and "cursor" (the actual pointer / hand you draw and use to interact in your game). The cursor can be 100% in your control, and you can choose when & how to apply mouse movement to steer it, and when to forcibly reposition it. Just note that if you separate these concepts, you may require separate handling for touch displays. \$\endgroup\$ – DMGregory Nov 6 '18 at 13:31
  • \$\begingroup\$ @DMGregory Hi, thanks for your response. You're absolutely right - I wrote my own cursor interface and was able to create a mouse-based implementation which controls this new cursor by locking the native mouse position and using Input.GetAxis. I'll be able to instantiate which implementation of the cursor I need now during startup based on the environment the program runs in (touch/mouse-based)! (I'm not quite sure how to do this right now, but one step at a time) thanks again \$\endgroup\$ – Sam Judge Nov 6 '18 at 21:41
1
\$\begingroup\$

So, following on from DMGregory's comment, I was able to create a simple abstract class derived from MonoBehaviour, as below

public abstract class Cursor : MonoBehaviour {
    public void SetPosition(float x, float y){
        this.transform.position = new Vector3(
            x,
            y,
            this.transform.position.z
        );
    }
    public abstract Vector2 GetPosition();
    public abstract void UpdatePosition();
    public virtual void Start(){ }
    public virtual void Awake(){ }
    public void Update(){
        UpdatePosition();
    }
}

I was then able to extend this abstract class with an appropriate implementation for my input device (in this case, the mouse)

public class MouseCursor : Cursor {

    [SerializeField]
    private float MouseSensitity = 1f;

    public override void Awake(){
        UnityEngine.Cursor.lockState = CursorLockMode.Locked;
    }

    public override Vector2 GetPosition() {
        return this.transform.position;
    }

    public override void UpdatePosition(){
        SetPosition(
            this.transform.position.x + Input.GetAxis("Mouse X") * MouseSensitity,
            this.transform.position.y + Input.GetAxis("Mouse Y") * MouseSensitity
        );
    }
}

During runtime, I access the cursor implementation from a manager-type object, and am able to get

//Where LevelManager.GetCursor() returns an instance of Cursor
Vector2 cursorPosition = LevelManager.GetCursor().GetPosition();
AStarPathfindAroundWalls pather = new AStarPathfindAroundWalls(
    LayerMask.GetMask("Walls"),
    Camera.main.ScreenToWorldPoint(cursorPosition) //since the cursor gameobject lives in an screen-space overlay canvas, I must still convert it's coordinates here
);

and also set the cursor location appropriately

LevelManager.GetCursor().SetPosition(snapToCursorPosition.x, snapToCursorPosition.y);

Going forward, it would probably be best to create a CursorAdapter interface (rather than extending MonoBehaviour as an abstract class and using separate prefabs/gameobjects for each implementation), and instead make it a property of a more general Cursor gameobject, which could independently resolve which adapter implementation it should use, keeping it out of my LevelManager all together, but for now this resolves my issue of being able to set the "mouse" location manually

\$\endgroup\$

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.