예제 #1
0
    // Based on the camera attributes and the target's special camera attributes, find out where the
    // camera should move to.
    public Vector3 GetGoalPosition()
    {
        // If there is no target, don't move the camera.  So return the camera's current position as the goal position.
        if (!target)
        {
            return(transform.position);
        }

        // Our camera script can take attributes from the target.  If there are no attributes attached, we have
        // the following defaults.

        // How high in world space should the camera look above the target?
        float heightOffset = 0.0f;
        // How much should we zoom the camera based on this target?
        float distanceModifier = 1.0f;
        // By default, we won't account for any target velocity in our calculations;
        float   velocityLookAhead = 0.0f;
        Vector2 maxLookAhead      = new Vector2(0.0f, 0.0f);

        // Look for CameraTargetAttributes in our target.
        CameraTargetAttributes cameraTargetAttributes = (CameraTargetAttributes)target.GetComponent("CameraTargetAttributes");

        // If our target has special attributes, use these instead of our above defaults.
        if (cameraTargetAttributes)
        {
            heightOffset      = cameraTargetAttributes.heightOffset;
            distanceModifier  = cameraTargetAttributes.distanceModifier;
            velocityLookAhead = cameraTargetAttributes.velocityLookAhead;
            maxLookAhead      = cameraTargetAttributes.maxLookAhead;
        }

        // First do a rough goalPosition that simply follows the target at a certain relative height and distance.
        Vector3 goalPosition = target.position + new Vector3(0, heightOffset, -distance * distanceModifier);

        // Next, we refine our goalPosition by taking into account our target's current velocity.
        // This will make the camera slightly look ahead to wherever the character is going.

        // First assume there is no velocity.
        // This is so if the camera's target is not a Rigidbody, it won't do any look-ahead calculations because everything will be zero.
        Vector3 targetVelocity = Vector3.zero;

        // If we find a Rigidbody on the target, that means we can access a velocity!
        targetRigidbody = (Rigidbody)target.GetComponent("Rigidbody");
        if (targetRigidbody)
        {
            targetVelocity = targetRigidbody.velocity;
        }

        // If we find a PlatformerController on the target, we can access a velocity from that!
        PlatformerController targetPlatformerController = (PlatformerController)(target.GetComponent("PlatformerController"));

        if (targetPlatformerController)
        {
            targetVelocity = targetPlatformerController.GetVelocity();
        }

        // If you've had a physics class, you may recall an equation similar to: position = velocity * time;
        // Here we estimate what the target's position will be in velocityLookAhead seconds.
        Vector3 lookAhead = targetVelocity * velocityLookAhead;

        // We clamp the lookAhead vector to some sane values so that the target doesn't go offscreen.
        // This calculation could be more advanced (lengthy), taking into account the target's viewport position,
        // but this works pretty well in practice.
        lookAhead.x = Mathf.Clamp(lookAhead.x, -maxLookAhead.x, maxLookAhead.x);
        lookAhead.y = Mathf.Clamp(lookAhead.y, -maxLookAhead.y, maxLookAhead.y);
        // We never want to take z velocity into account as this is 2D.  Just make sure it's zero.
        lookAhead.z = 0.0f;

        // Now add in our lookAhead calculation.  Our camera following is now a bit better!
        goalPosition += lookAhead;

        // To put the icing on the cake, we will make so the positions beyond the level boundaries
        // are never seen.  This gives your level a great contained feeling, with a definite beginning
        // and ending.

        Vector3 clampOffset = Vector3.zero;

        // Temporarily set the camera to the goal position so we can test positions for clamping.
        // But first, save the previous position.
        Vector3 cameraPositionSave = transform.position;

        transform.position = goalPosition;

        // Get the target position in viewport space.  Viewport space is relative to the camera.
        // The bottom left is (0,0) and the upper right is (1,1)
        // @TODO Viewport space changing in Unity 2.0?
        Vector3 targetViewportPosition = GetComponent <Camera>().WorldToViewportPoint(target.position);

        // First clamp to the right and top.  After this we will clamp to the bottom and left, so it will override this
        // clamping if it needs to.  This only occurs if your level is really small so that the camera sees more than
        // the entire level at once.

        // What is the world position of the very upper right corner of the camera?
        Vector3 upperRightCameraInWorld = GetComponent <Camera>().ViewportToWorldPoint(new Vector3(1.0f, 1.0f, targetViewportPosition.z));

        // Find out how far outside the world the camera is right now.
        clampOffset.x = Mathf.Min(levelBounds.xMax - upperRightCameraInWorld.x, 0.0f);
        clampOffset.y = Mathf.Min((levelBounds.yMax - upperRightCameraInWorld.y), 0.0f);

        // Now we apply our clamping to our goalPosition.  Now our camera won't go past the right and top boundaries of the level!
        goalPosition += clampOffset;

        // Now we do basically the same thing, except clamp to the lower left of the level.  This will override any previous clamping
        // if the level is really small.  That way you'll for sure never see past the lower-left of the level, but if the camera is
        // zoomed out too far for the level size, you will see past the right or top of the level.

        transform.position = goalPosition;
        Vector3 lowerLeftCameraInWorld = GetComponent <Camera>().ViewportToWorldPoint(new Vector3(0.0f, 0.0f, targetViewportPosition.z));

        // Find out how far outside the world the camera is right now.
        clampOffset.x = Mathf.Max((levelBounds.xMin - lowerLeftCameraInWorld.x), 0.0f);
        clampOffset.y = Mathf.Max((levelBounds.yMin - lowerLeftCameraInWorld.y), 0.0f);

        // Now we apply our clamping to our goalPosition once again.  Now our camera won't go past the left and bottom boundaries of the level!
        goalPosition += clampOffset;

        // Now that we're done calling functions on the camera, we can set the position back to the saved position;
        transform.position = cameraPositionSave;

        // Send back our spiffily calculated goalPosition back to the caller!
        return(goalPosition);
    }