Tuesday, April 7, 2015

Unity 2D Effectors Tutorial Overview


This is a quick overview of all the 2D Physics Effectors we have access to in Unity: Area Effector 2D, Platform Effector 2D, Point Effector 2D, and Surface Effector 2D.

Twitter: https://twitter.com/Devination3D
Google+ Community: https://plus.google.com/communities/108584850180626452949
Facebook: https://www.facebook.com/unity3Dtutorialsbydevin

Time Breakdown:
0:45 - Platform Effector 2D and Used By Effector checkbox
2:04 - Side bounce and PhysicsMaterials
3:34 - Surface Effector 2D (conveyor belt)
4:41 - Point Effector 2D (Mario Galaxy planets)
7:45 - Area Effector 2D (Water bouncy or wind force)
9:25 - Like and subscribe for more tutorials, thanks for watching :D

Friday, March 6, 2015

Unity Touch Joysticks Canvas UI Tutorial - Touch Input FPS Controller

In this Unity C# tutorial we will learn how to make touch joysticks using the 4.6 Canvas UI system. We will create 2 joystick to drive a FPS controller similar to mobile games like Dead Trigger or ShadowGun.

Check out the code below the videos

Time Breakdown:
0:48 - Download the Sample Assets package
1:35 - Importing only what we need
2:52 - Setting up our scene with the prefabs and settings we'll need
4:43 - Demonstration of touch joystick as-is before we edit it
5:47 - Fixing the joystick so it doesn't snap to the bottom right corner
6:55 - Quick tip: Adding custom Task tokens for comments
8:02 - Clamping the joysticks in a circle rather than a square
10:00 - Using the joystick as a slider instead
10:43 - Explanation of how the joystick GetAxis and how you can use it for other purpose, not just a FPS controller
13:13 - Adding the Look joystick in Unity
14:33 - Difference between GetAxis and GetAxisRaw
15:25 - Editing RotateView to work with our touch joystick
18:57 - Checking and limiting camera angles
23:42 - Drawn explaination of camera angles and when we need to limit them
28:50 - Demonstration of the look joystick working in Unity
30:47 - (Optional) Dampening the vertical look speed slightly
31:43 - Demonstration of everything working on Android
32:50 - Outro, thanks for watching :D



Support Further Tutorial Development
Comments?
Check out the video above to hear the explanation and see the code in action
/*
 * This script is from the 4.6 Sample Assets with edits from Devin Curry
 * Search for changes tagged with the //DCURRY comment
 * Watch the tutorial here: www.Devination.com
 */
using UnityEngine;
using UnityEngine.EventSystems;
using UnitySampleAssets.CrossPlatformInput;

public class Joystick : MonoBehaviour , IPointerUpHandler , IPointerDownHandler , IDragHandler {

    public int MovementRange = 100;

    public enum AxisOption
    {                                                    // Options for which axes to use                                                     
        Both,                                                                   // Use both
        OnlyHorizontal,                                                         // Only horizontal
        OnlyVertical                                                            // Only vertical
    }

    public AxisOption axesToUse = AxisOption.Both;   // The options for the axes that the still will use
    public string horizontalAxisName = "Horizontal";// The name given to the horizontal axis for the cross platform input
    public string verticalAxisName = "Vertical";    // The name given to the vertical axis for the cross platform input 

    private Vector3 startPos;
    private bool useX;                                                          // Toggle for using the x axis
    private bool useY;                                                          // Toggle for using the Y axis
    private CrossPlatformInputManager.VirtualAxis horizontalVirtualAxis;               // Reference to the joystick in the cross platform input
    private CrossPlatformInputManager.VirtualAxis verticalVirtualAxis;                 // Reference to the joystick in the cross platform input
      
 void Start () {//DCURRY changed this to Start from OnEnable

        startPos = transform.position;
        CreateVirtualAxes ();
    }

    private void UpdateVirtualAxes (Vector3 value) {

  var delta = startPos - value;
        delta.y = -delta.y;
        delta /= MovementRange;
        if(useX)
        horizontalVirtualAxis.Update (-delta.x);

        if(useY)
        verticalVirtualAxis.Update (delta.y);

    }

    private void CreateVirtualAxes()
    {
        // set axes to use
        useX = (axesToUse == AxisOption.Both || axesToUse == AxisOption.OnlyHorizontal);
        useY = (axesToUse == AxisOption.Both || axesToUse == AxisOption.OnlyVertical);

        // create new axes based on axes to use
        if (useX)
            horizontalVirtualAxis = new CrossPlatformInputManager.VirtualAxis(horizontalAxisName);
        if (useY)
            verticalVirtualAxis = new CrossPlatformInputManager.VirtualAxis(verticalAxisName);
    }


    public  void OnDrag(PointerEventData data) {

        Vector3 newPos = Vector3.zero;

        if (useX) {
            int delta = (int) (data.position.x - startPos.x);//DCURRY deleted clamp
            newPos.x = delta;
        }

        if (useY)
        {
   int delta = (int)(data.position.y - startPos.y);//DCURRY deleted clamp
            newPos.y = delta;
        }
  //DCURRY added ClampMagnitude
  transform.position = Vector3.ClampMagnitude( new Vector3(newPos.x , newPos.y , newPos.z), MovementRange) + startPos;
        UpdateVirtualAxes (transform.position);
    }


    public  void OnPointerUp(PointerEventData data)
    {
        transform.position = startPos;
        UpdateVirtualAxes (startPos);
    }


    public  void OnPointerDown (PointerEventData data) {
    }

    void OnDisable () {
        // remove the joysticks from the cross platform input
        if (useX)
        {
            horizontalVirtualAxis.Remove();
        }
        if (useY)
        {
            verticalVirtualAxis.Remove();
        }
    }
}


And this is the modified FirstPersonController script
/*
 * This script is from the 4.6 Sample Assets with edits from Devin Curry
 * Search for changes tagged with the //DCURRY comment
 * Watch the tutorial here: www.Devination.com
 */
using UnityEngine;
using UnitySampleAssets.CrossPlatformInput;
using UnitySampleAssets.Utility;

namespace UnitySampleAssets.Characters.FirstPerson
{
    [RequireComponent(typeof (CharacterController))]
    [RequireComponent(typeof (AudioSource))]
    public class FirstPersonController : MonoBehaviour
    {

        //////////////////////// exposed privates ///////////////////////
        [SerializeField] private bool _isWalking;
  [SerializeField] private float walkSpeed;
  [SerializeField] private float lookSpeed = 4;//DCURRY add
        [SerializeField] private float runSpeed;
        [SerializeField] [Range(0f, 1f)] private float runstepLenghten;
        [SerializeField] private float jumpSpeed;
        [SerializeField] private float stickToGroundForce;
        [SerializeField] private float _gravityMultiplier;
        [SerializeField] private MouseLook _mouseLook;
        [SerializeField] private bool useFOVKick;
        [SerializeField] private FOVKick _fovKick = new FOVKick();
        [SerializeField] private bool useHeadBob;
        [SerializeField] private CurveControlledBob _headBob = new CurveControlledBob();
        [SerializeField] private LerpControlledBob _jumpBob = new LerpControlledBob();
        [SerializeField] private float _stepInterval;

        [SerializeField] private AudioClip[] _footstepSounds;
                                             // an array of footstep sounds that will be randomly selected from.

        [SerializeField] private AudioClip _jumpSound; // the sound played when character leaves the ground.
        [SerializeField] private AudioClip _landSound; // the sound played when character touches back on ground.

        ///////////////// non exposed privates /////////////////////////
        private Camera _camera;
        private bool _jump;
        private float _yRotation;
        private CameraRefocus _cameraRefocus;
        private Vector2 _input;
        private Vector3 _moveDir = Vector3.zero;
        private CharacterController _characterController;
        private CollisionFlags _collisionFlags;
        private bool _previouslyGrounded;
        private Vector3 _originalCameraPosition;
        private float _stepCycle = 0f;
        private float _nextStep = 0f;
        private bool _jumping = false;

        // Use this for initialization
        private void Start()
        {
            _characterController = GetComponent<CharacterController>();
            _camera = Camera.main;
            _originalCameraPosition = _camera.transform.localPosition;
            _cameraRefocus = new CameraRefocus(_camera, transform, _camera.transform.localPosition);
            _fovKick.Setup(_camera);
            _headBob.Setup(_camera, _stepInterval);
            _stepCycle = 0f;
            _nextStep = _stepCycle/2f;
            _jumping = false;
        }

        // Update is called once per frame
        private void Update()
        {
            RotateView();
            // the jump state needs to read here to make sure it is not missed
            if (!_jump)
                _jump = CrossPlatformInputManager.GetButtonDown("Jump");

            if (!_previouslyGrounded && _characterController.isGrounded)
            {
                StartCoroutine(_jumpBob.DoBobCycle());
                PlayLandingSound();
                _moveDir.y = 0f;
                _jumping = false;
            }
            if (!_characterController.isGrounded && !_jumping && _previouslyGrounded)
            {
                _moveDir.y = 0f;
            }

            _previouslyGrounded = _characterController.isGrounded;
        }

        private void PlayLandingSound()
        {
            audio.clip = _landSound;
            audio.Play();
            _nextStep = _stepCycle + .5f;
        }

        private void FixedUpdate()
        {
            float speed;
            GetInput(out speed);
            // always move along the camera forward as it is the direction that it being aimed at
            Vector3 desiredMove = _camera.transform.forward*_input.y + _camera.transform.right*_input.x;

            // get a normal for the surface that is being touched to move along it
            RaycastHit hitInfo;
            Physics.SphereCast(transform.position, _characterController.radius, Vector3.down, out hitInfo,
                               _characterController.height/2f);
            desiredMove = Vector3.ProjectOnPlane(desiredMove, hitInfo.normal).normalized;

            _moveDir.x = desiredMove.x*speed;
            _moveDir.z = desiredMove.z*speed;


            if (_characterController.isGrounded)
            {
                _moveDir.y = -stickToGroundForce;

                if (_jump)
                {
                    _moveDir.y = jumpSpeed;
                    PlayJumpSound();
                    _jump = false;
                    _jumping = true;
                }
            }
            else
            {
                _moveDir += Physics.gravity*_gravityMultiplier;
            }

            _collisionFlags = _characterController.Move(_moveDir*Time.fixedDeltaTime);

            ProgressStepCycle(speed);
            UpdateCameraPosition(speed);
        }

        private void PlayJumpSound()
        {
            audio.clip = _jumpSound;
            audio.Play();
        }

        private void ProgressStepCycle(float speed)
        {
            if (_characterController.velocity.sqrMagnitude > 0 && (_input.x != 0 || _input.y != 0))
                _stepCycle += (_characterController.velocity.magnitude + (speed*(_isWalking ? 1f : runstepLenghten)))*
                              Time.fixedDeltaTime;

            if (!(_stepCycle > _nextStep)) return;

            _nextStep = _stepCycle + _stepInterval;

            PlayFootStepAudio();
        }

        private void PlayFootStepAudio()
        {
            if (!_characterController.isGrounded) return;
            // pick & play a random footstep sound from the array,
            // excluding sound at index 0
            int n = Random.Range(1, _footstepSounds.Length);
            audio.clip = _footstepSounds[n];
            audio.PlayOneShot(audio.clip);
            // move picked sound to index 0 so it's not picked next time
            _footstepSounds[n] = _footstepSounds[0];
            _footstepSounds[0] = audio.clip;
        }

        private void UpdateCameraPosition(float speed)
        {
            Vector3 newCameraPosition;
            if (!useHeadBob) return;
            if (_characterController.velocity.magnitude > 0 && _characterController.isGrounded)
            {
                _camera.transform.localPosition =
                    _headBob.DoHeadBob(_characterController.velocity.magnitude +
                                       (speed*(_isWalking ? 1f : runstepLenghten)));
                newCameraPosition = _camera.transform.localPosition;
                newCameraPosition.y = _camera.transform.localPosition.y - _jumpBob.Offset();
            }
            else
            {
                newCameraPosition = _camera.transform.localPosition;
                newCameraPosition.y = _originalCameraPosition.y - _jumpBob.Offset();
            }
            _camera.transform.localPosition = newCameraPosition;

            _cameraRefocus.SetFocusPoint();
        }

        private void GetInput(out float speed)
        {
            // Read input
            float horizontal = CrossPlatformInputManager.GetAxisRaw("Horizontal");
            float vertical = CrossPlatformInputManager.GetAxisRaw("Vertical");

            bool waswalking = _isWalking;

#if !MOBILE_INPUT
            // On standalone builds, walk/run speed is modified by a key press.
            // keep track of whether or not the character is walking or running
            _isWalking = !Input.GetKey(KeyCode.LeftShift);
#endif
            // set the desired speed to be walking or running
            speed = _isWalking ? walkSpeed : runSpeed;
            _input = new Vector2(horizontal, vertical);

            // normalize input if it exceeds 1 in combined length:
            if (_input.sqrMagnitude > 1) _input.Normalize();

            // handle speed change to give an fov kick
            // only if the player is going to a run, is running and the fovkick is to be used
            if (_isWalking != waswalking && useFOVKick && _characterController.velocity.sqrMagnitude > 0)
            {
                StopAllCoroutines();
                StartCoroutine(!_isWalking ? _fovKick.FOVKickUp() : _fovKick.FOVKickDown());
            }
        }

        private void RotateView()
        {
   //DCURRY added else for mobile input
   #if !MOBILE_INPUT
            Vector2 mouseInput = _mouseLook.Clamped(_yRotation, transform.localEulerAngles.y);

   _camera.transform.localEulerAngles = new Vector3(-mouseInput.y, _camera.transform.localEulerAngles.y,
                                                    _camera.transform.localEulerAngles.z);
   transform.localEulerAngles = new Vector3(0, mouseInput.x, 0);
   #else
   Vector2 mouseInput = new Vector2(CrossPlatformInputManager.GetAxisRaw("HorizontalLook"),
                                    CrossPlatformInputManager.GetAxisRaw("VerticalLook"));

   float camX = _camera.transform.localEulerAngles.x;

   if((camX > 280 && camX <= 360) ||
      (camX >= 0 && camX < 80) ||
      (camX >= 80 && camX < 180 && mouseInput.y > 0) ||
      (camX > 180 && camX <= 280 && mouseInput.y < 0))
   {
    _camera.transform.localEulerAngles += new Vector3(-mouseInput.y * lookSpeed * .7f, _camera.transform.localEulerAngles.y,
                                                      _camera.transform.localEulerAngles.z);
   }

   transform.localEulerAngles += new Vector3(0, mouseInput.x * lookSpeed, 0);
   #endif
            // handle the roation round the x axis on the camera
            
            _yRotation = mouseInput.y;
            _cameraRefocus.GetFocusPoint();
        }

        private void OnControllerColliderHit(ControllerColliderHit hit)
        {
            Rigidbody body = hit.collider.attachedRigidbody;
            if (body == null || body.isKinematic)
                return;

            //dont move the rigidbody if the character is on top of it
            if (_collisionFlags == CollisionFlags.CollidedBelow) return;

            body.AddForceAtPosition(_characterController.velocity*0.1f, hit.point, ForceMode.Impulse);

        }
    }
}

Tuesday, February 3, 2015

Unity 4.6 Platformer Tutorial - Part 3 - Art and Animation

This part 3 is a continuation of the videos found on this page: www.devination.com/2015/01/unity-46-touch-input-platformer-tutorial.html

I've separated part 3 from parts 1 and 2 because in this video we modify existing code from the previous videos to allow for animations. That modified code can be found on this page below the videos while the original code pre-animations can still be found on the previous page.



Check out the code below the videos




In this video, part 3 of our 4.6 Platformer series, we add animations to our player as he jumps, walks, and idles. We also add art for the ground tiles and UI buttons.

Part 1: https://www.youtube.com/watch?v=24YR_Uy-MyA
Part 2: https://www.youtube.com/watch?v=gKjKFZ30684

Google+ Community: https://plus.google.com/communities/108584850180626452949
Twitter: https://twitter.com/Devination3D
Facebook: https://www.facebook.com/unity3Dtutorialsbydevin

Time Breakdown:
1:10 - Adding ground tiles
4:10 - Setting up the character sprite sheet
5:55 - Setting up Animations
9:31 - Setting up the Animator
15:48 - Setting up the AnimatorController script
19:00 - FlipArt() function
22:00 - UpdateSpeed() function
24:13 - Updating our PlayerController script
26:45 - Demonstrating our working animations in game





Support Further Tutorial Development
Comments?
Check out the videos above to hear the explanation and see the code in action
Download the project up to this point here: https://drive.google.com/open?id=0Bwiz0Tss2NSqSUZMZkptMFVyM0k
/*/
* Script by Devin Curry
* www.Devination.com
* www.youtube.com/user/curryboy001
* Please like and subscribe if you found my tutorials helpful :D
* Google+ Community: https://plus.google.com/communities/108584850180626452949
* Twitter: https://twitter.com/Devination3D
* Facebook Page: https://www.facebook.com/unity3Dtutorialsbydevin
/*/
using UnityEngine;
using System.Collections;

public class AnimatorController : MonoBehaviour
{
 public static AnimatorController instance;

 Transform myTrans;
 Animator myAnim;
 Vector3 artScaleCache;

 void Start ()
 {
  myTrans = this.transform;
  myAnim = this.gameObject.GetComponent<Animator>();
  instance = this;

  artScaleCache = myTrans.localScale;
 }

 void FlipArt(float currentSpeed)
 {
  if((currentSpeed < 0 && artScaleCache.x == 1)|| //going left AND faceing right OR...
   (currentSpeed > 0 && artScaleCache.x == -1)) //going right AND facing left
  {
   //flip the art
   artScaleCache.x *= -1;
   myTrans.localScale = artScaleCache;
  }
  
 }

 public void UpdateSpeed(float currentSpeed)
 {
  myAnim.SetFloat ("Speed", currentSpeed);
  FlipArt(currentSpeed);
 }

 public void UpdateIsGrounded(bool isGrounded)
 {
  myAnim.SetBool ("isGrounded", isGrounded);
 }
}

And this is the modified PlayerController script
/*/
* Script by Devin Curry
* www.Devination.com
* www.youtube.com/user/curryboy001
* Please like and subscribe if you found my tutorials helpful :D
* Google+ Community: https://plus.google.com/communities/108584850180626452949
* Twitter: https://twitter.com/Devination3D
* Facebook Page: https://www.facebook.com/unity3Dtutorialsbydevin
/*/
using UnityEngine;
using System.Collections;

public class PlayerController : MonoBehaviour
{
 public float speed = 10, jumpVelocity = 10;
 public LayerMask playerMask;
 public bool canMoveInAir = true;
 Transform myTrans, tagGround;
 Rigidbody2D myBody;
 bool isGrounded = false;
 float hInput;
 AnimatorController myAnim;
 
 void Start ()
 {
//  myBody = this.rigidbody2D;//Unity 4.6-
  myBody = this.GetComponent<Rigidbody2D>();//Unity 5+
  myTrans = this.transform;
  tagGround = GameObject.Find (this.name + "/tag_ground").transform;
  myAnim = AnimatorController.instance;
 }
 void FixedUpdate ()
 {
  isGrounded = Physics2D.Linecast (myTrans.position, tagGround.position, playerMask);
  myAnim.UpdateIsGrounded (isGrounded);
  
  #if !UNITY_ANDROID && !UNITY_IPHONE && !UNITY_BLACKBERRY && !UNITY_WINRT || UNITY_EDITOR
  hInput = Input.GetAxisRaw("Horizontal");
  myAnim.UpdateSpeed(hInput);
  if(Input.GetButtonDown("Jump"))
   Jump();
  #endif

  Move(hInput);
 }
 
 void Move(float horizonalInput)
 {
  if(!canMoveInAir && !isGrounded)
   return;
  
  Vector2 moveVel = myBody.velocity;
  moveVel.x = horizonalInput * speed;
  myBody.velocity = moveVel;
 }
 
 public void Jump()
 {
  if(isGrounded)
   myBody.velocity += jumpVelocity * Vector2.up;
 }
 
 public void StartMoving(float horizonalInput)
 {
  hInput = horizonalInput;
  myAnim.UpdateSpeed(horizonalInput);
 }
}