Super Mario World camera in Unity (part 2)

In the previous post, I showed an example of a camera I made for my Arkanoid-style game. In this post, I’ll show you how to make it.

To follow along, you’ll need the Playmaker asset from the asset store. It’s not free, but it makes multiple state transitions a whole lot easier. This is the setup I used for the camera object:

  • SmartCamera (contains FSM which I’ll show later)
    • Camera (contains actual camera – starts at the lowest position of the camera)
    • FollowBallTrigger (contains box trigger and FollowBallTrigger script)
    • GhostCamera (another camera which only shows the “ghost” paddle – disabled by default)
    • TopCameraPosition (empty game object moved to the top position you want your camera to be in)

Here’s how the FSM looks:

Smart Camera FSM

Here’s what the states do:

  • Init: state just gets the initial camera Y Position and saves it
  • SnapToBottom: The only thing this does is switch the GhostCamera off, so that when we’re snapped to the bottom, we don’t see it
  • SnapToTop: Makes sure the ghost camera is still on
  • FollowBall: this is triggered by a script on the “FollowBallTrigger” object: Here’s the trigger script:
void OnTriggerEnter(Collider other)
 {
     if (other.tag == "Ball")
     {
         MainCameraFsm.SendEvent("FollowBall");
     }
 }

Most of the logic happens in the “CameraFollowBall” custom playmaker action. Here’s what that script looks like:

 

public class CameraFollowBall : FsmStateAction
{
    public float Damping = .3f;
    public FsmGameObject TopCameraPosition;
    public FsmGameObject CameraObject;
    public FsmGameObject GhostCameraObject;

    public FsmFloat BottomYPosition;

    private Transform _ballTransform;
    private float _currentVelocity;

    public override void Reset()
    {
        BottomYPosition = 0;
        TopCameraPosition = null;
        CameraObject = null;
        base.Reset();
    }

	// Code that runs on entering the state.
	public override void OnEnter()
	{
        _ballTransform = GameObject.FindGameObjectWithTag("Ball")
            .transform;

        base.OnEnter();
	}

    public override void OnExit()
    {
        _currentVelocity = 0;
        base.OnExit();
    }

    public override void OnLateUpdate()
    {
        if (_ballTransform != null)
        {
            //make the camera follow the ball on the y axis
            var cameraTransform = CameraObject.Value.transform;

            var ballYPos = _ballTransform.position.y;
            var cameraYPos = cameraTransform.position.y;

            var targetYPos = Mathf.SmoothDamp(cameraYPos, ballYPos, ref _currentVelocity, Damping);

            var cameraPos = cameraTransform.position;

            cameraPos.y = targetYPos;
            cameraTransform.position = cameraPos;

            //snap to bottom/top if we get close
            if (targetYPos > TopCameraPosition.Value.transform.position.y)
            {
                cameraPos.y = TopCameraPosition.Value.transform.position.y;
                cameraTransform.position = cameraPos;
                this.Fsm.Event("SnapToTop");
            }

            if (targetYPos < BottomYPosition.Value)
            {
                cameraPos.y = BottomYPosition.Value;
                cameraTransform.position = cameraPos;
                this.Fsm.Event("SnapToBottom");
            }
        }

    }

}

That’s basically it – please let me know if the comments if you have any questions, or if you’ve done it a different way!

Share and Enjoy:
  • Reddit
  • del.icio.us
  • Google Bookmarks
  • Twitter
  • StumbleUpon
  • Facebook
  • Yahoo! Buzz
  • Digg

Leave a Reply

Your email address will not be published. Required fields are marked *