浅谈游戏地图中位置实时更新的技术方案

7c984ff594a2472202b8346931a09461.gif

地图如今在游戏中发挥的作用越来越重要,随着电子竞技的兴起,地图逐渐成为了为玩家创造体验的直接舞台。希望本文能对有兴趣了解游戏地图背后实现原理的同学一些帮助。

baa20f9e71a2d584c90c93c28a8afd93.png

什么是游戏地图

在游戏中可以通过3D场景虚拟一个完整的世界,当3D场景较为广阔和地形比较复杂时,游戏玩家在场景中漫游行走,往往容易因为陌生的环境而迷失方向。而通过为玩家提供一张场景地图,就可以以一种更加直观的形式向他们呈现整个游戏的世界观。游戏地图通常是为以用户俯视的视角构建出来的描述场景地形环境的地图,例如平原、山地、树林和主干道等,并会显示各处的关键地点。

1ddb94cbe16dd9909032ccad8d5527e6.png

游戏地图的作用

  沉浸式体验

在很多游戏中,地图能够为玩家带来有更加沉浸式的体验。例如像赛车或者奔跑类的游戏,对地图的依赖性很高,游戏的乐趣也往往体现在地图上,通过地图能够唤起、激发玩家对游戏的兴趣,为玩家带来独特的游戏代入感。游戏代入感的一个重要来源就是能够让玩家感同深受,比如玩家控制的角色可以在虚拟的世界里奔跑飞翔,完成一些任务可能需要途径很多地方,在这过程中,设计地图实时更新玩家当前的位置,让游戏本身的设计就与地图的设计相辅相成。

  指路功能

当玩家初次进入复杂且场景范围较大的3D场景中,往往容易迷失方向,对自己当前身处的位置以及场景的全貌没有一个准确的认知。就像现实生活中出远门需要地图导航一样,在3D场景中同样需要为玩家提供一张3D场景对应的地图来承担指路功能,让玩家能清楚地意识到自己前进的方向,以及场景中存在什么建筑物体,就能保证玩家不会在复杂的3D场景地形地貌中迷路。如果是射击类的游戏,在地图中还可以标注出敌人的位置或补给物体的位置,为玩家解锁更多有意思的玩法。

6b9544d7f23416a647e04fd0c38fc68e.png

正俯视视角地图的技术实现

  需求说明
  • 前期准备

一张3D场景的正俯视角度下,与场景XZ平面1:1还原的地图图片素材。

274d3e42e00a0c695d4a59416b00a190.jpeg

图1 正俯视视角地图

  • 实现目标

用户在3D场景内漫游移动时,能够在地图中实时更新用户的位置,并且只展示用户当前位置所对应的地图局部区域,如下图左上角区域所示。

9d381eff35538d66d385028d30108ed5.jpeg

图2 效果展示图

  技术实现
  • 技术方案简述

当我们拥有一张根据3D场景正俯视视角下的地图图片时,用户在3D场景中的实时位置,从俯视视角看下来时就是用户应该位于2D地图所对应的位置。如此,当用户在场景中跑动时,我们计算人物在场景XZ平面上的百分比,即可还原出在2D地图中的百分比。

但是需求里并非简单地将俯视图作为地图进行完全的展示,要求是只展示用户所在位置的局部区域,这样我们可以让地图作为展示区域的背景图,进行局部展示,在人物移动时,移动地图即可形成相对运动的效果。

  • 实现步骤
  1. 绘制地图可见区域框,整张地图图片以canvas元素的形式加载,并作为可见区域背景图

  2. 人物在2D正俯视地图的位置,作为可见区域的中心

  3. 获取场景的AABB包围盒,得到3D场景的XZ平面宽高

  4. 计算人物当前位置在场景XZ平面的百分比

  5. 进而更新人物在地图图片上的百分比

  6. 人物行走,通过移动背景地图图片,更新人物实时位置

  7. 非边缘跑动:地图动,人物不动

  8. 边缘跑动:地图不动,人物动

3b1cb00bdaa839c08894a5464c7f2382.jpeg

图3 计算占比示意图

  • 实现效果说明

有些同学可能会提出疑惑,为何采用canvas绘制地图,而非普通dom元素?

原因是由于IOS机型在使用CSS属性clipPath对可见区域边框裁剪,溢出部分仍可响应触摸事件。改用canvas重新实现,解决了这个问题,并且性能比用dom节点实现更好。

6bda844435ba49a3f17988f2555ca889.jpeg

非正俯视视角地图的技术实现

  需求说明
  • 前期准备

3D场景任意俯视视角下的图片作为场景的大地图,并提供该视角下相机位置、旋转、缩放、远近平面、fov等参数。获得非正俯视视角地图的一个巧妙方法,在unity内查看场景模型时,调整一个合适的位置,截图可得。

a567acafe731453eaea35cd36c804155.jpeg

图4 非正俯视视角地图

c4caa94199d150615e477563f6a96824.jpeg

图5 俯视图相机参数

  • 实现目标

需要实现在相机任意俯视视角下,都能准确还原3D场景中的人物当前位置。

  技术实现
  • 概述

现在终于来到了非正俯视视角地图中,实时更新当前位置的具体技术的实现部分了。

读到这里,有些同学可能会有些疑问了,非正俯视视角地图和正俯视视角地图里都有人物当前位置对应地图的位置,为什么非正俯视视角的地图实现的方式需要赘述一次呢?

其实大家简单想象一下就可以发现,假设人物当前位置静止,当我们以上帝视角(即相机)去俯视3D场景时,从不同角度或位置观察3D场景,得到的如图6、图7所示的俯视图图片是不一样的,人物位置相对地图图片的百分比自然也是不一样的。如此,在非正俯视视角下,我们就不可能采取类似于正俯视视角的按计算百分比的方案去还原人物当前位置。

e0d25e133816486e11a718744b59653b.jpeg

图6 远俯视角度

e718e0c1de2c3b695182ff663216ab29.jpeg

图7 近俯视角度

  • 解决思路

由于所有我们看到的渲染结果,都是视锥体投影到近平面的结果,如果我们把地图的图片放置到近平面上,再从当前相机位置向3D场景中的人物位置发出一条射线,那么该射线与近平面(也就是地图)相交的交点,也就是当前人物的在地图上的实时位置。那么这个方法具体实现起来,可以分成以下这几个步骤。

  1. 定义一个地图所在的平面:法向是相机的朝向,距离是近平面的距离。

    其中相机的朝向在相机坐标系下可以设置为单位方向(0,0,-1),需要将该坐标变换到世界坐标系下,即应用V逆矩阵的变换得到相机朝向在世界坐标系下的表示。

    定义一个平面,除了相机朝向(也就是平面的法向)外,还需要在这个法向上的距离,即近平面的距离0.3。如此,得到世界坐标系下地图所在的平面的定义。

  2. 定义一条射线:世界坐标系下,从相机位置,到人物当前位置,发出一条射线。

  3. 得到一个交点:世界坐标系下,射线与平面的交点,是一个世界坐标系下的3D交点坐标。

  4. 将世界坐标系下的交点坐标,重新转为在相机坐标系下的3D坐标,并去掉不需要的z轴信息。

    世界坐标系转相机坐标系,即将该点做V变换,平移加XYZ轴对齐。

  5. 此时相机坐标系下的点在平面的比例,就是地图中的比例。

    计算近平面的宽高值,宽高值可以通过视锥体的aspect、fov、近平面距离等轻松求出。

    根据unity在相机坐标系是左手系,得到地图的左上角坐标值。

    计算当前交点相对于地图图片的百分比即可。

  • 步骤分解
  1. 定义一个地图所在的平面:法向是相机的朝向,距离是近平面的距离0.3

    const localN = new Vector(0,0,-1)

    const worldN = localN.apply(ModelMatrix)

    const plane = new Plane(worldN.normalize(), near)

  2. 定义一条射线:从相机位置,到人物当前位置,发出一条射线

    const ray = new Ray(ICameraPos, dir.normalize());

  3. 得到平面与射线的一个交点:射线与平面的交点,是一个世界坐标系下的3D坐标 targetPoint

    ray.intersectPlane(plane, targetPoint);

  4. 将世界坐标下的3D坐标转为相机坐标系下的3D坐标 : 相机平移+3个轴对齐--相机坐标系下看向Z轴

    targetPoint = targetPoint.subtract(ICameraPos);

    targetPoint = targetPoint.apply(ModelMatrix.inverse());

  5. 求近平面的宽高

    const halfH = Mat(8)tan(fov*0.5) * near;

    const aspect = imgW / imgH;

    const halfW = halfH * aspect;

    const leftTop = new Vector2(-halfW,halfH);

  6. 此时相机坐标系下的点在平面的比例,就是地图中的比例

    const px = Mat(8)abs(target2DP.x - leftTop.x) / (2 * halfW);

    const py = Mat(8)abs(target2DP.y - leftTop.y) / (2 * halfH);

5eb067263d8fd905295d6175b9927124.jpeg

实现效果

通过视频效果的展示,可以看出,当用户在3D场景中向右走,然后到达右侧交叉路口再向右走时,重新回到地图页,当前位置图标已经更新到对应的正确位置。

a0d0ffa2b78f66d8e956c489be9b2a6c.jpeg

结语

地图如今在游戏中发挥的作用越来越重要,随着电子竞技的兴起,地图逐渐成为了为玩家创造体验的直接舞台。希望本文能对有兴趣了解游戏地图背后实现原理的同学一些帮助。

参考资料:浅谈游戏中的小地图与大地图设计(地址:https://zhuanlan.zhihu.com/p/504211689)

5e56b44d58387fb3ad4a348166a1d954.jpeg

团队介绍

我们是天猫技术品牌线的行业前端团队,目前负责消费电子、3C数码、运动、家装家居、汽车、奢品等行业的线上线下模式的探索,面向淘内淘外,提供商家、门店、消费者最佳用户体验。团队在XR、3D、2D渲染引擎这些创新体验上有不错的沉淀,同时面向全栈领域团队探索了 Serverless 云端研发模式,在消费者前台,通过数据挖掘消费、意图识别提升消费者效率,同样面向工程领域,在跨端、前端工程化、中后台微前端都有一些沉淀,如果你是一位充满想象的终端极客,欢迎你的加入,通过自己的技术想法去改变天猫行业的终端表达。

¤ 拓展阅读 ¤

3DXR技术 | 终端技术 | 音视频技术

服务端技术 | 技术质量 | 数据算法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/757364.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Chrome不支持正则搜索?那我们自己写一个

说在前面 🎈Ctrl F 大家都用过了吧,最近在Chrome中使用搜索功能的时候,突然想要使用正则来进行搜索,发现Chrome浏览器自带的搜索功能并不支持正则搜索,于是便想着自己做了一个支持正则搜索的Chrome插件。 效果展示 实…

python3入门机器学习,知识点全面总结与代码实操示例

目录 写在前面的话 一、机器学习的基本任务与方法分类 机器学习的概念和定义 基本任务 二分类任务(Binary Classification): 多分类任务(Multi-class Classification): 多标签分类任务(Mu…

unity 学习笔记 4.坐标系

下载源码 UnityPackage 目录 1.基础知识 1.1.世界坐标和局部坐标 1.2.屏幕坐标 2.坐标系转换 3.练习:判断鼠标单击的位置 1.基础知识 1.1.世界坐标和局部坐标 1.2.屏幕坐标 2.坐标系转换 3.练习:判断鼠标单击的位置 步骤: 将脚本挂载到小…

JVM 垃圾回收机制:探秘对象生死判定与高效回收算法

目录 一、JVM 对象生死判定 1.1 引用技术算法 1.2 可达性分型算法 二、引用 三、 回收方法区 四、垃圾回收算法 4.1 标记-清楚算法 4.2 标记-复制算法 4.3 标记-整理算法 JVM 程序计数器、虚拟机栈、本地方法栈随着线程而生,随着线程而灭。栈中的栈帧随着方法的…

选数异或 (AcWing 4645)

题目链接: https://www.acwing.com/problem/content/description/4648/ 题目描述: 评价: 这道题感觉还是蛮有意思的&#xff0c;难度适中&#xff0c;而且有一定的思维含量&#xff0c;值得反复品味。 思路: 首先我们定义一个数组g[N], 其中的每个元素g[i] 表示在所有 i<j…

STM32通信协议

STM32通信协议 STM32通信协议 STM32通信协议一、通信相关概念二、通信协议引脚作用三、通信方式四、采样方式五、电平信号六、通信对象 一、通信相关概念 通信接口 通信的目的&#xff1a;将一个设备的数据传送到另一个设备&#xff0c;扩展硬件系统 通信协议&#xff1a;制定…

Python 全栈体系【四阶】(十六)

第五章 深度学习 一、基本理论 2. 深度神经网络结构 2.1 感知机 2.1.1 生物神经元 感知机&#xff08;Perceptron&#xff09;&#xff0c;又称人工神经元&#xff08;Artificial neuron&#xff09;&#xff0c;它是生物神经元在计算机中的模拟。下图是一个生物神经元示意…

wayland(xdg_wm_base) + egl + opengles 使用 Assimp 加载带光照信息的材质文件Mtl 实现光照贴图的最简实例(十七)

文章目录 前言一、3d 立方体 model 属性相关文件1. cube1.obj2. cube1.Mtl3. 纹理图片 cordeBouee4.jpg二、实现光照贴图的效果1. 依赖库和头文件1.1 assimp1.2 stb_image.h2. egl_wayland_obj_cube1.cpp3. Matrix.h 和 Matrix.cpp4. xdg-shell-client-protocol.h 和 xdg-shell…

小程序跨端组件库 Mpx-cube-ui 开源:助力高效业务开发与主题定制

Mpx-cube-ui 是一款基于 Mpx 小程序框架的移动端基础组件库&#xff0c;一份源码可以跨端输出所有小程序平台及 Web&#xff0c;同时具备良好的拓展能力和可定制化的能力来帮助你快速构建 Mpx 应用项目。 Mpx-cube-ui 提供了灵活配置的主题定制能力&#xff0c;在组件设计开发阶…

【计算机视觉】Gaussian Splatting源码解读补充

本文旨在补充gwpscut创作的博文学习笔记之——3D Gaussian Splatting源码解读。 Gaussian Splatting Github地址&#xff1a;https://github.com/graphdeco-inria/gaussian-splatting 论文地址&#xff1a;https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/3d_gauss…

Minio的安装和Java使用示例

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 Minio 是个基于 Go…

三段提交的理解

三阶段提交是在二阶段提交上的改进版本&#xff0c;3PC 最关键要解决的就是协调者和参与者同时挂掉的问题&#xff0c;所以3PC把2PC的准备阶段再次一分为二&#xff0c;这样三阶段提交。 处理流程如下 &#xff1a; 阶段一 协调者向所有参与者发出包含事务内容的 canCommit …

【MySQL】知识点 + 1

# &#xff08;1&#xff09;查询当前日期、当前时间以及到2022年1月1日还有多少天&#xff0c;然后通过mysql命令执行命令。 select curdate() AS 当前日期,curtime() AS 当前时间,datediff(2022-01-01, curdate()) AS 距离2022年1月1日还有天数;# &#xff08;2&#xff09;利…

Windows10蓝屏报错 inaccess boot device

当Windows 10操作系统启动时出现蓝屏错误“INACCESSIBLE_BOOT_DEVICE”时&#xff0c;这通常表明系统在尝试启动过程中无法识别或访问引导设备上的基本系统文件。这个问题可能是由以下几个原因引起的&#xff1a; 硬件兼容性问题&#xff1a; 硬盘控制器驱动不正确或丢失&#…

Go语言学习04~05 函数和面向对象编程

Go语言学习04-函数 函数是一等公民 <font color"Blue">与其他主要编程语言的差异</font> 可以有多个返回值所有参数都是值传递: slice, map, channel 会有传引用的错觉函数可以作为变量的值函数可以作为参数和返回值 学习函数式编程 可变参数 func s…

三种方式,浅谈 Cocos Creator 的动画添加

前言 虽然 Cocos 的官方文档对动画系统做了较详细的介绍&#xff0c;但是对于刚接触的同学&#xff08;比如我&#xff09;来说还是不太友好。尽管如此&#xff0c;我就按文档加社区帖子一起实践了一下。为了方便忘记后能快速捡起&#xff0c;所以就用我的方式结合使用场景&am…

python内存管理和垃圾回收一文详解(基于c语言源码底层逻辑)

引用计数器 首先我们大概回忆一下C语言中的环状双向链表&#xff0c;如图&#xff0c;在双向链表中对于一个结点来说会有前驱和后继&#xff1a; C语言中基本的定义方式如下&#xff1a; typedef struct {ElemType data; // 数据域Lnode* prior; // 前驱指针域Lnode* next;…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Hyperlink)

超链接组件&#xff0c;组件宽高范围内点击实现跳转。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。该组件仅支持与系统浏览器配合使用。 需要权限 使用网络时&#xff0c;需要申请权限ohos.per…

【PyTorch][chapter 22][李宏毅深度学习][ WGAN]【实战三】

前言&#xff1a; 本篇主要讲两个WGAN的两个例子&#xff1a; 1 高斯混合模型 WGAN实现 2 MNIST 手写数字识别 -WGAN 实现 WGAN 训练起来蛮麻烦的,如果要获得好的效果很多超参数需要手动设置 1&#xff1a; 噪声的维度 2: 学习率 3&#xff1a; 生成器&#xff0c;鉴别器…

【深度学习与神经网络】MNIST手写数字识别1

简单的全连接层 导入相应库 import torch import numpy as np from torch import nn,optim from torch.autograd import Variable import matplotlib.pyplot as plt from torchvision import datasets, transforms from torch.utils.data import DataLoader读入数据并转为ten…