【转载】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 中可以设置摇杆的显示位置与显示宽高。

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

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

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

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

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

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 上的效果,通过触摸游戏摇杆可以控制飞机的移动啦,不错吧,哇咔咔~~

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

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

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

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;

    ;     // 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 = -;

        position = Vector2.zero;

        fingerDownPos = Vector2.zero;

    }
    private bool IsFingerDown()
    {

        );

    }
    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;
        )

            tapTimeWindow -= Time.deltaTime;

        else

            tapCount = ;
        )

            ResetJoystick();

        else
        {

            ; 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

                 || 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

                    )

                        tapCount++;

                    else
                    {

                        tapCount = ;

                        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 / ), -, );

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

                    }
                    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 = ;

        }

        else if (normalize)
        {

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

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

        }
        if (absoluteY < deadZone.y)
        {

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

            position.y = ;

        }

        else if (normalize)
        {

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

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

        }
    }
}

C#版

Main.js的C#版:

public class MyPlane : MonoBehaviour
{
    //游戏摇杆对象
    public MyStick moveJoystick;

    //飞机的贴图
    public Texture plan;

    //飞机在屏幕中的坐标
    ;
    ;

    //避免飞机飞出屏幕,分别是X、Y最大坐标,最小坐标是0、0
    ;
    ;

    //飞机移动的速度
    ;

    void Start()
    {
        //初始化赋值
        x = ;
        y = ;
    }

    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;

        //摇杆向左
        )
        {
            x -= planSpeed;
        }
        //摇杆向右
        )
        {
            x += planSpeed;

        }
        //摇杆向上
        )
        {
            y += planSpeed;

        }
        //摇杆向下
        )
        {
            y -= planSpeed;

        }

        //防止飞机飞出屏幕,出界检测
        )
        {
            x = ;
        }
        else if (x > cross_x)
        {
            x = cross_x;
        }

        )
        {
            y = ;
        }
        else if (y > cross_y)
        {
            y = cross_y;
        }
    }

    void OnGUI()
    {
        //将飞机绘制在屏幕中
        GUI.DrawTexture(, ), plan);
    }
}

C#版

 摇杆背景图的代码(设置与摇杆在同一个位置):

[RequireComponent(typeof(GUITexture))]
public class myStickBg : MonoBehaviour
{
    void Start()
    {
        // * 前提:编辑状态下bg摇杆的背景图片的position必须要和摇杆的position一样,z可以不一样,不然背景图bg容易跑偏
        //获取滑动的图片
        GUITexture gui = (GUITexture)GetComponent(typeof(GUITexture));
        //获取滑动图片的位置和大小
        Rect defaultRect = gui.pixelInset;
        //设置滑动图片的x坐标
        defaultRect.x += transform.position.x * Screen.width;// + gui.pixelInset.x; // -  Screen.width * 0.5;
        //设置滑动图片的y坐标
        defaultRect.y += transform.position.y * Screen.height;// - Screen.height * 0.5;
        //设置图片的位置
        transform.position = , , -1f);
        //重新设置值
        gui.pixelInset = defaultRect;
    }
}

myStickBg

上一篇:saiku 分布式实践


下一篇:jsp篇 之 指令元素和动作元素