Joystick para Android/IOS en C# Unity3D

Buenas, hoy os comparto un script para hacer un Joystick en C#. Es un port de un script que ya viene en Unity3D y que podéis encontrar en Standard Asset Mobile, este Script estaba en javascript y bueno, lo pasé a C#. Espero que a alguien le sea útil.

using UnityEngine;

using System.Collections;

//////////////////////////////////////////////////////////////

// PORT DE Joystick.js A Joystick.cs

//Nicolás Sánchez Baile

// Joystick creates a movable joystick (via GUITexture) that

// handles touch input, taps, and phases. Dead zones can control

// where the joystick input gets picked up and can be normalized.

//

// Optionally, you can enable the touchPad property from the editor

// to treat this Joystick as a TouchPad. A TouchPad allows the finger

// to touch down at any point and it tracks the movement relatively

// without moving the graphic

//////////////////////////////////////////////////////////////



// A simple class for bounding how far the GUITexture will move

public class Boundary

{

public Vector2 min = Vector2.zero;

public Vector2 max = Vector2.zero;

}



public class Joystick : MonoBehaviour{

static private Joystick[] joysticks;                    // A static collection of all joysticks

static private bool enumeratedJoysticks=false;

static private float tapTimeDelta = 0.3f;               // Time allowed between taps



public bool touchPad;                                   // Is this a TouchPad?

public Rect touchZone;

public Vector2 deadZone = Vector2.zero;                     // Control when position is output

public bool normalize = false;                          // Normalize output after the dead-zone?

public Vector2 position;                                    // [-1, 1] in x,y

public int tapCount;                                            // Current tap count



private int lastFingerId = -1;                              // Finger last used for this joystick

private float tapTimeWindow;                            // How much time there is left for a tap to occur

private Vector2 fingerDownPos;

private float fingerDownTime;

//private float firstDeltaTime = 0.5f;



private GUITexture gui;                             // Joystick graphic

private Rect defaultRect;                               // Default position / extents of the joystick graphic

private Boundary guiBoundary = new Boundary();          // Boundary for joystick graphic

private Vector2 guiTouchOffset;                     // Offset to apply to touch input

private Vector2 guiCenter;                          // Center of joystick



private Vector3 tmpv3;

private Rect tmprect;

private Color tmpclr;



public void Start()

{

// Cache this component at startup instead of looking up every frame  

gui = (GUITexture) GetComponent( typeof(GUITexture) );


// Store the default rect for the gui, so we can snap back to it
defaultRect = gui.pixelInset;

defaultRect.x += transform.position.x * Screen.width;// + gui.pixelInset.x; // -  Screen.width * 0.5;
defaultRect.y += transform.position.y * Screen.height;// - Screen.height * 0.5;

transform.position = new Vector3(0,0,transform.position.z);


if ( touchPad )

{

// If a texture has been assigned, then use the rect ferom the gui as our touchZone

if ( gui.texture )

touchZone = defaultRect;

}

else

{            

// This is an offset for touch input to match with the top left

// corner of the GUI

guiTouchOffset.x = defaultRect.width * 0.5f;

guiTouchOffset.y = defaultRect.height * 0.5f;



// Cache the center of the GUI, since it doesn't change

guiCenter.x = defaultRect.x + guiTouchOffset.x;

guiCenter.y = defaultRect.y + guiTouchOffset.y;



// Let's build the GUI boundary, so we can clamp joystick movement

guiBoundary.min.x = defaultRect.x - guiTouchOffset.x;

guiBoundary.max.x = defaultRect.x + guiTouchOffset.x;

guiBoundary.min.y = defaultRect.y - guiTouchOffset.y;

guiBoundary.max.y = defaultRect.y + guiTouchOffset.y;

}

}



public void Disable()

{

gameObject.SetActive(false);

enumeratedJoysticks = false;

}



public void ResetJoystick()

{

// Release the finger control and set the joystick back to the default position

gui.pixelInset = defaultRect;

lastFingerId = -1;

position = Vector2.zero;

fingerDownPos = Vector2.zero;



//if ( touchPad ){

// tmpclr  = gui.color;

// tmpclr.a = 0.025f;

// gui.color = tmpclr;

//}

}



public bool IsFingerDown()

{

return (lastFingerId != -1);

}



public void LatchedFinger( int fingerId )

{

// If another joystick has latched this finger, then we must release it

if ( lastFingerId == fingerId )

ResetJoystick();

}



public void Update()

{

if ( !enumeratedJoysticks )

{

// Collect all joysticks in the game, so we can relay finger latching messages

joysticks = (Joystick[])  FindObjectsOfType( typeof(Joystick) );

enumeratedJoysticks = true;

}



int count = Input.touchCount;



// Adjust the tap time window while it still available

if ( tapTimeWindow > 0 )

tapTimeWindow -= Time.deltaTime;

else

tapCount = 0;



if ( count == 0 )

ResetJoystick();

else

{

for(int i = 0;i < count; i++)

{

Touch touch = Input.GetTouch(i);          

Vector2 guiTouchPos = touch.position - guiTouchOffset;



bool shouldLatchFinger = false;

if ( touchPad )

{            

if ( touchZone.Contains( touch.position ) )

shouldLatchFinger = true;

}

else if ( gui.HitTest( touch.position ) )

{

shouldLatchFinger = true;

}    



// Latch the finger if this is a new touch

if ( shouldLatchFinger && ( lastFingerId == -1 || lastFingerId != touch.fingerId ) )

{



if ( touchPad )

{

//tmpclr = gui.color;

//tmpclr.a = 0.15f;

//gui.color = tmpclr;



lastFingerId = touch.fingerId;

fingerDownPos = touch.position;

fingerDownTime = Time.time;

}



lastFingerId = touch.fingerId;



// Accumulate taps if it is within the time window

if ( tapTimeWindow > 0 )

tapCount++;

else

{

tapCount = 1;

tapTimeWindow = tapTimeDelta;

}



// Tell other joysticks we've latched this finger

foreach ( Joystick j in joysticks )

{

if ( j != this )

j.LatchedFinger( touch.fingerId );

}                    

}            



if ( lastFingerId == touch.fingerId )

{

// Override the tap count with what the iPhone SDK reports if it is greater

// This is a workaround, since the iPhone SDK does not currently track taps

// for multiple touches

if ( touch.tapCount > tapCount )

tapCount = touch.tapCount;



if ( touchPad )

{

// For a touchpad, let's just set the position directly based on distance from initial touchdown

position.x = Mathf.Clamp( ( touch.position.x - fingerDownPos.x ) / ( touchZone.width / 2 ), -1, 1 );

position.y = Mathf.Clamp( ( touch.position.y - fingerDownPos.y ) / ( touchZone.height / 2 ), -1, 1 );

}

else

{                

// Change the location of the joystick graphic to match where the touch is

tmprect = gui.pixelInset;

tmprect.x = Mathf.Clamp( guiTouchPos.x, guiBoundary.min.x, guiBoundary.max.x );

tmprect.y = Mathf.Clamp( guiTouchPos.y, guiBoundary.min.y, guiBoundary.max.y );  

gui.pixelInset = tmprect;

}



if ( touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled )

ResetJoystick();                  

}        

}

}



if ( !touchPad )

{

// Get a value between -1 and 1 based on the joystick graphic location

position.x = ( gui.pixelInset.x + guiTouchOffset.x - guiCenter.x ) / guiTouchOffset.x;

position.y = ( gui.pixelInset.y + guiTouchOffset.y - guiCenter.y ) / guiTouchOffset.y;

}



// Adjust for dead zone

float absoluteX = Mathf.Abs( position.x );

float absoluteY = Mathf.Abs( position.y );



if ( absoluteX < deadZone.x )

{

// Report the joystick as being at the center if it is within the dead zone

position.x = 0;

}

else if ( normalize )

{

// Rescale the output after taking the dead zone into account

position.x = Mathf.Sign( position.x ) * ( absoluteX - deadZone.x ) / ( 1 - deadZone.x );

}



if ( absoluteY < deadZone.y )

{

// Report the joystick as being at the center if it is within the dead zone

position.y = 0;

}

else if ( normalize )

{

// Rescale the output after taking the dead zone into account

position.y = Mathf.Sign( position.y ) * ( absoluteY - deadZone.y ) / ( 1 - deadZone.y );

}

}
}

Saludos ;)

Comentarios

  1. Me funcionó a la perfección gracias.

    ResponderEliminar
  2. Gracias, me funciona muy bien. Mi problema ahora es que mi nave se sale de la pantalla tanto en el eje x como vertical y estoy tratando de limitar la pantalla para impedir que la nave la sobrepase. En un foro me han comentado que esto lo puedo hacer con mathf clamp, y he visto ejemplos de su implementación pero ninguno para joystick y lo intento pero algo debo estar mal :( Sabes como puedo solucionarlo?
    Gracias de antemano.

    ResponderEliminar
  3. Ya he podido lograrlo ,llevo dos días con esto xD Voy a dejar el código que he utilizado por si a alguien le puede venir bien, si no te importa.
    Un saludo.

    using UnityEngine;
    using System.Collections;

    public class PlayerClamp : MonoBehaviour {

    public bool clampWidth = true;
    public bool clampHeight = true;

    private MeshRenderer _renderer;
    private Transform _transform;
    private Vector3 _lowerLeftCorner;
    private Vector3 _upperRightCorner;
    private Vector3 _newPosition;


    // Use this for initialization
    void Start () {
    _transform = transform;
    _renderer = GetComponent ();
    _lowerLeftCorner = Camera.main.ViewportToWorldPoint (new Vector3 (0,0,0));
    _upperRightCorner = Camera.main.ViewportToWorldPoint (new Vector3 (1,1,1));
    _newPosition = (Vector3)_transform.position;
    }

    // Update is called once per frame
    void LateUpdate () {
    Clamp ();
    }

    private void Clamp()
    {
    _newPosition = (Vector3)_transform.position;

    if (clampWidth)
    {
    _newPosition.x = Mathf.Clamp (_newPosition.x,_lowerLeftCorner.x,_upperRightCorner.x);
    }
    if (clampHeight)
    {
    _newPosition.z = Mathf.Clamp (_newPosition.z,_lowerLeftCorner.z,_upperRightCorner.z);
    }
    if (clampWidth ||clampHeight)
    {
    _transform.position = _newPosition;
    }
    }
    }

    ResponderEliminar
    Respuestas
    1. Me parece una gran idea porque seguro que más gente se ha encontrado con este problema. Un saludo!

      Eliminar

Publicar un comentario

Entradas populares de este blog

Metal Gear Solid V Analisis sin spoilers 2: Gráficos Antigua Generación VS Nueva Generación

IA: Seguir objetivo y esconderse en Unity3D

🌟 Crear un videojuego de plataformas con Unity 🎮 6 – Crear efecto parallax