首页 > 代码库 > 【转载】Unity3D研究院之IOS自定义游戏摇杆与飞机平滑的移动

【转载】Unity3D研究院之IOS自定义游戏摇杆与飞机平滑的移动

移动开发游戏中使用到的触摸游戏摇杆在iPhone上是非常普遍的,毕竟是全触摸屏手机,今天MOMO 通过一个小例子和大家讨论Unity3D 中如何自定义一个漂亮的全触摸游戏摇杆。

       值得高兴的是,Unity3D 游戏引擎的标准资源中已经帮助我们封装了一个游戏摇杆脚本,所以实现部分的代码可以完全借助它的,具体调用需要我们自己来。

 Joystick.js是官方提供的脚本,具体代码如下,有兴趣的朋友可以仔细研究研究,MOMO就不多说啦。哇咔咔~

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
//////////////////////////////////////////////////////////////
// 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
//////////////////////////////////////////////////////////////
 
@script RequireComponent( GUITexture )
 
// A simple class for bounding how far the GUITexture will move
class Boundary
{
var min : Vector2 = Vector2.zero;
var max : Vector2 = Vector2.zero;
}
 
static private var joysticks : Joystick[]; // A static collection of all joysticks
static private var enumeratedJoysticks : boolean = false;
static private var tapTimeDelta : float = 0.3; // Time allowed between taps
 
var touchPad : boolean; // Is this a TouchPad?
var touchZone : Rect;
var deadZone : Vector2 = Vector2.zero; // Control when position is output
var normalize : boolean = false; // Normalize output after the dead-zone?
var position : Vector2; // [-1, 1] in x,y
var tapCount : int; // Current tap count
 
private var lastFingerId = -1; // Finger last used for this joystick
private var tapTimeWindow : float; // How much time there is left for a tap to occur
private var fingerDownPos : Vector2;
private var fingerDownTime : float;
private var firstDeltaTime : float = 0.5;
 
private var gui : GUITexture; // Joystick graphic
private var defaultRect : Rect; // Default position / extents of the joystick graphic
private var guiBoundary : Boundary = Boundary(); // Boundary for joystick graphic
private var guiTouchOffset : Vector2; // Offset to apply to touch input
private var guiCenter : Vector2; // Center of joystick
 
function Start()
{
// Cache this component at startup instead of looking up every frame
gui = GetComponent( 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.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 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.5;
guiTouchOffset.y = defaultRect.height * 0.5;
 
// 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;
}
}
 
function Disable()
{
gameObject.active = false;
enumeratedJoysticks = false;
}
 
function ResetJoystick()
{
// Release the finger control and set the joystick back to the default position
gui.pixelInset = defaultRect;
lastFingerId = -1;
position = Vector2.zero;
fingerDownPosition = Vector2.zero;
 
if ( touchPad )
gui.color.a = 0.025;
}
 
function IsFingerDown() : boolean
{
return (lastFingerId != -1);
}
 
function LatchedFinger( fingerId : int )
{
// If another joystick has latched this finger, then we must release it
if ( lastFingerId == fingerId )
ResetJoystick();
}
 
function Update()
{
if ( !enumeratedJoysticks )
{
// Collect all joysticks in the game, so we can relay finger latching messages
joysticks = FindObjectsOfType( Joystick );
enumeratedJoysticks = true;
}
 
var 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(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;
}
 
// Latch the finger if this is a new touch
if ( 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 window
if ( tapTimeWindow > 0 )
tapCount++;
else
{
tapCount = 1;
tapTimeWindow = tapTimeDelta;
}
 
// Tell other joysticks we‘ve latched this finger
for ( 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 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
gui.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 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
var 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 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 );
}
}

单击Create 创建一个GUI Texture,命名为Joy ,它用来显示游戏摇杆,如下图所示将摇杆的图片资源,与摇杆的脚本连线赋值给Joy.  Pixel Inset 中可以设置摇杆的显示位置与显示宽高。

  

到这一步 build and run 就可以在iPhone上看到这个游戏摇杆,并且可以通过触摸它,360度平滑过度。

在屏幕中绘制一个飞机,通过游戏摇杆去控制飞机的移动。

创建一个脚本,命名为Main.js 如下图所示  将 Main.js 、joy、plan 分别 绑定在Main Camera 上。 

 

moveJoystick.position.x;

moveJoystick.position.y; 

这两个值是非常重要的两个信息,它们的取值范围是 -1 到 +1 ,表示 用户触摸摇杆的位置, 上 下 左 右 的信息。 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//游戏摇杆对象
var moveJoystick : Joystick;
 
//飞机的贴图
var plan : Texture;
 
//飞机在屏幕中的坐标
var x = 0;
var y = 0;
 
//避免飞机飞出屏幕,分别是X、Y最大坐标,最小坐标是0、0
var cross_x = 0;
var cross_y = 0;
 
//飞机移动的速度
var planSpeed = 20;
 
function Start() {
//初始化赋值
x = 100;
y = 100;
cross_x = Screen.width -  plan.width;
cross_y = Screen.height -  plan.height;
 
}
 
function Update () {
//得到游戏摇杆的反馈信息,得到的值是 -1 到 +1 之间
 
var touchKey_x =  moveJoystick.position.x;
var touchKey_y =  moveJoystick.position.y;
 
//摇杆向左
if(touchKey_x == -1){
x -= planSpeed;
 
}
//摇杆向右
else if(touchKey_x == 1){
x += planSpeed;
 
}
//摇杆向上
   if(touchKey_y == -1){
y += planSpeed;
 
}
//摇杆向下
else if(touchKey_y == 1){
y -= planSpeed;
 
}
 
//防止飞机飞出屏幕,出界检测
if(x < 0){
x = 0;
}else if(x > cross_x){
x = cross_x;
}
 
   if(y < 0){
y = 0;
}else if(y > cross_y){
y = cross_y;
}
}
 
function OnGUI () {
 
  //将飞机绘制在屏幕中
  GUI.DrawTexture(Rect(x,y,128,128),plan);  
 
}

导出 build and run  看看在iPhone 上的效果,通过触摸游戏摇杆可以控制飞机的移动啦,不错吧,哇咔咔~~

 

最后欢迎各位盆友可以和MOMO一起讨论Unity3D游戏开发,最近感冒的盆友越来越多,大家要多多注意身体健康噢。哇咔咔~~~ 附上Unity3D工程的下载地址,Xcode项目我就不上传了,须要的自己导出。

 

下载地址:http://vdisk.weibo.com/s/abtVb

  • 本文固定链接: http://www.xuanyusong.com/archives/526
  • 转载请注明: 雨松MOMO 2012年05月01日 于 雨松MOMO程序研究院 发表

 

Joystick.js的C#版:

[RequireComponent(typeof(GUITexture))]public class MyStick : MonoBehaviour{    class Boundary    {        public Vector2 min = Vector2.zero;        public Vector2 max = Vector2.zero;    }    private static MyStick[] joysticks;     // A static collection of all joysticks    private static bool enumeratedJoysticks = false;    private static float tapTimeDelta = 0.3f;     // Time allowed between taps    public bool touchPad;    public Vector2 position = Vector2.zero;    public Rect touchZone;    public Vector2 deadZone = Vector2.zero;     // Control when position is output    public bool normalize = false; // Normalize output after the dead-zone?    public int tapCount;    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;    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    void Start()    {        gui = (GUITexture)GetComponent(typeof(GUITexture));        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 = Vector3.zero;        if (touchPad)        {            // If a texture has been assigned, then use the rect ferom the gui as our touchZone            if (gui.texture)                touchZone = defaultRect;        }        else        {            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 Vector2 getGUICenter()    {        return guiCenter;    }    void Disable()    {        gameObject.SetActive(false);        //enumeratedJoysticks = false;    }    private void ResetJoystick()    {        gui.pixelInset = defaultRect;        lastFingerId = -1;        position = Vector2.zero;        fingerDownPos = Vector2.zero;    }    private 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();    }    void Update()    {        if (!enumeratedJoysticks)        {            // Collect all joysticks in the game, so we can relay finger latching messages            joysticks = (MyStick[])FindObjectsOfType(typeof(MyStick));            enumeratedJoysticks = true;        }        int count = Input.touchCount;        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)                    {                        //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 window                    if (tapTimeWindow > 0)                        tapCount++;                    else                    {                        tapCount = 1;                        tapTimeWindow = tapTimeDelta;                    }                    // Tell other joysticks we‘ve latched this finger                    //for (  j : Joystick in joysticks )                    foreach (MyStick 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                        Rect r = gui.pixelInset;                        r.x = Mathf.Clamp(guiTouchPos.x, guiBoundary.min.x, guiBoundary.max.x);                        r.y = Mathf.Clamp(guiTouchPos.y, guiBoundary.min.y, guiBoundary.max.y);                        gui.pixelInset = r;                    }                    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        var 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 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);        }    }}
C#版

Main.js的C#版:

public class MyPlane : MonoBehaviour{    //游戏摇杆对象    public MyStick moveJoystick;    //飞机的贴图    public Texture plan;    //飞机在屏幕中的坐标    float x = 0;    float y = 0;    //避免飞机飞出屏幕,分别是X、Y最大坐标,最小坐标是0、0    float cross_x = 0;    float cross_y = 0;    //飞机移动的速度    float planSpeed = 20;    void Start()    {        //初始化赋值        x = 50;        y = 50;    }    void Update()    {        cross_x = Screen.width - plan.width;        cross_y = Screen.height - plan.height;        //得到游戏摇杆的反馈信息,得到的值是 -1 到 +1 之间        var touchKey_x = moveJoystick.position.x;        var touchKey_y = moveJoystick.position.y;        //摇杆向左        if (touchKey_x == -1)        {            x -= planSpeed;        }        //摇杆向右        else if (touchKey_x == 1)        {            x += planSpeed;        }        //摇杆向上        if (touchKey_y == -1)        {            y += planSpeed;        }        //摇杆向下        else if (touchKey_y == 1)        {            y -= planSpeed;        }        //防止飞机飞出屏幕,出界检测        if (x < 0)        {            x = 0;        }        else if (x > cross_x)        {            x = cross_x;        }        if (y < 0)        {            y = 0;        }        else if (y > cross_y)        {            y = cross_y;        }    }    void OnGUI()    {        //将飞机绘制在屏幕中        GUI.DrawTexture(new Rect(x, y, 128, 128), plan);    }}
C#版

 

【转载】Unity3D研究院之IOS自定义游戏摇杆与飞机平滑的移动