- Home /
Pass Clicks Through Camera
Hi,
I have a scene with 2 cameras. The first cam (FPS Cam) is the first-person view. The second camera (Map Cam) takes a top-down view of the playing field. The Map Cam is then rendered as a Render Texture that is visible in the FPS Cam's field of view. In other words, the Map Cam is used to render a live map for the player, who views that map and the rest of the world through FPS Cam.
Now, I would like to make the map interactive, so that the player can click on enemies on the map and perform commands related to that enemy. To achieve this I would like to have those enemies detect mouse clicks on them (through the map) and register themselves as the currently selected enemy. But in my simple test I could not get the clicks to pass through the map/Map Cam and onto the enemy. I assume the clicks are simply being consumed by the plane with the map on it.
So my question is, is there a built-in or known way to allow clicks onto a Render Texture to be passed through to the corresponding camera in order to effectively click objects rendered in that second camera's view? Or are there any related tips/tricks which might help me?
Huge Thanks,
Pete.
Answer by skovacs1 · Jul 28, 2010 at 03:12 PM
You're sort of correct that the plane is eating your mouse click in the sense that the since the player is only ever seeing from the view of the FPS camera and the plane is seen by that camera (despite having a texture generated from another camera.
There is no "built-in" way to do this of which I am aware, but the problem does not seem too difficult overall. Here's a couple of solutions that come to mind:
Calculating your own pick ray from the map camera
You have the plane and probably even know the screen position and size of the plane. You know where the mouse is on the screen. You therefore can know where on the plane the user clicks. If you do something like
int planeLeft; //The screen position of the left side of the plane int planeBottom; //The screen position of the bottom side of the plane int planeWidth; //The width of the plane on the screen int planeHeight; //The height of the plane on the screen
//Get the point on the plane Vector3 screenPoint = Input.mousePosition - Vector3(planeLeft, planeBottom, 0);
//If not inside of an OnMouse function //Check for clicks off the plane if(screenPoint.x < 0 || screenPoint.y < 0 || screenPoint.x > planeWidth || screenPoint.y > planeHeight) return;
//If the plane is not the same screen size as the mapCam's resolution //Convert the coordinate to the mapCam's resolutiion screenPoint.x = mapCam.pixelWidth / planeWidth; screenPoint.y = mapCam.pixelHeight / planeHeight; //You could simply do the division in stead and later use ViewportPointToRay
//Raycast from the map camera RaycastHit hitInfo; //Stores the information about the hit int layerMask = kDefaultRaycastLayers; //The layers to raycast against if(Physics.Raycast(mapCam.ScreenPointToRay(screenPoint), out hitInfo, mapCam.farClipPlane, layerMask)) { if(hitInfo.collider.gameObject is Enemy) //you could also check the rigidBody { //do something } }
Using what you should know or be able to figure out, you can create your own raycast at the mapCamera's position and can check in the scene if you hit something and then using the information about what you hit, you have your answer.
Generate a map with collider representations
As 0V3RWR173D said, you could create colliders at the points on the map plane representing the things you want to collide with and then it would be a simple matter of using OnMouse events. The problem is how you know where on the map the enemies are. If your map camera rotates as your FPS camera does, then so too would the representative colliders have to.
This solution is valid, but can be trickier depending on your use case.
One way to generate your colliders is to place a trigger volume representing your mapped area and when stuff that you care about enters or is inside of the trigger volume, create the colliders on the mini map. Remember that event handling and message passing has overhead to be concerned about.
Another valid solution is to maintain a list of things that you want to be able to interact with on the mini map and check their positions. Using distance or distance squared (and the directional vector if using a non-circular mini map) you can check if the thing is within an area the mini map encompasses and if it is, you can then use the distance or distance squared and the directional vector to calculate the location that the thing should be at on the mini map.
Depending on your use case, you might want a representative map rather than a literal one (circles and lines in stead of the actual objects. This can be achieved numerous ways, but bear in mind that you can generate the map and the collider representations at the same time. Also, generating the map doesn't have the overhead of rendering your scene twice and, if elements of your map are static, you can generate an orhographic base map texture which you then scroll over and rotate as you need, rather than having to render it every frame.
Thanks skovacs, that's heaps of info and really helpful. The trigger volume sounds very interesting, and would probably be a nice way to easily make the 'range' of the map adjustable, though I will heed your warning about performance and consider that option with due caution.
What I might do is create representative objects in a '$$anonymous$$ap' layer linked to each enemy which tracks their x,z and has a static y, and then $$anonymous$$ap Cam can render only that layer (more performant?). Then I'll convert map clicks into ray casts as per the top of this answer to detect collisions with those. Thanks again.
Answer by 0V3RWR173D · Jul 28, 2010 at 02:09 AM
I'm not exactly sure on what you are trying to do but I assume that you have a picture that corresponds to the real map so that you are just clicking a plane when you click on it. If you could create a point that corresponds with the location of the enemy and add a box collider, then you could make it so that if you click it, it broadcasts a message to the enemy to make it do something in response. If you need more information or this is not the answer you are looking for, please comment telling me.
-0V3RWR173D
Thanks for the answer, that would certainly work. I've also considered mapping the click to a ray-cast from the $$anonymous$$ap Cam, to collide directly with real enemies on the playing field. Both these solutions will require some scripting, which is fine as plan B, but I'm still hoping for a simpler approach, hopefully built in to the cameras. Thanks again.