unity 摇杆

/***********************************************
                EasyTouch Controls
    Copyright © 2016 The Hedgehog Team
      http://www.thehedgehogteam.com/Forum/
        
      The.Hedgehog.Team@gmail.com
        
**********************************************/
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;

[System.Serializable]
public class ETCJoystick : ETCBase,IPointerEnterHandler,IDragHandler, IBeginDragHandler, IPointerDownHandler, IPointerUpHandler {
        
    #region Unity Events
    [System.Serializable] public class OnMoveStartHandler : UnityEvent{}
    [System.Serializable] public class OnMoveSpeedHandler : UnityEvent<Vector2> { }
    [System.Serializable] public class OnMoveHandler : UnityEvent<Vector2> { }
    [System.Serializable] public class OnMoveEndHandler : UnityEvent{ }

    [System.Serializable] public class OnTouchStartHandler : UnityEvent{}
    [System.Serializable] public class OnTouchUpHandler : UnityEvent{ }

    [System.Serializable] public class OnDownUpHandler : UnityEvent{ }
    [System.Serializable] public class OnDownDownHandler : UnityEvent{ }
    [System.Serializable] public class OnDownLeftHandler : UnityEvent{ }
    [System.Serializable] public class OnDownRightHandler : UnityEvent{ }

    [System.Serializable] public class OnPressUpHandler : UnityEvent{ }
    [System.Serializable] public class OnPressDownHandler : UnityEvent{ }
    [System.Serializable] public class OnPressLeftHandler : UnityEvent{ }
    [System.Serializable] public class OnPressRightHandler : UnityEvent{ }


    [SerializeField] public OnMoveStartHandler onMoveStart;
    [SerializeField] public OnMoveHandler onMove;
    [SerializeField] public OnMoveSpeedHandler onMoveSpeed;
    [SerializeField] public OnMoveEndHandler onMoveEnd;

    [SerializeField] public OnTouchStartHandler onTouchStart;
    [SerializeField] public OnTouchUpHandler onTouchUp;

    [SerializeField] public OnDownUpHandler OnDownUp;
    [SerializeField] public OnDownDownHandler OnDownDown;
    [SerializeField] public OnDownLeftHandler OnDownLeft;
    [SerializeField] public OnDownRightHandler OnDownRight;

    [SerializeField] public OnDownUpHandler OnPressUp;
    [SerializeField] public OnDownDownHandler OnPressDown;
    [SerializeField] public OnDownLeftHandler OnPressLeft;
    [SerializeField] public OnDownRightHandler OnPressRight;
    #endregion

    #region Enumeration
    public enum JoystickArea { UserDefined,FullScreen, Left,Right,Top, Bottom, TopLeft, TopRight, BottomLeft, BottomRight};
    public enum JoystickType {Dynamic, Static};
    public enum RadiusBase {Width, Height, UserDefined};
    #endregion

    #region Members

    #region Public members
    public JoystickType joystickType;
    public bool allowJoystickOverTouchPad;
    public RadiusBase radiusBase;
    public float radiusBaseValue;
    public ETCAxis axisX;
    public ETCAxis axisY;
    public RectTransform thumb;
    
    public JoystickArea joystickArea;
    public RectTransform userArea;

    public bool isTurnAndMove = false;
    public float tmSpeed = 10;
    public float tmAdditionnalRotation = 0;
    public AnimationCurve tmMoveCurve;
    public bool tmLockInJump = false;
    private Vector3 tmLastMove;

    #endregion
        
    #region Private members
    private Vector2 thumbPosition;
    private bool isDynamicActif;
    private Vector2 tmpAxis;
    private Vector2 OldTmpAxis;
    private bool isOnTouch;


    #endregion

    #region Joystick behavior option
    [SerializeField]
    private bool isNoReturnThumb;
    public bool IsNoReturnThumb {
        get {
            return isNoReturnThumb;
        }
        set {
            isNoReturnThumb = value;
        }
    }    

    private Vector2 noReturnPosition;
    private Vector2 noReturnOffset;

    [SerializeField]
    private bool isNoOffsetThumb;
    public bool IsNoOffsetThumb {
        get {
            return isNoOffsetThumb;
        }
        set {
            isNoOffsetThumb = value;
        }
    }
    #endregion

    #region Inspector


    #endregion

    #endregion
    
    #region Constructor
    public ETCJoystick(){
        joystickType = JoystickType.Static;
        allowJoystickOverTouchPad = false;
        radiusBase = RadiusBase.Width;

        axisX = new ETCAxis("Horizontal");
        axisY = new ETCAxis("Vertical");
    
        _visible = true;
        _activated = true;

        joystickArea = JoystickArea.FullScreen;

        isDynamicActif = false;
        isOnDrag = false;
        isOnTouch = false;

        axisX.unityAxis = "Horizontal";
        axisY.unityAxis = "Vertical";

        enableKeySimulation = true;

        isNoReturnThumb = false;

        showPSInspector = false;
        showAxesInspector = false;
        showEventInspector = false;
        showSpriteInspector = false;
    }
    #endregion

    #region Monobehaviours Callback
    protected override void Awake (){

        base.Awake ();

        if (joystickType == JoystickType.Dynamic){
            this.rectTransform().anchorMin = new Vector2(0.5f,0.5f);
            this.rectTransform().anchorMax = new Vector2(0.5f,0.5f);
            this.rectTransform().SetAsLastSibling();
            visible = false;
        }

        if (allowSimulationStandalone && enableKeySimulation && !Application.isEditor && joystickType!=JoystickType.Dynamic){
            SetVisible(visibleOnStandalone);
        }
    }

    public override void Start(){
    
        axisX.InitAxis();
        axisY.InitAxis();

        if (enableCamera){
            InitCameraLookAt();
        }

        tmpAxis = Vector2.zero;
        OldTmpAxis = Vector2.zero;

        noReturnPosition = thumb.position;

        pointId = -1;

        if (joystickType == JoystickType.Dynamic){
            visible = false;
        }

        base.Start();

        // Init Camera position
        if (enableCamera && cameraMode == CameraMode.SmoothFollow){
            if (cameraTransform && cameraLookAt){
                cameraTransform.position = cameraLookAt.TransformPoint( new Vector3(0,followHeight,-followDistance));
                cameraTransform.LookAt( cameraLookAt);
            }
        }

        if (enableCamera && cameraMode == CameraMode.Follow){
            if (cameraTransform && cameraLookAt){
                cameraTransform.position = cameraLookAt.position + followOffset;
                cameraTransform.LookAt( cameraLookAt.position);
            }
        }
    }


    public override void Update (){

        base.Update ();

        #region dynamic joystick
        if (joystickType == JoystickType.Dynamic && !_visible && _activated){
            Vector2 localPosition = Vector2.zero;
            Vector2 screenPosition = Vector2.zero;
            
            if (isTouchOverJoystickArea(ref localPosition, ref screenPosition)){
                
                GameObject overGO = GetFirstUIElement( screenPosition);
                
                if (overGO == null || (allowJoystickOverTouchPad && overGO.GetComponent<ETCTouchPad>()) || (overGO != null && overGO.GetComponent<ETCArea>() ) ) {

                    cachedRectTransform.anchoredPosition = localPosition;
                    visible = true;
                }
            }
        }


        if (joystickType == JoystickType.Dynamic && _visible){
            if (GetTouchCount()==0){
                visible = false;

            }
        }
        #endregion

    }

    public override void LateUpdate (){

        if (enableCamera && !cameraLookAt ){
            InitCameraLookAt();
        }
        base.LateUpdate ();

    }

    private void InitCameraLookAt(){

        if (cameraTargetMode == CameraTargetMode.FromDirectActionAxisX){
            cameraLookAt = axisX.directTransform;
        }
        else if (cameraTargetMode == CameraTargetMode.FromDirectActionAxisY){
            cameraLookAt = axisY.directTransform;
            if (isTurnAndMove){
                cameraLookAt = axisX.directTransform;
            }
        }
        else if (cameraTargetMode == CameraTargetMode.LinkOnTag){
            GameObject tmpobj = GameObject.FindGameObjectWithTag(camTargetTag);
            if (tmpobj){
                cameraLookAt = tmpobj.transform;
            }
        }

        if (cameraLookAt)
            cameraLookAtCC = cameraLookAt.GetComponent<CharacterController>();
    }

    protected override void UpdateControlState (){
    
        if (_visible){
            UpdateJoystick();
        }
        else{
            if (joystickType == JoystickType.Dynamic){
                OnUp( false);
            }
        }
    }

    #endregion
    
    #region UI Callback
    public void OnPointerEnter(PointerEventData eventData){

        if (joystickType == JoystickType.Dynamic && !isDynamicActif && _activated &&  pointId==-1){
            eventData.pointerDrag = gameObject;
            eventData.pointerPress = gameObject;

            isDynamicActif = true;
            pointId = eventData.pointerId;
        }

        if (joystickType == JoystickType.Dynamic &&  !eventData.eligibleForClick){
            OnPointerUp( eventData );
        }

    }

    public void OnPointerDown(PointerEventData eventData){
        onTouchStart.Invoke();
        pointId = eventData.pointerId;
        if (isNoOffsetThumb){
            OnDrag( eventData);
        }
    }

    public void OnBeginDrag(PointerEventData eventData){


    }
    
    public void OnDrag(PointerEventData eventData){

        if (pointId == eventData.pointerId){
            isOnDrag = true;
            isOnTouch = true;

            float radius =  GetRadius();


            if (!isNoReturnThumb){
                thumbPosition =  (eventData.position - eventData.pressPosition);// / (cachedRootCanvas.rectTransform().localScale.x  ) ;
            }
            else{
                thumbPosition =((eventData.position - noReturnPosition) /cachedRootCanvas.rectTransform().localScale.x) + noReturnOffset;
            }

            if (isNoOffsetThumb){
                thumbPosition =  (eventData.position - (Vector2)cachedRectTransform.position) / cachedRootCanvas.rectTransform().localScale.x;
            }

            thumbPosition.x = Mathf.FloorToInt( thumbPosition.x);
            thumbPosition.y = Mathf.FloorToInt( thumbPosition.y);


            if (!axisX.enable){
                thumbPosition.x=0;
            }

            if (!axisY.enable){
                thumbPosition.y=0;
            }

            if (thumbPosition.magnitude > radius){
                if (!isNoReturnThumb){
                    thumbPosition = thumbPosition.normalized * radius ;
                }
                else{
                    thumbPosition = thumbPosition.normalized * radius;
                }
            }

            thumb.anchoredPosition =  thumbPosition; 
        }
    }

    public void OnPointerUp (PointerEventData eventData){
        if (pointId == eventData.pointerId){
            OnUp();
        }
    }

    private void OnUp(bool real=true){

        isOnDrag = false;
        isOnTouch = false;
        
        if (isNoReturnThumb){
            noReturnPosition = thumb.position;
            noReturnOffset = thumbPosition;
        }
        
        if (!isNoReturnThumb){
            thumbPosition =  Vector2.zero;
            thumb.anchoredPosition = Vector2.zero;
            
            axisX.axisState = ETCAxis.AxisState.None;
            axisY.axisState = ETCAxis.AxisState.None;
        }
        
        if (!axisX.isEnertia && !axisY.isEnertia){
            axisX.ResetAxis();
            axisY.ResetAxis();
            tmpAxis = Vector2.zero;
            OldTmpAxis = Vector2.zero;
            if (real){
                onMoveEnd.Invoke();
            }
        }
        
        if (joystickType == JoystickType.Dynamic){
            visible = false;
            isDynamicActif = false;
        }

        pointId=-1;

        if (real){
            onTouchUp.Invoke();
        }
    }
    #endregion

    #region Joystick Update
    protected override void DoActionBeforeEndOfFrame (){
        axisX.DoGravity();
        axisY.DoGravity();
    }

    private void UpdateJoystick(){

        #region Unity axes
        //enableKeySimulation && 
        if (!isOnTouch && _activated && _visible ){

            float x = Input.GetAxis(axisX.unityAxis);
            float y= Input.GetAxis(axisY.unityAxis);

            if (!isNoReturnThumb){
                thumb.localPosition = Vector2.zero;
            }

            isOnDrag = false;

            if (x!=0){
                isOnDrag = true;
                thumb.localPosition = new Vector2(GetRadius()*x, thumb.localPosition.y);
            }

            if (y!=0){
                isOnDrag = true;
                thumb.localPosition = new Vector2(thumb.localPosition.x,GetRadius()*y );
            }

            thumbPosition = thumb.localPosition;
        }
        #endregion

        // Computejoystick value
        OldTmpAxis.x = axisX.axisValue;
        OldTmpAxis.y = axisY.axisValue;

        tmpAxis = thumbPosition / GetRadius();

        axisX.UpdateAxis( tmpAxis.x,isOnDrag, ETCBase.ControlType.Joystick,true);
        axisY.UpdateAxis( tmpAxis.y,isOnDrag, ETCBase.ControlType.Joystick,true);

        #region Move event
        if ((axisX.axisValue!=0 ||  axisY.axisValue!=0 ) && OldTmpAxis == Vector2.zero){
            onMoveStart.Invoke();
        }
        if (axisX.axisValue!=0 ||  axisY.axisValue!=0 ){

            if (!isTurnAndMove){
                // X axis
                if( axisX.actionOn == ETCAxis.ActionOn.Down && (axisX.axisState == ETCAxis.AxisState.DownLeft || axisX.axisState == ETCAxis.AxisState.DownRight)){
                    axisX.DoDirectAction();
                }
                else if (axisX.actionOn == ETCAxis.ActionOn.Press){
                    axisX.DoDirectAction();
                }

                // Y axis
                if( axisY.actionOn == ETCAxis.ActionOn.Down && (axisY.axisState == ETCAxis.AxisState.DownUp || axisY.axisState == ETCAxis.AxisState.DownDown)){
                    axisY.DoDirectAction();
                }
                else if (axisY.actionOn == ETCAxis.ActionOn.Press){
                    axisY.DoDirectAction();
                }
            }
            else{
                DoTurnAndMove();
            }
            onMove.Invoke( new Vector2(axisX.axisValue,axisY.axisValue));
            onMoveSpeed.Invoke( new Vector2(axisX.axisSpeedValue,axisY.axisSpeedValue));
        }
        else if (axisX.axisValue==0 &&  axisY.axisValue==0  && OldTmpAxis!=Vector2.zero) {
            onMoveEnd.Invoke();
        }        

        if (!isTurnAndMove){
            if (axisX.axisValue==0 &&  axisX.directCharacterController ){
                if (!axisX.directCharacterController.isGrounded && axisX.isLockinJump)
                    axisX.DoDirectAction();
            } 

            if (axisY.axisValue==0 &&  axisY.directCharacterController ){
                if (!axisY.directCharacterController.isGrounded && axisY.isLockinJump)
                    axisY.DoDirectAction();
            }
        }
        else{
            if ((axisX.axisValue==0 && axisY.axisValue==0) &&  axisX.directCharacterController ){
                if (!axisX.directCharacterController.isGrounded && tmLockInJump)
                    DoTurnAndMove();
            }
        }

        #endregion


        #region Down & press event
        float coef =1;
        if (axisX.invertedAxis) coef = -1;
        if (Mathf.Abs(OldTmpAxis.x)< axisX.axisThreshold && Mathf.Abs(axisX.axisValue)>=axisX.axisThreshold){

            if (axisX.axisValue*coef >0){
                axisX.axisState = ETCAxis.AxisState.DownRight;
                OnDownRight.Invoke();
            }
            else if (axisX.axisValue*coef<0){
                axisX.axisState = ETCAxis.AxisState.DownLeft;
                OnDownLeft.Invoke();
            }
            else{
                axisX.axisState = ETCAxis.AxisState.None;
            }
        }
        else if (axisX.axisState!= ETCAxis.AxisState.None) {
            if (axisX.axisValue*coef>0){
                axisX.axisState = ETCAxis.AxisState.PressRight;
                OnPressRight.Invoke();
            }
            else if (axisX.axisValue*coef<0){
                axisX.axisState = ETCAxis.AxisState.PressLeft;
                OnPressLeft.Invoke();
            }
            else{
                axisX.axisState = ETCAxis.AxisState.None;
            }
        }

        coef =1;
        if (axisY.invertedAxis) coef = -1;
        if (Mathf.Abs(OldTmpAxis.y)< axisY.axisThreshold && Mathf.Abs(axisY.axisValue)>=axisY.axisThreshold  ){
            
            if (axisY.axisValue*coef>0){
                axisY.axisState = ETCAxis.AxisState.DownUp;
                OnDownUp.Invoke();
            }
            else if (axisY.axisValue*coef<0){
                axisY.axisState = ETCAxis.AxisState.DownDown;
                OnDownDown.Invoke();
            }
            else{
                axisY.axisState = ETCAxis.AxisState.None;
            }
        }
        else if (axisY.axisState!= ETCAxis.AxisState.None) {
            if (axisY.axisValue*coef>0){
                axisY.axisState = ETCAxis.AxisState.PressUp;
                OnPressUp.Invoke();
            }
            else if (axisY.axisValue*coef<0){
                axisY.axisState = ETCAxis.AxisState.PressDown;
                OnPressDown.Invoke();
            }
            else{
                axisY.axisState = ETCAxis.AxisState.None;
            }
        }
        #endregion

    }        
    #endregion

    #region Touch manager
    private bool isTouchOverJoystickArea(ref Vector2 localPosition, ref Vector2 screenPosition){
        
        bool touchOverArea = false;
        bool doTest = false;
        screenPosition = Vector2.zero;
        
        int count = GetTouchCount();
        int i=0;
        while (i<count && !touchOverArea){
            #if ((UNITY_ANDROID || UNITY_IOS || UNITY_WINRT || UNITY_BLACKBERRY) && !UNITY_EDITOR) 
            if (Input.GetTouch(i).phase == TouchPhase.Began){
                screenPosition = Input.GetTouch(i).position;
                doTest = true;
            }
            #else
            if (Input.GetMouseButtonDown(0)){
                screenPosition = Input.mousePosition;
                doTest = true;

            }
            #endif
            
            if (doTest && isScreenPointOverArea(screenPosition, ref localPosition) ){
                touchOverArea = true;
            }
            
            i++;
        }
        
        return touchOverArea;
    }
    
    private bool isScreenPointOverArea(Vector2 screenPosition, ref Vector2 localPosition){
        
        bool returnValue = false;
        
        if (joystickArea != JoystickArea.UserDefined){
            if (RectTransformUtility.ScreenPointToLocalPointInRectangle( cachedRootCanvas.rectTransform(),screenPosition,null,out localPosition)){
                
                switch (joystickArea){
                case JoystickArea.Left:
                    if (localPosition.x<0){
                        returnValue=true;
                    }
                    break;
                    
                case JoystickArea.Right:
                    if (localPosition.x>0){
                        returnValue = true;
                    }
                    break;
                    
                case JoystickArea.FullScreen:
                    returnValue = true;
                    break;
                    
                case JoystickArea.TopLeft:
                    if (localPosition.y>0 && localPosition.x<0){
                        returnValue = true;
                    }
                    break;
                case JoystickArea.Top:
                    if (localPosition.y>0){
                        returnValue = true;
                    }
                    break;
                    
                case JoystickArea.TopRight:
                    if (localPosition.y>0 && localPosition.x>0){
                        returnValue=true;
                    }
                    break;
                    
                case JoystickArea.BottomLeft:
                    if (localPosition.y<0 && localPosition.x<0){
                        returnValue = true;
                    }
                    break;
                    
                case JoystickArea.Bottom:
                    if (localPosition.y<0){
                        returnValue = true;
                    }
                    break;
                    
                case JoystickArea.BottomRight:
                    if (localPosition.y<0 && localPosition.x>0){
                        returnValue = true;
                    }
                    break;
                }
            }
        }
        else{
            if (RectTransformUtility.RectangleContainsScreenPoint( userArea, screenPosition, cachedRootCanvas.worldCamera )){
                RectTransformUtility.ScreenPointToLocalPointInRectangle( cachedRootCanvas.rectTransform(),screenPosition,cachedRootCanvas.worldCamera,out localPosition);
                returnValue = true;
            }
        }
        
        return returnValue;
        
    }
    
    private int GetTouchCount(){
        #if ((UNITY_ANDROID || UNITY_IOS || UNITY_WINRT || UNITY_BLACKBERRY) && !UNITY_EDITOR) 
        return Input.touchCount;
        #else
        if (Input.GetMouseButton(0) || Input.GetMouseButtonUp(0)){
            return 1;
        }
        else{
            return 0;
        }
        #endif
    }
    #endregion

    #region Other private method
    public float GetRadius(){

        float radius =0;
        
        switch (radiusBase){
        case RadiusBase.Width:
            radius = cachedRectTransform.sizeDelta.x * 0.5f;
            break;
        case RadiusBase.Height:
            radius = cachedRectTransform.sizeDelta.y * 0.5f;
            break;
        case RadiusBase.UserDefined:
            radius = radiusBaseValue;
            break;
        }
        
        return radius;
    }

    protected override void SetActivated (){
    
        GetComponent<CanvasGroup>().blocksRaycasts = _activated;

        if (!_activated){
            OnUp(false);
        }
    }

    protected override void SetVisible (bool visible=true){

        bool localVisible = _visible;
        if (!visible){
            localVisible = visible;
        }
        GetComponent<Image>().enabled = localVisible;
        thumb.GetComponent<Image>().enabled = localVisible;
        GetComponent<CanvasGroup>().blocksRaycasts = _activated;


    }
    #endregion


    private void DoTurnAndMove(){

        float angle =Mathf.Atan2( axisX.axisValue,axisY.axisValue ) * Mathf.Rad2Deg;
        float speed = tmMoveCurve.Evaluate( new Vector2(axisX.axisValue,axisY.axisValue).magnitude) * tmSpeed;

        if (axisX.directTransform != null){

            axisX.directTransform.rotation = Quaternion.Euler(new Vector3(0,  angle + tmAdditionnalRotation,0));

            if (axisX.directCharacterController != null){
                if (axisX.directCharacterController.isGrounded || !tmLockInJump){
                    Vector3 move = axisX.directCharacterController.transform.TransformDirection(Vector3.forward) *  speed;
                    axisX.directCharacterController.Move(move* Time.deltaTime);
                    tmLastMove = move;
                }
                else{
                    axisX.directCharacterController.Move(tmLastMove* Time.deltaTime);
                }
            }
            else{
                axisX.directTransform.Translate(Vector3.forward *  speed * Time.deltaTime,Space.Self);
            }
        }

    }

    public void InitCurve(){
        axisX.InitDeadCurve();
        axisY.InitDeadCurve();
        InitTurnMoveCurve();
    }

    public void InitTurnMoveCurve(){
        tmMoveCurve = AnimationCurve.EaseInOut(0,0,1,1);
        tmMoveCurve.postWrapMode = WrapMode.PingPong;
        tmMoveCurve.preWrapMode = WrapMode.PingPong;
    }
}

 

posted on 2024-10-08 17:38  zqiang0803  阅读(6)  评论(0编辑  收藏  举报

导航