GÜNCELLEME – 10.09.2017: yazıda bahsi geçen UI direksiyon scriptini artık SimpleInput plugin’ime ekledim. SimpleInput plugin’i, multi-platform Input işlemlerini oldukça kolaylaştırdığı için bu direksiyon yerine SimpleInput’taki direksiyonu kullanmanızı öneririm: https://yasirkula.com/2017/07/19/unity-gelismis-input-sistemi-mobil-destekli/
Hepinize merhaba,
Bu dersimizde bir anlatım yapmayacağım ama yazdığım bir scripti sizinle paylaşacağım. Bu script sayesinde ekranda bir direksiyonu mouse ile ya da mobil cihazlarda parmak ile döndürebilir, scriptin döndürdüğü değeri kullanarak arabanıza yön verebilir ya da bir uçağı döndürebilirsiniz. Scripti olabildiğince comment’lemeye çalıştım.
Direksiyon scriptinin iki farklı versiyonu mevcut: eski GUI sistemini (OnGUI) destekleyen kod ve Unity 4.6 ile gelen yeni UI sistemini destekleyen kod. İkisini de ayrı ayrı paylaşıyorum:
OnGUI Üzerinde Çalışan Kod
Alttaki Direksiyon isimli C# scriptini projenize ekleyin. Ne zaman ki direksiyonun döndürdüğü değeri kullanmak istiyorsunuz, o zaman Direksiyon scriptinin EgimiAl() fonksiyonunu çağırın. Bu fonksiyon [-1,1] aralığında bir float döndürür; tıpkı Input.GetAxis(“Horizontal”) gibi.
using UnityEngine;
public class Direksiyon : MonoBehaviour
{
public float maksimumDonusAcisi = 500f; // Direksiyonun dönebileceği maksimum açı
public float direksiyonEbat = 256f; // Direksiyonun ekranda pixel cinsinden boyutu
public Vector2 deltaPivot = Vector2.zero; // Eğer direksiyon tam merkezinden dönmüyorsa pivotuyla oynayarak durumu düzeltebilirsin
public float direksiyonDuzelmeHizi = 200f; // Direksiyon bırakıldığında eski haline dönene kadar saniyede döneceği açı
public Texture2D direksiyonTexture; // Direksiyonun texture'si
private float direksiyonEgim; // Direksiyonun eğimi
private bool direksiyonTutuluyor; // Direksiyonun elle tutulup tutulmamakta olduğunu depolayan değişken
private Rect direksiyonKonum; // Direksiyonun ekrandaki konumu
private Vector2 direksiyonMerkez; // Direksiyonun ekran koordinatlarında (Rect değil) merkezi
private float direksiyonEskiEgim; // Gerekli bir değişken
// Bölüm başlayınca tek seferlik çalıştırılır
void Start()
{
// Değişkenlere değer ata ve direksiyonun ekrandaki konumunu hesapla
direksiyonTutuluyor = false;
direksiyonKonum = new Rect( 25, Screen.height - direksiyonEbat - 25, direksiyonEbat, direksiyonEbat );
direksiyonMerkez = new Vector2( direksiyonKonum.x + direksiyonKonum.width * 0.5f, Screen.height - direksiyonKonum.y - direksiyonKonum.height * 0.5f );
direksiyonEgim = 0f;
}
// Direksiyonun eğimini döndürür. Bu değeri kullanarak arabayı döndürmek gibi işlemler yapılabilir
// Döndürülen değer -1 ile 1 arasındadır ve direksiyonu sola kırınca negatif değer,
// sağa kırınca pozitif değer döndürülür.
public float EgimiAl()
{
return direksiyonEgim / maksimumDonusAcisi;
}
// Ekrana direksiyonu çizdir
void OnGUI()
{
// Alttaki satırın başındaki comment'i silerek direksiyonu tutabileceğin alanı görebilirsin
// GUI.Box( direksiyonKonum, "" );
Matrix4x4 eskiRotasyon = GUI.matrix;
GUIUtility.RotateAroundPivot( direksiyonEgim, direksiyonKonum.center + deltaPivot );
GUI.Box( direksiyonKonum, direksiyonTexture, GUI.skin.label );
GUI.matrix = eskiRotasyon;
}
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WEBPLAYER
/*
** Bu kod parçası sadece Unity editöründe, PC, MAC, Linux ve Web Player'da çalışır
*/
// Her frame'de (kare) bir kez çalıştırılır
void Update()
{
// Eğer direksiyon mouse tarafından tutuluyorsa
if( direksiyonTutuluyor )
{
Vector2 mouseKonum;
// Mousenin ekrandaki konumunu bul
mouseKonum = Input.mousePosition;
float direksiyonYeniEgim = Vector2.Angle( Vector2.up, mouseKonum - direksiyonMerkez );
// Eğer mouse direksiyonun merkezine çok yakınsa işlem yapma
if( Vector2.Distance( mouseKonum, direksiyonMerkez ) > 20f )
{
if( mouseKonum.x > direksiyonMerkez.x )
direksiyonEgim += direksiyonYeniEgim - direksiyonEskiEgim;
else
direksiyonEgim -= direksiyonYeniEgim - direksiyonEskiEgim;
}
// Direksiyonun eğiminin maksimum dönme açısını aşmadığından emin ol
if( direksiyonEgim > maksimumDonusAcisi )
direksiyonEgim = maksimumDonusAcisi;
else if( direksiyonEgim < -maksimumDonusAcisi ) // >
direksiyonEgim = -maksimumDonusAcisi;
direksiyonEskiEgim = direksiyonYeniEgim;
// Eğer mouse kaldırılırsa direksiyonu bırak
if( Input.GetMouseButtonUp( 0 ) )
direksiyonTutuluyor = false;
}
else // Eğer direksiyon tutulmuyorsa
{
// Eğer mouse direksiyonu tuttuysa durumu güncelle
if( Input.GetMouseButtonDown( 0 ) && direksiyonKonum.Contains( new Vector2( Input.mousePosition.x, Screen.height - Input.mousePosition.y ) ) )
{
direksiyonTutuluyor = true;
direksiyonEskiEgim = Vector2.Angle( Vector2.up, (Vector2) Input.mousePosition - direksiyonMerkez );
}
// Eğer direksiyon serbestse ve dönmüş durumdaysa onu direksiyonDuzelmeHizi ile eski haline döndür
if( !Mathf.Approximately( 0f, direksiyonEgim ) )
{
float deltaEgim = direksiyonDuzelmeHizi * Time.deltaTime;
if( Mathf.Abs( deltaEgim ) > Mathf.Abs( direksiyonEgim ) )
{
direksiyonEgim = 0f;
return;
}
if( direksiyonEgim > 0f )
direksiyonEgim -= deltaEgim;
else
direksiyonEgim += deltaEgim;
}
}
}
#else
/*
** Bu kod parçası sadece mobil cihazlarda çalışır
*/
private int parmakId = -1; // Dokunmatik ekranlarda kullanacağımız bir değişken
// Her frame'de (kare) bir kez çalıştırılır
void Update()
{
// Eğer direksiyon parmak tarafından tutuluyorsa
if( direksiyonTutuluyor )
{
Vector2 parmakKonum = new Vector2( 0f, 0f );
// Parmağın ekrandaki konumunu bul
for( int i = Input.touchCount - 1; i >= 0; i-- )
{
Touch parmak = Input.GetTouch( i );
if( parmak.fingerId == parmakId )
{
parmakKonum = parmak.position;
// Eğer parmak ekrandan kaldırılıyorsa bir sonraki frame'de direksiyonu serbest bırak
if( parmak.phase == TouchPhase.Ended || parmak.phase == TouchPhase.Canceled )
direksiyonTutuluyor = false;
}
}
float direksiyonYeniEgim = Vector2.Angle( Vector2.up, parmakKonum - direksiyonMerkez );
// Eğer parmak direksiyonun merkezine çok yakınsa işlem yapma
if( Vector2.Distance( parmakKonum, direksiyonMerkez ) > 20f )
{
if( parmakKonum.x > direksiyonMerkez.x )
direksiyonEgim += direksiyonYeniEgim - direksiyonEskiEgim;
else
direksiyonEgim -= direksiyonYeniEgim - direksiyonEskiEgim;
}
// Direksiyonun eğiminin maksimum dönme açısını aşmadığından emin ol
if( direksiyonEgim > maksimumDonusAcisi )
direksiyonEgim = maksimumDonusAcisi;
else if( direksiyonEgim < -maksimumDonusAcisi ) // >
direksiyonEgim = -maksimumDonusAcisi;
direksiyonEskiEgim = direksiyonYeniEgim;
}
else // Eğer direksiyon tutulmuyorsa
{
// Eğer bir parmak direksiyonu yeni tuttuysa durumu güncelle
for( int i = Input.touchCount - 1; i >= 0; i-- )
{
Touch parmak = Input.GetTouch( i );
if( parmak.phase == TouchPhase.Began )
{
if( direksiyonKonum.Contains( new Vector2( parmak.position.x, Screen.height - parmak.position.y ) ) )
{
direksiyonTutuluyor = true;
direksiyonEskiEgim = Vector2.Angle( Vector2.up, parmak.position - direksiyonMerkez );
parmakId = parmak.fingerId;
}
}
}
// Eğer direksiyon serbestse ve dönmüş durumdaysa onu direksiyonDuzelmeHizi ile eski haline döndür
if( !Mathf.Approximately( 0f, direksiyonEgim ) )
{
float deltaEgim = direksiyonDuzelmeHizi * Time.deltaTime;
if( Mathf.Abs( deltaEgim ) > Mathf.Abs( direksiyonEgim ) )
{
direksiyonEgim = 0f;
return;
}
if( direksiyonEgim > 0f )
direksiyonEgim -= deltaEgim;
else
direksiyonEgim += deltaEgim;
}
}
}
#endif
}
Yeni UI Sistemi Üzerinde Çalışan Kod
Alttaki SteeringWheel isimli C# scriptini projenize ekleyin ve istediğiniz bir objeye component olarak ekleyin. Ardından UI canvas‘ınızda bir Image oluşturun, Image’i istediğiniz gibi konumlandırın ve Image’e direksiyon sprite‘sini atayın. Image’a Event Trigger component‘i eklemeyin yoksa script düzgün çalışmayabilir. Son olarak, scripti verdiğiniz objeyi seçin ve UI_Element değişkenine değer olarak direksiyon Image’ini verin.
Ne zaman ki direksiyonun döndürdüğü değeri kullanmak istiyorsunuz, o zaman SteeringWheel scriptinin GetClampedValue() fonksiyonunu çağırın. Bu fonksiyon [-1,1] aralığında bir float döndürür; tıpkı Input.GetAxis(“Horizontal”) gibi.
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using System.Collections;
public class SteeringWheel : MonoBehaviour
{
public Graphic UI_Element;
RectTransform rectT;
Vector2 centerPoint;
public float maximumSteeringAngle = 200f;
public float wheelReleasedSpeed = 200f;
float wheelAngle = 0f;
float wheelPrevAngle = 0f;
bool wheelBeingHeld = false;
public float GetClampedValue()
{
// returns a value in range [-1,1] similar to GetAxis("Horizontal")
return wheelAngle / maximumSteeringAngle;
}
public float GetAngle()
{
// returns the wheel angle itself without clamp operation
return wheelAngle;
}
void Start()
{
rectT = UI_Element.rectTransform;
InitEventsSystem();
}
void Update()
{
// If the wheel is released, reset the rotation
// to initial (zero) rotation by wheelReleasedSpeed degrees per second
if( !wheelBeingHeld && !Mathf.Approximately( 0f, wheelAngle ) )
{
float deltaAngle = wheelReleasedSpeed * Time.deltaTime;
if( Mathf.Abs( deltaAngle ) > Mathf.Abs( wheelAngle ) )
wheelAngle = 0f;
else if( wheelAngle > 0f )
wheelAngle -= deltaAngle;
else
wheelAngle += deltaAngle;
}
// Rotate the wheel image
rectT.localEulerAngles = Vector3.back * wheelAngle;
}
void InitEventsSystem()
{
// Warning: Be ready to see some extremely boring code here
// You are warned!
EventTrigger events = UI_Element.gameObject.GetComponent<EventTrigger>();
if( events == null )
events = UI_Element.gameObject.AddComponent<EventTrigger>();
if( events.triggers == null )
events.triggers = new System.Collections.Generic.List<EventTrigger.Entry>();
EventTrigger.Entry entry = new EventTrigger.Entry();
EventTrigger.TriggerEvent callback = new EventTrigger.TriggerEvent();
UnityAction<BaseEventData> functionCall = new UnityAction<BaseEventData>( PressEvent );
callback.AddListener( functionCall );
entry.eventID = EventTriggerType.PointerDown;
entry.callback = callback;
events.triggers.Add( entry );
entry = new EventTrigger.Entry();
callback = new EventTrigger.TriggerEvent();
functionCall = new UnityAction<BaseEventData>( DragEvent );
callback.AddListener( functionCall );
entry.eventID = EventTriggerType.Drag;
entry.callback = callback;
events.triggers.Add( entry );
entry = new EventTrigger.Entry();
callback = new EventTrigger.TriggerEvent();
functionCall = new UnityAction<BaseEventData>( ReleaseEvent );//
callback.AddListener( functionCall );
entry.eventID = EventTriggerType.PointerUp;
entry.callback = callback;
events.triggers.Add( entry );
}
public void PressEvent( BaseEventData eventData )
{
// Executed when mouse/finger starts touching the steering wheel
Vector2 pointerPos = ( (PointerEventData) eventData ).position;
wheelBeingHeld = true;
centerPoint = RectTransformUtility.WorldToScreenPoint( ( (PointerEventData) eventData ).pressEventCamera, rectT.position );
wheelPrevAngle = Vector2.Angle( Vector2.up, pointerPos - centerPoint );
}
public void DragEvent( BaseEventData eventData )
{
// Executed when mouse/finger is dragged over the steering wheel
Vector2 pointerPos = ( (PointerEventData) eventData ).position;
float wheelNewAngle = Vector2.Angle( Vector2.up, pointerPos - centerPoint );
// Do nothing if the pointer is too close to the center of the wheel
if( Vector2.Distance( pointerPos, centerPoint ) > 20f )
{
if( pointerPos.x > centerPoint.x )
wheelAngle += wheelNewAngle - wheelPrevAngle;
else
wheelAngle -= wheelNewAngle - wheelPrevAngle;
}
// Make sure wheel angle never exceeds maximumSteeringAngle
wheelAngle = Mathf.Clamp( wheelAngle, -maximumSteeringAngle, maximumSteeringAngle );
wheelPrevAngle = wheelNewAngle;
}
public void ReleaseEvent( BaseEventData eventData )
{
// Executed when mouse/finger stops touching the steering wheel
// Performs one last DragEvent, just in case
DragEvent( eventData );
wheelBeingHeld = false;
}
}
Scripti projelerinizde credit vermeden kullanabilirsiniz ama eğer credit verirseniz elbette ki memnun olurum.
Scripti kullanarak oluşturduğum örnek projeyi Web Player üzerinden test etmek isterseniz (OnGUI versiyonunu kullandım): http://yasirkula.freeiz.com/Projects/SimpleCarProject.html
Örnek projeyi indirmek isterseniz (Unity 5 desteklemez): https://www.dropbox.com/s/ex1yagax4k236p7/SimpleCarProject.rar?dl=0
Başka derslerde görüşmek dileğiyle!