基於Unity5的TPS整理


1、游戲管理器

游戲管理器負責管理游戲的整體流程,還可以系統管理用於游戲的全局數據以及游戲中判斷勝敗的條件。
游戲管理器並不是單一的模塊,更像是能控制游戲的功能集合。
1)怪獸出現邏輯:專門設置一些位置用於隨機生成怪獸。

public Transform[] points; points = GameObject.Find("MonsterPos").GetComponentsInChildren<Transform>();

 

2)訪問游戲管理器:聲明GameManager類的變量,在Start函數中分配為局部變量。

private GameManager gameMgr; gameMgr = GameObject.Find("GameManager").GetComponent<GameManager>();

 

3)單例模式:該模式下只有一個對象,可以獲得全局訪問。

public static GameManager instance = null; void Awake() { //將GameManager帶入實例
              instance = this; }


4)對象池:對於需要在游戲中反復生成的模型,可以在游戲一開始運行並加載場景時就全部生成,運行時可從中直接取出使用。

void Start () { //生成怪獸並保存到對象池
        for(int i = 0;i<maxMonster;i++) { GameObject monster = (GameObject)Instantiate(monsterPrefab); monster.name = "Monster_" + i.ToString(); monster.SetActive(false); monsterList.Add(monster); } }


5)共享函數:聲音處理

//聲音共享函數
    public void PlaySfx(Vector3 pos,AudioClip sfx) { //如果靜音選項為true,則立即停止聲音
        if (isSfxMute) return; //動態生成游戲對象
        GameObject soundObj = new GameObject("Sfx"); soundObj.transform.position = pos; //向生成的游戲對象添加AudioSource組件
        AudioSource audioSource = soundObj.AddComponent<AudioSource>(); //設置AudioSource屬性
        audioSource.clip = sfx; audioSource.minDistance = 10; audioSource.maxDistance = 30; audioSource.volume = sfxVolumn; audioSource.Play(); //聲音結束播放后,刪除之前動態生成的游戲對象
 Destroy(soundObj, sfx.length); }

2、射線投射

射線投射不但常用於發射邏輯,還可用於游戲中的傳感器以及通過鼠標移動、旋轉玩家角色。

射線無法用肉眼觀察,可以使用Debug.DrawRay()函數使射線在場景視圖中顯示。

void Update() { //使用以下函數在場景中顯示射線
        Debug.DrawRay(FirePos.position,FirePos.forward * 10, Color.green); }

使怪獸被射線擊中時受傷

 RaycastHit hit; if (Physics.Raycast(FirePos.position, FirePos.forward, out hit, 10)) { if (hit.collider.tag == "Monster") { //SendMessage函數要傳遞的參數數組
                    object[] _params = new object[2]; _params[0] = hit.point;//被射線擊中的位置
                    _params[1] = 20;//怪獸受到的傷害值
 hit.collider.gameObject.SendMessage("OnDamage", _params, SendMessageOptions.DontRequireReceiver); } }

 激光束

使用Line Renderer組件,因為Line Renderer將在Player的中心位置生成,所以不需要勾選Use World Space選項。

using UnityEngine; using System.Collections; public class LaserBeam : MonoBehaviour { private Transform tr; private LineRenderer line; private RaycastHit hit; void Start () { tr = GetComponent<Transform>(); line = GetComponent<LineRenderer>(); line.useWorldSpace = false; line.enabled = false; //設置頭部寬度和尾部寬度
        line.SetWidth(0.3f,0.01f); } void Update () { Ray ray = new Ray(tr.position + (Vector3.up * 0.02f),tr.forward); Debug.DrawRay(ray.origin, ray.direction * 10, Color.blue); if (Input.GetMouseButtonDown(0)) { //設置Line Renderer的初始位置
            line.SetPosition(0,tr.InverseTransformPoint(ray.origin)); //(局部坐標基准Transform組件).InverseTransformPoint(全局坐標) //將物體被射線擊中的位置設置為Line Renderer的終點位置
            if (Physics.Raycast(ray,out hit,100)) { line.SetPosition(1,tr.InverseTransformPoint(hit.point)); } else line.SetPosition(1, tr.InverseTransformPoint(ray.GetPoint(100))); StartCoroutine(ShowLaserBeam()); } } IEnumerator ShowLaserBeam() { line.enabled = true; yield return new WaitForSeconds(Random.Range(0.01f,0.2f)); line.enabled = false; } }

 3、制作Player

1)玩家移動

using UnityEngine;
using System.Collections;

[System.Serializable]
public class Anim
{
    public AnimationClip idle;
    public AnimationClip runForward;
    public AnimationClip runBackward;
    public AnimationClip runRight;
    public AnimationClip runLeft;
}
public class PlayerCtrl : MonoBehaviour
{
    private Transform tr;
    public float moveSpeed = 5;
    public float rotSpeed = 100;
    public Anim anim;//要顯示到檢視視圖的動畫變量
    public Animation _animation;//Animation組件對象的變量
    public int hp = 100;

    //聲明委托和事件
    public delegate void PlayerDieHandler();
    public static event PlayerDieHandler OnPlayerDie;

    //Player的生命初始值
    private int initHp;
    //Player的生命圖像
    public Image imgHpBar;
    public Text txtHp;

    //private GameMgr gameMgr;

    void Start ()
    {
        //設置生命值
        initHp = hp;
        tr = GetComponent<Transform>();
        //查找位於自身下級的Animation組件並分配到變量
        _animation = GetComponentInChildren<Animation>();
        //保存並運行Animation組件的動畫片段
        _animation.clip = anim.idle;
        _animation.Play();

        //gameMgr = GameObject.Find("GameManager").GetComponent<GameMgr>();
    }
    
    void Update ()
    {
        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");

        Vector3 dir = h * Vector3.right + v * Vector3.forward;//前后左右移動方向向量
        tr.Translate(dir.normalized * moveSpeed * Time.deltaTime,Space.Self);//向量方向*速度*時間,以局部坐標為基准移動
        tr.Rotate(Vector3.up * rotSpeed * Time.deltaTime * Input.GetAxis("Mouse X"));

        //以鍵盤輸入值為基准,執行要操作的動畫
        //動畫合成:讓動畫片段之間平滑過渡,用到CrossFade合成函數
        if (v >= 0.1f)
        {
            _animation.CrossFade(anim.runForward.name, 0.3f);
        }
        else if (v <= -0.1f)
        {
            _animation.CrossFade(anim.runBackward.name, 0.3f);
        }
        else if (h <= -0.1f)
        {
            _animation.CrossFade(anim.runLeft.name, 0.3f);
        }
        else if (h >= 0.1f)
        {
            _animation.CrossFade(anim.runRight.name, 0.3f);
        }
        else
        {
            _animation.CrossFade(anim.idle.name, 0.3f);
        }
    }

    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Punch")
        {
            hp -= 10;
            //調整Image的fillAmount屬性,以調整生命條長度
            float xueliang = (float)hp / (float)initHp;
            imgHpBar.fillAmount = xueliang;
            txtHp.text = "<color=#ffffff>" + (xueliang * 100).ToString() + "%</color>";
            Debug.Log("Player HP =" + hp.ToString());

            if (hp <= 0)
            {
                PlayerDie();
            }
        }
    }

    void PlayerDie()
    {
        Debug.Log("Player Die!!");

        //GameObject[] monsters = GameObject.FindGameObjectsWithTag("Monster");
        //foreach (GameObject monster in monsters)
        //{
        //    monster.SendMessage("OnPlayerDie",SendMessageOptions.DontRequireReceiver);
        //}
        //或者
        //觸發事件
        OnPlayerDie();

        //更新游戲管理器的isGameover變量以停止生成怪獸
        //gameMgr.isGameOver = true;
        GameMgr.instance.isGameOver = true;
    }


}

使用CharacterController組件后代碼

float h = Input.GetAxisRaw("Horizontal"); float v = Input.GetAxisRaw("Vertical"); movDir = (tr.forward * v) + (tr.right * h);//計算移動方向 movDir.y -= 20 * Time.deltaTime;//更改y值使其受重力影響下落 tr.Rotate(Vector3.up * rotSpeed * Time.deltaTime * Input.GetAxis("Mouse X")); controller.Move(movDir * moveSpeed * Time.deltaTime);//移動玩家

 

2)攝像機追蹤

將該腳本添加到主攝像機下,並把Player拖拽到targetTr變量。

 

using UnityEngine;
using System.Collections;

public class FollowCam : MonoBehaviour
{
    public Transform tr;
    public Transform targetTr;//要追蹤的游戲對象的Trans變量
    public float dist = 10;//與攝像機之間的距離
    public float height = 3;//設置攝像機高度
    public float dampTrace = 20;//實現平滑追蹤的變量

    void Start ()
    {
        tr = GetComponent<Transform>();
    }

    //要追蹤的目標游戲對象停止移動后,調用LaterUpdate函數
    void LateUpdate ()
    {
        //將攝像機放置在被追蹤的目標后方的dist距離的位置
        //將攝像機向上抬離height
        tr.position = Vector3.Lerp(tr.position,                                     //起始位置
            targetTr.position - (targetTr.forward * dist) + (Vector3.up * height),  //終止位置
            Time.deltaTime * dampTrace);                                            //內插時間
        //使攝像機朝向游戲對象
        tr.LookAt(targetTr.position);
    }
}

 4、網絡管理器

Network View組件提供與其他玩家通信的功能,必須已經在網絡上生成游戲或已訪問游戲,所以其前提是游戲房間已經生成或已連接其他游戲客戶端。

1)通過GUI類實現畫面輸入所需UI及制作網絡玩家

 

using UnityEngine;
using System.Collections;

public class NetworkManager : MonoBehaviour
{
    //連接IP
    private const string ip = "127.0.0.1";
    //連接Port端口號
    private const int port = 30000;
    //是否使用NAT功能
    private bool _useNat = false;

    public GameObject player;

    void OnGUI()
    {
        //判斷當前玩家是否連接網絡
        if (Network.peerType == NetworkPeerType.Disconnected)
        {
            //生成啟動游戲服務器按鈕
            if (GUI.Button(new Rect(20,20,200,25),"Start Server"))
            {
                //初始化游戲服務器
                Network.InitializeServer(20,port,_useNat);//連接數,端口號,是否用NAT
            }
            //生成連接游戲的按鈕
            if (GUI.Button(new Rect(20, 50, 200, 25), "Connect to Server"))
            {
                //連接游戲服務器
                Network.Connect(ip, port);
            }
        }
        else
        {
            //初始化服務器時輸出信息
            if (Network.peerType == NetworkPeerType.Server)
            {
                GUI.Label(new Rect(20, 20, 200, 25), "Initialization Server...");
                //查看連接網絡的用戶數
                GUI.Label(new Rect(20, 50, 200, 25), "Clint Count = " + Network.connections.Length.ToString());
            }
            //以客戶端身份連接時輸出信息
            if (Network.peerType == NetworkPeerType.Client)
            {
                GUI.Label(new Rect(20, 20, 200, 25), "Connect to Server");
            }
        }
    }

    //游戲服務器初始化正常完成時創建player
    void OnServerInitialized()
    {
        CreatePlayer();
    }
    //有玩家連接服務器時調用
    void OnConnectedToServer()
    {
        CreatePlayer();
    }

    void CreatePlayer()
    {
        //隨機生成玩家的初始位置
        Vector3 pos = new Vector3(Random.Range(-20,20),0,Random.Range(-20,20));
        //網絡上動態生成玩家
        Network.Instantiate(player,pos,Quaternion.identity,0);
    }

}

 

2)攝像機追蹤邏輯

先導入SmoothFollow腳本(Import Package→Utility),並把target變量修改為public。掛在Player預設。

using UnityEngine;
using System.Collections;
//為了使用SmoothFollow腳本,需要先聲明命名空間
using UnityStandardAssets.Utility;

public class NetPlayerCtrl : MonoBehaviour
{
    private Transform tr;
    private NetworkView _networkView;

    void Awake ()
    {
        tr = GetComponent<Transform>();
        _networkView = GetComponent<NetworkView>();
        //查看NetworkView組件是否為自己的Player組件
        if (_networkView.isMine)
        {
            //設置Main.Camera要追蹤的對象
            Camera.main.GetComponent<SmoothFollow>().target = tr;
        }
    }
}

禁用服務器端的玩家移動腳本,使其不要根據鍵盤輸入值執行移動邏輯。

//如果當前玩家是遠程服務器端的玩家,則禁用此腳本
        this.enabled = GetComponent<NetworkView>().isMine;

 3)平滑的同步處理

在檢視面板,把NetPlayerCtrl腳本拖拽到NetworkView組件的Observed屬性。

修改腳本,使NetworkView組件不會在每個SendRate周期都向Transform組件傳送數據,而是調用NetPlayerCtrl腳本內的OnSerializeNetworkView函數。

//聲明接收傳送位置信息時使用的變量並設置初始值
    private Vector3 currPos = Vector3.zero;
    private Quaternion currRot = Quaternion.identity;

void Update()
    {
        if (_networkView.isMine)
        {
        }
        else//遠程玩家
        {
            //將遠程玩家平滑地移動旋轉到NetworkView傳送來的目標位置
            tr.position = Vector3.Lerp(tr.position,currPos,Time.deltaTime * 10);
            tr.rotation = Quaternion.Slerp(tr.rotation,currRot,Time.deltaTime * 10);
        }
    }

     //NetworkView組件調用的回調函數
   //BitStream stream:序列化數據並保存到數據流
    //NetworkMessageInfo info:保存從網絡接收的信息
void OnSerializeNetworkView(BitStream stream,NetworkMessageInfo info) { if (stream.isWriting) { Vector3 pos = tr.position; Quaternion rot = tr.rotation; //數據傳送 stream.Serialize(ref pos); stream.Serialize(ref rot); } else { Vector3 revpos = Vector3.zero; Quaternion revrot = Quaternion.identity; //數據接收 stream.Serialize(ref revpos); stream.Serialize(ref revrot); currPos = revpos; currRot = revrot; } }

 


免責聲明!

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



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