- Home /
weapon aiming in 3d space using the (2d) mouse
This question is very hard to explain, as seen in my previous question http://answers.unity3d.com/questions/535643/rotate-the-player-to-face-where-the-mouse-is-point.html so bear with me.
Basically I have a 3d, 3rd person game set in space where the use controls a spaceship. To control the rotation of the ship the user moves the mouse using the following script:
var lookFactor = 100; //This amount indicates how far away the ship and weapons are aiming
function Update ()
{
var distance = (transform.position.z - Camera.main.transform.position.z)*lookFactor;
var position = Vector3(Input.mousePosition.x, Input.mousePosition.y, distance);
position = Camera.main.ScreenToWorldPoint(position);
transform.LookAt(position);
}
This code works quite well as a mouse look code but at the moment here's how my weapons function: When the player clicks the left mouse button a missile prefab is instantiated directly infront of the ship with the same rotation as the ship then force is applied to its rigid body to propel it forward. But I'm trying to change this so that when the player clicks the left mouse button, a missile prefab is instantiated infront of the ship facing the mouse (instead of sharing the ships rotation). Except in 3d space using the ScreenToWorldPoint I give the mouse a fixed z co-ordinate, this is no good performing weapon aiming in 3d space. I need a way of changing the 'fixed' z co-ordinate to match that of the object the mouse is pointing at but never exceeding the max range of the selected weapon (hope that made sense). I'll give an example:
The players ship has two beam cannon turrets (one on each wing) which can rotate independently from the ship. These turrets follow the mouse (with it's annoyingly fixed z co-ordinate ;D). Lets say the player wants to shoot at a ship that's 250m away, but the z co-ordinate is fixed at 1000m, even though the player places the cursor directly on the ship, there is a chance that 1 or even both lasers will miss because they're aiming far behind the target ship. This, I'm sure you realise is not what is wanted, I need to know if there's a way (and what it is) to change the z co-ordinate of the mouse depending on what it's pointing at (wow, hope that made sense) I also need the z co-ordinate of the mouse to change appropriately if the player aims near a ship (because they could be accounting for movement and aiming infront of the ship might hit it (if the z co-ordinate is changed properly))
like I said, to me this is very hard to explain, if you need anything clarified please just ask, also refer to the question linked at the top for (a little bit) more information. This problem has had me stuck for weeks and this question took me like 30 mins to write, trying to make it as clear as possible. Thanks in advance.
If you still don't quite understand what I'm aiming for, look at a game like Star Conflict, I need an aiming system like that, but I just can't think of how to change the z correctly, especially if the player is aiming not even at but near the ship to account for movement D:
EDIT: here's what I'm thinking so far (this is just a concept, no code at the moment):
when the mouse is over an object, we compare the objects z position to the camera's z position, the difference becomes the z position of the mouse, is this possible? and would it work? and all objects could have an invisible sphere attached to them which counts as part of the object in terms of comparing z position (for if they aim not directly at the ship)
I'm assu$$anonymous$$g that the game displays something like this...
(sorry for my poor illustration)
or perhaps more like ...
Trailer @ http://www.youtube.com/watch?v=X-wmo4$$anonymous$$5-ls
In which case - can't you just cast a ray from the ship towards the enemy (clicked position) to deter$$anonymous$$e the angle of the missile?
As for ai$$anonymous$$g near a enemy, you could try placing a temporary, invisible plane (or sphere around the enemy) (that is facing the camera and positioned at the enemies position) to have the ray hit this "invisible object" and be fired into that direction, but in this case you would still have to figure out which enemy is closer to the mouse cursor (in 2D space) to chose which enemy to use that invisible wall on.
I'm guessing that the missiles would still miss, but they would at last fly in that direction. Unfortunately this doesn't really sound like a pretty solution, but I wanted to mention it anyway.
[edit] Or you could actually just display the 3D game while running a little 2D simulation (not sure if this is the correct term) in the background. Basically playing a 2D game while displaying the 3D version. Checking the coordinates in 2D (x,y or y,x I guess) would probably be a lot easier than 3D.
your top image isnt displaying, but yeah sort of. note: I know javascript but am somewhat new to unity(script), although I'm sure your input would normally help someone who knows how to use all the unity functions, I don't. For your answers to be truly helpful please include adleast a small amount of sample code
@empire2202, @ShadoX said to use "raycasts", so why not just look it up? It's not work posting code because it's been done so many times before. On very, very similar questions
@empire2202 Sorry, not sure why the image isn't working. Seems fine to me. As for the code - I do post code sometimes, but I'd have to post everything. I rather enjoy seeing people figure things out by themselves than giving them code (since that's pretty much what program$$anonymous$$g is about).
I'll try to post some example project/scene a bit later as I'm a bit busy right now.
I understand what raycasting is and, with the help of google I know how to use it, but I'm not sure how to apply it to this problem. As far as I can tell you're suggesting casting a ray from the play ship in the direction of the mouse, but that won't help, I might aswell have just fired my weapon. The problem is that the mouse operates in 2d space, but to accurately aim and fire at a distant ship, we have to give it an accurate z position, the problem is getting and setting that z value. I am not looking for some sort of auto-aim system where the player clicks on or near a ship and it always hits, I'm just looking for a way to give the mouse an accurate enough z position that, with ti$$anonymous$$g, ai$$anonymous$$g and estimation, the player could hit a moving target. With my current system where the mouse is treated as being 10 units away from the ship, unless the enemy is 10 units away, the ai$$anonymous$$g is inaccurate and I can't hit because, by the time the projectile goes x distance (where x is the distance from the player to the enemy), it is nowhere near where the mouse was.
Answer by empire2202 · Sep 18, 2013 at 05:33 PM
I've got it to work! But I've been told that OnMouseOver only logs the most forward object, so if anyone can suggest a modification to my code to allow the detection of multiple potential targets so my code can select the highest priority target. Here is code I attach to objects so they can be targeted: static var mouseOver = null; static var mouseOverDist : float; static var mouseOverPriority : int = 0; var self = gameObject; var selfPriority = 2;
function Start () {
if(self.tag == "aimingSphere"){
selfPriority = 1;
}
else selfPriority = 2;
Debug.Log("Self Priority is" + selfPriority + "because self is" + gameObject);
}
function setMouseOver () {
mouseOver = self;
mouseOverPriority = selfPriority;
mouseOverDist = transform.position.z;
Debug.Log("Mouse is now over " + gameObject);
}
function OnMouseOver () {
if(mouseOver == null){
setMouseOver ();
}
else if(self == SpaceShip.Target) {
setMouseOver ();
}
else if(mouseOverPriority < selfPriority) {
setMouseOver ();
}
//else if((mouseOverPriority == selfPriority) && ((self.z - Player.z) < (mouseOver.z - SpaceShip.Player.z) {
// setMouseOver ();
//}
}
function OnMouseExit (){
mouseOver = null;
mouseOverPriority = 0;
Debug.Log("Mouse is no longer over " + gameObject);
}
and this is the code I use to shoot:
var bulletTest : Transform;
var cam : Transform;
public static var test = [0, 2, 15, 2, 1, 1, 1000];
var names = [];
var bulletArray = [bulletTest];
public static var weapons = [test];
//<id>, <range>, <damage>, <shots>, <cooldown(1)/overheat(0)>, <time>, <prefabBullet>
var shootForce = 1000;
public var id = 0;
public var range = 1;
public var damage = 2;
public var shots = 3;
public var cooldown = 4;
public var coolTime = 5;
public var force = 6;
//These variables are just to make it easier for me when I want to access stats of weapons
public static var equippedWeapon = 0;
public static var lastShot : float;
public static var lastShotInt : int;
public static var lastWeapon : int;
public static var cooltime : float;
public static var lastWeaponShot : int;
var distanceFromShip = 15;
private var hitdist : float = 30;
public static var weaponCooldown;
public static var currentBullet : Transform;
var ray = null;
var targetPoint = null;
var targetRotation = null;
function Start () {
}
function Update () {
lastShot += Time.deltaTime;
weaponCooldown = weapons[equippedWeapon][cooldown];
hitdist = mouseOver.mouseOverDist - cam.position.z;
Shoot ();
}
function Shoot () {
if(Input.GetButton("Fire1")) {
if(weapons[equippedWeapon][cooldown])
{
if(lastShot > weaponCooldown)
{
ray = Camera.main.ScreenPointToRay (Input.mousePosition);
targetPoint = ray.GetPoint(hitdist);
targetRotation = Quaternion.LookRotation(targetPoint - transform.position);
lastShot = 0.0;
var equippedId = test[0];
lastWeaponShot = weapons[equippedWeapon][id];
var instanceLaser = Instantiate(bulletArray[equippedId], transform.position + transform.forward*distanceFromShip, targetRotation);
instanceLaser.rigidbody.AddForce(instanceLaser.forward * weapons[equippedWeapon][force]);
}
}
}
}
The parts where the solution is (for those interested) in the update function and the shoot function, the 1st code just detects which object the player is looking at. But again this system only detects the front most object, if anyone cane supply an alternate detection method that can, please tell me. Thanks for all your help.
To make this code detect objects placed behind others, should I cast an array from the camera.x, camera.y, and player.z into the world in a straight line then log and compare all the objects it hits and decide which is the highest priority target (the most likely the user is actually trying to shoot at)?
If so can someone please supply a link or an answer that is a small tutorial/explanation as to how I would do this. thanks.
@ArkaneX how do I make a raycast log all the objects it hits? I've finally gotten round to program$$anonymous$$g this new system using raycasting but I tried to detect which object that it hit that is most likely for the player to be ai$$anonymous$$g at by comparing hit[x] to other things but I get the error type object doesn't support slicing. I know this means that it stores it's hit as an object not an array, which means it can only store one hit (correct me if I'm wrong). How can I make it so it stores everything hit?
You can use Physics.RaycastAll. Additionally, it's worth mentioning that both Raycast and RaycastAll can be used with layermask parameter. You can use this parameter to force raycast to ignore objects belonging to specific layer(s).
wow fast response, thanks for help, I didn't know there was a whole different thing for this, I'll add that in and get round to testing, should be able to tell you if it's worked or not pretty soon.
Your answer
Follow this Question
Related Questions
Rotate an 3d Object 1 Answer
Why is the rotation missing when I animate? 1 Answer
Rotate 180 degrees over x axis over time 1 Answer
Rotation Question in Top-down 3D Game 0 Answers
Player wont follow touch controls? 1 Answer