Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • 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 /
avatar image
1
Question by Mischa · Mar 23, 2011 at 10:46 PM · rotationcollisionpositioncharactercontroller

How to get the relative position of a Collider with OnControllerColliderHit()?

I want to make my character walking on walls with the tag "walkable". So if I hit a wall to the left the character should rotate clockwise around his z axis until the wall is below and then walk up the wall.

Is there a possibility to do this with the OnControllerColliderHit function? I can't find a way to get the appropriate relative position of the hit.collider. The problem mainly is, that the floor i'm walking on has also the tag "walkable"

I tried it with CollisionFlags but same problem there: Its not possible to get to know on which side (right or left) the collision happens.

Any suggestions?

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

2 Replies

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

Answer by Bunny83 · Mar 24, 2011 at 03:23 AM

First of all you should keep in mind that you can rotate GameObjects (or the Transform) in general, but a CharacterController will always be aligned along the y-axis. Everything will rotate but the capsule of the CharacterController will stay.
You have three possibilities:

  1. Use a rigidbody with a "normal" capsule collider instead of the CharacterController
  2. rotate the world around you
  3. Keep the CharacterController but make it a sphere at the bottom of your character. If you rotate your char you have to rotate around the controller center. So place the pivot there.

In almost all cases (1.) will be a lot easier ;)


Now to your "real" problem. If you use a rigidbody with capsule collider you have to use OnCollisionEnter instead of OnControllerColliderHit. OnCollisionEnter provides a Collision struct that looks different from the ControllerColliderHit. You can use one of the ContactPoints from Collision.contacts. The contact point have a face normal that can be used to determine the orientation of the wall surface.

If you want to keep the CharacterController you can't rely on the information "above", "below", ... that the controller provides, because below will always be negative y. ControllerColliderHit doesn't have multiple contact points but it provides also a single point and the face normal of the colliding object.

Comment
Add comment · 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
1
Best Answer

Answer by Mischa · Apr 07, 2011 at 02:52 PM

Thank's a lot Bunny83, your answer put me into the right direction. Finaly I got it working. Just in case any of you want to have a look at the code, here it is. If you have any suggestions on making it simpler, just tell me. I'm still new to unity.. :)

alt text


using UnityEngine; using System.Collections;

public class Walker : MonoBehaviour {

 public float speed = 0.02f;
 public float maxWorldRotationSpeed = 2f;
 public float maxRotationSpeed = 10f;

 private CharacterController controller;
 private RaycastHit hit;
 private Vector3 moveVector;

 private GameObject geometry;

 void Start () {
     //get character controller and mesh for animation controls
     controller = GetComponent<CharacterController>();
     geometry = transform.Find("geometry").gameObject;
 }

 void FixedUpdate () {

     //get all Inputs
     Vector3 directionVector = new Vector3(Input.GetAxis("Horizontal"),0 , Input.GetAxis("Vertical"));

     if(directionVector != Vector3.zero){

         //we are walking
         geometry.animation.CrossFade("walk");

         //rotate the direction vector to the characters rotation
         Quaternion inputTransformRotation = Quaternion.LookRotation(Vector3.forward, transform.up);
         directionVector = inputTransformRotation * directionVector;

         //add speed
         moveVector = directionVector * speed;

         //cast a ring around the character to get collision informations
         //check the Ringcast function for more details
         if(Ringcast(transform.position,transform.localScale.y/2+0.2f,16,out hit,Vector3.Cross(transform.up,directionVector),1<<10)){
             if(hit.collider.tag == "walkable"){

                 //we can walk on the thing we hit
                 //calculate new up vector lerping to the walkable colliders normal, so the character walks head up
                 Vector3 newUp = ConstantSlerp(transform.up,hit.normal,maxWorldRotationSpeed);
                 if(Vector3.Angle(newUp,transform.up)==0f){

                     //character allready walks head up
                     controller.Move(moveVector);
                 }else{

                     //character needs to turn
                     //wee need to create a plane, to check our distance to the walkable collider. for walking up a wall we have to turn before we pass it, for walking down a wall we have to turn after we pass it.
                     Vector3 wallNormal;

                     //get the normal of the plane, so the normal points away from the character
                     if(Vector3.Angle(hit.normal,directionVector)<90){
                         wallNormal = hit.normal;
                     }else{
                         wallNormal = -hit.normal;
                     }

                     //create the distance plane. as soon as the distance on next move is 0, we have to turn
                     Plane wall = new Plane(wallNormal,hit.point+hit.normal*(transform.localScale.y/2+0.01f));
                     float distance = Mathf.Abs(wall.GetDistanceToPoint(transform.position));

                     if(distance-moveVector.magnitude<=0){

                         //move the character to the plane
                         if(wall.GetSide(transform.position)){
                             controller.Move(-wallNormal*distance);
                         }else{
                             controller.Move(wallNormal*distance);
                         }

                         //rotate the character to the new up vector
                         Vector3 forwd = Vector3.Cross(transform.right,newUp);
                         transform.rotation = Quaternion.LookRotation(forwd, newUp);
                     }else{

                         //move normal if the distance to the turning plane is too big
                         controller.Move(moveVector);
                     }
                 }
             }
         }

         // Set rotation of the character to the move direction (same as in Platform Input controller)
         if (directionVector.sqrMagnitude > 0.01) {
             Vector3 newForward = ConstantSlerp(transform.forward,directionVector,maxRotationSpeed);
             newForward = ProjectOntoPlane(newForward, transform.up);
             transform.rotation = Quaternion.LookRotation(newForward, transform.up);
         }

     }else{

         //the character doesnt move
         geometry.animation.CrossFade("idle",0.1f);
     }
 }

 bool Ringcast(Vector3 castCenter, float castRadius, int castSteps, out RaycastHit hit, Vector3 castAxis, int castLayerMask){

     //Ringcast lets us cast a ring around any axis and get the first collider we hit
     float castLength;
     Vector3 rotationOffset = transform.position;
     Vector3 castVector;
     Vector3 castFrom;
     Vector3 castTo;

     //set up axis
     castAxis = castAxis.normalized;

     //set up rotation around axis
     Quaternion stepRotation = Quaternion.AngleAxis(360/castSteps,castAxis);

     //set up start point of the first raycast
     castFrom = castCenter + Vector3.Cross(transform.up,castAxis)*castRadius;

     //draw fancy stuff into the editor window ;)
     Debug.DrawLine(castCenter,castFrom);

     //rotate the first point to get the target point of the first raycast
     castTo = stepRotation * (castFrom-rotationOffset)+rotationOffset;
     castVector = castTo - castFrom;
     castLength = castVector.magnitude;

     //do as many raycasts as needed
     for(int i=0;i<castSteps;i++){

         if(Physics.Raycast(castFrom,castVector,out hit,castLength,~castLayerMask)){
             //if theres any hit return true
             return(true);
         }

         //draw more fancy stuff into the editor window!
         Debug.DrawLine(castFrom,castTo);

         //rotate again and set up for the next raycast
         castFrom = castTo;
         castTo = stepRotation * (castFrom-rotationOffset)+rotationOffset;
         castVector = castTo - castFrom;
     }

     //if theres no hit set it to default and return false
     hit = default(RaycastHit);
     return(false);
 }

 Vector3 ProjectOntoPlane (Vector3 v, Vector3 normal) {
     return v - Vector3.Project(v, normal);
 }

 Vector3 ConstantSlerp (Vector3 start, Vector3 to, float angle) {
     float step = Mathf.Min(1, angle / Vector3.Angle(start, to));
     return Vector3.Slerp(start, to, step);
 }

 float ConstantSlerp (float start, float to, float vel){
     float step = Mathf.Min(1, vel / (to-start));
     return Mathf.Lerp(start,to,step);
 }

}

Comment
Add comment · 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

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.

Follow this Question

Answers Answers and Comments

No one has followed this question yet.

Related Questions

How to change the direction the player is facing when colliding with an object 2 Answers

CharacterController "breaks" collision, goes haywire 2 Answers

Game Object won't match rotation of new positions transform 1 Answer

Player Rotating after Collision... 0 Answers

Physics AddForce reduced when 3 objects are colliding 1 Answer


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