1. 程式人生 > >RPG遊戲《黑暗之光》流程介紹與程式碼分析之(三):角色控制系統的實現

RPG遊戲《黑暗之光》流程介紹與程式碼分析之(三):角色控制系統的實現

第三章:角色控制

本篇部落格主要對人物移動及其相關操作進行分析,主要包括主角以及鏡頭的移動。在遊戲介面中,我們使用Camera作為視角。為了方便之後判斷當前tag,我們新建一個Tag指令碼,存入一些tag資訊,之後呼叫就不容易出錯
using UnityEngine;
using System.Collections;

public class Tags : MonoBehaviour {

    public const string ground = "Ground";
    public const string player = "Player";    //新建角色與地面的tag資訊,之後還會新增物品等資訊
}

3.1 點選地板

Map中的Terrain可以判斷滑鼠是否點選到地面,因此我們向Map中拖入一個Magician,為它新增一個指令碼PlayerDirection,實現點選地板產生效果的功能。程式碼如下
using UnityEngine;
using System.Collections;
public class PlayerDirection : MonoBehaviour {
    public GameObject effect_click_prefab;
    
    // Update is called once per frame
    void Update () {
        if (Input.GetMouseButtonDown (0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);    //建立一個射線,將在Camera中點選的點轉化為一條射線
            RaycastHit hitInfo;    //建立碰撞資訊
            bool isCollider = Physics.Raycast(ray,out hitInfo);    //isCollider檢測是否碰撞,其中Physics.Raycast中的兩個形參表示射線以及碰撞資訊
            if(isCollider && hitInfo.collider.tag == Tags.ground)    //發生碰撞且碰撞的物體其Tag為ground
            {
                showClickEffect(hitInfo.point);    //例項化點選效果
            }
        }
    }
    void showClickEffect(Vector3 hitPoint)
    {
        hitPoint = new Vector3 (hitPoint.x, hitPoint.y + 0.1f, hitPoint.z);    //將碰撞點的y向上移動一些,以完整顯示
        GameObject.Instantiate (effect_click_prefab, hitPoint, Quaternion.identity);    //建立一個例項,顯示點選資訊
    }
}
即可之後將點選效果匯入,即可實現
執行後,點選地面時即可產生碰撞效果。

3.2 角色朝向

在獲取點選目標後,我們需要控制角色朝向目標區域。對PlayerDirection修改如下
    private bool isMoving = false;
    public Vector3 targetPosition = Vector3.zero;

    // Update is called once per frame
    void Update () {
        if (Input.GetMouseButtonDown (0))    //這裡是判斷是否按下
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hitInfo;
            bool isCollider = Physics.Raycast(ray,out hitInfo);
            if(isCollider && hitInfo.collider.tag == Tags.ground)
            {
                isMoving = true;    
                ShowClickEffect(hitInfo.point);
                LookAtTarget(hitInfo.point);    //將朝向設定為一個方法
            }
        }
        if(Input.GetMouseButtonUp(0))    //是否擡起
        {
            isMoving = false;
        }
        
        if (isMoving)    //是否持續按下
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hitInfo;
            bool isCollider = Physics.Raycast(ray,out hitInfo);
            if(isCollider && hitInfo.collider.tag == Tags.ground)
            {
                LookAtTarget(hitInfo.point);    //持續朝向hitInfo.point
            }
        }
    }
    
    void LookAtTarget(Vector3 hitPoint)
    {
        targetPosition = hitPoint;    //點選的位置資訊傳給 targetPosition
        targetPosition = new Vector3(targetPosition.x,transform.position.y,targetPosition.z);    //不改變y軸資訊,別的資訊參照 targetPosition
        this.transform.LookAt(targetPosition);    //改變朝向
    }
}

3.3 角色移動

為角色新增一個角色控制器,用以控制移動
對Magician新增一個指令碼PlayerMove控制移動
using UnityEngine;
using System.Collections;
public class PlayerMove : MonoBehaviour {
    public float speed = 3f;
    private PlayerDirection dir;
    private CharacterController controller;
    void Start(){
        dir = this.GetComponent<PlayerDirection> ();    //獲取目標位置
        controller = this.GetComponent<CharacterController> ();    //為controller賦值
    }
    // Update is called once per frame
    void Update () {
        float distance = Vector3.Distance (dir.targetPosition, transform.position);    //獲取目標位置與當前位置的距離
        if (distance >= 0.1f) {
            controller.SimpleMove(transform.forward*speed);    //這裡的速度應帶有方向
        }
    }
}
即可,但是剛開始時的targetPosition處於(0,0,0),會直接移動,需要在PlayerDirection初始化targetPositio為初始人物的位置即
targetPosition = transform.position;
實現移動效果如下

3.4 動畫效果的加入

角色移動時應當有動畫,在Animation中,新增所有素材
現在需要在PlayerMove中控制角色的狀態

3.5 角色狀態,站立,運動

在Magician下新建一個指令碼PlayerAnimation控制運動播放,首先在PlayerMove中指定站立或者運動的狀態資訊
using UnityEngine;
using System.Collections;
public enum PlayerState{
    Moving,
    Idle
}
public class PlayerMove : MonoBehaviour {
    public float speed = 3f;
    public PlayerState state = PlayerState.Idle;    //指定預設的Animation狀態是Idle
    private PlayerDirection dir;
    private CharacterController controller;

    // Update is called once per frame
    void Update () {
        float distance = Vector3.Distance (dir.targetPosition, transform.position);
        if (distance >= 0.05f)
        {
                state = PlayerState.Moving;    //運動時state狀態變為Moving
        }
        else
        {
            state = PlayerState.Idle;       //不運動時狀態為Idle
        }
    }
}
利用上述狀態的初始化,之後根據狀態變化在PlayerAnimation控制角色移動
using UnityEngine;
using System.Collections;
public class PlayerAnimation : MonoBehaviour {
    private PlayerMove move;
    // Use this for initialization
    void Start () {
        move = this.GetComponent<PlayerMove> ();
    }
    
    // Update is called once per frame
    void LateUpdate () {
        if (move.state == PlayerState.Moving)
        {
            PlayAnim ("Run");    //對應下圖Run
        }
        else if (move.state == PlayerState.Idle)
        {
            PlayAnim("Idle");    //對應下圖Idle
        }
    }
    void PlayAnim(string animName)
    {
        animation.CrossFade (animName);    //播放對應的Animation
    }
}

3.6 讓相機跟隨主角移動以及鏡頭的拉近拉遠

對Camera新增一個指令碼PlayerFollow
using UnityEngine;
using System.Collections;
public class PlayerFollow : MonoBehaviour {
    private Transform player;
    private Vector3 offsetPosition;
    // Use this for initialization
    void Start () {
        player = GameObject.FindGameObjectWithTag (Tags.player).transform;    //得到角色資訊
        this.transform.LookAt (player.position);    //使player處在Camera中心
        offsetPosition = transform.position - player.position;    //判斷相機與人物的偏移量,之後用這個偏移量作為參考控制相機移動
    }
    
    // Update is called once per frame
    void Update () {
        transform.position = offsetPosition + player.position;    //使用偏移量作為相機位置的更新
    }
}
即可為了提高可玩性,需要新增滑鼠滑輪的功能實現拉遠與縮排的功能。在Edit——Project Setting中有一個滑鼠滾輪的控制
在Camera中的指令碼PlayerFollow中加入一個方法ScrollView()控制滾輪的拉近拉遠操作
   public float scrollSpeed = 10f;    //滾輪速度
    public float distance = 0f;
    void ScrollView()
    {
        //print (Input.GetAxis("Mouse ScrollWheel"));    //Input.GetAxis("Mouse ScrollWheel")表示滑鼠滾輪的滑動值,滑的速度越快,值越大
        distance = offsetPosition.magnitude;    //用位置偏移表示鏡頭與角色的距離
        distance -= Input.GetAxis ("Mouse ScrollWheel") * scrollSpeed;    //通過distance的減少拉近(遠)視野
        distance = Mathf.Clamp(distance,4f,14f);        //限定拉近拉遠的最值
        offsetPosition = offsetPosition.normalized * distance;    //取得offsetPosition的單位向量,再乘distance表示改變視野後Camera與角色的距離
    }
與上類似,我們在Camera中的指令碼PlayerFollow中加入一個方法RotateView()函式控制滑鼠右鍵的左右、上下移動
    private bool isRotation = false;    //是否旋轉視野的標誌位
    public float rotateSpeed = 2f;    //旋轉速度
    void RotateView()
    {
        if (Input.GetMouseButtonDown (1))    //如果滑鼠右鍵(1表示右鍵,0表示左鍵,2表示滾輪)按下,旋轉開啟
        {
            isRotation = true;
        }
        if (Input.GetMouseButtonUp (1))    //若右鍵擡起,旋轉關閉
        {
            isRotation = false;
        }
        if (isRotation)
        {
            transform.RotateAround(player.position,player.up,rotateSpeed*Input.GetAxis("Mouse X"));    //RotateAround表示繞著第一個引數進行旋轉,其中第一個引數表示圍繞player進行旋轉,第二個引數表示旋轉圍繞的軸,在這裡表示垂直主角的軸,第三個表示旋轉速度
            transform.RotateAround(player.position,transform.right,-rotateSpeed*Input.GetAxis("Mouse Y"));    //圍繞的軸改為水平方向
        }
        offsetPosition = transform.position - player.position;    //旋轉後更新offsetPosition
    }
旋轉角度的限定,即Camera中的Rotation對應的x,在 if (isRotation)中修改
        if (isRotation)
        {
            transform.RotateAround(player.position,player.up,rotateSpeed*Input.GetAxis("Mouse X"));
            Vector3 originalPos = transform.position;    //儲存原始位置資訊
            Quaternion originalRotation = transform.rotation;    //儲存原始角度資訊
            transform.RotateAround(player.position,transform.right ,-rotateSpeed*Input.GetAxis("Mouse Y"));
            float x = transform.eulerAngles.x;    //對應Camera的Rotation中的x
            if(x<10 || x>80)    //如果超過限制,返回原始值
            {
                transform.position = originalPos;
                transform.rotation = originalRotation;
            }
        }
總結:角色控制的大致功能都已實現,有許多細節需要注意,需要花時間研究。之後將對任務、道具等系統進行建立。