script / camera help!

Hey im new to unity so please take that into account! i have got to grips with unity but now have started looking towards the code side. Im doing a project for university at the moment. We have a character running about with a camera above his head but when turning the camera tends to hit building and objects i.e inside the building blocking the view of the char. How would i go about putting a collision on the camera so it wouldnt end up inside building/ behind object when turning. Also im trying to have the camera go from above head view to a first person on the entering of a building. I set up 2 boxes and a firt person controller the idea being when the char hits the second box the “char” camera turns off and the first person controller comes on and another box on exit which would switch back to the “char” cam! i understand this is confusing but any help would be much appreciated!

can some one tell me the code for the possibility of putting a camera inside the shop and on the entering of the shop the camera switches to that view leaving the player to roam around inside?

Hi

This “get the camera inside the wall” issue has come up a couple of times, and although I’ve tried a few peoples suggestions, it still remains a mystery. What you can do is go to the examples, and find the third person goober demo, and check out how this script is used.

/* Fades out any objects between the player and this transform.
   The renderers shader is first changed to be an Alpha/Diffuse, then alpha is faded out to fadedOutAlpha.
   Only objects 
   
   In order to catch all occluders, 5 rays are casted. occlusionRadius is the distance between them.
*/
var layerMask : LayerMask = 2;
var target : Transform;
var fadeSpeed = 1.0;
var occlusionRadius = .3;
var fadedOutAlpha = 0.3;

private var fadedOutObjects = Array ();

class FadeoutLOSInfo
{
	var renderer : Renderer;
	var originalMaterials : Material[];
	var alphaMaterials : Material[];
	var needFadeOut = true;
}

function FindLosInfo (r : Renderer) : FadeoutLOSInfo
{
	for (var fade : FadeoutLOSInfo in fadedOutObjects)
	{
		if (r == fade.renderer)
			return fade;
	}
	return null;
}

function LateUpdate () {
	var from = transform.position;
	var to = target.position;
	var castDistance = Vector3.Distance(to, from);
	
	// Mark all objects as not needing fade out
	for (var fade : FadeoutLOSInfo in fadedOutObjects)
	{
		fade.needFadeOut = false;
	}
	
	var offsets = [Vector3(0, 0, 0), Vector3(0, occlusionRadius, 0), Vector3(0, -occlusionRadius, 0), Vector3(occlusionRadius, 0, 0), Vector3(-occlusionRadius, 0, 0)];
	
	// We cast 5 rays to really make sure even occluders that are partly occluding the player are faded out
	for (var offset in offsets)
	{
		var relativeOffset = transform.TransformDirection(offset);
		// Find all blocking objects which we want to hide
		var hits : RaycastHit[] = Physics.RaycastAll(from + relativeOffset, to - from, castDistance, layerMask.value);
		for (var hit : RaycastHit in hits)
		{
			// Make sure we have a renderer
			var hitRenderer : Renderer = hit.collider.renderer;		
			if (hitRenderer == null || !hitRenderer.enabled)
				continue;
			
			var info = FindLosInfo(hitRenderer);
	
			// We are not fading this renderer already, so insert into faded objects map
			if (info == null)
			{
				info = new FadeoutLOSInfo ();
				info.originalMaterials = hitRenderer.sharedMaterials;
				info.alphaMaterials = new Material[info.originalMaterials.length];
				info.renderer = hitRenderer;
				for (var i=0;i<info.originalMaterials.length;i++)
				{
					var newMaterial = new Material (Shader.Find("Alpha/Diffuse"));
					newMaterial.mainTexture = info.originalMaterials[i].mainTexture;	
					newMaterial.color = info.originalMaterials[i].color;
					newMaterial.color.a = 1.0;
					info.alphaMaterials[i] = newMaterial;
				}
				
				hitRenderer.sharedMaterials = info.alphaMaterials;
				fadedOutObjects.Add(info);
			}
			// Just mark the renderer as needing fade out
			else
			{
				info.needFadeOut = true;
			}
		}
	}
		
	// Now go over all renderers and do the actual fading!
	var fadeDelta = fadeSpeed * Time.deltaTime;
	for (i=0;i<fadedOutObjects.Count;i++)
	{
		var fade = fadedOutObjects[i];
		// Fade out up to minimum alpha value
		if (fade.needFadeOut)
		{
			for (var alphaMaterial : Material in fade.alphaMaterials)
			{
				var alpha = alphaMaterial.color.a;
				alpha -= fadeDelta;
				alpha = Mathf.Max(alpha, fadedOutAlpha);
				alphaMaterial.color.a = alpha;
			}
		}
		// Fade back in
		else
		{
			var totallyFadedIn = 0;
			for (var alphaMaterial : Material in fade.alphaMaterials)
			{
				alpha = alphaMaterial.color.a;
				alpha += fadeDelta;
				alpha = Mathf.Min(alpha, 1.0);
				alphaMaterial.color.a = alpha;
				if (alpha >= 0.99)
					totallyFadedIn++;
			}
			
			// All alpha materials are faded back to 100%
			// Thus we can switch back to the original materials
			if (totallyFadedIn == fade.alphaMaterials.length)
			{
				if (fade.renderer)
					fade.renderer.sharedMaterials = fade.originalMaterials;
					
				for (var newMaterial in fade.alphaMaterials)
					Destroy(newMaterial);
				
				fadedOutObjects.RemoveAt(i);
				i--;
			}
		}
	}
}

@script AddComponentMenu ("Third Person Camera/Fadeout Line of Sight")

It would be a good idea if using this method to make each wall a seperate object.
hopefully thats some help.
AC

thnanks very much for your help! been playing round all morning and still having issues, the camera just goes straight through the walls of building etc when turning rather anoying!!

Kleren, it sounds to me like you have a floating 3rd-person camera following the player object and want it to zoom in (i.e. move closer) when it would otherwise pass through some object behind the player such as a wall or a tree, etc.

If that is a valid interpretation of the problem, it seems to me that you could simply do a RayCast backwards from the Player object towards the camera and find the first collision point. Then all you do is move the camera to slightly less than the distance to that collision point.

For instance you could use Physics.LineCast() from the Player towards the camera (remember that the direction of the line matters!) which returns a RaycastHit and RaycastHit.distance is how far away that was from the Player.

I can’t actually throw any code together for you at the moment but this is how I’d start looking into it.

Cheers!

Hoji.

Hello all :slight_smile: I’m very new to programming but I’m managing to get closer to some camera collision. As you stated Hojiman, it would be ideal to move the camera in when the raytrace has hit an object, and I’ve done that in the code below, but my problem is moving the camera back out to its initial position when you are far enough away from the object you initially hit.

Any Ideas? Also I keep trying to initialize hit.transform, but I seem to be doing it wrong because every time the raytrace hits an object with the following code, I get an error message in Unity that says: “Object reference not set to an instance of an object” Can you also tell me how to get the “transform” correctly of the “hit” object?

Thanks

function checkCollision()
{	
	var hit : RaycastHit; 
	// beginning of ray detecting for collision of camera object
	if(Physics.Linecast(target.position,transform.position))
	{
		tempDistance = Vector3.Distance(target.position,hit.point);
		hitDistance = tempDistance / 3;
		if(distance > hitDistance)
		{
			distance = Mathf.Max(hitDistance,distance-3*Time.deltaTime);
		}
// Is this next line correct?? 
		var tempObject : Transform = hit.transform;
		print(tempObject.transform.position);
	}
	else
	{
		
		// move away if far enough away from the transform you have hit
		
//		if(hit.transform)
//		{
//			var awayDistance = Vector3.Distance(hit.transform.position,target.position);
//		    print(awayDistance);
//		    if(Vector3.Distance(hit.transform.position,target.position) > initCameraDistance)
//		    {				
//			    distance = Mathf.Min(tempDistance,distance+3*Time.deltaTime);
//		    }
//		}
	}
}

[/code]

One nice/simple way to do this, is to simply set the current distance from the player to the camera each frame.

The next frame, cast your ray back from the camera anchor position, and set the distance to either a new collision point (if you hit something), or some ratio of the distance from your vector length last frame to the desired distance this frame… whichever is the shorter (this is known as a feedback loop).

(i.e., if you set your ratio to 0.5 for example, and last frame you hit collision but this frame your test ray extends back past your desired distance, you might get something like this:

Last frame, collision happened at 5m.
This frame ray travels past desired distance of 10m
Set new distance along ray to half the difference between current and last frame (7.5m)

Next frame, you would get half the distance again (8.75m), and so on.

I personally like feedback loops better than hermite style blends, because they are fast to react to large changes, but still blend nicely into the desired position… they also never overshoot, but are always critically damped to exactly the value you want over time.

good luck.

If you read through the AIGPW books, one thing you will see is that this camera challenge isn’t trivial – having a camera that centers the content that needs to be centered and avoids obstacles while behaving in a predictable and stable way isn’t trivial.

Unfortunately, as Targos mentioned, it does come up a lot – which means there is a common need to address this non-trivial problem in Unity (which also probably means that implementing a “flexible” solution would be useful to a variety of people.)

I was just starting to suggest that a group of people put together something on the Wiki or a tutorial or some kind of content… when I realized that it might be an appropriate article for Unity Developer Magazine. Since this does seem to fall into the “frequent Unity challenges” category that I’m trying to make sure each issue addresses, it might be a good thing to do.

Thanks for the replies, I’m still struggling abit to get this worked out. Tz, I’m not quite sure if you are talking about a raycast from the target to the camera transform, or a raycast from the camera transform to some desired distance back.

I tried the latter method, I’m still not getting the mathmatics right:

var rev = transform.TransformPoint(-Vector3.forward); // set a direction to trace behind camera

This works good, for setting a distance behind the camera, then I use a RayCast to cast a line a certain distance behind the camera:

var traceLength = 10.0;
if(Physics.Raycast(transform.position,rev,traceLength)
{
  // "distance" controls the actual distance between the camera transform and the player
// reduce distance because we are colliding
  distance = Mathf.Max(0,distance-1.5*Time.deltaTime));
}
else
{
  // this is where I'm not sure what to do
}

Tz, I’m probably not getting your concept at all, I probably need to keep reading, but hopefully you can elaborate a little more in detail on your idea.

I’ll keep trying things on my end. I like CharlesHinshaw’s idea too, once we get this nailed, I’m all for adding it to the wiki, for everyone to use.

Thanks

I’ve been involved in plenty of 3rd person camera code for major console releases, and what we’ve discovered over time, is that with Camera systems, it’s really, really easy to outsmart yourself and overthink the problem.

In the worst case, we made a camera that would smoothly transition through tight spaces, and adjust itself to the ideal position for almost any case, and the players ended up hating it, because in the end, you’ll never be 100% in sync with what the player wants/expects from their camera system, hence we find that simpler is better.

The camera system I described is used in many shipped games, and is a very nice balance of simplicity and consistency.

  1. The camera target sits directly on the player, and doesn’t try to get fancy by leading him, or drifting away. Keeping the target perfectly aligned to the “rod” connecting the pivot point to the camera means you never have to solve the dual problem of being in a reasonable collision situation relative to the connecting rod, but out of view of your target.

  2. The camera should be as smooth as is possible, while meeting the player’s immediate needs. This means “popping” the camera in when the player puts it in the path of collision, but allowing a smooth drift back from that distance when the opportunity presents itself.

So the proposition is this (World of Warcraft’s default camera works like this I believe)

  1. Set a target at the base position you will pivot your camera around (the target is at the base of the swing arm of your eye).

  2. Cast a ray some desired length along the view axis… it there is no collision, then place the eye there.

  3. If there IS a collision, immediately snap the eye closer to the target such that it is just inside the collision point. View snaps inwards towards the eye are not overly disorienting, and are the biggest red herring that developers try to solve that gets them into trouble with overly complex camera solutions.

  4. On each frame, continue to project a ray from the target point towards the desired eye point. The eye point will be placed on this frame the closest (to the target) of the following:
    a. The first collision point detected.
    b. Half the length along the ray between the desired length and the length calculated last frame.
    c. The desired length.

That’s it. It may seem overly simplistic, but it solves a lot of issues, and it’s shortcomings (primarily poor scene composition due to a large portion of your view obstructed by objects in the foreground) are easily and consistently addressed by the player correcting the camera himself.

Thanks once again Tz, I appreciate you following up with me on this and all your input. I agree with you on how things like this can easily become over-complicated, I tend to do that alot with programming, but I’m learning.

I still was not able to implement your theory, but I believe I get the way your approaching it. The big thing for me is still learning programming, mainly object programming and how to gather information to use.

Edit: Now that I understand properly how to get the “hitInfo” back from the raytrace, I should be able to do the math on the distance returned from the trace when it collides and reduce the camera distance to the target based on that, which I believe is what you are talking about.

I may spend some more time on re-writing the code, but I did manage to put this idea together and make it work.

  1. Make an empty game object, align it with your camera and then move it back a small distance, then add it as a child of your camera so it follows it around.

  2. Add this code/ function to your SmoothFollow.js and call checkCollision() from your LateUpdate()

// expose a movement speed for the camera when collision occurs
var collisionForwardSpeed = 3.0;
// define a GameObject to use
private var camCollider : GameObject;

// Collision Checker 
function checkCollision()
{	
	var hit : RaycastHit; 
	// beginning of ray detecting for collision of camera object
	if(Physics.Linecast(target.position,transform.position,hit))
	{
		Debug.DrawLine(target.position,transform.position,Color.red);
		var hitDistance = Vector3.Distance(target.position,transform.position);
		var hitTarget = hitDistance / 3; // ratio
		print(hit.point);
		if(hitDistance > hitTarget)
		{
			distance = Mathf.Max(0,distance-collisionForwardSpeed*Time.deltaTime);
		}
	}
	else
	{
		if(!Physics.Linecast(transform.position,camCollider.transform.position))
		{
			// move away if far enough away from the transform you have hit
		    Debug.DrawLine(transform.position,camCollider.transform.position,Color.green);
			distance = Mathf.Min(tempDistance,distance+ collisionForwardSpeed*Time.deltaTime);
		}
		else 
		{
			Debug.DrawLine(transform.position,camCollider.transform.position,Color.red);
		}
	}
}

You can remove the Debug.DrawLine/print stuff, I just added it to see what was happening. Please keep in mind this code is used in conjunction with the SmoothFollow.js from the standard assets package.

Thanks once again Tz, you have been very helpful and have helped me understand even more about programming.