前言这篇文章是针对 针对Godot(2D)游戏架构的研究-CSDN博客 文章的整体架构研究的一个重要内容的解释与补充在本架构中四大坐标系承载着不同需求位置放置的核心功能实现例如游戏场景中背包处于单独渲染图层(只在屏幕上单独渲染而不随着全局坐标被移动)时使用的坐标系(视口坐标Viewport)以及生成生成物时候使用的坐标系(全局坐标)等等。这里将对四大坐标进行详细的解释以及常用功能使用示范。(本文所使用的词语将会和前两篇架构细分以及总架构文章对应)首先解释需要的基础知识坐标系基础解释1. Vector2(x, y)这是一个二维向量它需要有确定的原点然后根据数学向量可以进行计算。官方解释是使用浮点数坐标的 2D 向量。此外还有Vector2i(x, y)这是整数类型的向量坐标可以直接通过() Vector(x, y)的方式来进行位置计算最终位置在向量的头或者说是坐标的最终计算值的XY。2. deltafunc _process(delta: float) - void: pass这个是每帧运行所返回的时间(秒)用来手动计时(不推荐但是可以写因为有自带的Timer函数)用来使位置变换的速度正常化(位置坐标变换的单位是像素一秒如果写 Vector(400, 0)这样的会瞬间消失乘上delta就可以避免这种情况发生)。delta在_process和_physics_process自动作为系统传参传进去其他自定义函数需要则需要从其中再次传参进去(把delta在主循环函数里作为实际参数传给自定义函数)3. 轴心(Pivot Point)轴心位置是旋转的中心缩放的中心以及代表节点本身实际的位置点。位置的相对性是根据轴心位置的相对性而言的默认Node2D节点以及其衍生节点轴心在其中心control(UI控件总节点)一般在左上角。4. 绝对坐标系绝对坐标系是Godot(2D)中所有场景都通用且一致的编辑器坐标系原点在图形化界面的十字中心如图所示主要框架内容1.两大普通坐标系1. 全局坐标系(global_position)这个坐标系是控制整个场景(可以理解为装地图的场景)的不同节点以及实例化进来的场景的交互的核心坐标是一个属性(global_position)。这个属性几乎所有的节点场景都有包括前面节点介绍里面除了CanvasLayer节点和Timer节点之外的所有节点。要点:1.是一个Vector2向量2. 坐标原点为场景绝对坐标系的原点(所有场景绝对坐标原点一致)获取当前节点或者当前选择的节点的全局位置示例extends Node2D ##以下演示的所有节点均是架构细化文章2中解释过 ##通过右侧栏拖动的方式引入(需要export暴露引入, 在架构细化1中解释过) ##这里只是演示实际按照需要引入 #这是外面引入的Node2D节点 export var test_node : Node2D #外面引入的Area2D节点 export var test_area2D : Area2D #外面引入的AnimatedSprite2D节点 export var test_anim : AnimatedSprite2D ##放入_ready()函数执行的时候一定要考虑代码的优先顺序 ##不同脚本的_ready()执行先后是不同的我这里是演示(所以直接写了) ##如果获取节点位置的时候节点没有给初始化很可能是没有位置或者位置为Vector(0, 0)的 ##变量名字也别写下面那么长这里是为了演示不然代码多了会混乱 #脚本自带代码进入场景(add_child())时候自动执行一遍 func _ready() - void: #获取全局位置 var test_node_global_position test_node.global_position var test_area2d_global_position test_area2D.global_position var test_anim_global_position test_anim.global_position有全局位置属性的节点都可以直接更改其全局位置但是物理节点不建议直接更改比如刚体(RigidBody2D)完全由引擎自带的物理节点运算的节点直接更改会出现未知问题(闪烁不动等)一定需要更改的时候要手动切换其物理模式此外玩家节点(CharacterBody2D)虽然属于物理节点但是直接更改可以(手动试验过但是可能影响移动(move_and_slide()操作执行完后自带物理计算函数这个函数要手动加在玩家节点脚本_process或者_physics_process末尾)的物理计算目前测试来看并没有问题)补充玩家的move_and_slide()函数建议写在_physics_process(一秒处理固定60次)里面防止_process(按帧数处理一帧一次)处理出现异常以下是更改全局位置的演示#直接更改全局位置 test_node.global_position Vector2(0, 0) #计算更改全局位置 var offset : Vector2 Vector2(100, 100) test_node.global_position offset全局坐标可以直接从外面暴露进来的变量(就是拖动进右侧编辑栏的节点或者场景等)直接通过name.global_position获得演示代码采用的就是此类做法。利用全局坐标是基于全局绝对位置并且可以通过脚本修改的特点可以实现1. 位置判断(判断是否进入范围)2. 玩家传送3. 生成物生成初始位置4.AnimatableBody2D节点实现推动玩家等操作。全局坐标是使用最频繁的坐标系节点也可以实现全局相机自动跟随(这个相机是单独的场景如果相机挂在某个节点下相机会跟随着节点位置的移动而移动。而全局相机需要手动跟随(脚本写持续变更全局位置)或者是使用官方自带跟随函数(Godot4.x已经被移除))实现Area2D节点以及其它带global_position属性的节点的移动(需要乘以delta)。此外就是global_position的自由发挥操作这里只讲基础操作2. 局部坐标系(position)这个坐标系的原点的位置是父节点(最近一层父节点)的轴心位置而定的同样属于物体属性相对的是子节点的轴心和父节点的轴心位置。图中白色的区域是父节点贴图蓝色的是子节点贴图初始轴心均位于各自贴图中心第一张图片中心红色的是父节点的轴心这里可以看到的是子节点的位置会随着父节点的轴心的移动而移动而子节点的position仍然是Vector(0, 0)。局部坐标系和全局坐标系操作规则一样属性名从原来的global_position换成position即可#获取相对父节点轴心的位置 var test_node_position test_node.position var test_area2d_position test_area2D.position var test_anim_position test_anim.position需要注意的是父节点改变全局位置(global_position)旋转位置(rotation) 以及缩放(Scale)的时候会带动子节点一起变化但子节点的相对父节点的position始终保持不变2. 两大易混淆坐标系特别注意如果根节点是窗口节点那么字节的global_position的绝对坐标系将不是编辑器的绝对坐标系此时子节点的global_position是以窗口的左上角作为坐标原点的以下是演示extends Node2D ##写在_ready里是为了方便演示真正实写的时候建议都不要写在_ready里面 ##因为_ready()是脚本进来执行的第一个函数(一般而言) ##若是直接获其他引入(export)的变量可能会导致取到null或者0值 ##演示global_position的更改 func _ready() - void: print(global_position)以上代码是子节点的代码可以看到test_4子节点是挂在windows根节点下的而且可以看到子节点不在绝对坐标系中心这里直接运行global_position可以看到输出是Vector2(0, 0)这是因为窗口节点改变了子节点的全局坐标1. 视口坐标系(Viewport)对应屏幕的坐标系统(下面图中蓝色的区域就是屏幕渲染UI的区域粉色区域为相机渲染区域)这个坐标系是以屏幕左上角为原点的坐标系(蓝色框的左上角)其渲染根据屏幕(或者说是游戏窗口屏幕左上角)的相对位置渲染可以用来放置UI制作物品栏悬停逻辑等。这个坐标系容易和画布坐标系混淆这里讲直接使用演示的方式直观解释其功能(这里首先对其变换原理进行解释)视口变换原理(简单阐释一下后面有文章会解释底层具体运行原理)首先四大坐标全部基于Transform2D这是一个3x3的矩阵实现坐标转化的原理有1. 矩阵 A → B 点 × 矩阵 从 A 坐标变成 B 坐标2. 矩阵 B → A (使用.affine_inverse () 反转矩阵) :从 B 坐标变成 A 坐标3. 屏幕变换矩阵 × 视口坐标 全局坐标4. 屏幕变换矩阵的逆矩阵 × 全局坐标 视口坐标注意逆矩阵乘以原矩阵等于单位矩阵3和4可用利用这个推导屏幕变换矩阵从视口节点中使用自带函数获得每个视口都有不同的屏幕变换矩阵这里给出代码和实际演示extends Node2D ##写在_ready里是为了方便演示真正实写的时候建议都不要写在_ready里面 ##因为_ready()是脚本进来执行的第一个函数(一般而言) ##若是直接获其他引入(export)的变量可能会导致取到null或者0值 func _ready() - void: #得到当前视口(屏幕的视口) var viewport get_viewport() #获得当前视口的屏幕变换矩阵 var viewport_pos_screen viewport.get_screen_transform() #应用公式: 屏幕变换矩阵 × 视口坐标 全局坐标 #这里得到的是全局坐标 var viewport_pos viewport_pos_screen * global_position #这里用来显示算出的坐标 print(viewport_pos)可以看到代码节点被附属在windows节点下这里我设置窗口的位置是Vector(100, 100)图片中窗口内显示区域左上角的位置是含子节点(test_4)的位置就是窗口内左上角(不是蓝色的区域是窗口的左上角位置的一个点)运行代码可以看到输出的是绝对坐标系的全局坐标(相对绝对坐标系原点为100, 100)这里应用的就是屏幕变换矩阵 × 视口坐标 全局坐标这个公式根据特别提示里面的内容可以看到的是此时节点位于窗口的左上角所以它的global_position(窗口内全局坐标)为Vector2(0, 0)。此时global_position的作用是翻译了窗口坐标原点所以将代码直接更改为Vector2(0, 0)也是输出(100, 100)extends Node2D ##写在_ready里是为了方便演示真正实写的时候建议都不要写在_ready里面 ##因为_ready()是脚本进来执行的第一个函数(一般而言) ##若是直接获其他引入(export)的变量可能会导致取到null或者0值 func _ready() - void: #得到当前视口(屏幕的视口) var viewport get_viewport() #获得当前视口的屏幕变换矩阵 var viewport_pos_screen viewport.get_screen_transform() #应用公式: 屏幕变换矩阵 × 视口坐标 全局坐标 #这里得到的是全局坐标 var viewport_pos viewport_pos_screen * Vector2(0, 0) #这里用来显示算出的坐标 print(viewport_pos)此时输出仍然是(100, 100)视口坐标是蓝色区块里面的坐标如图(下图的蓝色框就是屏幕上显示的坐标内容)而左上角的粉色区域是我放的相机区域但是可以看到视口的渲染仍然是按照蓝色区域渲染然后投影到相机上的这里整理一下重点:视口坐标是屏幕渲染坐标基于屏幕(游戏窗口)左上角为原点的渲染坐标系(在编辑器里就是蓝色框的左上角)可以和全局坐标(绝对坐标系的全局坐标)相互转换2. 画布坐标系画布坐标系是Godot引擎中给的一个单独自定义坐标原点的坐标系是为了单独脱离其他三个坐标系用于专注自己坐标系渲染的坐标系。它的作用是渲染的时候只根据自己的位置渲染而不用去受到其他三个坐标的影响。在普通CanvasLayer节点中会默认生成一个和视口坐标系等大同样的画布。由于CanvasLayer是独立渲染节点默认固定在屏幕上的节点所以直接对其画布更改坐标也可以实现UI固定的功能窗口节点生成的时候也会生成一个独立画布基于窗口左上角位置为坐标原点这里将进行实例演示(将全局坐标位置转化为画布坐标位置这时候注意全局坐标系的原点选取是窗口的左上角)extends Node2D ##写在_ready里是为了方便演示真正实写的时候建议都不要写在_ready里面 ##因为_ready()是脚本进来执行的第一个函数(一般而言) ##若是直接获其他引入(export)的变量可能会导致取到null或者0值 func _ready() - void: #获得画布坐标系的矩阵 var canvas_mat get_canvas_transform() ##这里输出的是 ##同上面视口坐标系一样乘以当前相对该窗口的全局位置 ##但是由于该子节点位于窗口的左上角所以此时的global_position为Vector(0, 0) ##而且由于该窗口的画布和该窗口的自己的全局坐标系重合原点 ##所以这时候输出的画布上的位置也是(0, 0) ##这句话原本的意义是: 将全局位置转化为画布坐标上的位置 var canvas_pos canvas_mat * global_position print(canvas_pos)窗口仍然是处于绝对坐标系的Vector2(100, 100)位置可以看到的是根据窗口本身全局位置转化到其对应的完全重合的画布上的坐标仍然是Vector2(0, 0)重点整理:画布坐标系是独立于三大坐标系的为物体提供自身坐标系用来渲染自己的渲染坐标系建立CanvasLayer节点利用该节点单独在屏幕上渲染的特点可以实现UI的独立渲染。3. 补充该内容是对Godot(2D)坐标系的基础作用和概念的详细讲解补充对应本框架的UI管理器的渲染方式是实现玩家UI渲染的多种核心方式(本架构使用的是视口节点渲染方式)跳转回主框架内容:针对Godot(2D)游戏架构的研究-CSDN博客