unity零基礎開始學習做游戲(四)biu~biu~biu發射子彈打飛機


-------小基原創,轉載請給我一個面子

  主角都能移動了,那不得做點什么伸張正義,守護世界和平的事嘛,拿起家伙biu~biu~biu~

 

首先得做一個好人和一個壞人

老規矩,Canvas下創建兩個Image,一個叫做player,一個叫做enemy1好了

 

一個紅色,一個藍色(自古紅藍出CP,不好意思,走錯片場了●﹏●)

 新知識:要加BoxCollider2D

子彈打到別人,其實是碰撞檢測的過程

一種是根據位置坐標,判斷子彈有沒有打中,另一種是使用物理碰撞系統(小基這里使用后者)

 兩個物體物理碰撞檢測條件

1.物體A,物體B,必須都有Collider

2.運動的一方要有剛體(本例對應的是子彈,后面介紹)

最終效果是這樣的。

 

 

下面開始制作子彈

Canvas下創建一個Image,修改名字叫做Bullet,使用自帶的圓形圖片湊合用吧

同樣添加BoxCollider2D,最后切記添加RigidBody,不然沒法碰撞成功

額外提一句,重力調成0吧,不然子彈拋物線飛=_=

現在看起來是這樣的。

 

接下來想想怎么讓子彈動起來,上代碼(創建一個腳本Bullet),給那個子彈掛上

 1 using System.Collections;
 2 using System.Collections.Generic;
 3 using UnityEngine;
 4 
 5 public class Bullet : MonoBehaviour {
 6 
 7     public float speed = 15;
 8     public Vector2 dir = new Vector2(1, 0);
 9     public float lifeTime = 5f;
10 
11     public float damage = 1;
12     // Use this for initialization
13     void Start () {
14         DestroySelf(lifeTime);
15     }
16     
17     // Update is called once per frame
18     void Update () {
19         this.transform.Translate(dir * speed * Time.deltaTime, Space.World);
20     }
21 
22     void DestroySelf(float time)
23     {
24         Destroy(this.gameObject, time);
25     }
26 
27     void OnTriggerEnter2D(Collider2D other)
28     {
29         //Debug.Log("enter");
30         if (other.gameObject.tag == "Enemy")
31         {
33 Destroy(this.gameObject); 34 } 35 } 36 }

start()里面調用DestroySelf(),意思是子彈存活lifeTime 時間后,自動消失。不然“讓子彈飛”飛到啥時候是個頭啊,飛出屏幕外就沒有意義了。存活時間可以自己控制

Update()方法里面,每幀都在按照指定的dir方向移動,你想讓子彈怎么飛,朝哪飛,就自行設計吧,螺旋升天都沒有問題~

OnTriggerEnter2D這個方法是Unity自帶的,用處就是當進入碰撞盒Collider時調用,小基的內容是銷毀自己,不是~意思是銷毀掉子彈

這里加了一個if的判斷,判斷tag是不是Enemy,畢竟打到友軍可是會被噴豬隊友的

Tag一般默認是Untagged的,點擊一下后有彈出AddTag這個選項,然后

點擊加號,可以自己創建需要的tag,這樣可以給不同類別的物體做區分。

 

 下面考慮怎么讓主角射出子彈,這個就需要代碼控制了

  1 using System.Collections;
  2 using System.Collections.Generic;
  3 using UnityEngine;
  4 
  5 public class MyInput2 : MonoBehaviour {
  6     //移動方向枚舉
  7     enum MoveDir
  8     {
  9         None = 0,       //不動
 10         Up = 1,         //上8
 11         Down = -1,      //下2
 12         Left = 10,      //左4
 13         Right = -10,    //右6
 14         UL = 11,        //左上7
 15         UR = -9,        //右上9
 16         DL = 9,         //左下1
 17         DR = -11,       //右下3
 18     }
 19 
 20     //輸入按鍵常量(之后走配置)
 21     const KeyCode INPUT_UP = KeyCode.W;
 22     const KeyCode INPUT_DOWN = KeyCode.S;
 23     const KeyCode INPUT_LEFT = KeyCode.A;
 24     const KeyCode INPUT_RIGHT = KeyCode.D;
 25 
 26     //默認移動方向
 27     private MoveDir moveDir = MoveDir.None;
 28     //按壓值
 29     private int moveDirValue = 0;
 30     //按壓記錄
 31     private bool isUpPress = false;
 32     private bool isDownPress = false;
 33     private bool isLeftPress = false;
 34     private bool isRightPress = false;
 35 
 36     //是否可以移動
 37     private bool canMove = true;
 38     //右移動
 39     private Vector3 MOVE_RIGHT = new Vector3(1, 0, 0);
 40     //上移動
 41     private Vector3 MOVE_UP = new Vector3(0, 1, 0);
 42 
 43     //外部調控速度
 44     public float speed = 2f;
 45     //移動速度向量
 46     private Vector3 move_speed_dir = Vector3.zero;
 47     //移動距離
 48     private Vector3 move_dis = Vector3.zero;
 49 
 50     //控制目標
 51     public Transform target;
 52 
 53 
 54     //鼠標按下時的坐標
 55     private Vector3 mouseStartPos = Vector3.zero;
 56     //鼠標是否按下
 57     private bool isMousePress = false;
 58     //鼠標枚舉
 59     const KeyCode INPUT_MOUSE = KeyCode.Mouse0;
 60     //鼠標拖動范圍
 61     const float MOUSE_RADIUS = 20;
 62     //鼠標移動向量
 63     private Vector3 mouseDir = Vector3.zero;
 64     //鼠標速度衰減
 65     private float mouseSpeedRate = 0;
 66     //鼠標速度
 67     public float mouseSpeed = 100;
 68     //搖桿底
 69     public RectTransform joyStickDown;
 70     //搖桿頂
 71     public RectTransform joyStickUp;
 72     //攝像機
 73     public Camera camera;
 74 
 75     //子彈prefab
 76     GameObject prefabBullet;
 77     //是否開火
 78     bool isFire = false;
 79     // Use this for initialization
 80     void Start () {
 81         prefabBullet = (GameObject)Resources.Load("Prefab/Bullet");
 82         //Debug.Log("bullet:" + prefabBullet);
 83     }
 84     
 85     // Update is called once per frame
 86     void Update () {
 87         CheckInputKey();
 88         CheckMoveDir();
 89         CheckMouseDir();
 90     }
 91 
 92     void FixedUpdate()
 93     {
 94         CheckMove();
 95         CheckFire();
 96     }
 97 
 98     //檢測輸入按鍵
 99     void CheckInputKey()
100     {
101         //檢測單一輸入
102         foreach (KeyCode kcode in System.Enum.GetValues(typeof(KeyCode)))
103         {
104             if (Input.GetKeyDown(kcode))
105             {
106                 //Debug.Log("Single KeyCode Down: " + kcode);
107                 ChangeKeyPressState(kcode, true);
108             }
109 
110             if (Input.GetKeyUp(kcode))
111             {
112                 //Debug.Log("Single KeyCode Up: " + kcode);
113                 ChangeKeyPressState(kcode, false);
114             }
115         }
116     }
117 
118     //記錄按鍵的按壓狀態
119     void ChangeKeyPressState(KeyCode keyCode, bool isPress)
120     {
121         switch(keyCode)
122         {
123             case INPUT_UP:
124                 isUpPress = isPress;
125                 break;
126             case INPUT_DOWN:
127                 isDownPress = isPress;
128                 break;
129             case INPUT_LEFT:
130                 isLeftPress = isPress;
131                 break;
132             case INPUT_RIGHT:
133                 isRightPress = isPress;
134                 break;
135             case INPUT_MOUSE:
136                 MouseStateChange(isPress);
137                 break;
138             case INPUT_FIRE:
139                 isFire = isPress;
140                 break;
141         }
142     }
143 
144     //鼠標按鍵輸入
145     void MouseStateChange(bool isPress)
146     {
147         isMousePress = isPress;
148         mouseStartPos = isPress ? Input.mousePosition : Vector3.zero;
149         joyStickDown.gameObject.SetActive(isPress);
150         joyStickDown.position = camera.ScreenToWorldPoint(mouseStartPos);
151     }
152 
153     //鼠標移動
154     void CheckMouseDir()
155     {
156         if(isMousePress)
157         {
158             mouseDir = Input.mousePosition - mouseStartPos;
159             mouseSpeedRate = Mathf.Min(mouseDir.magnitude / MOUSE_RADIUS, 1);
160             move_dis = mouseSpeed * mouseSpeedRate * Time.deltaTime * mouseDir.normalized;
161             target.position += move_dis;
162             joyStickUp.localPosition = mouseDir.normalized * mouseSpeedRate * MOUSE_RADIUS;
163         }
164     }
165 
166     //確定移動方向
167     void CheckMoveDir()
168     {
169         moveDirValue = 0;
170         //確定方向
171         if(isUpPress)
172         {
173             moveDirValue += (int)MoveDir.Up;
174         }
175         if (isDownPress)
176         {
177             moveDirValue += (int)MoveDir.Down;
178         }
179         if (isLeftPress)
180         {
181             moveDirValue += (int)MoveDir.Left;
182         }
183         if (isRightPress)
184         {
185             moveDirValue += (int)MoveDir.Right;
186         }
187     }
188 
189     //檢測是否可以移動
190     void CheckMove()
191     {
192         //某些情況下可能禁止移動,例如暫停,播放CG等
193         if(canMove && moveDirValue != (int)MoveDir.None)
194         {
195             PlayerMove(target, speed);
196         }
197     }
198 
199     //移動
200     void PlayerMove(Transform target, float speed)
201     {
202         move_dis = speed * Time.deltaTime * GetSpeedDir();
203         target.position += move_dis;
204     }
205 
206     //速度向量
207     Vector3 GetSpeedDir()
208     {
209         switch(moveDirValue)
210         {
211             case (int)MoveDir.Up:
212                 move_speed_dir = MOVE_UP;
213                 break;
214             case (int)MoveDir.Down:
215                 move_speed_dir = -MOVE_UP;
216                 break;
217             case (int)MoveDir.Left:
218                 move_speed_dir = -MOVE_RIGHT;
219                 break;
220             case (int)MoveDir.Right:
221                 move_speed_dir = MOVE_RIGHT;
222                 break;
223             case (int)MoveDir.UL:
224                 move_speed_dir = MOVE_UP - MOVE_RIGHT;
225                 break;
226             case (int)MoveDir.UR:
227                 move_speed_dir = MOVE_UP + MOVE_RIGHT;
228                 break;
229             case (int)MoveDir.DL:
230                 move_speed_dir = -MOVE_UP - MOVE_RIGHT;
231                 break;
232             case (int)MoveDir.DR:
233                 move_speed_dir = -MOVE_UP + MOVE_RIGHT;
234                 break;
235         }
236         return move_speed_dir.normalized;
237     }
238 
239     //開火
240     public const KeyCode INPUT_FIRE = KeyCode.Space;
241     //開火時間cd
242     public float fireCD = 0.5f;
243     //上次開火時間
244     float lastTime = 0;
245     //當前時間
246     float curTime = 0;
247 
248     void CheckFire()
249     {
250         if(isFire)
251         {
252             Fire();
253         }
254     }
255 
256     void Fire()
257     {
258         curTime = Time.time;
259         if(curTime - lastTime > fireCD)
260         {
261             //創建子彈
262             CreateBullet();
263             lastTime = curTime;
264         }
265     }
266 
267     //畫布
268     public Canvas canvas;
269     //創建子彈
270     void CreateBullet()
271     {
272         GameObject bullet = Instantiate(prefabBullet, target.localPosition, target.rotation);
273         bullet.transform.SetParent(canvas.transform, false);
274     }
275 }

還是之前的移動代碼,增加了

這里是需要先把Bullet制作成Prefab預制體,使用Resources.Load方法載入子彈prefab

在Project下創建文件夾Resources(必須用這個才可以載入)下面Prefab里面都是做好的預制體,做法就是把Hierarchy下的對象直接拖到Prefab文件夾下(請無視與本例無關的東西)

ChangeKeyPressState()方法里面增加判斷開火的按鍵,FixedUpdate里面檢測是不是開火,if里面可以加很多條件,比如CG時候不能開火,沒有子彈不能開火等等。

Fire()這個方法里面記錄了開火的當前時間,下次開火的時候判斷是不是已經過了CD時長,不然按住按鍵,每幀都會開火,火力太猛沒法玩了,氪金大佬專屬。

CreateBullet()這里面使用了Instantiate這個自帶的方法,通過前面載入的子彈預設體prefab可以克隆出bullet(就是通過模版復制的過程)

切記要把bullet的父物體設置成Canvas,就是通過代碼把bullet放到canvas下面,不然你子彈可能不在UI里面哦~

SetParent()方法的第二個參數用false,這樣可以保證bullet的size不會因為父節點變化而變化。好奇的話,你可以寫成true試試

來試試發射子彈,打到敵人就消失了。

 

 

不過這敵人木頭人太愣了,給他點反應吧,enemy1上添加腳本enemy1(腳本名字你隨便起)

 1 using System.Collections;
 2 using System.Collections.Generic;
 3 using UnityEngine;
 4 
 5 public class Enemy : MonoBehaviour {
 6     void ByHit()
 7     {
 8         Debug.Log("byHit");
 9         this.transform.Translate(Vector2.one);
10     }
11 }

就寫一個方法,被揍了,效果就是朝着vector2.one也就是(1,1)這個方向移動。你想讓有啥反映就在這里寫就可以,想變大變小變色都沒問題

那么怎么才能被調用呢?

1 void OnTriggerEnter2D(Collider2D other)
2     {
3         Debug.Log("enter");
4         if (other.gameObject.tag == "Enemy")
5         {
6             other.gameObject.SendMessage("ByHit");
7             Destroy(this.gameObject);
8         }
9     }

剛才子彈的碰撞里面,增加sendMessage這個方法,參數是被調用方法的名字。這樣就可以調用到碰撞的對方身上的腳本里面的"ByHit"這個方法

雖然敵人暫時還不能反擊,不過起碼不那么木訥了∠( ᐛ 」∠)_

敵人怎么反擊的話,大家自己發揮想象力去做就好了,讓他發出散彈打你也是可以的哦~

但願你可以不用參考這個工程就能自己做出來https://pan.baidu.com/s/1e9rQIJt8AYDgHVPvbyJEuQ

 

不過,這個敵人為啥打不死呢?!哦,因為他沒有血條啊!那咋整啊?下一篇介紹制作通用進度條(血條)(= ̄ω ̄=)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM