Unity3D教学 开发简单版第一人称射击游戏 可以多人联机(附源码)

简介:

这一篇文章主要是和大家分享如何制作一个属于自己的“第一人称射击游戏”,而且是要可以多人联机的。这个游戏属于比简单的,大神可以直接无视,如果有做错的地方请大家多多指点,我也是刚学如何做游戏。代码是用C#编写,主要实现的功能有三个:第一人称移动控制、角色控制(如射击)、TCP服务端和客户端。我将项目命名为FPS。


游戏运作流程:

服务端

1. 创建服务端

2. 从各个客户端接收数据,然后广播给所有客户


客户端

1. 连接至服务端

2. 从服务端接收到其他客户的数据(比如当前位置,是否有开枪等等),然后更新到游戏上

3. 发送自己当前在游戏的状态给服务端


联机效果图:

服务端



客户端



完整源代码与已编译好的游戏程序:

下载


C#脚本简介:

1. FirstPersonControl.cs

这个脚本是用来做第一人称控制的

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

public class FirstPersonControl : MonoBehaviour {

	// Use this for initialization
	void Start () {
        // 获取摄像头对象
        mCamera = transform.Find("Main Camera");
        // 获取右手对象
        mRightHand = transform.Find("RightHand");
        // 获取脚步声播放组建
        mAudio = transform.GetComponent<AudioSource>();
	}

    // Update is called once per frame
    void Update() {
        if (UpdateMovement()) {
            PlayStepSound();
        } else {
            StopPlayStepSound();
        }

        UpdateLookAt();
    }

    // 第一人称控制器的常量与变量
    private Transform mCamera;
    private Transform mRightHand;
    private AudioSource mAudio;
    public float mMoveSpeed = 8;         // 物体移动速度
    public float mMouseSensitivity = 5;     // 鼠标旋转的敏感度
    public float mMinimumX = 325;       // 向下望的最大角度
    public float mMaximumX = 45;        // 向上望的最大角度
    public float mMinimumY = -360;         // 向左望的最大角度
    public float mMaximumY = 360;       // 向右望的最大角度
    private Vector3 _curRotation = new Vector3(0,0,0);       // 当前旋转角度

    // 更新移动位置
    private bool UpdateMovement() {
        float distance = mMoveSpeed * Time.deltaTime;   // 移动距离
        
        // 前
        if (Input.GetKey(KeyCode.W)) {
            float x = Mathf.Sin(transform.eulerAngles.y * Mathf.Deg2Rad) * distance;
            float z = Mathf.Cos(transform.eulerAngles.y * Mathf.Deg2Rad) * distance;
            transform.Translate(new Vector3(x,0,z), Space.World);
            return true;
        }

        // 后
        if (Input.GetKey(KeyCode.S)) {
            float x = Mathf.Sin(transform.eulerAngles.y * Mathf.Deg2Rad) * distance;
            float z = Mathf.Cos(transform.eulerAngles.y * Mathf.Deg2Rad) * distance;
            transform.Translate(new Vector3(-x, 0, -z), Space.World);
            return true;
        }

        // 左
        if (Input.GetKey(KeyCode.A)) {
            transform.Translate(new Vector3(-distance, 0, 0));
            return true;
        }

        // 右
        if (Input.GetKey(KeyCode.D)) {
            transform.Translate(new Vector3(distance, 0, 0));
            return true;
        }

        return false;
    }

    // 更新摄像头指向位置
    private void UpdateLookAt() {
        // 左右旋转
        _curRotation.y = _curRotation.y + Input.GetAxis("Mouse X") * mMouseSensitivity;
        _curRotation.y = Mathf.Clamp(_curRotation.y, mMinimumY, mMaximumY);

        // 设置身体
        Vector3 rotation = transform.eulerAngles;
        rotation.y = _curRotation.y;
        transform.eulerAngles = rotation;

        // 上下旋转
        _curRotation.x = _curRotation.x  - Input.GetAxis("Mouse Y") * mMouseSensitivity;
        _curRotation.x = Mathf.Clamp(_curRotation.x, mMinimumX, mMaximumX);

        // 设置摄像头
        mCamera.localEulerAngles = new Vector3(_curRotation.x, 0, 0);

        // 设置右手
        rotation = mRightHand.eulerAngles;
        rotation.x = _curRotation.x;
        mRightHand.eulerAngles = rotation;
    }

    // 播放脚步声
    private void PlayStepSound() {
        if (!mAudio.isPlaying) {
            mAudio.Play();
        }
    }

    // 停止播放声音
    private void StopPlayStepSound() {
        if (mAudio.isPlaying) {
            mAudio.Stop();
        }
    }
}


2. Character.cs

角色控制脚本,主要是用来控制开枪等角色动作

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

public class Character : MonoBehaviour {

    // Use this for initialization
    void Start() {
        // 获取摄像头对象
        mCamera = transform.Find("Main Camera");
        // 获取右手对象
        mRightHand = transform.Find("RightHand");
        // 获取枪声播放组件
        mGunAudio = transform.Find("RightHand/Pistol").GetComponent<AudioSource>();
        // 获取火花效果
        mFireEffect = transform.Find("RightHand/Pistol/FireEffect").GetComponent<ParticleSystem>();
        // 获取网络组件
        mNetwork = transform.GetComponent<Network>();
    }

    // Update is called once per frame
    void Update() {
        UpdateFire();

        // 发送当前状态到服务端,然后服务端就会转发给其他客户
        mNetwork.SendStatus(transform.position, transform.eulerAngles, 
        mCamera.eulerAngles, mRightHand.eulerAngles, _isShooted, _hp);

        // 处理服务器发过来的数据包,数据包里装着其他客户的信息
        ProcessPackage();
    }

    private Transform mCamera;
    private Transform mRightHand;
    private AudioSource mGunAudio;
    public GameObject mPiece;       // 开枪后撞击产生的碎片
    private ParticleSystem mFireEffect;     // 开枪后的火花
    private bool _isShooted;    // 判断是否开了枪
    private Network mNetwork;       // 网络组件
    public GameObject mEnemyCharacter;  // 其他客户的实例
    private Hashtable _htEnemies = new Hashtable();    // 其他客户的控制脚本

    // 开枪
    private void UpdateFire() {
        if (Input.GetButtonUp("Fire1")) {
            // 射击音效与画面
            PlayShotSound();

            // 播放火花效果
            PlayFireEffect();

            // 判断射击位置
            RaycastHit hit;
            if (Physics.Raycast(mCamera.position, mCamera.forward, out hit, 100)) {
                // 被枪击中的地方会有碎片弹出
                DrawPieces(hit);
            }

            // 设置开枪判断
            _isShooted = true;
        } else {
            // 设置开枪判断
            _isShooted = false;
        }
    }

    // 播放枪声
    private void PlayShotSound() {
        mGunAudio.PlayOneShot(mGunAudio.clip);
    }

    // 画碎片
    private void DrawPieces(RaycastHit hit) {
        for (int i = 0; i < 5; ++i) {
            GameObject p = Transform.Instantiate(mPiece);

            // 碎片撞击到物体后的反弹位置
            Vector3 fwd = mCamera.forward * -1;
            p.transform.position = hit.point;
            p.GetComponent<Rigidbody>().AddForce(fwd * 100);

            // 0.3秒后删除
            Destroy(p, 0.3f);
        }
    }

    // 播放火花效果
    private void PlayFireEffect() {
        mFireEffect.Play();
    }

    // 人物变量
    private int _hp = 100;

    // 受到伤害
    public void GetHurt() {
        _hp -= 10;

        if (_hp <= 0) {
            // 复活
            Revive();
        }
    }

    // 复活
    private void Revive() {
        _hp = 100;
        transform.position = new Vector3(0,1,0);
    }

    // 处理数据包
    private void ProcessPackage() {
        Network.Package p;

        // 获取数据包直到完毕
        while (mNetwork.NextPackage(out p)) {
            // 确定不是本机,避免重复
            if (mNetwork._id == p.id) {
                return;
            }

            // 获取该客户相对应的人物模组
            if (!_htEnemies.Contains(p.id)) {
                AddEnemyCharacter(p.id);
            }

            // 更新客户的人物模型状态
            EnemyCharacter ec = (EnemyCharacter)_htEnemies[p.id];

            // 血量
            ec.SetHP(p.hp);

            // 移动动作
            ec.Move(p.pos.V3, p.rot.V3, p.cameraRot.V3, p.rightHandRot.V3);

            // 开枪
            if (p.isShooted) {
                ec.Fire();
            }
        }
    }

    // 增加客户的人物模组
    private EnemyCharacter AddEnemyCharacter(int id) {
        GameObject p = GameObject.Instantiate(mEnemyCharacter);
        EnemyCharacter ec = p.GetComponent<EnemyCharacter>();
        
        // 修改ID
        ec.SetID(id);

        // 加入到哈希表
        _htEnemies.Add(id, ec);

        return ec;
    }

    // 删除客户的人物模组
    private void RemoveEnemyCharacter(int id) {
        EnemyCharacter ec = (EnemyCharacter)_htEnemies[id];
        ec.Destroy();
        _htEnemies.Remove(id);
    }

    // 删除所有客户的人物模组
    public void RemoveAllEnemyCharacter() {
        foreach (int id in _htEnemies.Keys) {
            EnemyCharacter ec = (EnemyCharacter)_htEnemies[id];
            ec.Destroy();
        }
        _htEnemies.Clear();
    }
}


3.Network.cs

网络组件,用来创建服务端或客户端。服务端负责广播从各个客户端接收的数据给所有客户。客户端则是接收从服务端接收其他客户的数据。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Net;
using System.Net.Sockets;

public class Network : MonoBehaviour {

	// Use this for initialization
	void Start () {
        // 获取人物组件
        mCharacter = transform.GetComponent<Character>();
        // 数据包大小
        _packageSize = PackageSize(); 
	}
	
	// Update is called once per frame
	void Update () {

	}

    // 网络设置UI
    void OnGUI() {
		GUI.Box (new Rect(0,0,800,60),"网络设置");

        if (_connected) {
		    GUI.Label (new Rect(10,25,100,25), _isServer ? "已建立服务端":"已连接服务端");
        } else if (!_connected) {
		    GUI.Label (new Rect(10,25,100,25), "未连接");
        }

		GUI.Label (new Rect(130,25,20,25), "IP:");
        GUI.Label (new Rect(270,25,40,25), "端口:");
        GUI.Label (new Rect(380,25,40,25), "ID:");

		if (!_connected && !_isServer) {
			_ip = GUI.TextField (new Rect (150, 25, 100, 25), _ip, 100);
            _port = System.Convert.ToInt32 (GUI.TextField (new Rect (310, 25, 50, 25), _port.ToString (), 100));
            _id = System.Convert.ToInt32 (GUI.TextField (new Rect (420, 25, 100, 25), _id.ToString(), 100));
		} else {
			GUI.TextField (new Rect (150, 25, 100, 25), _ip, 100);
            GUI.TextField (new Rect (310, 25, 50, 25), _port.ToString (), 100);
            GUI.TextField (new Rect (420, 25, 100, 25), _id.ToString(), 100);
        }

        if (!_connected && !_isServer) {
            if (GUI.Button(new Rect(540,25,100,25), "开启服务端")) {
                StartServer();
                ConnectServer();
            }

            if (GUI.Button(new Rect(660,25,100,25), "连接至服务端")) {
                ConnectServer();
            }
        } else {
            if (_isServer) {
                if (GUI.Button(new Rect(540,25,100,25), "关闭服务端")) {
                    StopServer();
                }
            } else if (_connected) {
                if (GUI.Button(new Rect(540,25,100,25), "取消连接")) {
                    DisconnectServer();
                }
            }
        }
	}

    // 服务端和客户端的共有变量
    private Character mCharacter; // 人物组件
    private bool _connected=false;  // 判断是否已经建立连接或开启服务端
    private bool _isServer=false;   // 判断本程序建立的是服务端还是客户端
    private string _ip = "127.0.0.1";           // 主机IP
    private int _port = 18000;             // 端口
    public int _id = 1 ;           // 人物id
    List<Package> _packages=new List<Package>();        // 数据包
    private int _packageSize;        // 数据包大小

    // 数据包
    [Serializable]
    public struct Package {
        public int id;
        public Vector3Serializer pos;   // 人物位置
        public Vector3Serializer rot;   // 人物旋转角度
        public Vector3Serializer cameraRot;     // 摄像头旋转角度
        public Vector3Serializer rightHandRot;  // 右手旋转角度
        public bool isShooted;      // 判断是否有开枪
        public int hp;      // 血量
    }

    // 获取包大小
    private int PackageSize() {
        Package p = new Package();
        byte[] b;
        Serialize(p, out b);
        return b.Length;
    }

    // 可序列化的Vector3
    [Serializable]
	public struct Vector3Serializer {
		public float x;
		public float y;
		public float z;

		public void Fill(Vector3 v3) {
			x = v3.x;
			y = v3.y;
			z = v3.z;
		}

		public Vector3 V3
		{ get { return new Vector3(x, y, z); } }
	}

    // 序列化数据包
    public bool Serialize(object obj, out byte[] result) {
        bool ret = false;
        result = null;

        try {
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(ms, obj);
            result = ms.ToArray(); 
        
            ret = true;
        } catch (Exception e) {
            ret = false;
            Debug.Log(e.Message);
        }

        return ret;
    }

    // 反序列化数据包
    public bool Deserialize(byte[] data,out object result) {
        bool ret = false;
        result = new object();

        try {
            MemoryStream ms = new MemoryStream(data);
            BinaryFormatter bf = new BinaryFormatter();
            result = bf.Deserialize(ms);
        
            ret = true;
        } catch (Exception e) {
            ret = false;
            Debug.Log(e.Message);
        }

        return ret;
    }

    // 服务端变量
    TcpListener _listener;
    
    // 储存已连接客户的结构体
    private struct Client {
        public TcpClient client;
        public byte[] buffer;               // 接收缓冲区
        public List<byte> pendingData;    // 还未处理的数据
    }

    // 客户列表
    List<Client> _clients = new List<Client>();

    // 开启服务端
    private void StartServer() {
        try {
            _listener = new TcpListener(IPAddress.Any, _port);
            _listener.Start();
            _listener.BeginAcceptSocket(HandleAccepted, _listener);

            _isServer = true;
        } catch (Exception e) {
            Debug.Log(e.Message);
        }
    }

    // 停止停止服务端
    private void StopServer() {
        try {
            _listener.Stop();

            // 清空客户列表
            lock (_clients) {
                foreach (Client c in _clients) {
                    RemoveClient(c);
                }
                _clients.Clear();
            }

            // 清空数据包
            lock (_packages) {
                _packages.Clear();
            }
            
            _isServer = false;
        } catch (Exception e) {
            Debug.Log(e.Message);
        }
    }

    // 处理客户端连接的回调函数
    private void HandleAccepted(IAsyncResult iar) {
        if (_isServer) {
            TcpClient tcpClient = _listener.EndAcceptTcpClient(iar);
            Client client = new Client();
            client.client = tcpClient;
            client.buffer = new byte[tcpClient.ReceiveBufferSize];
            client.pendingData = new List<byte>();

            // 把客户加入到客户列表
            lock (_clients) {
                AddClient(client);
            }

            // 开始异步接收从客户端收到的数据
            tcpClient.GetStream().BeginRead(
                    client.buffer,
                    0,
                    client.buffer.Length,
                    HandleClientDataReceived,
                    client);

            // 开始异步接收连接
            _listener.BeginAcceptSocket(HandleAccepted, _listener);
        }
    }

    // 从客户端接收数据的回调函数
    private void HandleClientDataReceived(IAsyncResult iar) {
        try {
            if (_isServer) {
                Client client = (Client)iar.AsyncState;
                NetworkStream ns = client.client.GetStream();
                int bytesRead = ns.EndRead(iar);

                // 连接中断
                if (bytesRead == 0) {
                    lock (_clients) {
                        _clients.Remove(client);
                    }
                    return;
                }

                // 保存数据
                for (int i=0; i<bytesRead; ++i) {
                    client.pendingData.Add(client.buffer[i]);
                }

                // 把数据解析成包
                while (client.pendingData.Count >= _packageSize) {
                    byte[] bp = client.pendingData.GetRange(0, _packageSize).ToArray();
                    client.pendingData.RemoveRange(0, _packageSize);

                    // 把数据包分发给所有客户
                    lock(_clients) {
                        foreach (Client c in _clients) {
                            c.client.GetStream().Write(bp, 0, _packageSize);
                            c.client.GetStream().Flush();
                        }
                    }
                }

                // 开始异步接收从客户端收到的数据
                client.client.GetStream().BeginRead(
                        client.buffer,
                        0,
                        client.buffer.Length,
                        HandleClientDataReceived,
                        client);
            }
        } catch (Exception e) {
            Debug.Log(e.Message);
        }
    }

    // 加入客户
    private void AddClient(Client c) {
        _clients.Add(c);
    }

    // 删除客户
    private void RemoveClient(Client c) {
        c.client.Client.Disconnect(false);
    }

    // 客户端变量
    Client _server;

    // 连接至服务端
    private void ConnectServer() {
        try {
            TcpClient tcpServer = new TcpClient();
            tcpServer.Connect(_ip, _port);
            _server = new Client();
            _server.client = tcpServer;
            _server.buffer = new byte[tcpServer.ReceiveBufferSize];
            _server.pendingData = new List<byte>();
            
            // 异步接收服务端数据
            tcpServer.GetStream().BeginRead(
                _server.buffer, 
                0, 
                tcpServer.ReceiveBufferSize,
                HandleServerDataReceived,
                _server);

            _connected = true;
        } catch (Exception e) {
            Debug.Log(e.Message);
        }
    }

    // 从服务端断开
    private void DisconnectServer() {
        try {
            lock (_server.client) {
                _server.client.Client.Close();
            }

            // 清空数据包
            lock (_packages) {
                _packages.Clear();
            }

            // 删除所有客户人物模型
            mCharacter.RemoveAllEnemyCharacter();

            _connected = false;
        } catch (Exception e) {
            Debug.Log(e.Message);
        }
    }

    // 从服务端读取数据的回调函数
    private void HandleServerDataReceived(IAsyncResult iar) {
        if (_connected) {
            Client server = (Client)iar.AsyncState;
            NetworkStream ns = server.client.GetStream();
            int bytesRead = ns.EndRead(iar);
      
            // 连接中断
            if (bytesRead == 0) {
                DisconnectServer();
                return;
            }

            // 保存数据
            for (int i=0; i<bytesRead; ++i) {
                server.pendingData.Add(server.buffer[i]);
            }

            // 把数据解析成包
            while (server.pendingData.Count >= _packageSize) {
                byte[] bp = server.pendingData.GetRange(0, _packageSize).ToArray();
                server.pendingData.RemoveRange(0,_packageSize);

                // 把数据转换成包然后再储存包列表
                object obj;
                Deserialize(bp, out obj);

                lock (_packages) {
                    _packages.Add((Package)obj);
                }
            }

            // 异步接收服务端数据
            server.client.GetStream().BeginRead(
                server.buffer, 
                0, 
                server.client.ReceiveBufferSize,
                HandleServerDataReceived,
                server);
        }
    }
    
    // 发送自己的当前的状态包给服务端
    public void SendStatus(Vector3 pos, Vector3 rot,Vector3 cameraRot, 
        Vector3 rightHandRot, bool isShooted, int hp) {
        try {
            if (_connected) {     
                Package p = new Package();
                p.id = _id;
                p.pos.Fill(pos);
                p.rot.Fill(rot);
                p.cameraRot.Fill(cameraRot);
                p.rightHandRot.Fill(rightHandRot);
                p.isShooted = isShooted;
                p.hp = hp;

                // 发送包到服务端
                byte[] bp;
                Serialize(p, out bp);

                lock (_server.client) {
                    _server.client.GetStream().Write(bp, 0, _packageSize);
                    _server.client.GetStream().Flush();
                }
            }
        } catch (Exception e) {
            Debug.Log(e.Message);

            // 断开服务端
            DisconnectServer();
        }
    }

    // 获取包
    public bool NextPackage(out Package p) {
        lock (_packages) {
            if (_packages.Count == 0) {
                p = new Package();
                return false;
            }

            p = _packages[0];
            _packages.RemoveAt(0);
        }

        return true;
    }
}

4.EnemyCharacter.cs

这个脚本负责控制其他客户的行为,比如从服务端接收到其他客户移动或开枪的数据,就要用这个脚本来更新其他客户的当前行为

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

public class EnemyCharacter : MonoBehaviour {

	// Use this for initialization
	void Start () {
        // 获取本机玩家的对象
        mCharacter = GameObject.Find("Character").transform;
        mCharacterComponent = mCharacter.GetComponent<Character>();
        // 获取摄像头对象
        mCamera = transform.Find("Camera");
		// 获取右手对象
        mRightHand = transform.Find("RightHand");
        // 获取枪声播放组件
        mGunAudio = transform.Find("RightHand/Pistol").GetComponent<AudioSource>();
        // 获取火花效果
        mFireEffect = transform.Find("RightHand/Pistol/FireEffect").GetComponent<ParticleSystem>();
        // 获取脚步声播放组件
        mAudio = transform.GetComponent<AudioSource>();
        // 显示血量和ID的组件
        txID = transform.Find ("ID");
        txIDText = transform.Find ("ID").GetComponent<TextMesh> ();
        txHP = transform.Find ("HP");
		txHPText = transform.Find ("HP").GetComponent<TextMesh> ();
	}
	
	// Update is called once per frame
	void Update () {
		// 摧毁对象
        if (isDestroy) {
            Destroy(gameObject);
        }

        // 更新对象属性
        UpdataProperties();
	}
    
    private Transform mCharacter;
    private Character mCharacterComponent;
    private Transform mCamera;
    private Transform mRightHand;
    private AudioSource mGunAudio;
    private AudioSource mAudio;
    private ParticleSystem mFireEffect;     // 开枪后的火花
    public GameObject mPiece;       // 开枪后撞击产生的碎片
    private bool isDestroy = false;

    // 销毁角色
    public void Destroy() {
        isDestroy = true;
    }

    // 角色移动动作
    public void Move(Vector3 pos, Vector3 rot, Vector3 cameraRot, Vector3 rightHandRot) {
        if (pos != transform.position) {
            transform.position = pos;
            PlayStepSound();
        } else {
            StopPlayStepSound();
        }

        mCamera.eulerAngles = cameraRot;

        transform.eulerAngles = rot;

        mRightHand.eulerAngles = rightHandRot;
    }

    // 播放脚步声
    private void PlayStepSound() {
        if (!mAudio.isPlaying) {
            mAudio.Play();
        }
    }

    // 停止播放声音
    private void StopPlayStepSound() {
        if (mAudio.isPlaying) {
            mAudio.Stop();
        }
    }

    // 开枪
    public void Fire() {
        // 射击音效与画面
        PlayShotSound();

        // 播放火花效果
        PlayFireEffect();

        // 判断射击位置
        RaycastHit hit;
        if (Physics.Raycast(mCamera.position, mCamera.forward, out hit, 100)) {
            // 被枪击中的地方会有碎片弹出
            DrawPieces(hit);

            // 判断本机玩家是否中枪如果是就减
            print(hit.collider.name);
            if (hit.collider.name == mCharacter.name) {
                mCharacterComponent.GetHurt();
            }
        }
    } 

    // 播放枪声
    private void PlayShotSound() {
        mGunAudio.PlayOneShot(mGunAudio.clip);
    }

    // 画碎片
    private void DrawPieces(RaycastHit hit) {
        for (int i = 0; i < 5; ++i) {
            GameObject p = Transform.Instantiate(mPiece);

            // 碎片撞击到物体后的反弹位置
            Vector3 fwd = mCamera.forward * -1;
            p.transform.position = hit.point;
            p.GetComponent<Rigidbody>().AddForce(fwd * 100);

            // 0.3秒后删除
            Destroy(p, 0.3f);
        }
    }

    // 播放火花效果
    private void PlayFireEffect() {
        mFireEffect.Play();
    }

    // 人物变量
    private int _id = 1;
    private int _hp = 100;
    private Transform txID;
    private TextMesh txIDText;
    private Transform txHP;
    private TextMesh txHPText;

    // 角色id
    public void SetID(int id) {
        _id = id;
    }

    // 角色血量
    public void SetHP(int hp) {
        _hp = hp;
    }

    // 更新角色变量/属性
    private void UpdataProperties() {
        // 显示血量和ID
        txIDText.text = "ID:"+_id.ToString();
        txHPText.text = "HP:"+_hp.ToString();

        // 血量和ID的方向,面向着本机玩家
        txID.rotation = mCharacter.rotation;
        txHP.rotation = mCharacter.rotation;
    }
}


  • 84
    点赞
  • 364
    收藏
    觉得还不错? 一键收藏
  • 61
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 61
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值