1. 程式人生 > >[Unity小專案]3D畫素跑酷遊戲

[Unity小專案]3D畫素跑酷遊戲

FFFFFlipping

1. 遊戲截圖

[待補充]>>>emm錄製gif的時候出錯了…

2. APK下載

3. 怎麼玩?

  • 點鍵help按鈕, 會顯示操作提示
  • 你可以向左跳, 向右跳, 每次只能跳一步
  • 你可以向後跳一步來調整戰略, 防止陷入僵局, 或者踩爆更多的敵人!
  • 你可以收集金幣, 然後打破紀錄! (但是現在還沒有商店)
  • 踩爆敵人獲得加血, 踩到陷阱☠則會死亡
  • 長時間沒有加血, 呼吸和速度都會變慢! 然後死掉!
  • 開始彈跳之旅吧

4. 開發環境

  • Unity 2018.1

5. 技術關鍵點解析和拓展

5.1 碰撞檢測

專案中的使用場景

  • 無限地圖的拼接: 通過在相機處設定碰撞體, 當相機看不見某一行block時, 將該block移到最後一排, 實現無限地圖
  • cell(金幣/敵人/陷阱☠)的處理: 主角跳到敵人或陷阱的cell, 會有不同的反饋

拓展:

5.2 相機跟隨

效果圖:

在這裡插入圖片描述

實現細節:

  • 只做前後跟隨, 即只需要z值跟隨就好了
  • 跟隨有延遲效果, 採用 Mathf.Lerp 插值實現

關鍵程式碼如下:

using UnityEngine;
public class CameraMovement : MonoBehaviour {
    public GameObject followTarget;
    public float moveSpeed;
    void Update() {
        if (followTarget !=
null) { //相機位置Z值與目標點的Z值做插值, 實現相機前後跟隨, 而目標點運動不影響 var newZ = Mathf.Lerp(transform.position.z, followTarget.transform.position.z, Time.deltaTime * moveSpeed); var newVector3 = new Vector3(transform.position.x, transform.position.y, newZ); transform.position =
newVector3; } } }

5.3 無限地圖拼接

效果圖:

在這裡插入圖片描述

關鍵程式碼如下:

using UnityEngine;
public class BlockMovement : MonoBehaviour {
    void OnTriggerExit(Collider other) {
        //當相機看不見某一行block時, 將該block移到最後一排, 實現無限地圖
        if (other.gameObject.tag == "Floor") {
            BlockManager.Instance.ChangeBlockPosition(other.transform);
        }
        if (other.gameObject.tag == "PoolItem") {
            var poolItem = other.transform.GetComponent<Cell>();
            poolItem.ReleaseIntoPool();
        }
    }
}

5.4 敵人視覺模擬和角度跟隨

敵人看到主角時, 會轉向主角

怎樣算看到呢? 專案中, 主角與敵人的z值距離小於指定值時, 可以斷定主角進入敵人的視覺範圍

要怎麼轉向主角呢? 用四元數來表示旋轉, 同時限制尤拉角中的y值, 來限制旋轉角度

關鍵程式碼如下:

protected virtual bool SeesPlayer =>
        ((Player != null) && (Mathf.Abs((int)(transform.position.z - Player.transform.position.z)) <= visionRange));
//...
protected virtual void Update() {
    if (Player != null) {
        if (SeesPlayer) TurnTowardPlayer();
    }
}
//...
private void TurnTowardPlayer() {
    Vector3 position = Player.transform.position;
    //忽略y值的影響
    position.y = transform.position.y;
    Vector3 forward = transform.position - position;
    if (forward != Vector3.zero) {
        Quaternion b = Quaternion.LookRotation(forward);
        transform.rotation = Quaternion.Lerp(transform.rotation, b, Time.deltaTime * turnRate);
        float y = transform.eulerAngles.y;
        //確定旋轉角度
        if (y > 180f)  y -= 360f;
        //將旋轉角度限制在-45度和+45度之間
        transform.rotation = Quaternion.Euler(0f, Mathf.Clamp(y, -mLookLimit, mLookLimit), 0f);
    }
}

拓展:

  • 3D數學中表現旋轉的方式有:
    • 旋轉矩陣: 行列式為1的正交矩陣
      • 旋轉矩陣有9個量,但是一次旋轉只有3個自由度,因此這種表達方式是冗餘的
      • 旋轉矩陣自身帶有約束:它必須是個正交矩陣,且行列式為1
    • 尤拉角: 最直觀的旋轉描述方式,也是一個3維向量,分別代表繞某個軸的旋轉角度
      • 相同的角度,旋轉次序的不同,旋轉結果不一樣。一般常見的是rpy角(旋轉順序是ZYX)
      • 最大的缺點是萬向鎖問題:俯仰角為±90度時,第一次旋轉和第三次旋轉將使用同一個軸,使得系統丟失了一個自由度
      • 無法實現球面平滑插值
    • 四元數: 四元數就是一個高階複數,也就是一個四維空間
      • 它們結構緊湊
      • 不受萬向節鎖定的影響
      • 可提供球面平滑插值
      • 不夠直觀
      • Unity內部使用四元數來表示所有旋轉

5.5 主角跳躍穿越效果

效果圖為:

在這裡插入圖片描述

關鍵程式碼為:

    /// <summary>
    /// 左右跳時, 主角也向前跳
    /// </summary>
    /// <param name="direction">-1表示向左跳, 1表示向右跳</param>
    private void ForwardJump(int direction) {
        mCurrentX += direction;
        if (direction == -1) {
            //越界了
            if (mCurrentX < 0) {
                mCurrentX = 2;
                var newVector3 = transform.position;
                newVector3.x = 2 * characterPositionOffset;
                transform.position = newVector3;
            }
            transform.Translate(Vector3.left * characterPositionOffset);
        }
        if (direction == 1) {
            //越界了
            if (mCurrentX > 2) {
                mCurrentX = 0;
                var newVector3 = transform.position;
                newVector3.x = -2 * characterPositionOffset;
                transform.position = newVector3;
            }
            transform.Translate(Vector3.right * characterPositionOffset);
        }
        JumpForward();
    }

5.6 血量不足時的減速處理

    public void LowBlood() {
        Time.timeScale = 0.5f;
        lowBloodView.SetActive(true);
    }

    public void ResumeNormalBlood() {
        Time.timeScale = 1;
        lowBloodView.SetActive(false);
    }

5.7 過載場景後燈光變暗問題處理

原因:
  • 選擇的光照是GI realtime實時光照,編輯器在當前場景時,它的燈光是已經渲染好了,但重新載入的時候燈光沒有進行渲染
解決方法:
  • Window->lighting->settings->右下角取消勾選auto,這時候是沒有烘焙燈光的情形,重新載入場景後不再會變暗。
  • 如果需要烘培燈光,則點選Generate按鈕即可,這時候將儲存光照貼圖資訊,重新載入後也不會再變暗。
解決方法參考:

6. 哪些還可以做得更好

敵人AI, 專案當中暫時沒有實現AI功能. 後期有機會我將會補上.

參考:

7. 為什麼叫做FFFFFlipping呢?

純屬因為個人喜歡的兩款放蕩不羈的遊戲

8. 個人部落格

9. 專案地址