1. 程式人生 > >Unity官方案例-Space Shooter學習筆記(1)

Unity官方案例-Space Shooter學習筆記(1)

本文記錄了筆者在跟著官方視訊製作教程遊戲的過程,僅是個人的學習總結和記錄,不作為任何教材使用。

1、玩家飛船:將Model資料夾中的Player飛船模組拖拽到Hierarchy檢視中,然後首先設定Rigidbody和Collider;Collider選擇Mesh Collider,但其輪廓太複雜,資源包中有自機的Collider預製件,直接拖入即可。另外在Prefabs資料夾下,有引擎VFX預製件,直接拖拽至Player子層。

碰撞體

2、下面設定Main Camera和光源,由於是俯視視角,鏡頭Rotation.x設定為90,Position.y設定為10,Position.z適當設定,使自機處在遊戲畫面下方位置。Projection設定為Orthographic。Orthographic模式下,遠近物體大小一樣。

3、光源方面,教程中給機身設定了三種光,分別是Main Light、Fill Light和Sim Light,從三個角度打光。最後調整出來效果大致如下,調整好後單獨建立一個空物體,將Light統一管理並放到Player子層。

光

4、下面設定背景,建立3DObject->Quad,設定角度、長、寬,鋪滿螢幕範圍;新增貼圖效果,由於是背景不需要Collider,刪除自帶的Collider設定。在Shader中,選擇Unlit->Texture,這樣會看起來更鮮豔明亮些。

背景

5、下面我們編寫控制自機移動的指令碼。

using System.Collections;
using
System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { private Rigidbody rb;//抓取Rigidbody用 public float speed; void Start () { rb = GetComponent<Rigidbody>(); } void FixedUpdate() { float moveHorizontal = Input.GetAxis("Horizontal"
) ;//獲取鍵盤橫軸輸入 float moveVertical = Input.GetAxis("Vertical") ;//獲取鍵盤縱軸輸入 Vector3 movement= new Vector3(moveHorizontal, 0.0f, moveVertical);//設定一個儲存移動方向的Vector3變數 rb.velocity = movement * speed;//移動方向*移動速度 } }

其中,Rigidbody.velocity的個人大致理解,用此函式可控制物體的移動。

官方說明:

  • Rigidbody の速度ベクトル
  • ほとんどの場合、非現実的な挙動になるため速度を直接修正するべきではありません。 オブジェクトの速度を物理ステップごとに設定しないでください。これは非現実的な物理シミュレーションに繋がります。 速度を変更する上での典型例は、ファーストパーソン・シューティングでのジャンプ時にあります。即座に速度を変更したいためです。

官方示例程式碼:

using UnityEngine;
using System.Collections;

// The velocity in y is 10 units per second.  If the GameObject starts at (0,0,0) then
// it will reach (0,100,0) units after 10 seconds.

public class ExampleClass : MonoBehaviour
{
    public Rigidbody rb;

    private float t = 0.0f;
    private bool moving = false;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void FixedUpdate()
    {
        if (Input.GetButtonDown("Jump"))
        {
            // the cube is going to move upwards in 10 units per second
            rb.velocity = new Vector3(0, 10, 0);
            moving = true;
            Debug.Log("jump");
        }

        if (moving)
        {
            // when the cube has moved over 1 second report it's position
            t = t + Time.deltaTime;
            if (t > 1.0f)
            {
                Debug.Log(gameObject.transform.position.y + " : " + t);
                t = 0.0f;
            }
        }
    }
}

6、現在我們有一個問題,在控制自機移動時,會移動出畫面之外,因為遊戲場景本身是無邊界的。所以我們現在需要控制自機移動範圍;這裡用到了一個很重要的函式“Mathf”;

該函式用處眾多,目前我們需要用的是“Mathf.Clamp”。先看下官方說明:

Mathf.Clamp

説明 public static float Clamp (float value, float min, float max); 與えられた最小 float 値と最大 float 値の範囲に値を制限します。

using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour {
    void Update() {
        transform.position = new Vector3(Mathf.Clamp(Time.time, 1.0F, 3.0F), 0, 0);
    }
}

説明 public static int Clamp (int value, int min, int max); min と max の範囲に値を制限し、その値を返します。

using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour
{
    // Use this for initialization
    void Start()
    {
        // Clamps the value 10 to be between 1 and 3.
        // prints 3 to the console

        Debug.Log(Mathf.Clamp(10, 1, 3));
    }
}

嗯,基本可以理解為設定一個範圍,將值限定在其之內。我們可以利用這個將移動範圍限制起來。編寫程式碼如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]//使用其他的Class時,需要新增

//建立了一個新的儲存移動界限的類,為了Unity介面方便除錯
public class Boundary
{
    public float minZ, maxZ, minX, maxX;
}

public class PlayerController : MonoBehaviour
{
    public Boundary boundary;//要使用新建類,這一步不可少
    public float tilt;//機身側傾幅度

    void FixedUpdate()
    {           
        //下面就是限制移動範圍的程式碼
        rb.position = new Vector3
            (
            Mathf.Clamp(rb.position.x, boundary.minX, boundary.maxX),
            0.0f,
            Mathf.Clamp(rb.position.z, boundary.minZ, boundary.maxZ)
            );

        //另外添加了這一條程式碼,作用是左右移動時,機身會側傾
         rb.rotation = Quaternion.Euler(0.0f, 0.0f, rb.velocity.x * -tilt);         
    }
}

7、接下來是子彈的建立。

首先建立空的父物件以及Quad子物件,設定下位置和角度。而後給子物件新增子彈的材質。在這裡要說明的是,按照官方教程中的說法,建立一個空的父物件,是為了可以新增不同子彈材質作為子物件。

現在有了子彈的效果,接下來是新增Righidbody和Collider給父物件,調整Collider形狀,另外由於子物件本身自帶了一個Collider,我們不需要這個自帶的,所以要記得刪除。完成後大概是下面這樣:

子彈

下面是讓子彈飛出去的程式碼:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Mover : MonoBehaviour
{
    private Rigidbody rb;
    public float speed;

    void Start ()
    {
        rb = GetComponent<Rigidbody>();
        rb.velocity = Vector3.forward * speed;//vector3.forward相當於Vector3(0,0,1)
    }
}

最後別忘了把建立好的子彈模型,加入到Prefabs中,方便以後使用。

8、有了子彈模型接下來就該發射子彈了,這裡要用到一個很重要的函式“Instantiate”。

先不管程式碼,首先我們要建立自機的空子物件,用來作為子彈發出口,稍微調整下位置。然後我們看一下Instantiate函式的官方說明:

Object.Instantiate

public static Object Instantiate(Object original);
public static Object Instantiate(Object original, Transform parent);
public static Object Instantiate(Object original, Transform parent, bool instantiateInWorldSpace);
public static Object Instantiate(Object original, Vector3 position, Quaternion rotation);
public static Object Instantiate(Object original, Vector3 position, Quaternion rotation, Transform parent);

Returns Object The instantiated clone.

Description Clones the object original and returns the clone.

This function makes a copy of an object in a similar way to the Duplicate command in the editor. If you are cloning a GameObject then you can also optionally specify its position and rotation (these default to the original GameObject’s position and rotation otherwise). If you are cloning a Component then the GameObject it is attached to will also be cloned, again with an optional position and rotation.

When you clone a GameObject or Component, all child objects and components will also be cloned with their properties set like those of the original object.

By default the parent of the new object will be null, so it will not be a “sibling” of the original. However, you can still set the parent using the overloaded methods. If a parent is specified and no position and rotation is specified, the position and rotation of the original will be used for the cloned object’s local position and rotation, or its world position and rotation if the instantiateInWorldSpace parameter is true. If the position and rotation is specified, they will be used as the object’s position and rotation in world space.

The active status of a GameObject at the time of cloning will be passed on, so if the original is inactive then the clone will be created in an inactive state too.

完全看不懂,總之從結果來看,Instantiate函式可以理解為,在某一個Vector3位置建立一個GameObject的克隆體。我們就需要利用它來發射我們的子彈。官方有一類似示例:

using UnityEngine;
using System.Collections;

public class Missile : MonoBehaviour
{
    public int timeoutDestructor;

    // ...other code...
}


public class ExampleClass : MonoBehaviour
{
    // Instantiate a prefab with an attached Missile script
    public Missile projectile;

    void Update()
    {
        // Ctrl was pressed, launch a projectile
        if (Input.GetButtonDown("Fire1"))
        {
            // Instantiate the projectile at the position and rotation of this transform
            Missile clone = (Missile)Instantiate(projectile, transform.position, transform.rotation);

            // Set the missiles timeout destructor to 5
            clone.timeoutDestructor = 5;
        }
    }
}

很好,那我們照貓畫虎,再根據本身程式需要修改,編寫程式碼如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    private GameObject fireSpawn;//抓取發射口用
    private float nextFire;//計算下一發子彈可發射的時間

    public GameObject fire;//抓取子彈模型用
    public float fireRate;//射擊間隔

    void Start ()
    {
        fireSpawn = GameObject.Find("FireSpawn");//找到場景中的發射口
        nextFire = 0.0f;
    }

    void Update()
    {
        if (Input.GetButton("Fire1") && (Time.time > nextFire))
        {            
            Instantiate(fire, fireSpawn.transform);//在發射口位置,發射子彈
            nextFire = Time.time + fireRate;//加上射擊間隔
        }
    }
}

以上。