[Unity小專案]3D畫素跑酷遊戲
阿新 • • 發佈:2018-12-15
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內部使用四元數來表示所有旋轉
- 旋轉矩陣: 行列式為1的正交矩陣
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呢?
純屬因為個人喜歡的兩款放蕩不羈的遊戲