Recommended for learning unity scripts: index on the unity3d Official Website
Joystick is very common in mobile game development, that is, the virtual joystick on the mobile phone screen. However, the joystick texture provided by unity3d is relatively primitive, So you often need to use custom textures.
The following shows how to customize joystick textures.
First, import the texture. Change the default texture to Gui or the size will change:
Click the texture option on the inspector panel to implement simple texture switching:
After selection, you will find that joystick in the scenario has changed:
Similarly, you can make the same changes to joystick on the right side:
Of course, this simple modification is often difficult to meet our needs.
The following are common adjustments to joystick.
First, adjust the coordinates. Generally, set the postition to zero and adjust the pixel inset in guitexture:
However, this will still cause problems. This problem occurs when full screen is used because absolute coordinates are used:
So we need to make some adjustments in the script.
Add a background image to joystick.
Create a JS script joystickbackgroundgui:
@script RequireComponent(Joystick)@script ExecuteInEditMode ()var background = new SwitchGUI();var location = new Location();private var GUIalpha:float = 1;private var joystick : Joystick;joystick = GetComponent (Joystick);var noGuiStyle : GUIStyle;function Update() {if (joystick.IsFingerDown()) {background.up();} else {background.down();}if (background.texture != null){location.updateLocation();}}function OnGUI () {GUI.color.a = GUIalpha;GUI.Box(Rect(location.offset.x + background.offset.x - background.texture.width/2,location.offset.y + background.offset.y - background.texture.height/2,background.texture.width,background.texture.height),background.texture,noGuiStyle);}
Joystick is the encapsulated object of Unity. If you have any function such as isfingerdown, you can refer to the instruction documents on the Official unity website.
Location and switchgui are used in the script. These two functions are defined in another script _ guiclasses:
import System.Collections.Generic;// TextureGUI Class: create a basic class for creating and placing GUI elements// texture = the texture to display// offset = pixel offset from top left corner, can be modified for easy positioningclass TextureGUI {var texture:Texture; //useful: texture.width, texture.heightvar offset:Vector2; // .x and .yprivate var originalOffset:Vector2; //store the original to correctly reset anchor pointenum Point { TopLeft, TopRight, BottomLeft, BottomRight, Center} //what part of texture to position around?var anchorPoint = Point.TopLeft; // Unity default is from top left corner of texturefunction setAnchor() { // meant to be run ONCE at Start.originalOffset = offset;if (texture) { // check for null textureswitch(anchorPoint) { //depending on where we want to center our offsetscase anchorPoint.TopLeft: // Unity default, do nothingbreak;case anchorPoint.TopRight: // Take the offset and go to the top right corneroffset.x = originalOffset.x - texture.width;break;case anchorPoint.BottomLeft: // bottom left corner of textureoffset.y = originalOffset.y - texture.height;break;case anchorPoint.BottomRight: //bottom right corner of textureoffset.x = originalOffset.x - texture.width;offset.y = originalOffset.y - texture.height;break;case anchorPoint.Center: //and the center of the texture (useful for screen center textures)offset.x = originalOffset.x - texture.width/2;offset.y = originalOffset.y - texture.height/2;break;}}}}//Timer Class:class TimerGUI extends TextureGUI { // Extend functionality from TextureGUI for a depreciating timer graphicvar textureLEnd:Texture; // left side of full texture (non stretching part)var offsetLEnd:Vector2; // left side of full texture (non stretching part) start positionvar textureCenter:Texture; // center of timer (will be stretched across width)var offsetCenter:Vector2; var textureREnd:Texture;var offsetREnd:Vector2;var timerPerct:float = 1; // percentage (0 to 1) this stretches the centervar desiredWidth:float = 403; // max width of the timer in pixelsfunction setTime(newTime:float) {timerPerct = newTime; // sets the percent based on value}}// SwitchGUI Class: Extends the TextureGUI to be able to load in multiple textures and switch between themclass SwitchGUI extends TextureGUI {var switchableTextures = new List.<Texture>();var currentTexture:int = 0;function Start() {if (switchableTextures.Count > 0) {texture = switchableTextures[currentTexture];}}function changeTexture(switchTo:int) {if (switchTo < switchableTextures.Count && switchTo >= 0) {texture = switchableTextures[switchTo];currentTexture = switchTo;} else {//Debug.Log( this + ": tried to call invalid part of switchTextures array!");}}function up() {if ((currentTexture+1) < switchableTextures.Count) {++currentTexture;texture = switchableTextures[currentTexture];} else {//Debug.Log( this + ": at the top!");}}function nextTexture() {if ((currentTexture+1) < switchableTextures.Count) { // if we are at the end of the array++currentTexture;texture = switchableTextures[currentTexture];} else {// loop to the beginningcurrentTexture = 0;texture = switchableTextures[currentTexture];}}function down() {if ((currentTexture-1) >= 0) {--currentTexture;texture = switchableTextures[currentTexture];} else {//Debug.Log( this + ": at the bottom!");}}}// Location class: class Location {enum Point { TopLeft, TopRight, BottomLeft, BottomRight, Center}var pointLocation = Point.TopLeft;var offset:Vector2;function updateLocation() {switch(pointLocation) {case pointLocation.TopLeft:offset = Vector2(0,0);break;case pointLocation.TopRight:offset = Vector2(Screen.width,0);break;case pointLocation.BottomLeft:offset = Vector2(0,Screen.height);break;case pointLocation.BottomRight:offset = Vector2(Screen.width,Screen.height);break;case pointLocation.Center:offset = Vector2(Screen.width/2,Screen.height/2);break;}}}class TextureAnchor {enum Point { TopLeft, TopRight, BottomLeft, BottomRight, Center}var anchorPoint = Point.TopLeft;var offset:Vector2;function update() {switch(anchorPoint) {case anchorPoint.TopLeft:offset = Vector2(0,0);break;case anchorPoint.TopRight:offset = Vector2(Screen.width,0);break;case anchorPoint.BottomLeft:offset = Vector2(0,Screen.height);break;case anchorPoint.BottomRight:offset = Vector2(Screen.width,Screen.height);break;case anchorPoint.Center:offset = Vector2(Screen.width/2,Screen.height/2);break;}}}
Drag the script to joystick and deploy the texture to run the visible background texture of joystick. Of course, there are points in the coordinates:
We set it to bottomleft in the script and set switchtexture:
After the configuration is complete, click Run. The joystick texture is displayed in the lower left corner:
You can use the pixel settings in the script to adjust the coordinates of the two texture maps and make them consistent:
Adjusted Results
At the same time, replace the joystick script with the following script to hide the joystick and only display joystick in the joystick area:
//////////////////////////////////////////////////////////////// Joystick.js// Penelope iPhone Tutorial//// 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//////////////////////////////////////////////////////////////#pragma strict@script RequireComponent( GUITexture )// A simple class for bounding how far the GUITexture will moveclass Boundary {var min : Vector2 = Vector2.zero;var max : Vector2 = Vector2.zero;}static private var joysticks : Joystick[];// A static collection of all joysticksstatic private var enumeratedJoysticks : boolean = false;static private var tapTimeDelta : float = 0.3;// Time allowed between tapsvar touchPad : boolean; // Is this a TouchPad?var touchZone : Rect;var deadZone : Vector2 = Vector2.zero;// Control when position is outputvar normalize : boolean = false; // Normalize output after the dead-zone?var position : Vector2; // [-1, 1] in x,yvar tapCount : int;// Current tap countprivate var lastFingerId = -1;// Finger last used for this joystickprivate var tapTimeWindow : float;// How much time there is left for a tap to occurprivate var fingerDownPos : Vector2;private var fingerDownTime : float;private var firstDeltaTime : float = 0.5;private var gui : GUITexture;// Joystick graphicprivate var defaultRect : Rect;// Default position / extents of the joystick graphicprivate var guiBoundary : Boundary = Boundary();// Boundary for joystick graphicprivate var guiTouchOffset : Vector2;// Offset to apply to touch inputprivate var guiCenter : Vector2;// Center of joystickprivate var alphaOff:float = 0.0;function Start(){// Cache this component at startup instead of looking up every framegui = GetComponent( GUITexture );// Store the default rect for the gui, so we can snap back to itdefaultRect = gui.pixelInset;gui.color.a = alphaOff; defaultRect.x += transform.position.x * Screen.width; // + gui.pixelInset.x; // - Screen.width * 0.5; defaultRect.y += transform.position.y * Screen.height; //+ gui.pixelInset.y; // - Screen.height * 0.5; transform.position.x = 0.0; transform.position.y = 0.0; if ( touchPad ){// If a texture has been assigned, then use the rect ferom the gui as our touchZoneif ( gui.texture )touchZone = defaultRect;}else{// This is an offset for touch input to match with the top left// corner of the GUIguiTouchOffset.x = defaultRect.width * 0.5;guiTouchOffset.y = defaultRect.height * 0.5;// Cache the center of the GUI, since it doesn't changeguiCenter.x = defaultRect.x + guiTouchOffset.x;guiCenter.y = defaultRect.y + guiTouchOffset.y;// Let's build the GUI boundary, so we can clamp joystick movementguiBoundary.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;}}function Disable(){gameObject.active = false;enumeratedJoysticks = false;}function ResetJoystick(){// Release the finger control and set the joystick back to the default positiongui.pixelInset = defaultRect;lastFingerId = -1;position = Vector2.zero;fingerDownPos = Vector2.zero;gui.color.a = alphaOff;}function IsFingerDown() : boolean{return (lastFingerId != -1);}function LatchedFinger( fingerId : int ){// If another joystick has latched this finger, then we must release itif ( lastFingerId == fingerId )ResetJoystick();}function Update(){if ( !enumeratedJoysticks ){// Collect all joysticks in the game, so we can relay finger latching messagesjoysticks = FindObjectsOfType(Joystick) as Joystick[];enumeratedJoysticks = true;}var count = Input.touchCount;// Adjust the tap time window while it still availableif ( tapTimeWindow > 0 )tapTimeWindow -= Time.deltaTime;elsetapCount = 0;if ( count == 0 )ResetJoystick();else{for(var i : int = 0;i < count; i++){var touch : Touch = Input.GetTouch(i);var guiTouchPos : Vector2 = touch.position - guiTouchOffset;var shouldLatchFinger = false;if ( touchPad ){if ( touchZone.Contains( touch.position ) )shouldLatchFinger = true;}else if ( gui.HitTest( touch.position ) ){shouldLatchFinger = true;gui.color.a = .5;}// Latch the finger if this is a new touchif ( shouldLatchFinger && ( lastFingerId == -1 || lastFingerId != touch.fingerId ) ){if ( touchPad ){//gui.color.a = 0.15;lastFingerId = touch.fingerId;fingerDownPos = touch.position;fingerDownTime = Time.time;}lastFingerId = touch.fingerId;// Accumulate taps if it is within the time windowif ( tapTimeWindow > 0 )tapCount++;else{tapCount = 1;tapTimeWindow = tapTimeDelta;}// Tell other joysticks we've latched this fingerfor ( var j : Joystick 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 touchesif ( touch.tapCount > tapCount )tapCount = touch.tapCount;if ( touchPad ){// For a touchpad, let's just set the position directly based on distance from initial touchdownposition.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 isgui.pixelInset.x = Mathf.Clamp( guiTouchPos.x, guiBoundary.min.x, guiBoundary.max.x );gui.pixelInset.y = Mathf.Clamp( guiTouchPos.y, guiBoundary.min.y, guiBoundary.max.y );}if ( touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled ) {ResetJoystick();}}}}if ( !touchPad ){// Get a value between -1 and 1 based on the joystick graphic locationposition.x = ( gui.pixelInset.x + guiTouchOffset.x - guiCenter.x ) / guiTouchOffset.x;position.y = ( gui.pixelInset.y + guiTouchOffset.y - guiCenter.y ) / guiTouchOffset.y;}// Adjust for dead zonevar absoluteX = Mathf.Abs( position.x );var absoluteY = Mathf.Abs( position.y );if ( absoluteX < deadZone.x ){// Report the joystick as being at the center if it is within the dead zoneposition.x = 0;}else if ( normalize ){// Rescale the output after taking the dead zone into accountposition.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 zoneposition.y = 0;}else if ( normalize ){// Rescale the output after taking the dead zone into accountposition.y = Mathf.Sign( position.y ) * ( absoluteY - deadZone.y ) / ( 1 - deadZone.y );}}
Run the following project to find that joystick is missing:
However, clicking the screen will show: