当前位置: 首页 > article >正文

MMO地图传送

本篇由以下四个点讲解:

创建传送点

传送点配置

编辑器扩展:传送点数据生成

传送协议与实现

创建传送点

建碰撞器触发

//位置归零

建一个传送门cube放到要传送的位置(这个teleporter1是传出的区域

这是从另一张地图传入时的传送门

创建一个脚本TeleporterObject给每个传送cube都绑上脚本

通过脚本,让传送门在编辑器下面还能绘制出来

给每个传送点编号

把特效挂在传送点上

//把野外场景的传送也加上(并把传送门的id改了

碰撞检测

TelePorterObject:OnTriggerEnter

private void OnTriggerEnter(Collider other)
{
    PlayerInputController playerInputController=other.GetComponent<PlayerInputController>();
    //传入的对象是否有玩家控制器
    if(playerInputController!=null&&playerInputController.isActiveAndEnabled)
    {
        //得到传送点的ID
        TeleporterDefine teleDefine = DataManager.Instance.Teleporters[this.ID];
        if(teleDefine==null)
        {
            //从角儿控制器取得角色character,第几个传送点
            Debug.LogErrorFormat("TeleporterObject: Character [{0}] Enter Teleporter [{1}] ,But TeleporterDefine not existed", playerInputController.character.Info.Name, this.ID);
            return;
        }
        Debug.LogFormat("TeleporterObject: Character[{0}] Enter Telepoter [{1}:{2}] ",playerInputController.character.Info.Name, teleDefine.ID,teleDefine.Name); ;
        if(teleDefine.LinkTo>0)
        {
            if(DataManager.Instance.Teleporters.ContainsKey(teleDefine.LinkTo))
                MapService.Instance.SendMapTeleporter(this.ID);
            else Debug.LogErrorFormat("Teleporter ID:{0} LinkID {1} error!",teleDefine.ID,teleDefine.LinkTo);   
        }
    }
}

在MapService中发送进入传送点的信息SendMapTeleporter

SendMapTeleporter
public void SendMapTeleport(int teleporterID)
{
    Debug.LogFormat("MapTeleporterRequest :teleporterID:{0}", teleporterID);
    NetMessage message = new NetMessage();
    message.Request = new NetMessageRequest();
    message.Request.mapTeleport = new MapTeleportRequest();
    message.Request.mapTeleport.teleporterId = teleporterID;
    NetClient.Instance.SendMessage(message);
}

向客户端发送有角色进入传送点的信息

message MapTeleportRequest
{
	int32 teleporterId = 1;
}

只需要传一个传送点id即可(也可以传地图的id,再传送点的id)

 服务端的协议处理MapService:OnMapTeleport

在MapService()中, 

订阅:

     MessageDistributer<NetConnection<NetSession>>.Instance.Subscribe<MapTeleportRequest>(this.OnMapTeleport);

void OnMapTeleport(NetConnection<NetSession> sender,MapTeleportRequest request)
{
    //得到客户端进行传送点传送的对象
    Character character=sender.Session.Character;
    Log.InfoFormat("OnMapTeleporter: characterID:{0}:{1} TeleporterId:{2}", character.Id, character.Data, request.teleporterId);

    //没有该传送点
    if(!DataManager.Instance.Teleporters.ContainsKey(request.teleporterId))
    {
        Log.WarningFormat("Source TeleporterID[{0}] not existed", request.teleporterId);
        return;
    }
    

    TeleporterDefine teleportDefine=DataManager.Instance.Teleporters[request.teleporterId]; 
    if(teleportDefine.LinkTo==0||!DataManager.Instance.Teleporters.ContainsKey(teleportDefine.LinkTo))
    {
        Log.WarningFormat("Source TeleporterID [{0}] LinkTo ID [{1}] not existed", request.teleporterId, teleportDefine.LinkTo);
    }

    //从客户端传过来的传送点数据表teleportDefine.LinkTo:6 
    //取的key为6 传送目标点
    TeleporterDefine teleporterDefine1 = DataManager.Instance.Teleporters[teleportDefine.LinkTo];

    //角色所在的地图,角色离开处理
    MapManager.Instance[teleportDefine.MapID].CharacterLeave(character);
    //把新位置信息填充给角色
    character.Position=teleporterDefine1.Position;
    character.Direction=teleporterDefine1.Direction;
    //角色进入新地图
    MapManager.Instance[teleporterDefine1.MapID].CharacterEnter(sender,character);
}

关于传送点配置表TeleporterDefine:

点击这里查看是否有TeleporterDefine配置表生成

扩展编辑器

MapTool:遍历所有传送点,把里面的世界坐标转换成逻辑坐标存到配置表中

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEditor.SceneManagement;
using UnityEditor;
using Common.Data;
public class MapTool : MonoBehaviour
{
    [MenuItem("Map Tools/Export Teleportters")]
    //扩展功能:static
    public static void ExportTeleporters()
    {
        DataManager.Instance.Load();

        Scene current=EditorSceneManager.GetActiveScene();
        string currentScene=current.name;
        //把当前场景记录下来,并检查有无保存
        if(current.isDirty)
        {
            EditorUtility.DisplayDialog("提示", "请先保存当前场景", "确定");
            return;
        }

        List<TeleporterObject> allTeleporters=new List<TeleporterObject>();

        foreach(var map in DataManager.Instance.Maps)
        {//根据地图里配置名字生成原始路径
            string sceneFile = "Assets/Levels/" + map.Value.Resource + ".unity";
            if(!System.IO.File.Exists(sceneFile))
            {//判断每一个场景文件是否存在
                Debug.LogWarningFormat("Scene {0} not existed!", sceneFile);
                continue;
            }
            //打开单个场景
            EditorSceneManager.OpenScene(sceneFile,OpenSceneMode.Single);

            //检查所有的传送点
            TeleporterObject[] teleporters=GameObject.FindObjectsOfType<TeleporterObject>();    
            foreach(var teleporter in teleporters)
            {
                Debug.Log("传送点ID" + teleporter.ID);
                if(!DataManager.Instance.Teleporters.ContainsKey(teleporter.ID))
                {//检查传送点的id在配置表中是否存在
                    EditorUtility.DisplayDialog("错误", string.Format("地图:{0} 中配置的 Teleporter:[{1}]中不存在", map.Value.Resource, teleporter.ID), "确定");
                    return;
                }

                TeleporterDefine def=DataManager.Instance.Teleporters[teleporter.ID];
                if (def.MapID != map.Value.ID)
                {//地图配的mapID是否正确
                    EditorUtility.DisplayDialog("错误", string.Format("地图:{0} 中的配置的Teleporter:[{1}] MapID:{2} 错误", map.Value.Resource,teleporter.ID,def.MapID), "确定");
                    return;
                }
                def.Position=GameObjectTool.WorldToLogicN(teleporter.transform.position);
                def.Direction=GameObjectTool.WorldToLogicN(teleporter.transform.forward);
            }
        }
        //Save逻辑写在DataMangaer下,运行时是不会受影响的
        DataManager.Instance.SaveTeleporters();
        EditorSceneManager.OpenScene("Assets/Levels/" + currentScene + ".unity");
        EditorUtility.DisplayDialog("提示", "传送点导出完成", "确定");
    }
    
}

把所有传送点的layer改成teleporter;因为角色是Default;Default之间是不可能碰撞的

演示:

传送成功;

 传送请求:1号传送点传送到野外的6号点

//从野外传回主城

5号传送点,传LinkTo2号传送点

//但是在野外的相机没有对着角色;在两个场景的切换时,角色会浮空

//Add:可以在场景切换时做一个Loading进度条掩盖

->Map01只有MainPlayerCamera带过来的相机发挥跟随角色的作用/创建角色时相机已经挂上了

关于到了新的场景中固定UI没有显示

把UIMainCity做成单例

(在加载新场景时UIMainCity会再创建实例

//可以看到现在加载到另一个场景,显示了UIMainCity和MainPlayerCamera以及UIWorldElementManager等;还有一些在每个场景中必要的GameObject:

//它们都是挂了单例脚本的物体

//关于刷新数据

例如小地图的mapImage

//小地图需要在次世界场景下加一个BoundingBox;根据当前角色的位置更新在小地图上的位置

需要将每次切换场景时把角色数据都拉一次进来

在UIMinmap.cs中,只有在启动时才加载了小地图

//在后面的文章会讲到


http://www.kler.cn/a/294982.html

相关文章:

  • 计算机的错误计算(一百四十八)
  • CSS文本样式与浮动
  • 【Python】图片处理
  • 基于Spring Boot的船舶监造系统的设计与实现,LW+源码+讲解
  • 【Java SE语法】抽象类(abstract class)和接口(interface)有什么异同?
  • 默认 iOS 设置使已锁定的 iPhone 容易受到攻击
  • 计网名词解释
  • 【qt踩坑】路径含中文导致的报错,以及 OpenGL的链接报错
  • STM32学习笔记4 --- USART
  • 实现点击 `el-dialog` 里面的一个图标将对话框放大至全屏
  • QT自动获取编译日期与git commit ID
  • 【C++11】深入理解与应用右值引用
  • python可执行文件exe
  • Openharmony 下载到rk3568实现横屏
  • 案例-上海某科技公司:监控易7.0重塑服务器监控模式
  • 简单梳理一个历史脉络
  • urllib与requests爬虫简介
  • 【Nginx系列】Nginx中rewrite模块
  • 牛客(除2!)
  • 设计模式 19 观察者模式
  • 【AIGC】AI编程工具合集及其特点介绍
  • 1-18 平滑处理——高斯滤波 opencv树莓派4B 入门系列笔记
  • 【LabVIEW学习篇 - 17】:人机交互界面设计01
  • 以后写代码都是AI自动写了,Cursor+Claude-3.5-Sonnet,Karpathy 点赞的 AI 代码神器。如何使用详细教程
  • 解决异步任务上下文丢失问题
  • 【Python】6.基础语法(6)文件