IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity联机人物加入游戏、同屏移动、与攻击 -> 正文阅读

[游戏开发]Unity联机人物加入游戏、同屏移动、与攻击

1.加入游戏

BattleFieldReuqest里面JoinRequest:把告诉服务器我要加入了,发送的数据里面key是type,值是join。

然后BattleFieldHandler里面对code判断,如果code是join,就返回player的Index给客户端,并且推送Event给所有客户端。

客户端在解析数据,拿到plauerIndex,并且在对应点实例化出来这个玩家。并且将index赋值给其PlayerMove脚本,并且将isMe设置为true。添加到客户端的PlayerList里面。

在处理Event方面,其他玩家判断type是不是join,然后把新的玩家添加到场景中。

2.同屏移动。

如果当前是自己,按下键盘获取到轴,向发起移动请求MoveRequest,发Horizatal和verital的数值,0或者+-1。

然后服务端对code判断,如果code是move,就发送event给所有客户端。

然后客户端处理Event,如果type是move,就解析数据,并且调用MovePlayer方法,MovePlayer根据index找到要移动的那个物体,去移动。(这里主要要更新一下坐标,((只要更新一下就行了!不要一直更新

3.攻击

如果当前是自己,点击攻击,遍历客户端的玩家列表,如果item不是自己并且距离小于xxx,就播放攻击动画,并且在攻击动画结束的那一帧向服务端发起Attack请求,里面包含被攻击玩家的index。

然后服务端根据code判断,如果code是attack,就将被攻击玩家的id发送Event给所有客户端。

客户端处理Event,判断code是否为Attack,然后拿到index,调用attack方法:遍历玩家列表,谁的index等于当前index,谁就扣血,被攻击,在其出实例化攻击特效。

_____________________________________________________________________________

客户端

PhotonEngine.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ExitGames.Client.Photon;
using Common;

public class PhotonEngine : MonoBehaviour,IPhotonPeerListener {
    public static PhotonEngine Instance;


    public static PhotonPeer peer;
    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            //当前Gameobject不会随场景的销毁而销毁
            DontDestroyOnLoad(this.gameObject);
        }
    }

    void Start () {
        peer = new PhotonPeer(this,ConnectionProtocol.Udp);
        peer.Connect("127.0.0.1:5055", "MyGame6");
	}
	
	void Update () {
        peer.Service();
	}

    private void OnDestroy()
    {
        if(peer!=null&&peer.PeerState == PeerStateValue.Connected)
        {
            peer.Disconnect();
        }
    }

    public void DebugReturn(DebugLevel level, string message)
    {
    }

    public void OnEvent(EventData eventData)
    {
        DicTool.GetValue(RequestDic, (OperationCode)eventData.Code).
    OnEvent(eventData);

        return;
        if ((byte)OperationCode.Chat == eventData.Code)
        {
            var data = eventData.Parameters;
            object intValue;
            data.TryGetValue((byte)ParaCode.Chat, out intValue);
            Debug.Log("收到服务器:" + intValue+ eventData.Code);
        }
       

        switch (eventData.Code)
        {
            //case 1:
            //    //解析数据
            //    var data = eventData.Parameters;
            //    object intValue, StringValue;
            //    data.TryGetValue(1, out intValue);              
            //    data.TryGetValue(2, out StringValue);
            //    Debug.Log("收到服务器的响应Event推送,OpCode:1" + intValue.ToString() + ":" + StringValue.ToString());
            //    break;
            
            default:
                break;
        }
    }

    public void OnOperationResponse(OperationResponse operationResponse)
    {
        DicTool.GetValue(RequestDic, (OperationCode)operationResponse.OperationCode).
            OnOprationRespionse(operationResponse);
      
        return;
        switch (operationResponse.OperationCode)
        {
            case 1:
                Debug.Log("收到服务器的响应,OpCode:1");

                //解析数据
                var data = operationResponse.Parameters;
                object intValue;
                data.TryGetValue(1, out intValue);
                object StringValue;
                data.TryGetValue(2, out StringValue);
                Debug.Log("收到客户端的请求,OpCode:1" + intValue.ToString() + ":" + StringValue.ToString());
                break;
            default:
                break;
        }
    }

    public void OnStatusChanged(StatusCode statusCode)
    {
        Debug.LogError(statusCode);
    }

    private Dictionary<OperationCode, Request> RequestDic = new Dictionary<OperationCode, Request>();

    public void AddRequest(Request r)
    {
        RequestDic.Add(r.OpCode, r);
    }
    public void RemoveRequest(Request r)
    {
        RequestDic.Remove(r.OpCode);
    }
    public Request GetRequest(OperationCode code)
    {
        return DicTool.GetValue(RequestDic, code);
    }
}

BattleFieldRequest.cs:

using System.Collections;
using System.Collections.Generic;
using ExitGames.Client.Photon;
using UnityEngine;
using Common;
using System;

public class BattleFieldRequest : Request
{

    public static BattleFieldRequest Instance;
    private int curX;
    private int curY;

    private void Awake()
    {
        base.Awake();
        if (Instance == null)
        {
            Instance = this;
        }
    }

    public override void DefaultRequest()
    {

    }

    public override void OnOprationRespionse(OperationResponse operationResponse)
    {
        //解析数据
        if (operationResponse.ReturnCode == (byte)ReturnCode.Success)
        {
            int playerIndex = (int)DicTool.GetValue<byte, object>(operationResponse.Parameters, (byte)ParaCode.BF_Join);
            Debug.Log("playerIndex:" + playerIndex);
            BattleFieldManager.Instance.InitBattleField(playerIndex);
        }
        else
        {
            Debug.Log("请求失败");
        }
    }

    internal void MoveRequest(int posX, int posY, Vector3 pos)
    {
        //在更新操作的时候,才发送请求
        if (curX == posX && curY == posY) return;
        curX = posX;
        curY = posY;

        //构造参数
        var data = new Dictionary<byte, object>();

        data.Add((byte)ParaCode.ParaType, ParaCode.BF_Move);
        data.Add((byte)ParaCode.BF_Move, BattleFieldManager.Instance.MyPlayerIndex.ToString() + "," + posX + "," + posY + "," + pos.x + "," + pos.y + "," + pos.z);
        //发送
        PhotonEngine.peer.OpCustom((byte)OpCode, data, true);
    }

    internal void AttackRequest(int playerIndex)
    {
        //构造参数
        var data = new Dictionary<byte, object>();
        //构造参数
        data.Add((byte)ParaCode.ParaType, ParaCode.BF_Att);
        data.Add((byte)ParaCode.BF_Att, playerIndex);

        //发送
        PhotonEngine.peer.OpCustom((byte)OpCode, data, true);

    }

    void Start()
    {

    }

    void Update()
    {

    }

    public void JoinRequest()
    {
        var data = new Dictionary<byte, object>();
        //key 是 本次请求类型 ,value 是 加入战场
        data.Add((byte)ParaCode.ParaType, ParaCode.BF_Join);

        PhotonEngine.peer.OpCustom((byte)OpCode, data, true);

    }


    public override void OnEvent(EventData data)
    {
        //解析数据

        ParaCode type = (ParaCode)DicTool.GetValue<byte, object>(data.Parameters, (byte)ParaCode.ParaType);
        // Debug.Log("收到服务器:" + type);
        if (type == ParaCode.BF_Join)
        {
            string allPlayer = (string)DicTool.GetValue<byte, object>(data.Parameters, (byte)ParaCode.BF_Join);
            // Debug.Log("收到服务器:" + allPlayer);
            BattleFieldManager.Instance.AddPlayer(allPlayer);
        }
        else if (type == ParaCode.BF_Move)
        {
            string para = (string)DicTool.GetValue<byte, object>(data.Parameters, (byte)ParaCode.BF_Move);
            //   Debug.Log("收到服务器BF_Move:" + para);

            var list = para.Split(',');
            BattleFieldManager.Instance.MovePlayer(Convert.ToInt16(list[0]), Convert.ToInt16(list[1]), Convert.ToInt16(list[2]), float.Parse(list[3]), float.Parse(list[4]), float.Parse(list[5]));

        }
        else if (type == ParaCode.BF_Att)
        {
            int para = (int)DicTool.GetValue<byte, object>(data.Parameters, (byte)ParaCode.BF_Att);

            Debug.Log("收到服务器BF_Move:" + para);
            BattleFieldManager.Instance.AttPlayer(para);
        }

    }
}

PlayerMove.cs:

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

public class PlayerMove : MonoBehaviour
{
    public int playerIndex;
    public bool isMe = false;
    internal short DirX;
    internal short DirY;

    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        //把指令发送给服务器
        if (isMe)
        {
            //获取输入轴
            int posX = (int)Input.GetAxis("Horizontal");
            int posY = (int)Input.GetAxis("Vertical");
            BattleFieldRequest.Instance.MoveRequest(posX, posY, transform.position);

            if (Input.GetKeyDown(KeyCode.Mouse0))
            {
                //获取到攻击对象,如果有,发送攻击指令
                foreach (var item in BattleFieldManager.Instance.playerList)
                {
                    if (item != this)
                    {
                        var dis = Vector3.Distance(item.transform.position, this.transform.position);
                        if (dis < 1f)
                        {
                            //切换动画状态机
                            GetComponent<Animator>().SetTrigger("attack");
                            attIndex = item.playerIndex;


                            GetComponent<Transform>().LookAt(item.GetComponent<Transform>().position);
                        }
                    }
                }
            }
        }

        Move(DirX, DirY);
    }
    public int attIndex;
    [HideInInspector]
    public int HP = 100;

    public void OnAttackAnimation()
    {
        //发送攻击请求
        BattleFieldRequest.Instance.AttackRequest(attIndex);
    }

    public void Move(int x, int y)
    {
        var speed = new Vector3(x, 0, y);
        GetComponent<Transform>().LookAt(GetComponent<Transform>().position + speed);

        //控制 nav agent移动
        GetComponent<NavMeshAgent>().velocity = new Vector3(x, 0, y) / 2.4f;
        //切换动画状态机
        GetComponent<Animator>().SetFloat("speed", speed.magnitude);
    }
}

BattleFieldManager.cs:

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

public class BattleFieldManager : MonoBehaviour
{
    public static BattleFieldManager Instance;
    public int MyPlayerIndex = -1;

    public GameObject Hero;
    public List<Transform> H1Pos;

    public List<PlayerMove> playerList = new List<PlayerMove>();

    public GameObject attFx;
    private void Awake()
    {
        Instance = this;
    }

    void Start()
    {

    }

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

    }

    internal void InitBattleField(int playerIndex)
    {
        //初始化场景
        MyPlayerIndex = playerIndex;
        //开始实例化
        PlayerMove p = (Instantiate(Hero, H1Pos[MyPlayerIndex - 1].position, Quaternion.identity) as GameObject).GetComponent<PlayerMove>();
        p.playerIndex = MyPlayerIndex;
        p.isMe = true;

        playerList.Add(p);
    }

    internal void AddPlayer(string allPlayer)
    {
        //解析
        var arr = allPlayer.Split(',');
        foreach (var item in arr)//遍历在场玩家的index
        {
            try//最后多了一个空格,所以转换成int会失败,不过这里忽略不计
            {
                //转换成int
                int i = Convert.ToInt16(item);

                if (i != MyPlayerIndex)
                {
                    print("不是自己:" + i);
                    PlayerMove p = (Instantiate(Hero, H1Pos[i - 1].position, Quaternion.identity) as GameObject).GetComponent<PlayerMove>();
                    p.playerIndex = i;
                    playerList.Add(p);
                }
            }
            catch (Exception)
            {

            }

        }
    }

    internal void MovePlayer(short index, short x, short y, float Px, float Py, float Pz)
    {


        foreach (var item in playerList)
        {
            //拿到对应index的对象
            if (item.playerIndex == index)
            {
                //设置移动方向
                item.DirX = x;
                item.DirY = y;
                item.transform.position = new Vector3(Px, Py, Pz);
            }
        }
    }

    internal void AttPlayer(int index)
    {
        //扣血
        foreach (var item in playerList)
        {
            if (item.playerIndex == index)
            {
                item.HP -= 20;
                print(index + " 被攻击,剩余血量 " + item.HP);
                //播放特效fx
                Instantiate(attFx, item.transform.position, Quaternion.identity);
            }

        }
    }
}

———————————————————————————————————————————

服务端

GameModel.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RRGameServer
{
    class GameModel
    {
        public static GameModel Instance = new GameModel();
        public List<ClientPeer> PeerList = new List<ClientPeer>();//用户保存所有的peer,一个客户端连接就会有一个peer
    }
}

MyGameServer.cs:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common;
using ExitGames.Logging;
using ExitGames.Logging.Log4Net;
using log4net.Config;
using Photon.SocketServer;
using RRGameServer.Handler;
using RRGameServer.Manager;

namespace RRGameServer
{
    //所有的Server,都要继承ApplicationBase,然后实现ApplicationBase的三个方法
    public class MyGameServer : ApplicationBase
    {
        public static MyGameServer Instance;
        public static readonly ILogger log = LogManager.GetCurrentClassLogger();
       

        //当有一个客户端连接上以后,就会执行此方法
        protected override PeerBase CreatePeer(InitRequest initRequest)
        {
            log.Info("一个客户端连接");
            var p = new ClientPeer(initRequest);
            return p;
        }

        //服务器初始化函数
        protected override void Setup()
        {
            Instance = this;
            log4net.GlobalContext.Properties["Photon:ApplicationLogPath"] = Path.Combine(
                Path.Combine(this.ApplicationRootPath, "bin_Win64"), "log");
            FileInfo configFileInfo = new FileInfo(Path.Combine(this.BinaryPath, "log4net.config"));

            if (configFileInfo.Exists)
            {
                LogManager.SetLoggerFactory(Log4NetLoggerFactory.Instance);//photon知道日志输出
                XmlConfigurator.ConfigureAndWatch(configFileInfo);//读取配置
            }
            log.Info("服务器启动啦");

            //log.Info(UserManager.Instance.GetUserById(7).Username);
            InitHandler();//初始化
        }

        //服务器关闭函数
        protected override void TearDown()
        {
            log.Info("服务器关闭啦");
        }

        public Dictionary<OperationCode, BaseHandler> HandlerDic = new Dictionary<OperationCode, BaseHandler>();

        public void InitHandler()
        {
            //一开始就全部初始化
            BattleFieldHandler bh = new BattleFieldHandler();
            HandlerDic.Add(bh.OpCode, bh);

        }

    }
}

ClientPeer.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common;
using Photon.SocketServer;
using PhotonHostRuntimeInterfaces;
using RRGameServer.Handler;

namespace RRGameServer
{
    public class ClientPeer : Photon.SocketServer.ClientPeer
    {
        public int playerIndex;
        public int id;
        public ClientPeer(InitRequest initRequest) : base(initRequest)
        {
            GameModel.Instance.PeerList.Add(this);
        }

        //当每个客户端断开时
        protected override void OnDisconnect(DisconnectReason reasonCode, string reasonDetail)
        {
            GameModel.Instance.PeerList.Remove(this);
        }

        //当客户端发起请求的时候
        protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters)
        {
            BaseHandler handler = DicTool.GetValue(MyGameServer.Instance.HandlerDic, (OperationCode)operationRequest.OperationCode);
            if (handler != null)
            {
                handler.OnOperationRequest(operationRequest, sendParameters,this);
            }
            else
            {
                MyGameServer.log.Info("找不到操作码:" + (OperationCode)operationRequest.OperationCode);
            }

            return;
            //switch (operationRequest.OperationCode)
            //{
            //    case 1:
            //        //解析数据
            //        var data = operationRequest.Parameters;
            //        object intValue;
            //        data.TryGetValue(1, out intValue);
            //        object StringValue;
            //        data.TryGetValue(2, out StringValue);
            //        //输出参数
            //        MyGameServer.log.Info("收到客户端的请求,opcode:1"+intValue.ToString()+":"+StringValue.ToString());

            //        //返回响应
            //        OperationResponse opResponse = new OperationResponse(operationRequest.OperationCode);

            //        //构造参数
            //        var data2 = new Dictionary<byte, object>();
            //        data2.Add(1, 100);
            //        data2.Add(2, "这是服务端做的响应");
            //        opResponse.SetParameters(data2);
            //        //返回code,为发送过来的code,返回的参数,为发送过来的参数
            //        SendOperationResponse(opResponse, sendParameters);

            //        //推送一个Event
            //        EventData ed = new EventData(1);
            //        ed.Parameters = data2;
            //        SendEvent(ed, new SendParameters());
            //        break;
            //    default:
            //        break;
            //}
        }
    }
}

BattleFieldHandler.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common;
using Photon.SocketServer;

namespace RRGameServer.Handler
{
    class BattleFieldHandler : BaseHandler
    {
        public BattleFieldHandler()
        {
            OpCode = Common.OperationCode.BattleField;
        }

        public override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters, ClientPeer peer)
        {
            //取出key为ParaType的参数,判断一下我们是什么类型的请求
            ParaCode code = (ParaCode)DicTool.GetValue<byte, object>(operationRequest.Parameters,(byte)ParaCode.ParaType);
            if (code == ParaCode.BF_Join)
            {
                //加入的时候,发送PlayerIndex给对方。根据当前玩家的数量返回序号
                OperationResponse response = new OperationResponse(operationRequest.OperationCode);
                //获取序号
                peer.playerIndex = GameModel.Instance.PeerList.Count;

                //参数创建一个dic
                var data2 = new Dictionary<byte, object>();
                data2.Add((byte)ParaCode.BF_Join, peer.playerIndex);

                response.ReturnCode = (short)ReturnCode.Success;
                response.Parameters = data2;
                peer.SendOperationResponse(response, sendParameters);

                //广播到所有客户端,有新玩家加入
                //获取所有玩家的序列号,保存为一条字符串
                var allplayer = "";
                foreach (var peerItem in GameModel.Instance.PeerList)
                {
                    allplayer += peerItem.playerIndex + ",";
                }//1,2,

                //推送一个Event
                EventData ed = new EventData((byte)operationRequest.OperationCode);
                var data3 = new Dictionary<byte, object>();
                //注意,这里2个参数
                data3.Add((byte)ParaCode.ParaType, ParaCode.BF_Join);
                data3.Add((byte)ParaCode.BF_Join, allplayer);
                ed.Parameters = data3;

                foreach (var peerItem in GameModel.Instance.PeerList)
                {
                    peerItem.SendEvent(ed, new SendParameters());
                }
            }
            else if (code == ParaCode.BF_Move)
            {
                string p = (string)DicTool.GetValue<byte, object>(operationRequest.Parameters, (byte)ParaCode.BF_Move);
                //转发给所有客户端

                EventData ed = new EventData((byte)operationRequest.OperationCode);
                var data3 = new Dictionary<byte, object>();
                //注意,这里2个参数
                data3.Add((byte)ParaCode.ParaType, ParaCode.BF_Move);
                data3.Add((byte)ParaCode.BF_Move,p);
                ed.Parameters = data3;

                foreach (var peerItem in GameModel.Instance.PeerList)
                {
                    peerItem.SendEvent(ed, new SendParameters());
                }
            }
            else if (code == ParaCode.BF_Att)
            {
                int p = (int)DicTool.GetValue<byte, object>(operationRequest.Parameters, (byte)ParaCode.BF_Att);
                //转发给所有客户端

                EventData ed = new EventData((byte)operationRequest.OperationCode);
                var data3 = new Dictionary<byte, object>();
                //注意,这里2个参数
                data3.Add((byte)ParaCode.ParaType, ParaCode.BF_Att);
                data3.Add((byte)ParaCode.BF_Att, p);
                ed.Parameters = data3;

                foreach (var peerItem in GameModel.Instance.PeerList)
                {
                    peerItem.SendEvent(ed, new SendParameters());
                }
            }
        }
    }
}

_____________________________________________________________________________

协议:

Class1.cs:

namespace Common
{
    public enum OperationCode : byte
    {
        Login,
        SignIn,
        Chat,
        Show,
        Move,
        BattleField
    }

    public enum ParaCode : byte
    {
        UserName,
        Password,
        Chat,
        Show,
        Move,
        ParaType,
        BF_Join,
        BF_Move,
        BF_Att
    }

    public enum ReturnCode : short
    {
        Success,
        Failed
    }

}

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-01-16 13:26:03  更:2022-01-16 13:27:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 18:47:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码