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

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技术 | 终端技术 | 音视频技术

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

综合练习(python)

前言 有了前面的知识积累,我们这里做两个小练习,都要灵活运用前面的知识。 First 需求 根据美国/英国各自YouTube的数据,绘制出各自的评论数量的直方图 第一版 import numpy as np from matplotlib import pyplot as plt import matplo…

25.6 MySQL 子查询

1. 子查询 子查询(Subquery): 是SQL查询语句中的一个重要概念, 它允许在一个查询语句(主查询)中嵌套另一个查询语句(子查询). 这意味着一个查询可以作为另一个查询的输入或条件, 子查询可以出现在SQL语句的多个位置, 例如SELECT, FROM, WHERE等子句中.子查询通常用于以下几种情…

AMPQ和rabbitMQ

RabbitMQ 的 Channel、Connection、Queue 和 Exchange 都是按照 AMQP(Advanced Message Queuing Protocol)标准实现的。 AMPQ的网络部分 AMQP没有使用HTTP,使用TCP自己实现了应用层协议。 AMQP实现了自己特有的网络帧格式。 一个Connection…

Pyro —— SOP Nodes

Volume Rasterize Attributes —— 采样点属性以创建对应VDB 根据输入点属性,创建对应的VDB;是Volume Rasterize Particles节点的封装;

【晴问算法】提高篇—动态规划专题—最大连续子序列和

题目描述 输入描述 输出描述 输出一个整数&#xff0c;表示最大连续子序列和。 样例1 输入 6 -2 11 -4 13 -5 -2 输出 20 解释 连续子序列和的最大值为&#xff1a; #include<bits/stdc.h> using namespace std; const int MAXN 100; int dp[MAXN];//dp[i]表示以a[i]元…

初始 Navicat BI 工具

早前&#xff0c;海外 LearnBI online 博主 Adam Finer 对 Navicat Charts Creator 这款 BI&#xff08;商业智能&#xff09;工具进行了真实的测评。今天&#xff0c;我们来看下他对 Navicat BI 工具的初始之感&#xff0c;希望这能给用户一些启发与建议。LearnBI online 作为…

杰发科技AC7801——Flash数据读取

0. 简介 因为需要对Flash做CRC校验&#xff0c;第一步先把flash数据读出来。 1. 代码 代码如下所示 #include "ac780x_eflash.h" #include "string.h" #define TestSize 1024 ///< 4K #define TestAddressStart 0x08000000 uint8_t Data[7000]; int…

【CKA模拟题】学会JSONPath,精准定位Pod信息!

题干 For this question, please set this context (In exam, diff cluster name) kubectl config use-context kubernetes-adminkubernetesyou have a script named pod-filter.sh . Update this script to include a command that filters and displays the label with the…

Sublime Text3 C/C++一键调试运行代码

minGW的系统环境配置&#xff1a; 使用的C/C编译器是minGW&#xff0c;点此进入官网链接&#xff0c;下载后需要在线安装&#xff0c;安装后需要将安装目录下的bin目录所在路径加入path环境变量。本菜鸡的电脑里安装了CodeBlocks&#xff0c;在CodeBlocks的安装目录下有MinGW&…

micro-app搭建微前端

创建两个以上的项目&#xff08;使用vue&#xff0c;react&#xff0c;angular都可以&#xff09; 使用vue搭建项目 vue create project-name 区分一下基座和子项目 安装依赖 npm i micro-zoe/micro-app --save在main.js进行初始化 在app.vue嵌入子项目 子项目配置跨域 在vue.…

力扣hot100:153. 寻找旋转排序数组中的最小值(二分的理解)

由力扣hot100&#xff1a;33. 搜索旋转排序数组&#xff08;二分的理解&#xff09;-CSDN博客&#xff0c;我们知道二分实际上就是找到一个策略将区间“均分”。对于旋转数组问题&#xff0c;在任何位置分开两个区间&#xff0c;如果原区间不是顺序的&#xff0c;分开后必然有一…

15届蓝桥杯备赛(2)

文章目录 刷题笔记(2)二分查找在排序数组中查找元素的第一个和最后一个位置寻找旋转排序数组中的最小值搜索旋转排序数组 链表反转链表反转链表II 二叉树相同的树对称二叉树平衡二叉树二叉树的右视图验证二叉搜索树二叉树的最近公共祖先二叉搜索树的最近公共祖先二叉树层序遍历…

linux 安装/升级 svn

文章目录 下载最新版本安装包安装 下载最新版本安装包 wget https://dlcdn.apache.org/subversion/subversion-1.14.3.tar.gz tar -zxf subversion-1.14.3.tar.gz cd subversion-1.14.3 安装 ./configure 报错&#xff0c;提示缺少 apr-util 库&#xff0c;有的环境可能 apr 库…

大模型日报3月19日

资讯 研究 ICLR 2024 | 连续学习不怕丢西瓜捡芝麻&#xff0c;神经形态方法保护旧知识 https://mp.weixin.qq.com/s/-inS55h-MSUX51Kj061big 来自北京大学林宙辰教授团队的研究者们提出了一种新的基于赫布学习的正交投影的连续学习方法&#xff0c;其通过神经网络的横向连接…

锂电池寿命预测 | Matlab基于ALO-SVR蚁狮优化支持向量回归的锂离子电池剩余寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 锂电池寿命预测 | Matlab基于ALO-SVR蚁狮优化支持向量回归的锂离子电池剩余寿命预测 基于蚁狮优化和支持向量回归的锂离子电池剩余寿命预测: 1、提取NASA数据集的电池容量&#xff0c;以历史容量作为输入&#xff0c;…

docker实践教程,nginx中使用数据卷映射修改前端网页(一)

小白专用docker教程&#xff0c;图文介绍要怎么在docker中使用nginx修改配置前端网页 docker基础 Docker Hub (https://hub.docker.com) Docker 官网&#xff1a;https://www.docker.com 不懂docker的搭配菜鸟教程食用更佳&#xff0c;个人觉得菜鸟教程说得太简单了&#xff…

3500/22M 288055-01 瞬态数据接口模块 本特利内华达机械状态监控

3500/22M瞬态数据接口是接口 3500监控系统和兼容 软件&#xff08;系统1状态监控和诊断 软件和3500系统配置软件&#xff09;。TDI 结合了3500/20框架接口模块的功能 &#xff08;RIM&#xff09;具有通信数据收集功能 处理器&#xff0c;如TDXnet。 TDI位于与电源相邻的插槽中…

数学建模综合评价模型与决策方法

评价方法主要分为两类&#xff0c;其主要区别在确定权重的方法上 一类是主观赋权法&#xff0c;多次采取综合资讯评分确定权重&#xff0c;如综合指数法&#xff0c;模糊综合评判法&#xff0c;层次评判法&#xff0c;功效系数法等 另一类是客观赋权法&#xff0c;根据各指标…

zabbix6.4报错问题汇总:zabbix server无法连接zabbix agent主机

在配置zabbix server连接本机agent时报错&#xff1a; Get value from agent failed: cannot connect to[[xxx.xxx.xxx.xxx]:10050]: [111] Connection refused 检查10050端口是否开放&#xff0c;以下三种方式都可以查看端口是否开放。 1.nc -zv <服务器IP> <端口号…

【linux】环境变量(进程二)

这里写目录标题 命令行参数&#xff1a;环境变量&#xff1a; 命令行参数&#xff1a; 不谈命令行参数就谈环境变量就是耍流氓。 相信我们在C语言阶段都在main函数里见过参数。 例如int main(int argc, char* argv[]) 这是什么东西呢&#xff1f; 话不多说我们直接打印一下看…