- Home /
How can I visualize a CapsuleCast?
When programming a raycast one can simply use Debug.DrawLine/DrawRay in order to be able to visualize the raycast when the game is running; I would like to be able to do something similar for a CapsuleCast. I am trying to debug part of a script which, uses a CapsuleCast to detect some things, but it would be a lot easier if I could actually see it.
Is there any way to do this?
Answer by aldonaletto · Aug 25, 2013 at 01:23 AM
Rendering the volume occupied by the capsule cast would be complicated, but you could instead just render a rectangular mesh that completely encapsulate the volume - this would give a reasonable idea about the real volume. You could for instance create a standard Unity cube, remove its collider and assign a semitransparent material to it, then adjust its scale to match the capsule cast volume. IMPORTANT NOTE: the example in the docs wrongly assume that the parameters point1 and point2 are the bottom and top of the capsule: they actually are the centers of the bottom and top hemispheres, thus they are at a distance equal to the capsule radius from the extremes - this causes a lot of trouble until we realize that (maybe that's why you want to visualize the volume).
The script below measures and displays the free distance ahead of a CharacterController when you press the key P (limited to range distance). The function RenderVolume creates the mesh (if necessary), assigns the material defined in mat and adjust its dimensions according to the parameters passed to CapsuleCast. When you don't need to show the volume anymore, call HideVolume to make it invisible. Attach this script to a character that contains a CharacterController (like the standard First Person Controller).
public var mat: Material; // drag a semitransparent material here
public var range: float = 10; // range of the capsule cast
private var shape: Transform;
function RenderVolume(p1: Vector3, p2: Vector3, radius: float, dir: Vector3, distance: float){
if (!shape){ // if shape doesn't exist yet, create it
shape = GameObject.CreatePrimitive(PrimitiveType.Cube).transform;
Destroy(shape.collider); // no collider, please!
shape.renderer.material = mat; // assign the selected material to it
}
var scale: Vector3; // calculate desired scale
var diam: float = 2 * radius; // calculate capsule diameter
scale.x = diam; // width = capsule diameter
scale.y = Vector3.Distance(p2, p1) + diam; // capsule height
scale.z = distance + diam; // volume length
shape.localScale = scale; // set the rectangular volume size
// set volume position and rotation
shape.position = (p1 + p2 + dir.normalized * distance) / 2;
shape.rotation = Quaternion.LookRotation(dir, p2 - p1);
shape.renderer.enabled = true; // show it
}
function HideVolume(){ // hide the volume
if (shape) shape.renderer.enabled = false;
}
// Example:
private var freeDistance : float = 0;
function Update () {
if (Input.GetKey("p")){ // while P pressed...
var hit : RaycastHit;
var charContr : CharacterController = GetComponent(CharacterController);
var radius = charContr.radius;
// find centers of the top/bottom hemispheres
var p1 : Vector3 = transform.position + charContr.center;
var p2 = p1;
var h = charContr.height/2 - radius;
p2.y += h;
p1.y -= h;
// draw CapsuleCast volume:
RenderVolume(p1, p2, radius, transform.forward, range);
// cast character controller shape range meters forward:
if (Physics.CapsuleCast(p1, p2, radius, transform.forward, hit, range)){
// if some obstacle inside range, save its distance
freeDistance = hit.distance;
} else {
// otherwise shows that the way is clear up to range distance
freeDistance = range;
}
}
if (Input.GetKeyUp("p")){
HideVolume(); // hide volume when P is released
}
}
function OnGUI(){ // shows the free distance ahead:
GUI.Label(Rect(10, 60, 150, 40), "Free distance = " + freeDistance .ToString("F2"));
}
Thanks for pointing out the p1, p2 issue, this was definitely one of the main sources of confusion. I think a similar one is the way in which "scale" is handled on a regular capsule gameobject, which is what I actually ended up using to try to visualize my capsulecast. For example creating a capsule with a scale of 1 I assumed meant it would have a height of 1 meter but it doesn't. This really threw of my visualizer when I tried to match the capsulecast's size with my capsule gameobject, so it's a good thing to know. Hopefully the documentation will get updated to include this information.
In hindsight I think it would be VERY useful to have a built in way to visualize both capsulecasts and spherecasts for the same reason that visualizers for raycasts exist. One for the Unity suggestion box ;)
Yes, capsules and cylinder primitives are 2 meters tall at unitary scale. I preferred a rectangular shape because a capsule with non-uniform scale looks really bad. But a visualizer for capsule and sphere casts would be great, for sure - it took a long time to notice that p1,p2 issue!
Thank you so much, here is C# version, public $$anonymous$$aterial mat;
void RenderVolume(Vector3 p1 , Vector3 p2 , float radius , Vector3 dir, float distance )
{
if (!shape)
{ // if shape doesn't exist yet, create it
shape = GameObject.CreatePrimitive(PrimitiveType.Cube).transform;
Destroy(shape.GetComponent<Collider>()); // no collider, please!
shape.GetComponent<Renderer>().material = mat; // assign the selected material to it
}
Vector3 scale ; // calculate desired scale
float diam = 2 * radius; // calculate capsule diameter
scale.x = diam; // width = capsule diameter
scale.y = Vector3.Distance(p2, p1) + diam; // capsule height
scale.z = distance + diam; // volume length
shape.localScale = scale; // set the rectangular volume size
// set volume position and rotation
shape.position = (p1 + p2 + dir.normalized * distance) / 2;
shape.rotation = Quaternion.LookRotation(dir, p2 - p1);
shape.GetComponent<Renderer>().enabled = true; // show it
}
void HideVolume()
{ // hide the volume
if (shape) shape.GetComponent<Renderer>().enabled = false;
}
private Transform shape ;
public float range = 10; // range of the capsule cast
private float freeDistance = 0;
void Update()
{
if (Input.Get$$anonymous$$ey("p"))
{ // while P pressed...
RaycastHit hit;
CharacterController charContr = GetComponent<CharacterController>();
var radius = charContr.radius;
// find centers of the top/bottom hemispheres
Vector3 p1 = transform.position + charContr.center;
var p2 = p1;
var h = charContr.height / 2 - radius;
p2.y += h;
p1.y -= h;
// draw CapsuleCast volume:
RenderVolume(p1, p2, radius, transform.forward, range);
// cast character controller shape range meters forward:
if (Physics.CapsuleCast(p1, p2, radius, transform.forward,out hit, range))
{
// if some obstacle inside range, save its distance
freeDistance = hit.distance;
}
else {
// otherwise shows that the way is clear up to range distance
freeDistance = range;
}
}
if (Input.Get$$anonymous$$eyUp("p"))
{
HideVolume(); // hide volume when P is released
}
}
Your answer
Follow this Question
Related Questions
CapsuleCast not registering 0 Answers
Filing and debuging web plugin crashes 1 Answer
"It is not possible to invoke an expression of type 'boolean'" in switching cameras 1 Answer
Turn off blue mesh lines 2 Answers
Debug Wrapper Class 6 Answers