• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
This post has been wikified, any user with enough reputation can edit it.
avatar image
5
Question by AngryOldMan · Apr 06, 2011 at 02:32 PM · instantiation

How to ShereCast consistently from a rotating object

Apologies for the long question I wasn't sure what else to title it.

I currently have a cube that is fired and when it collides with something it instantiates a cylinder (large flat cylinder, looks like a decal but its an object, which is what we need just comparing it to a decal in visual aspects, I do not want to use decals!) What I am trying to do is have the cylinder's up/y axis to allign to the surface of the object it has hit. For example if it hits the floor it should be flat against the floor and if it hits a wall it should be flat against the wall. At the moment it hits a wall but instantiates as though it has hit floor. So I looked into sphere casting and cannot implement it properly, I've looked at and deconstructed several scripts to get to where I am now as well as following useless tutorials which tell me to do one thing instead of explaining the theory behind using spherecast.

var hit : RaycastHit;

function Start() { //THis is the line that gives me errors, not sure what to put in the brackets //i'v tried so many things Iv lost the plot of what i was trying to do with this if (Physics.SphereCast (hit.transform)) { var ImpactPoint = hit.transform.rotation; var currentPos = transform.rotation; var newpos = Quaternion.Lerp (currentPos, ImpactPoint,1); transform.rotation = newpos; } }

What I've managed to achieve doesn't run as I keep hitting errors due to my lack of understanding, but I'm hoping it will work and if it wont then should I just start looking for another method to achieve this? (like messing around with tagged colliders, don't really want to do it but it's always a last resort)

Edit

apologies for not being here for a couple of days, started at a party, end up in Cambodia enough said. Anyway I've had a look at all your helpful answers (many thanks to you all), had a good read of your explanations and had a look at the script reference to try understand it all. I have tried implementing the answers I have recieved here and came across a common error, when my object (projectile that spawns the prefab SugarAOE) hits the floor and spawns the projectile it is always facing forward in stead of facing up/down. Likewise when it hits a wall it spawns facing up/down instead of facing sideways/forward. Loving the fact that it's working better than my (admittedly shody) attempt at spherecasting, feels like I'm close. Will work on it constantly till I crack it but if any more generous people would like to explain what I'm doing wrong I'd be thrilled to listen.

As per someones request here is the script that instantiates the prefab that I'm trying to spawn flat on a surface. This is on a projectile which is instantiated via another script. I've removed the other attempt (as above) from it's script.

var SugarAOE : GameObject;

function OnCollisionEnter(collision : Collision) { SpawnPrefab(); Destroy(gameObject); //audio.clip = bang; //audio.Play(); }

function SpawnPrefab() { var origin : Vector3 = transform.position; var direction : Vector3 = transform.up; var hit : RaycastHit; var distance : float = Mathf.Infinity;

 if (Physics.Raycast(origin, direction, hit, distance))
 {
     Instantiate(SugarAOE, hit.point, Quaternion.LookRotation(hit.normal));
 }

}

As you can see I decided against the spherecast as mainly it was $h|? and didn't work at all, very unexpected things kept on happening, when it hit something and spawned it would pick a random place to do it, eg if i fired at exactly 0,0,0 in the scene and it hit my floor (which is around 0,0,0) then some how it would end up in a completely random place. Maybe that's me being useless or setting it up wrong I've no idea? I still don't fully understand it even though I'm trying hard, going to look at some more documentation and cross reference it with your answers to see where I am going wrong.

So in this edit's conclusion I either need to figure out why the sphereCast acts so strangely or I need to figure out when using raycast how to alter the direction in which it is spawned?

EDIT #2

Haha I've figured out why it acts so strangely! I have a random rotation added to my projectile! (It's a rigidbody so I mean it has AddTorque) I believe this is why i need the sphere cast limited to a small radius. Only noticed when every now and then it doesn't find the right object and instead does an infinite raycast to the edge of my level (didn't even know there was a collider on it? Lucky there is) obviously if I adjust the raycast length to be less every now and then I get nothing (which is expected).

EDIT #3

Having major problems getting the sphere cast to work properly, it appears completely random as to what it decides to align itself to and it's orientation. I changed the title of the question, now what I'm having trouble with, like I said, is ShpereCasting properly from a rotating object. I can't wrap my head around it, I don't get consistent or monitorable results so I've no clue how to go about fixing it. My brain hurts :(

This is what I have so far:- I instantiate a randomly spinning (all rotational axis) projectile which has this script on it

var SugarAOE : GameObject; var PosCast : Vector3; var RotCast : Vector3;

function Update() { var origin : Vector3 = transform.position; var radius : float = 2; var direction : Vector3 = transform.forward; var hit : RaycastHit; var distance : float = 2;

 if (Physics.SphereCast(origin, radius, direction, hit, distance))
 {
     PosCast = hit.point;
     RotCast = hit.normal;
 }

}

function OnCollisionEnter(collision : Collision) { SpawnPrefab(); Destroy(gameObject); //audio.clip = bang; //audio.Play(); }

function SpawnPrefab() { scaoe = Instantiate (SugarAOE); scaoe.transform.position = PosCast; scaoe.transform.up = RotCast; }

The problem now is it pretty much never targets the floor. Seriously What should I do? any direction that may yeild a positive result. Other things I've tried include :-

function SpawnPrefab() { var origin : Vector3 = transform.position; var direction1 : Vector3 = transform.up; var direction2 : Vector3 = transform.forward; var direction3 : Vector3 = transform.right; var hit : RaycastHit; var distance : float = Mathf.Infinity;

 if (Physics.Raycast(origin, direction1, hit, distance))
 {
     SAOE = Instantiate(SugarAOE);
     SAOE.transform.position = hit.point;
     SAOE.transform.up = hit.normal;
 }
 if (Physics.Raycast(origin, direction2, hit, distance))
 {
     SAOE = Instantiate(SugarAOE);
     SAOE.transform.position = hit.point;
     SAOE.transform.up = hit.normal;
 }
 if (Physics.Raycast(origin, direction3, hit, distance))
 {
     SAOE = Instantiate(SugarAOE);
     SAOE.transform.position = hit.point;
     SAOE.transform.up = hit.normal;
 }

}

FINAL EDIT

Huzzah I've done it, it's probably a bit messy and I will keep this question open to see if anyone has a better way?

var SugarAOE : GameObject; var HasSpawned : boolean = false; private var where : Vector3; private var whichway : Vector3;

function OnCollisionEnter(collision : Collision) { SpawnPrefab(); Destroy(gameObject); }

function SpawnPrefab() { var origin : Vector3 = transform.position; var direction1 : Vector3 = transform.up; var direction2 : Vector3 = transform.forward; var direction3 : Vector3 = transform.right; var hit : RaycastHit; var distance : float = 1;

 if (Physics.Raycast(origin, direction1, hit, distance))
 {
     if (HasSpawned == false)
     {
         where = hit.point;
         whichway = hit.normal;
         AreaOfEffect();
     }
     //SAOE = Instantiate(SugarAOE);
     //SAOE.transform.position = hit.point;
     //SAOE.transform.up = hit.normal;
 }
 if (Physics.Raycast(origin, direction2, hit, distance))
 {
     if (HasSpawned == false)
     {
         where = hit.point;
         whichway = hit.normal;
         AreaOfEffect();
     }
 }
 if (Physics.Raycast(origin, direction3, hit, distance))
 {
     if (HasSpawned == false)
     {
         where = hit.point;
         whichway = hit.normal;
         AreaOfEffect();
     }
 }

}

function AreaOfEffect() { HasSpawned = true; SAOE = Instantiate(SugarAOE); SAOE.transform.position = where; SAOE.transform.up = whichway; }

So in answer to my question "How to ShereCast consistently from a rotating object?" Don't would be the answer. Much as Statment said very early on use raycast instead. The whole rotating object thing makes it quite difficult.

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

5 Replies

· Add your reply
  • Sort: 
avatar image
2
Best Answer

Answer by Statement · Apr 10, 2011 at 05:09 PM

Since you haven't accepted any answer for a few days (I think any proposed answers are useful), I thought I'd try rewrite your code to allow spherecasting.


// This example is designed to be placed on the camera. // Each time you fire (Left Mouse Button by default), // A prefab is spawned at the point of impact.

var prefab : GameObject;

function Update() { if (Input.GetButtonDown("Fire1")) SpawnPrefab(); }

function SpawnPrefab() { var origin : Vector3 = transform.position; var direction : Vector3 = transform.forward; var hit : RaycastHit; var distance : float = Mathf.Infinity;

 if (Physics.Raycast(origin, direction, hit, distance))
 {
     var instance = Instantiate(prefab) as GameObject;
     instance.transform.position = hit.point;
     instance.transform.up = hit.normal;
 }

}


In response to your edit: The part with your object facing forward all the time was expected, and I thought it was the proper way to go. However, if you want the up vector of the object to be pointing along the normal, try the above (changed) code.

Above script in action in my world:

Decals

Comment
Add comment · Show 10 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Statement · Apr 10, 2011 at 05:12 PM 0
Share

If you prefer them to lie exactly on the face they hit, you should replace the call to SphereCast with RayCast, and remove the radius from the parameters. Or you could look into a workaround (works only with mesh colliders): http://answers.unity3d.com/questions/44775/how-do-i-obtain-the-surface-normal-for-a-point-on-a-collider-cant-use-raycasthi/

avatar image AngryOldMan · Apr 11, 2011 at 12:19 PM 0
Share

Apologies for my absence, bit of a debacle lol. 1st I tried to implement your sphere cast technique into my script which went well till i tried it lol. $$anonymous$$y projectile hit the floor and just disappeared!?! found out that it was just spawning anywhere around the object it hits. So I altered it like you suggested to a raycast which works alot better but still not working (check updated post) As per your comment above yes I do want them to lie exactly on the face they hit, thanks for the added insight. (obviously taken your advice :)

avatar image Statement · Apr 11, 2011 at 12:25 PM 0
Share

I have updated my code.

avatar image Statement · Apr 11, 2011 at 12:36 PM 0
Share

Basically, just set the transform.up to the normal.

avatar image AngryOldMan · Apr 11, 2011 at 02:06 PM 0
Share

Great! that works thanks (but you already knew that, lol) what I failed to mention is a have a random rotation applied to my projectile which is why I think I need the sphere cast limited to a small radius. Going to give it a try now but I'm pretty sure you've already got these points in the bag ;)

Show more comments
avatar image
2

Answer by efge · Apr 06, 2011 at 04:20 PM

You could use normal of the surface the ray hit, something like:

Quaternion.FromToRotation(Vector3.up, RaycastHit.normal);
Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image AngryOldMan · Apr 06, 2011 at 04:30 PM 0
Share

I'l try that once I get the code to stop giving me errors lol. Check the updated post I'v included my problem line (cheers for post though made me aware I needed to be more clear)

avatar image Statement · Apr 09, 2011 at 10:06 AM 2
Share

Note that it's hit.normal, not RaycastHit.normal, when you try to add it to your code.

avatar image Statement · Apr 10, 2011 at 05:40 PM 0
Share

Also note that it's easier to just use Quaternion.LookRotation(hit.normal). I am not sure what to expect from the above code, it isn't immediately clear to me just looking at it if it would perform the intended behaviour :)

avatar image
3

Answer by Bunny83 · Apr 09, 2011 at 04:22 AM

Physics.SphereCast needs some more parameters. Like a Raycast you need a position a direction and length of your spherecast. In addition it needs a radius to specify the size of the sphere.

I'm not sure from what point and towards what point you want to cast. Here's an example how SphereCast works:

var origin : Vector3 = transform.position;
var radius : float = 2.0;
var direction : Vector3 = transform.forward;
var hit : RaycastHit;
if (Physics.SphereCast(origin, radius, direction, hit))
{
    // We hit something
}

I just used variables to show each parameter. The origin, the radius and the direction can of course be passed directly to the function.


hit.transform is just the transform of the GameObject that we hit with our spherecast. It has no relation to the hitpoint. Like efge mentioned the RaycastHit structure contains the surface normal at the point we hit the other object.

To align an object along the normal, the object's forward vector should match the normal. So if we hit the ground the normal points in positive y - axis (aka up). When the object lays flat on the ground the forward axis (z) should point upwards. If you have setup your object like this you can use transform.LookAt or Quaternion.LookRotation to rotate the object.

obj.transform.LookAt(obj.transform.position + hit.normal);

or

obj.transform.rotation = Quaternion.LookRotation(hit.normal);

You said you instantiated the object. Where is your above script attached to? The flat, instantiated object? If you used collisions to get the position where you need to instantiate the object you should set the rotation there. Maybe you can post the script that instantiate the object?

Good luck

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image AngryOldMan · Apr 11, 2011 at 12:25 PM 0
Share

Thank you very much for your post it has definitely helped me in understanding what is going on when using the shperecast. As per your request I'v included the updated spawn script (turns out I had the shpere cast on the wrong object) I'm still getting problems which I am trying to work out (check updated post if your interested) +1 for being informative

avatar image
5

Answer by Statement · Apr 09, 2011 at 10:29 AM

Please know that SphereCast may not be ideal, especially for placing decals or doing anything that involve normals. There seems to be a bug in SphereCast and CapsuleCast where normals become quite crazy.

Funky blue normal.
Funky blue normal. Click image to read whole question & discussion.

Maybe you should stick to a RayCast, if appropriate, since it return a decent normal at least. However, if you are using mesh colliders, there's a potential workaround posted here.


As for your original question, I could repeat what efge and Bunny83 already said but I doubt it adds anything to the discussion. You get the rotation from Quaternion.LookRotation(hit.normal) and given that SphereCast would have worked as expected it would been sufficient. I doubt it will be, as you probably soon will see.

Comment
Add comment · Show 2 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Statement · Apr 09, 2011 at 10:31 AM 0
Share

If you are using small radius (< 5? absolutely not as big as 10) you might get away with it as errors were generally noticed with bigger spherecasts.

avatar image AngryOldMan · Apr 11, 2011 at 12:38 PM 0
Share

yes I wish to use a tiny radius it doesn't need to be big at all. Just big enough to encompass projectile object so that when the projectile collides with something the spherecast parameters are in effect.

avatar image
0
Wiki

Answer by Statement · Apr 12, 2011 at 11:59 AM

Apologies, I just now checking into some of your updates, and your intentions become somewhat clearer now. It seems you only are interested in the normal of the point you are colliding with, since you seem to call SpawnPrefab in OnCollisionEnter.

Maybe this is all you need?

var SugarAOE : GameObject; var HasSpawned : boolean = false;

function OnCollisionEnter(collision : Collision) { if (HasSpawned) return;

 var contact = collision.contacts[0]; // Just pick one
 var SAOE : GameObject = Instantiate(SugarAOE);
 SAOE.transform.position = contact.point;
 SAOE.transform.up = contact.normal;
 Destroy(gameObject);
 HasSpawned = true;

}

Comment
Add comment · Show 2 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image AngryOldMan · Apr 12, 2011 at 04:38 PM 0
Share

using this method seems to work alot better and more consistently, however the $$anonymous$$arAOE always appears under the object it has just hit. I added a moveup vairable to 'SAO$$anonymous$$transform.position = contact.point;' but that doesnt work for when it hits the side.

avatar image AngryOldMan · Apr 12, 2011 at 04:43 PM 0
Share

As a fix i'v just increased the objects size,cheers for all your help :)

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

No one has followed this question yet.

Related Questions

Using Transform on instantiated objects 1 Answer

Missing Materials on build 1 Answer

Caching Transform Javascript Help 0 Answers

Help with Prefab script running on wrong instance... 0 Answers

The AI-Players do not see new install (Instantiate) object (the ball) after they set the first goal in gates and old object (the ball) was destroyed. 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges