- Home /
Instantiate w/ Mouse Click: Random(?) Z.Axis problem (2D)
Hey everyone,
Struggling with a bit of code here. I'm working primarily in the X and Y-axes. Since the game will be 2D, I'm ignoring the Z-axis (or at least trying to.) This is where I am at now.
A) Player clicks a spot at will and an object(Exploder) instantiates at that point.
B) Object comes in and throws out an AddExplosionForce - pushing away any surrounding game objects in the opposite direction.
C) Object then destroys itself.
The problem I'm having is that the Exploders are coming it at wildly varying Z-axes.
I've tried using RaycastHits, Input.mousePosition, and camera.ScreenPointToRay to ignore or at least manually set the Z-axis to 0 but haven't had any luck.
Any suggestions?
Trigger Code
using UnityEngine;
using System.Collections;
public class Trigger : MonoBehaviour {
public GameObject exploderPrefab;
Vector3 mousePosition;
void Start ()
{
}
void Update ()
{
if(Input.GetMouseButtonDown(0))
{
// Construct a ray from the current mouse coordinates
mousePosition = Input.mousePosition;
Ray ray = camera.ScreenPointToRay(new Vector3(mousePosition.x, mousePosition.y, 10));
mousePosition.z = 10;
RaycastHit hit;
if(Physics.Raycast(ray, out hit))
{
//Create the explosion prefab
Instantiate(exploderPrefab, hit.point, Quaternion.identity);
Debug.Log(hit.point);
}
}
}
}
Exploder Code
using UnityEngine;
using System.Collections;
public class Exploder : MonoBehaviour {
public float radius = 5.0f;
public float power = 500.0f;
public Vector3 explosionPos;
void Start ()
{
}
void Awake()
{
AddExplosion();
Debug.Log("Exploder enters.");
//Destroy(this.gameObject);
//Debug.Log("Exploder exits");
}
// Update is called once per frame
void Update ()
{
}
void AddExplosion()
{
Debug.Log("Detector on.");
Collider[] objectsInRange = Physics.OverlapSphere(transform.position, radius);
foreach (Collider hit in objectsInRange)
{
if (!hit)
Debug.Log("Miss!");
if (hit.rigidbody)
hit.rigidbody.AddExplosionForce(power, transform.position, radius, 0);
Debug.Log("Hit " + hit.rigidbody);
}
}
}
Thanks for your time!
edit:
Changed the code of Trigger to match Mr. Reynold's suggestion. Added screen shots describe further troubles.
I tried implementing the code suggested by Mr. Reynold's and was met with mixed results. While my Exploder game object now instantiates at an almost 0-coordinate on the Z axis, it doesn't instantiate at all along the Y axis. (See screen shot 1 and debug.log to see spawn coordinates.)
The exception is that if you click almost exactly on the last spawned instance of Exploder, it will kind of "stack" allowing you to gradually move up along the Y-axis. (Screen shot 2)
edit2: On further examination - the Exploder instantiates if you click directly on another game object. I have a game object w/ rigidbody (Petal) that floats down the screen and if you click directly on it the Exploder instantiates fine. I need to make it so it can instantiate in an empty space and then generate force (via AddExplosionForce or some other tool) on the petal object to make it move in a certain direction. Weird how that works...
edit 3:
After finding that I can instantiate my exploder prefab if I click on an actual object, I got the idea to use a cube game object as a sort of wall to be placed right behind my playing field (about z=1 for my 2D playing field.) I dropped the mesh to make it look like nothing is there. Now my clicks actually instantiate off the cube instead of empty air. The initial result is positive, but now I have to figure out to keep the mesh on my Petal game object from getting snagged on my wall. :P
I'm still VERY open to suggestions / alternatives that don't rely on this method. There has to be some way to make this work.
You can only instantiate the Exploder over an object because Physics.Raycast only returns a result when the ray hits a collider. Have you tried the Plane.Raycast method I suggested? It seems the best alternative, since you would not need the "wall object" - the plane would do this job, but without be drawn or interfere with physics. By the way, the Petal object should have a rigidbody to be affected by explosion forces (you can set Use Gravity to false to disable gravity effects).
I thought that I'd given it a shot, but found that I hadn't saved the script. :P
Inputting the script as is gives me the following two errors -
Assets/Scripts/Trigger.cs(25,35): error CS0120: An object reference is required to access non-static member UnityEngine.Plane.Raycast(UnityEngine.Ray, out float)' and Assets/Scripts/Trigger.cs(27,62): error CS0165: Use of unassigned local variable
distance'
Invisible backstop boxColliders seem a common solution to "catching" raycasts. To avoid physics on it, can also use a Triggerbox (if you look on Edit->Settings->Physics you'll see a RaycastsCanHitTriggers option, which starts checked.) Just check isTrigger on your boxCollider.
The PlaneCast method does the same thing (lower-case plane.Raycast
.) Useful if you need other "real" triggers that let raycasts through.
If this is really 2D, you might want try an Orthographic camera (otherwise perspective can make things look wrong.) That would, among other things, allow you to move the backcube to z=50 and have it work the same.
Thank you for the suggestions! They've been a great help!
Aldonaletto,
Thank you very much for your suggestions, as well. I will be keeping your plane.Raycast on hand in case the wall becomes problematic.
There are a few reasons I'm keeping the wall for now and none of them suggest that it is better than your suggestion. The two biggest reasons are probably 1) the wall was an idea I came up with myself and if I'm asked to explain why I used it or how it works later on I'll be able to do so and 2) I was asked to try to keep it in C# (for my own educational purposes) and since I don't know how to convert the above Uniscript into C#, I'll stick with what I know for now.
Having said that, I'd like to keep your code on hand in case I need it later on. Thanks for all your help!
p.s. Tried to look for a way to send this directly to you (via personal message) but couldn't find one. Sorry if this is not the place to write this!
Answer by Owen-Reynolds · Oct 11, 2011 at 01:33 PM
You can set the z-value directly:
// from your code:
if(Physics.Raycast(ray, out hit)) {
Vector3 hp = hit.point; hp.z=10; // or hit.point.z=10 ?
//Create the explosion prefab
Instantiate(exploderPrefab, hp, Quaternion.identity);
This is solving the problem that when you raycast to a cube at z=0, hit.point is at z=-0.5.
mousePosition.z
is how far from the camera you start the ray, so is only useful for skipping nearby objects. You'll also want the locking on z (above A,) since the explosion can easily send things skittering a bit sideways.
Will give both of these a try tomorrow. I have the sneaking suspicion that they're not going to cut it but I'd be more than willing to be wrong if it works out.
Thanks for the suggestions! Looking forward to trying them out - especially the rbody.constraints line! Wow!
I gave the above code (submitted by $$anonymous$$r. Reynolds) a try and found that it did curb the z axis problem but spawned its own sort of trouble.
I need to upload some screen shots of how everything is turning out but I'm not sure how to do that in the comment section. Will go ahead and edit the original question.
Answer by aldonaletto · Oct 11, 2011 at 11:06 AM
You could freeze movements in the Z axis in all objects. It can be done by hand at the Inspector (very boring!), or you can just add an instruction before AddExplosionForce:
...
if (hit.rigidbody){
hit.rigidbody.constraints = RigidbodyConstraints.FreezePositionZ;
hit.rigidbody.AddExplosionForce(power, transform.position, radius, 0);
}
...
This can control the objects affected by the explosion, but if one of them hit other non freezed object, it may move in z. The best thing could be to find all rigidbodies at start and freeze Z in each one:
function Start(){
var rBodies: Rigidbody[] = FindObjectsOfType(Rigidbody);
for (var rbody in rBodies){
rbody.constraints = RigidbodyConstraints.FreezePositionZ;
}
}
I forgot the square brackets in the rBodies declaration, but now it's fixed (take a look at the answer above).
I tried implementing this code but hit a few snags. The first being the "var rbody" line in "for (var rbody in rBodies). Since I'm using C#, I had a hard time figuring out how to get Unity to accept that line. Also, using RigidbodyConstraints.FreezePositionZ kept my Petal GameObject from responding to force for some reason.
To be perfectly honest, I'm not at the stage where I should be worrying about how my objects react to Exploder (the prefab that I'm instantiating to move my petal around like a gust of wind.) I need to nail down how the Exploder instantiates before I worry about the effects it has on other objects.
I will come back to this though, so thank you for the help.
Sorry, I thought your problem was the reaction to the explosion - anyway, you may find it useful later!
About your current problem, the first screenshot shows a weird thing: the Y coordinate is fixed at 0.3, while z varies! Using @Owen Reynolds suggestion, (see below) Z should be fixed to the value set, and X and Y vary according to the point you've clicked:
//Create the explosion prefab
hit.point.z = 0f; // force z to 0
Instantiate(exploderPrefab, hit.point, Quaternion.identity);
A more elegant alternative could be to create a geometric plane in XY fixed at z=0, and use Plane.Raycast; since this function returns the distance from the ray origin, you must use ray.GetPoint(distance) to get a 3D point:
if(Input.Get$$anonymous$$ouseButtonDown(0)) { // Construct ray from mouse position Ray ray = camera.ScreenPointToRay(Input.mousePosition); // create a plane facing the camera at pos (0,0,0) Plane plane = new Plane(-Vector3.forward, new Vector3(0,0,0)); float distance; if(Plane.Raycast(ray, out distance)) { Vector3 point = ray.GetPoint(distance); //Create the explosion prefab Instantiate(exploderPrefab, point, Quaternion.identity); Debug.Log(point); } }
Aldonaletto says, "About your current problem, the first screenshot shows a weird thing: the Y coordinate is fixed at 0.3, while z varies! Using @Owen Reynolds suggestion, (see below) Z should be fixed to the value set, and X and Y vary according to the point you've clicked:"
I know, right? I can set it up so that hp.z = 0 (and instantiates that way) but the hp.y is frozen at 0.25! I don't get it!
I will give your geometric plane solution a twirl right now. Thanks again for the help!
Initial testing shows that this method works a lot like the previous one. I don't really have any other scripts that could be interfering so I'm not sure what's wrong. Pretty disheartening but I'm not giving up yet. Thanks again for your time! :D