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!
Selam. Hocam bir nesneyi, cihazın ekran boyutuna kadar hareket ettirmek istiyorum.Yani nesnenin hareketi edişi, ekranın boyutunu aşmamalı.Bununla ilgili c# dilinde bir kod önerebilir misiniz?
Camera sınıfının ViewportToWorldPoint veya ScreenToWorldPoint fonksiyonlarına bakabilirsiniz.
Teşekkür ederim hocam. Bu kodların ne işe yaradığını açiklayabilir misiniz? Yani amacı nedir ona göre mantık yürütmeye çalışacağım.Sitesinden çeviri yaptım ama hala amaçlarını tam kavrayamadım.3d nesneyi ekranın dışına çıkmadan durdurmaya çalışıyorum.Canvas kullanmama rağmen, 3d nesneye velocity uyguladığım için dışarıya fırlıyor,engel olamadım.Bunu çözmeye çalışıyorum.Tekrar teşekkür ederim.Allah razı olsun.
ScreenToWorldPoint fonksiyonu, ekrandaki bir pikselin 3D uzayda hangi koordinata denk geldiğini döndürür. Örneğin “Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, 10f))” fonksiyonu, ekranın en sağ üst noktasının 3D uzayda hangi nokta olduğunu döndürür. Döndürülen noktanın kameraya uzaklığı ise 10f’tir. ViewportToWorldPoint’te ise [0,Screen.width] olan aralığı [0,1] olarak düşünebilirsiniz, başka bir farkı yok. Bu fonksiyonlar ile ekranın kenarlarının 3D koordinatlarını bulup objenin bu koordinatlardan dışarı çıkmamasını sağlayabilirsiniz.