当前位置: 首页 > news >正文

unity制作连连看小教程

连连看的规则大家应该都知道,选中的两个图片相同,并且不能多于两个拐点能连在一块,这两个图片就可以消掉;
连通的类型:
直线型;
一个拐点;
两个拐点;
下面开始介绍这三种连通类型
直线型:
直线型分两种,一种是横向,一种是竖向;
首先是横向连接


A,B 两点的 x 坐标相同,图片类型相同,从 A 点开始到 B 点检测,如果 AB 两点之间没有其他图片就销毁 AB 两个图片,竖向的和横向的类似
一个拐点:

AB 两点的 x 坐标和 y 坐标都不相同的时候开始检测一个拐点是否可以连接,通过 AB 两点计算出 CD 两点,然后分别检测 AC BC,AD,BD 是否可以通过直线型连接到一起,显然 AB 两点可以通过 A>C,C>B 连接到一起,
两个拐点:

AB 两点,从 A 开始,横向和竖向把所有和 A 能直线连接的点找出来,用这些点和 B 点做一个拐点的检测,显然 A 点下边的那个点可以通过绿色的那个点可以通过一个拐点的方式和 B 点连接起来,
 
哈哈,是不是很简单啊,上边的就是连连看的核心内容
 
接下来详细介绍一下各个脚本的作用,(哇哈哈,以注释的形式给大家介绍吧)
GameManager.cs  游戏的核心代码,产生图片,判断是否可以销毁等

复制代码
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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
[color=#008ef1][font=宋体]using UnityEngine;[/font][/color]
using System.Collections;
using System.Collections.Generic;
 
public class GameManager : MonoBehaviour
{
     public DrawLine drawLine;//画线
     public GameObject tilePrefab;//tile的预制
     public List<Tile> tiles;//开始实例化的时候存放tile
     public List<Tile> _tiles;//存放随机摆放的tile
     public List<Tile> tilesEdge;//为了边界处可以有拐点,把棋盘四周的tile放在这里,这里的tile看不到
     public int x, y;//棋牌的大小,两个数必须是偶数
     private Tile tileA;
     private Tile tileB;
     private bool destroy;
     private Vector3 mousePos;
     private enum stepType//控制游戏的状态
     {
         one,
         two,
         three
     }
     private stepType _stepType;
 
     void Start ()
     {
         this.gameObject.transform.position = Vector3.zero;
         Spawn ();
         _stepType = stepType.one;
     }
     
     private void Spawn ()//实例化tile
     {
         float num = (x * y - (2 * x + 2 * y - 4)) * 0.5f;
         for (int i = 0; i <num; i ++) {
             int idTex = Random.Range (20, 45);
             GameObject obj = Instantiate (tilePrefab) as GameObject;
             GameObject obj2 = Instantiate (tilePrefab) as GameObject;
             Tile tile = obj.GetComponent<Tile> ();
             Tile tile2 = obj2.GetComponent<Tile> ();
             tile.Init (idTex);
             tile2.Init (idTex);
             tiles.Add (tile);
             tiles.Add (tile2);
         }
         for (int i = 0; i<((2*x+2*y) -4); i++) {//实例化边缘的tile
             GameObject obj = Instantiate (tilePrefab) as GameObject;
             obj.name = "edage";
             Tile tile = obj.GetComponent<Tile> ();
             tilesEdge.Add (tile);
         }
         CreatTile ();
         for (int i = 0; i < _tiles.Count; i++) {
             _tiles [i].transform.name = i.ToString ();
             _tiles [i].id = i;
         }
         this.transform.position = new Vector3 (-(x / 2.0f - 0.5f), -(y / 2.0f - 0.5f), 0);
     }
     
     private void CreatTile ()//随机摆放tile,如果是边缘的就放在边缘位置
     {
         int idTex = 0;
         float _y = 0.0f;
         for (int i = 0; i < y; i ++) {
             float _x = 0.0f;
             for (int j = 0; j < x; j ++) {
                 if (i == 0 || j == 0 || i == y - 1 || j == x - 1) {
                     tilesEdge [0].transform.position = new Vector3 (_x, _y, 0);
                     tilesEdge [0].pos = new Vector2 (_x, _y);
                     tilesEdge [0].transform.rotation = new Quaternion (0, 0, 180, 0);
                     tilesEdge [0].transform.parent = this.gameObject.transform;
                     _tiles.Add (tilesEdge [0]);
                     tilesEdge [0].transform.localScale = Vector3.zero;
                     tilesEdge [0].type = false;
                     tilesEdge.RemoveAt (0);
                 } else {
                     int id = Mathf.FloorToInt (Random.Range (0, tiles.Count));
                     tiles [id].transform.position = new Vector3 (_x, _y, 0);
                     tiles [id].pos = new Vector2 (_x, _y);
                     tiles [id].transform.rotation = new Quaternion (0, 0, 180, 0);
                     tiles [id].transform.parent = this.gameObject.transform;
                     _tiles.Add (tiles [id]);
                     tiles.RemoveAt (id);
                 }
                 _x += 1;
             }
             _y += 1;
         }
     }
     
     private void SelectTile ()//开始选择图片,通过射线方式选中,如果tileA和tileB不相同,则tileA等于tileB开始下一个检测
     {
         Ray ray = Camera.mainCamera.ScreenPointToRay (mousePos);
         RaycastHit hit;
         int mask = 1 << 8;
         if (Physics.Raycast (ray, out hit, mask)) {
             if (tileA == null) {
                 tileA = hit.transform.GetComponent<Tile> ();
                 tileA.SetTileTexture (1);
//                print ("tileA = hit.transform.GetComponent<Tile> ();" + tileA.transform.name);
             } else {
                 tileB = hit.transform.GetComponent<Tile> ();
                 tileB.SetTileTexture (1);
//                print ("tileB = hit.transform.GetComponent<Tile> ();" + tileB.transform.name);
                 Compare (tileA, tileB);
                 if (tileA == null ;; tileB == null) {
                     
//                    print ("a and b is null");
                 }
             }
//            hit.transform.GetComponent
         }
     }
     
     private void Compare (Tile tile1, Tile tile2)//比较两个点是否可以连接到一起
    
         // same card
         _stepType = stepType.one;
         drawLine.waypoints.Add (tile1.transform); //第一个选择的tile是画线的起始位置,
         drawLine.transform.position = tile1.transform.position;
         destroy = false;
         print ("compare");
         if (tile1.pos.x == tile2.pos.x ;; tile1.pos.y == tile2.pos.y) {如果选中的是同一个图片返回
             tileA.SetTileTexture (0);
//            tileB.SetTileTexture (0);
             tileA = tileB;
             tileB = null;
//            tileA.SetTileTexture (1);
             return;
         } else if (tile1.pos.x == tile2.pos.x ;; tile1.pos.y != tile2.pos.y) {//如果两点的x相等,竖向检测
             print ("check y");
             destroy = CheckY (tile1, tile2);
             if (destroy)
                 drawLine.waypoints.Add (tile2.transform);
         } else if (tile1.pos.x != tile2.pos.x ;; tile1.pos.y == tile2.pos.y) {//如果两点的y相等,横向检测
             print ("check x");
             destroy = CheckX (tile1, tile2);
             if (destroy)
                 drawLine.waypoints.Add (tile2.transform);
         }
         if (!destroy) {//不符合直线连接方式的开始进行一个拐点的检测
             _stepType = stepType.two;
             destroy = CheckTwoStep (tile1, tile2);
//            print ("destroy = CheckTwoStep (tile1, tile1);:" + destroy);
             print ("check two step");
             if (!destroy) {//不符合直线和一个拐点检测的开始进行两个拐点的检测
                 _stepType = stepType.three;
                 destroy = CheckThreeStep (tile1, tile2);
                 print ("check three:" + destroy);
                 print ("tile1.idTex:" + tile1.idTex + "tile1.idTex:" + tile1.idTex);
             }
         }
         if (destroy) {//如果符合销毁条件销毁图片,并开始画线
             tile1.transform.localScale = Vector3.zero;
             tile2.transform.localScale = Vector3.zero;
             tile1.type = false;
             tile2.type = false;
             tileA = null;
             tileB = null;
             drawLine.MoveToWaypoint ();
 
         } else {//不符合的话,清除画线中的路径
             drawLine.ClearPath ();
             tileA.SetTileTexture (0);
//            tileB.SetTileTexture (0);
             tileA = tileB;
             tileB = null;
             return;
         }
     }
 
// one step横向检测
     private bool CheckX (Tile a, Tile b)
     {
         bool compare = true;
         int _min, _max;
         if (a.idTex == b.idTex) {
             CompareID (a, b, out _min, out _max);
             _min += 1;
             if (_min == _max)
                 return true;
             for (int i = _min; i < _max; i++) {
                 if (_tiles [i].type == true) {
                     compare = false;
                     break;
                 }
             }
             return compare;
         } else
             return false;
     }
//竖向检测
     private bool CheckY (Tile a, Tile b)
     {
         bool compare = true;
         int _min, _max;
//        int idA = (int)(a.x * x + a.y);
//        int idB = (int)(b.x * x + b.y);
//        print (_tiles [idA].id.ToString () + "idA:" + idA);
//        print (_tiles [idB].id.ToString () + "idB:" + idB);
//        print ("a.idtex:" + a.idTex + "b.idtex:" + b.idTex);
         if (a.idTex == b.idTex) {
             CompareID (a, b, out _min, out _max);
             _min += x;
             if (_min == _max)
                 return true;
             for (int i = _min; i < _max; i+=x) {
//                print ("1step");
                 if (_tiles [i].type == true) {
                     compare = false;
                     break;
                 }
             }
//            if (compare) {
//                print ("2step");
//                a.type = false;
//                b.type = false;
//            }
             return compare;
         } else
             return false;
     }
// two step一个拐点的检测
     private bool CheckTwoStep (Tile a, Tile b)
     {
         if (a.pos.x == b.pos.x || a.pos.y == b.pos.y)
             return false;
         int id1 = (int)(a.pos.y * x + b.pos.x);
         if (_tiles [id1].type == false) {
             _tiles [id1].idTex = a.idTex;
             
             if (CheckY (_tiles [id1], b)) {
                 if (CheckX (a, _tiles [id1])) {
                     if (_stepType == stepType.two) {
                         drawLine.waypoints.Add (_tiles [id1].transform);
                         drawLine.waypoints.Add (b.transform);
                     } else if (_stepType == stepType.three) {
                         drawLine.waypoints.Add (_tiles [id1].transform);
                         print ("=====================:" + 1);
                     }
                     return true;
                 }
//                else
//                    return false;
             }       
         }
         int id2 = (int)(b.pos.y * x + a.pos.x);
         if (_tiles [id2].type == false) {
             _tiles [id2].idTex = b.idTex;
             
             if (CheckY (a, _tiles [id2])) {
                 if (CheckX (b, _tiles [id2])) {
                     if (_stepType == stepType.two) {
                         drawLine.waypoints.Add (_tiles [id2].transform);
                         drawLine.waypoints.Add (b.transform);
                     } else if (_stepType == stepType.three) {
                         drawLine.waypoints.Add (_tiles [id2].transform);
                         print ("=====================:" + 2);
                     }
                     return true;
                 }
//                else
//                    return false;
             }
         }
         return false;
 
     }
// three step两个拐点的检测
     private bool CheckThreeStep (Tile a, Tile b)
     {
         print ("a:" + a.idTex + "b:" + b.idTex);
//        if (a.pos.x == b.pos.x || a.pos.y == b.pos.y) return false;
         bool returnValue = false;
         print ("returnValue:" + returnValue);
         List<Tile> _comparrPointsB;
         ComparePoints (b, out _comparrPointsB);//返回b点可以横竖直线相连的点
         for (int i =0; i<_comparrPointsB.Count; i++) {
             returnValue = CheckTwoStep (a, _comparrPointsB [i]);
             if (returnValue) {
                 drawLine.waypoints.Add (_comparrPointsB [i].transform);
                 drawLine.waypoints.Add (b.transform);
                 return returnValue;
             }
         }
         if (!returnValue) {
             List<Tile> _comparrPointsA;
             ComparePoints (a, out _comparrPointsA);
             print (a.name);
             print (b.name);
             for (int i =0; i<_comparrPointsA.Count; i++) {
                 print ("--------------" + b.idTex);
                 returnValue = CheckTwoStep (b, _comparrPointsA [i]);
                 if (returnValue) {
                     drawLine.waypoints.Add (_comparrPointsA [i].transform);
                     drawLine.waypoints.Add (b.transform);
                     return returnValue;
                 }
             }
             
         }
         return returnValue;
     }
//两个拐点的时候返回可以与a横竖直线相连的点
     private void ComparePoints (Tile a, out List<Tile> comparePoints)
     {
         print ("a.idtex" + a.idTex);
         comparePoints = new List<Tile> ();
         comparePoints.Clear ();
//        for (int i = 0; i < y; i ++) {
//            if (i != a.y) {
//                int id = (int)(i * x + a.pos.x);
//                if (_tiles [id].type == false) {
//                    comparePoints.Add (_tiles [id]);
//                    _tiles [id].idTex = a.idTex;
//                }
//            }
//        }
         for (int i = (int)a.pos.y - 1; i >-1; i--) {
             int id = (int)(i * x + a.pos.x);
//            print ("three step :" + id);
             if (_tiles [id].type == false) {
                 comparePoints.Add (_tiles [id]);
                 _tiles [id].idTex = a.idTex;
                 print ("_tiles [id].idTex = a.idTex; " + _tiles [id].idTex);
             } else
                 break;
         }
         for (int i = (int)a.pos.y + 1; i < y; i++) {
             int id = (int)(i * x + a.pos.x);
//            print ("three step :" + id);
             if (_tiles [id].type == false) {
                 comparePoints.Add (_tiles [id]);
                 _tiles [id].idTex = a.idTex;
                 print ("_tiles [id].idTex = a.idTex; " + _tiles [id].idTex);
             } else
                 break;
         }
         for (int i = (int)a.pos.x -1; i >-1; i --) {
             int id = (int)(a.pos.y * x + i);
             if (_tiles [id].type == false) {
                 comparePoints.Add (_tiles [id]);
                 _tiles [id].idTex = a.idTex;
                 print ("_tiles [id].idTex = a.idTex; " + _tiles [id].idTex);
             } else
                 break;
         }
         for (int i = (int)a.pos.x +1; i < x; i ++) {
             int id = (int)(a.pos.y * x + i);
             if (_tiles [id].type == false) {
                 comparePoints.Add (_tiles [id]);
                 _tiles [id].idTex = a.idTex;
                 print ("_tiles [id].idTex = a.idTex; " + _tiles [id].idTex);
             } else
                 break;
         }
//        for (int i = 0; i < x; i ++) {
//            if (i != a.x) {
//                int id = (int)(a.pos.y * x + i);
//                if (_tiles [id].type == false) {
//                    comparePoints.Add (_tiles [id]);
//                    _tiles [id].idTex = a.idTex;
//                }
//            }
//        }
 
     }
 
     private void CompareID (Tile a, Tile b, out int min, out int max)
     {
         
         if (a.id < b.id) {
             min = a.id;
             max = b.id;
         } else {
             min = b.id;
             max = a.id;
         }
     }
 
     Vector2 TexSize ()
     {
         Vector2 size = new Vector2 (1 / x, 1 / y);
         return size;
     }
 
     Vector2 TexOffset (int _idTex)
     {
         int a = (int)(_idTex / x);
//        print (a + "a:" + _idTex);
         int b = (int)(_idTex % x);
//        print (b + "b:" + _idTex);
         Vector2 offset = new Vector2 (b / x, (y - 1 - a) / y);
         return offset;
     }
 
     void Update ()
     {
         
         if (Input.GetMouseButtonUp (0)) {
             mousePos = Input.mousePosition;
             SelectTile ();
         }
      
     }
 
     private void ClearTiles (List<Tile> tiles)
     {
         
         tiles.Clear ();
//         this.gameObject.transform.DetachChildren();
     }
}
 
// ari
DrawLine.cs  画线脚本   用的itween,哈哈,

复制代码
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
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
 
public class DrawLine : MonoBehaviour
{
     public List<Transform> waypoints = new List<Transform> ();
     public float rate = 1;
     private int currentWaypoint = 1;
 
     public void MoveToWaypoint ()
     {           
         print ("public void MoveToWaypoint (): " + waypoints.Count);
         StartCoroutine ("move");
 
     }
 
     public void ClearPath ()
     {
         waypoints.Clear ();
         print ("path.Clear ();");
     }
 
     IEnumerator move ()
     {
         for (int i = 0; i < waypoints.Count; i++) {
             iTween.MoveTo (this.gameObject, waypoints [i].position, rate);
             print ("now id:" + i);
             yield return new WaitForSeconds(rate);
         }
         waypoints.Clear ();
     }
}
Tile.cs    

复制代码
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
using UnityEngine;
using System.Collections;
 
public class Tile : MonoBehaviour
{
     public int id;
     public int idTex; //通过这个判断两个图片是否相同
     public Vector2 pos ;
     public bool type = true;//控制图片的状态,当销毁的时候为false,其他判断的时候可以通过该点
     public float x, y;
     public Texture texA, texB;//鼠标选中的时候可以换贴图
     public GameObject mask;//鼠标选中的时候上边显示的框框
 
     public void Init (int _idTex)
     {
         idTex = _idTex;
         Vector2 offset = TexOffset (_idTex);
         this.renderer.material.SetTextureOffset ("_MainTex", offset);
         this.renderer.material.SetTextureScale ("_MainTex", new Vector2 (0.2f, 0.1f));
         
     }
//设置tile显示的贴图和大小
     public void SetTileTexture (int i)
     {
         if (i == 0) {
             this.renderer.material.mainTexture = texA;
             mask.transform.localScale = Vector3.zero;
         }
         
         if (i == 1) {
             this.renderer.material.mainTexture = texB;
             mask.transform.localScale = new  Vector3 (0.1380835f, 0.1380835f, 0.1380835f);
         }
     }
//这个就是裁剪一张大图,变成一个个小的,贴到tile上
     Vector2 TexOffset (int _idTex)
     {
         int a = (int)(_idTex / x);
         int b = (int)(_idTex % x);
         Vector2 offset = new Vector2 (b / x, (y - 1 - a) / y);
         return offset;   
     }
 
     Vector2 TexSize ()
     {
         Vector2 size = new Vector2 (1 / x, 1 / y);
         return size;
     }
 
}

Menu.cs    加了两个按钮,可以重新开始,哇哈哈

复制代码
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
using UnityEngine;
using System.Collections;
 
public class Menu : MonoBehaviour
{
 
     public GameManager gameManager;
     private GameManager _gameManger;
     private bool start = true;
     
     void OnGUI ()
     {
         if (start) {
             if (GUI.Button (new Rect (10, 10, 100, 50), "start")) {
                 start = false;
                 _gameManger = Instantiate (gameManager) as GameManager;
             }
         }
         if (GUI.Button (new Rect (10, 70, 100, 50), "restart")) {
             if (_gameManger != null) {
                 Destroy (_gameManger.gameObject);
                 print ("Destroy(_gameManger.gameObject);");
             }
             _gameManger = Instantiate (gameManager) as GameManager;
         }
         
     }
}


from:http://game.ceeger.com/forum/read.php?tid=4855&fid=2&page=1

相关文章:

  • iOS应用内支付(IAP)的那些坑
  • Unity 游戏引擎的移动基本版从今天起免费提供
  • 我是如何在GitHub上开源一个项目的(截图说明)
  • Unity3D NGUI事务 UIEvents
  • 解决Win7 64位安装 Microsoft .NET Framework 4 失败的情况
  • Win7上Git安装及配置过程
  • 宝石碰碰:HTML5开发Android本地化App游戏案例
  • HTML5游戏框架cnGameJS开发实录
  • 基于HTML5的超级玛丽游戏demo
  • 利用HTML5开发Android
  • 设计模式--6大原则--开闭原则
  • 设计模式--单例模式
  • 设计模式--6大原则--迪米特法则
  • 设计模式--6大原则--依赖倒置原则
  • 设计模式--6大原则--接口隔离原则
  • Android Volley源码解析
  • C++11: atomic 头文件
  • Cumulo 的 ClojureScript 模块已经成型
  • Debian下无root权限使用Python访问Oracle
  • ES6之路之模块详解
  • iOS筛选菜单、分段选择器、导航栏、悬浮窗、转场动画、启动视频等源码
  • Javascripit类型转换比较那点事儿,双等号(==)
  • JavaScript的使用你知道几种?(上)
  • JAVA多线程机制解析-volatilesynchronized
  • Mithril.js 入门介绍
  • spring cloud gateway 源码解析(4)跨域问题处理
  • spring-boot List转Page
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • webpack入门学习手记(二)
  • WordPress 获取当前文章下的所有附件/获取指定ID文章的附件(图片、文件、视频)...
  • 诡异!React stopPropagation失灵
  • 回顾2016
  • 普通函数和构造函数的区别
  • 腾讯优测优分享 | 你是否体验过Android手机插入耳机后仍外放的尴尬?
  • 微信端页面使用-webkit-box和绝对定位时,元素上移的问题
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • 携程小程序初体验
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • 源码安装memcached和php memcache扩展
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • 怎样选择前端框架
  • 自定义函数
  • Java性能优化之JVM GC(垃圾回收机制)
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • 阿里云移动端播放器高级功能介绍
  • ​云纳万物 · 数皆有言|2021 七牛云战略发布会启幕,邀您赴约
  • (11)MATLAB PCA+SVM 人脸识别
  • (4.10~4.16)
  • (floyd+补集) poj 3275
  • (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
  • (附源码)计算机毕业设计SSM基于java的云顶博客系统
  • (入门自用)--C++--抽象类--多态原理--虚表--1020
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (已解决)报错:Could not load the Qt platform plugin “xcb“
  • .NET CORE Aws S3 使用