Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

3D Game - Targeting Crosshairs Pseudo UI ? Or actually UI?

Discussion in 'Scripting' started by digital_kaizer, Sep 8, 2018.

  1. digital_kaizer

    digital_kaizer

    Joined:
    Aug 24, 2018
    Posts:
    8
    Alright, so I've been making this 3D game which consists of a camera that orbits almost an arena type map,
    where in the center there are blocks to knock down off their foundations by shooting balls, that's the basic premise.

    Anyway, I am having trouble getting a solid targeting system in place. It's all well an good, if you never move the camera, but that kinda defeats the intent of having the camera rotate around the 'puzzle' of sorts.

    The targeting system consists of a game object that is currently an elongated transparent sphere, which is attached to a script that fires a ball "forward" relative to said sphere. That all works fine. The problem is compensating for the cameras position and rotation, I feel like I have tried everything with varying degrees of failure and success, but nothing spot on the desired effect just yet. I want the cross-hairs, a flattened semitransparent sphere, (which is a child of the spawner sphere, and offset so you see it on screen.) stays in the same location *on screen* or "in the UI" while the camera moves around the map.

    Honestly, this is just what I came up with, I am willing to scrap my entire targeting system if there is a better way.

    If it helps, here is a video of me demonstration my issue:



    And here are the scripts involved with these functions:
    Player.cs -- https://pastebin.com/PYK2MAGG
    CameraControl.cs -- https://pastebin.com/4mr3EZff
    Targeting.cs -- https://pastebin.com/wLbEAXrk

    And I've attached a screenshot of my editor scene view to give greater clarity on how things are setup.

    Any tips, advice or assistance is greatly appreciated. Thank you for your time.
     

    Attached Files:

  2. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    It should just need the cross hair being parented to the Camera. Then, the cross hair will remain stationary on the screen when the camera moves / rotates.
     
  3. digital_kaizer

    digital_kaizer

    Joined:
    Aug 24, 2018
    Posts:
    8
    It's been many iterations of my code since I actually had it set up that way, because at one point I did.
    And if I do that, it actually is a lot closer now. But I'm still getting a Z rotation on my spawner.
    Also the Y axis controls are inverted on the opposite side of the map from the starting point.



    I'm sure it's something simple I've missed, I've changed my code and reverted so many times, I'm sure it's just that I'm missing the right combination of factors to make it truly do what I want. Could it have something to do with update vs lateupdate? Would I have to force 0 Z rotation in both or one over the other?
     
  4. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,315
    Why is your targeting reticle in world space at all? I would keep track of it entirely in screen space.
     
  5. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    This is a perennial issue when working with (non-trivial) software systems. :)

    Just by way of throwing an idea around, could you not set things up something like this :-
    1. Create an empty GameObject called 'FocalPoint'. Place it at the centre of the targets.
    2. Set the Camera (at an appropriate distance from the targets) looking directly at FocalPoint.
    3. Parent the Camera to FocalPoint.
    4. Parent the cross hair to the Camera.
    5. Now, to move the Camera, simply rotate FocalPoint based on user input.
    6. Rotate the cross hair (locally) based on user input.
    7. When firing a projectile, instantiate it with the same (world) position and (world) orientation as the cross hair object.
    I'm just thinking that may simplify down the code quite considerably making it easier to see what is going on?
     
  6. digital_kaizer

    digital_kaizer

    Joined:
    Aug 24, 2018
    Posts:
    8
    Simply because I don't know what's possible. I've only been using Unity for three weeks. I've done some minor work with Python before, but I only have 3 weeks experience with C#, or any C for that matter. This is how far I've come in a short time because of my overall technical skill and proficiency at self teaching, but I'm still learning. I spent 3 days trying to figure this out on my own before I broke down and asked for help. I try not to be "that guy" who posts for every single little issue or tries to have other people do his work for him, but I really got stuck on this one.

    What you say implies that you can instantiate a 3D object from 2D space and have it shoot in the direction I want, if so, that would be a big step in the right direction for what I want it do to. I guess I can just "try" that, but for some reason I didn't think it would work that way.
     
  7. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    You could look into ScreenPointToRay to do that.

    As I'm sure you will be aware from using Python, there is very often a multitude of ways of achieving a given goal. It's not like there is one correct way to do what you are wanting here. Play around with different stuff until you find something that suits you- it will all be valuable experience along the way. :)
     
    digital_kaizer likes this.
  8. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    324
    So this is basically how you want to do this from a highlevel:

    1. You'll have a reticule-sprite, in a canvas, using Screen Overlay.

    2. When you move the target joystick, it will move this targeting reticule. You'll set up some bounds so the reticule can't be moved off screen.

    3. Using ScreenPtToRay, you RayCast from the 2d targeting reticule. If the rays hit anything, position your aimPt there, thats the pt in world space you want to target. You probably don't need to show this aimPt in 3d space, since the 2d reticule will be covering this area anyways.

    That should basiically work, and you can spin your camera, and it will always be shooting rays from wherever the reticule is positioned in 2d screenspace.
     
    Last edited: Sep 8, 2018
  9. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,315
    No worries. Asking for help isn't any issue.

    That's correct. Remember that for any 3d image to be displayed on the 2d screen, it first needs to projected into 2d. You can take advantage of all this pre-existing math and create logic that goes back and forth from world to screen at will. This comes in very handy for a lot of applications.

    @shawnblais lays it out pretty well.

    First, you need a way to keep track of where your cursor is. You can keep track of this with a simple 2d vector, where the x is the horizontal pixel position and the y is the vertical. This value can be modified by your on-screen joystick the same way you're currently rotating your targeting object.

    Next, you need to change that point into a direction to shoot. Your camera has a method called
    ScreenPointToRay
    , whose purpose is self-explanatory. Feed it your screen position and it will return a ray that goes from where the camera is, through the point specified on the screen, out into the world.



    This is for raytracing, but the concept is the same. Your reticle point is represented by one of those x's. The ray returned from
    ScreenPointToRay
    is a normalized version of one of the red arrows.

    Now that you have a direction, you can simply shoot it! You'd replace
    ballSpawn.forward
    in your Player code with that ray.

    Finally, you probably want some way to represent your shooting direction on the screen. You can do this by adding another image to your canvas (same place your on-screen joysticks are) and syncing its position to your logical reticle vector2.
     
  10. digital_kaizer

    digital_kaizer

    Joined:
    Aug 24, 2018
    Posts:
    8
    You're absolutely right about that, definitely many paths to the same goal.

    That is exactly the kind of info I'm looking for, I can definitely work with this. Sounds like this is exactly what I need.

    Yes, this is perfect. I love it. Thank you all for this info, this is precisely what I needed to know. I should be able to get the results I'm looking for here.
     
    Doug_B and Madgvox like this.
  11. digital_kaizer

    digital_kaizer

    Joined:
    Aug 24, 2018
    Posts:
    8
    Okay so, I got the raytrace set up in my canvas and it works.

    Code (CSharp):
    1. public void Raytrace()
    2.     {
    3.  
    4.         RaycastHit hit;
    5.         posX = cursor.transform.localPosition.x + pixelX / 2f;
    6.         posY = cursor.transform.localPosition.y + pixelY / 2f;
    7.         Vector3 cursorPos = new Vector3(posX, posY, 0);
    8.         //Ray ray = Camera.main.ScreenPointToRay(new Vector3(Targeting.Instance.posX, Targeting.Instance.posY, 0));
    9.         Ray ray = Camera.main.ScreenPointToRay(cursorPos);
    10.         Debug.DrawRay(ray.origin, ray.direction * 100, Color.yellow);
    11.         if (Physics.Raycast(transform.position, -Vector3.up, out hit))
    12.         {
    13.             print("Found an object - distance: " + hit.distance);
    14.         }
    The part I'm getting hung up on, for the past four hours, is how to actually get my projectile to shoot in the direction of the ray. Because all the tutorials I've found that deal with this, simply fire the projectile at a static crosshair that doesn't leave the center of the screen.

    Code (CSharp):
    1. Rigidbody ballRigidbody;
    2.                 // Ball Spawns at Camera presently.
    3.                 ballRigidbody = Instantiate(ball, ballSpawn.position, ballSpawn.rotation) as Rigidbody;
    4.                 // Reference Targeting Script Ray Direction ?
    5.                 ballRigidbody.AddForce(transform.forward * finalForce * forceMultiplier);
    6.                 // Actually getting the ball to align to face raytrace direction to fire on target.
    7.                 ballForce = 0f;
     

    Attached Files:

  12. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    What is
    ballSpawn
    (how does it get its position and rotation)?

    Is the projectile moving with the force applied at line 5? If so, is it moving along the wrong line? (I'm not sure how to read the images you posted.)
     
  13. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,315
    You're getting the screen point from a transform; there's no transform associated with the cursor position. The cursor position is just a simple vector2 variable. The cursor you see on screen is simply showing that vector2 value, it is not informing it. Your ray is so offset because it's not actually using correct values.

    As I said before, your object needs to get shot at your ray's direction. Replace
    transform.forward
    with your
    ray.direction
    . I'm not sure where
    ballSpawn
    is, but the intention of my original post was that the object would be spawned at the camera position. If you want the object to come from somewhere else other then the camera position then that's slightly different (more complicated) math. For now just keep it simple and get it working with the camera position first.

    It seems like you're using some kind of amalgamation of your previous techniques and the ones explained here. That won't work very well. Make sure you try to understand the thought process of our original posts, and if you're not grokking something feel free to ask. Flailing doesn't get you very far for very long. :p
     
    Doug_B likes this.
  14. digital_kaizer

    digital_kaizer

    Joined:
    Aug 24, 2018
    Posts:
    8
    You are right, I am doing my best to understand. And the remainders of my older code are mostly left in place because any changes I have tried to make to complete the new code, don't compile. And in the mean time I try put it in a position where it compiles as often as possible, so I can kind of test and see how my changes affect things. I have tried a dozen variations on what I believe I understand is what is required but it is apparent I do not understand the proper syntax. Generally speaking, I understand what you're telling me to do. And I want to do it. But based on my hours of trial and error, I'm just not getting it right. Like every-time I tried to understand how to fetch and use ray.direction I got errors that the type didn't match or it couldn't be a float, or what have you.

    Ballspawn is indeed currently the main camera, and I have tried to use ray.direction to LookAt the hit point, match rotation, etc, etc but my code didn't compile when I did, I had a hard time understanding the exact syntax of the code. And usually I learn best with visual aids and I learn from people who have done similar things, and use the code for my own purposes, but I cannot find anyone who has talked about how exactly they did this. Most games that involve shooting made on unity use the camera/canvas centerpoint as the crosshair and leave it to the camera rotation to make the aiming happen. Which, granted, seems a lot easier to do. But further reason I wanted to pursue something more original for my work.

    So, not previously shared, this is one of the many things I tried, in vain;
    Of course, it would be too easy to just type
    ballRigidbody.AddForce(ray.directon * finalForce * forceMultiplier);

    Which I suppose, obviously, does not compile.
    Code (CSharp):
    1. Assets/Scripts/PlayerTargeting.cs(178,44): error CS1061: Type `UnityEngine.Ray' does not contain a definition for `directon'
    2. and no extension method `directon' of type `UnityEngine.Ray' could be found.
    3. Are you missing an assembly reference?
    EDIT: I just realized now that after I've rewritten my script and made this post that direction was misspelled. [[[[HEADDESK]]]] Now let me try to revert my mouse position changes for a moment...

    Another one of my big problems was I have a hard time wrapping my brain around the functions that allow scripts to access things from other scripts, so I went ahead and rewrote the player script and targeting script into one script so the shooting and targeting is in one script to make things easier on myself. Now the only thing I really need to do is transfer a bool from a new micro script for the fire button, which is much simpler.

    This is the level of my understanding here. I tried to assign ray.direction to a variable, but that didn't work either.
    And quite frankly, from my surfing the Unity documentation, some of the pages related to ray casting have very little information compared to other functions. Like some sub-functions literally have a 1 sentence description.

    Being table to target independently of the camera's center point was a mechanic I really wanted in my game, but after 3-4 days of trying to figure this out with not much success, I am closing in on my breaking point. I'd also settle for deleting the right joystick and going with a tap on the screen to lock target, then using the fire button to shoot. I am currently attempting to make that work.
     
    Last edited: Sep 11, 2018
  15. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,315
    I understand and can commiserate. Everyone's been there at some point. The things that I'm saying -- "replace transform.forward with your ray.direction" -- do indeed come with implied implementation details that as a new programmer you might not have an inuitive grasp on yet.

    If you posted your player script on pastebin or whatever, and I went through and reviewed it, would that help out your understanding? Perhaps some concrete examples of what I'm talking about could help connect it to the abstract concepts I've been using in my previous posts.
     
  16. digital_kaizer

    digital_kaizer

    Joined:
    Aug 24, 2018
    Posts:
    8
    I would appreciate that, I suppose writing it all down helped me get a clearer head about it and double checking my code, I actually have ray.direction working now, whew. The ball shoots in the direction of the raycast. I almost can't believe it lol.

    All I need is the raycast to be accurate to the cursor position and I'm set... what a relief.
    I could use a few tips on that front, it was mentioned about needing the Vector2 instead of the transform.. Entire Script: https://pastebin.com/XatQypFV
    You will find it under:
    // RightJoyStick - Move UI Crosshair


    EDIT: Oh and because I imagine it makes a difference, my canvas is a Screen Space Overlay.
     
  17. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,315
    https://pastebin.com/3Ugr74HE

    Here's the modified script, which should work, though it's untested.

    Here's a diff of the old script and the new script: https://www.diffchecker.com/K3eSText

    Keep in mind that the
    cursorMoveSpeed
    is in pixels per second, not per frame. It's a good idea to attenuate your values by Time.deltaTime to make them more frame-rate independent.
     
    digital_kaizer likes this.
  18. digital_kaizer

    digital_kaizer

    Joined:
    Aug 24, 2018
    Posts:
    8
    It works perfectly, I can't thank you enough. Now I can move on to more content creation related work. All coding from here should be within my abilities.
     
    Madgvox likes this.
  19. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    324
    You don't need math to have it fire from some other position, come on. use unity for it's strengths, it has an amazing child/parent transform system that is flexible and easy to use, and it does all the math for ya!

    Just parent a transform under the camera, offset it any way you like, and use that transform's world pos as your start pos when you instantiate your bullet.

    1. Raycast from cursor into world, when you hit something, grab the hitPt from the RaycastHit
    2. Spawn your bullet, position at startPt
    3. Give it a direction: bullet.rigidbody.velocity = (rayHitPt - bullet.transform.position).normalized * bulletSpeed;

    Assuming the bullet has no gravity, it will hit the exact spot the cursor ray hit in the game world.