Godot C++开发指南:正确获取节点的Forward/Up/Right方向向量

一、引言

在Godot游戏引擎的C++开发中,正确获取节点的方向向量(Forward、Up、Right)是实现角色移动、相机控制、物理交互等功能的基础。然而,许多开发者在从GDScript或C#迁移到C++时,会遇到API语法差异带来的困惑。

本文将详细解析Godot C++中方向向量的获取方法,澄清常见误解,并提供实用的代码示例。

二、问题澄清

2.1 常见误解

有开发者认为:

"在Godot C++中,无法像GDScript或C#那样使用-basis.z来获取前向向量"

这是一个误解! 实际上,Godot C++完全支持获取方向向量,只是语法稍有不同。

2.2 真相

cpp 复制代码
// ✅ 在Godot C++中,这样做完全正确!
Vector3 forward = -transform.basis[2];      // C++语法
Vector3 forward = -basis.get_column(2);     // 替代语法

// 等价于GDScript中的:
// var forward = -transform.basis.z

// 等价于C#中的:
// Vector3 forward = -transform.Basis.Z

核心结论: -basis[2] 就是前向向量,这与其他语言的逻辑完全一致

三、Godot坐标系统基础

3.1 右手坐标系

Godot使用右手坐标系,这是理解方向向量的关键:

复制代码
        +Y (上)
         |
         |
         |_______ +X (右)
        /
       /
    +Z (后)

坐标轴定义:

  • +X轴 → 向右(Right)
  • +Y轴 → 向上(Up)
  • +Z轴 → 向后(Backward,朝向摄像机)
  • -Z轴 → 向前(Forward,远离摄像机)

3.2 为什么是-Z而不是+Z?

这是Godot(以及许多3D引擎)的约定:

  • +Z指向后方,这样在默认视角下,摄像机看向-Z方向
  • -Z指向前方,这是物体"面朝"的方向
  • 这种设计使得默认场景布局更加直观

3.3 Basis矩阵结构

Basis是一个3x3旋转矩阵,存储了物体的三个局部坐标轴:

复制代码
Basis矩阵布局:
┌                    ┐
│ Rx  Ux  Bx  │  ← 第0列:右向量 (Right)
│ Ry  Uy  By  │  ← 第1列:上向量 (Up)
│ Rz  Uz  Bz  │  ← 第2列:后向量 (Back)
└                    ┘
  ↑   ↑   ↑
  |   |   |
  右  上  后

访问方式:

  • basis[0]basis.get_column(0) → 右向量
  • basis[1]basis.get_column(1) → 上向量
  • basis[2]basis.get_column(2) → 后向量

四、获取方向向量的标准方法

4.1 基础语法

cpp 复制代码
#include <godot_cpp/classes/node3d.hpp>

// 在任何Node3D派生类中
void some_function() {
    // 获取全局变换
    Transform3D transform = get_global_transform();
    Basis basis = transform.basis;
    
    // 方法1:直接使用索引访问(推荐)
    Vector3 forward = -basis[2];    // 前向 = -后向
    Vector3 up = basis[1];          // 上向
    Vector3 right = basis[0];       // 右向
    
    // 方法2:使用get_column()方法
    Vector3 forward = -basis.get_column(2);
    Vector3 up = basis.get_column(1);
    Vector3 right = basis.get_column(0);
}

4.2 语法对比表

语言 前向向量 上向向量 右向量
GDScript -transform.basis.z transform.basis.y transform.basis.x
C# -Transform.Basis.Z Transform.Basis.Y Transform.Basis.X
C++ -basis[2]-basis.get_column(2) basis[1] basis[0]

关键点: 所有语言的逻辑相同,只是访问语法不同!

4.3 局部 vs 全局变换

cpp 复制代码
// 局部方向(相对于父节点)
Transform3D local_transform = get_transform();
Vector3 local_forward = -local_transform.basis[2];

// 全局方向(世界空间)
Transform3D global_transform = get_global_transform();
Vector3 global_forward = -global_transform.basis[2];

// 大多数情况下使用全局变换

五、实战代码示例

5.1 基础角色控制器

cpp 复制代码
#include <godot_cpp/classes/node3d.hpp>
#include <godot_cpp/classes/input.hpp>

using namespace godot;

class PlayerController : public Node3D {
    GDCLASS(PlayerController, Node3D)

private:
    float move_speed = 5.0f;
    float rotation_speed = 3.0f;

protected:
    static void _bind_methods() {
        // 绑定方法...
    }

public:
    void _process(double delta) override {
        // 获取全局变换
        Transform3D transform = get_global_transform();
        
        // 提取方向向量
        Vector3 forward = -transform.basis[2];
        Vector3 right = transform.basis[0];
        Vector3 up = transform.basis[1];
        
        // 获取输入
        Input* input = Input::get_singleton();
        Vector3 movement = Vector3();
        
        // WASD移动
        if (input->is_key_pressed(KEY_W)) {
            movement += forward;
        }
        if (input->is_key_pressed(KEY_S)) {
            movement -= forward;
        }
        if (input->is_key_pressed(KEY_A)) {
            movement -= right;
        }
        if (input->is_key_pressed(KEY_D)) {
            movement += right;
        }
        
        // 归一化并应用速度
        if (movement.length() > 0) {
            movement = movement.normalized() * move_speed * delta;
            translate(movement);
        }
    }
};

5.2 第一人称相机控制

cpp 复制代码
class FPSCamera : public Camera3D {
    GDCLASS(FPSCamera, Camera3D)

private:
    float mouse_sensitivity = 0.002f;
    float pitch = 0.0f;
    float yaw = 0.0f;
    const float MAX_PITCH = Math_PI / 2.0f - 0.01f;

public:
    void _input(const Ref<InputEvent>& event) override {
        Ref<InputEventMouseMotion> mouse_motion = event;
        if (mouse_motion.is_valid()) {
            // 获取鼠标移动
            Vector2 relative = mouse_motion->get_relative();
            
            // 更新旋转角度
            yaw -= relative.x * mouse_sensitivity;
            pitch -= relative.y * mouse_sensitivity;
            pitch = Math::clamp(pitch, -MAX_PITCH, MAX_PITCH);
            
            // 应用旋转
            Transform3D transform = get_transform();
            transform.basis = Basis();  // 重置旋转
            transform.basis = transform.basis.rotated(Vector3(0, 1, 0), yaw);
            transform.basis = transform.basis.rotated(Vector3(1, 0, 0), pitch);
            set_transform(transform);
        }
    }
    
    // 获取相机观察方向
    Vector3 get_look_direction() {
        return -get_global_transform().basis[2];
    }
};

5.3 发射系统

cpp 复制代码
class WeaponController : public Node3D {
    GDCLASS(WeaponController, Node3D)

private:
    PackedScene* bullet_scene;
    float bullet_speed = 50.0f;
    float spawn_distance = 1.5f;

public:
    void fire() {
        if (!bullet_scene) return;
        
        // 获取武器的前向方向
        Transform3D weapon_transform = get_global_transform();
        Vector3 forward = -weapon_transform.basis[2];
        
        // 计算子弹生成位置(武器前方)
        Vector3 spawn_position = weapon_transform.origin + forward * spawn_distance;
        
        // 实例化子弹
        Node* bullet = bullet_scene->instantiate();
        get_tree()->get_root()->add_child(bullet);
        
        // 设置子弹位置和方向
        Node3D* bullet_3d = Object::cast_to<Node3D>(bullet);
        if (bullet_3d) {
            bullet_3d->set_global_position(spawn_position);
            
            // 如果子弹有RigidBody组件
            RigidBody3D* rb = Object::cast_to<RigidBody3D>(bullet);
            if (rb) {
                rb->set_linear_velocity(forward * bullet_speed);
            }
        }
    }
};

5.4 AI视线检测

cpp 复制代码
class AIVision : public Node3D {
    GDCLASS(AIVision, Node3D)

private:
    float vision_range = 20.0f;
    float vision_angle = 60.0f;  // 度数

public:
    bool can_see_target(Node3D* target) {
        if (!target) return false;
        
        // 获取AI的前向方向
        Transform3D ai_transform = get_global_transform();
        Vector3 forward = -ai_transform.basis[2];
        
        // 计算目标方向
        Vector3 to_target = target->get_global_position() - ai_transform.origin;
        float distance = to_target.length();
        
        // 检查距离
        if (distance > vision_range) {
            return false;
        }
        
        // 检查角度
        to_target = to_target.normalized();
        float dot = forward.dot(to_target);
        float angle = Math::acos(dot) * (180.0f / Math_PI);
        
        if (angle > vision_angle / 2.0f) {
            return false;
        }
        
        // TODO: 添加射线检测遮挡物
        
        return true;
    }
};

5.5 轨道相机

cpp 复制代码
class OrbitCamera : public Camera3D {
    GDCLASS(OrbitCamera, Camera3D)

private:
    Node3D* target = nullptr;
    float orbit_distance = 10.0f;
    float orbit_height = 5.0f;
    float rotation_speed = 2.0f;

public:
    void _process(double delta) override {
        if (!target) return;
        
        // 获取目标位置
        Vector3 target_pos = target->get_global_position();
        
        // 获取相机当前的方向向量
        Transform3D cam_transform = get_global_transform();
        Vector3 forward = -cam_transform.basis[2];
        Vector3 right = cam_transform.basis[0];
        
        // 处理输入旋转
        Input* input = Input::get_singleton();
        if (input->is_key_pressed(KEY_LEFT)) {
            rotate_y(rotation_speed * delta);
        }
        if (input->is_key_pressed(KEY_RIGHT)) {
            rotate_y(-rotation_speed * delta);
        }
        
        // 更新相机位置(始终在目标后方)
        Vector3 offset = -forward * orbit_distance + Vector3(0, orbit_height, 0);
        set_global_position(target_pos + offset);
        
        // 看向目标
        look_at(target_pos);
    }
};

六、高级技巧

6.1 方向向量的单位化

cpp 复制代码
// 确保方向向量是单位向量(长度为1)
Vector3 forward = -transform.basis[2].normalized();

// 注意:从basis直接获取的向量通常已经是归一化的
// 但如果进行了缩放变换,可能需要手动归一化

6.2 检查向量有效性

cpp 复制代码
// 检查basis是否有效(避免非法变换)
if (transform.basis.determinant() == 0) {
    UtilityFunctions::printerr("Invalid basis matrix!");
    return;
}

// 检查向量长度
Vector3 forward = -transform.basis[2];
if (forward.length_squared() < 0.001f) {
    UtilityFunctions::printerr("Forward vector too small!");
    return;
}

6.3 自定义"前向"方向

cpp 复制代码
// 有时你可能想使用不同的轴作为"前向"
// 例如,对于自顶向下游戏,可能使用-Y作为前向

class TopDownController : public Node3D {
public:
    Vector3 get_forward() {
        // 对于2D平面移动,使用-Y作为前向
        return -get_global_transform().basis[1];  // -Up = Forward in 2D
    }
    
    Vector3 get_right() {
        return get_global_transform().basis[0];   // Right stays the same
    }
};

6.4 平滑方向插值

cpp 复制代码
class SmoothRotation : public Node3D {
private:
    Vector3 target_forward = Vector3(0, 0, -1);
    float rotation_smoothness = 5.0f;

public:
    void _process(double delta) override {
        // 获取当前前向
        Vector3 current_forward = -get_global_transform().basis[2];
        
        // 平滑插值到目标方向
        Vector3 new_forward = current_forward.lerp(target_forward, 
                                                    rotation_smoothness * delta);
        new_forward = new_forward.normalized();
        
        // 应用新方向
        look_at(get_global_position() + new_forward);
    }
    
    void set_target_direction(Vector3 direction) {
        target_forward = direction.normalized();
    }
};

七、常见陷阱与解决方案

7.1 陷阱1:忘记负号

cpp 复制代码
// ❌ 错误:这会给你"后向"而不是"前向"
Vector3 forward = transform.basis[2];

// ✅ 正确:记住要加负号
Vector3 forward = -transform.basis[2];

7.2 陷阱2:混淆局部和全局坐标

cpp 复制代码
// ❌ 错误:使用局部变换但需要世界空间方向
Transform3D local_transform = get_transform();
Vector3 world_forward = -local_transform.basis[2];  // 这是局部方向!

// ✅ 正确:使用全局变换获取世界空间方向
Transform3D global_transform = get_global_transform();
Vector3 world_forward = -global_transform.basis[2];

7.3 陷阱3:缩放影响

cpp 复制代码
// 如果物体有非均匀缩放,basis列向量可能不是单位向量
Transform3D transform = get_global_transform();
Vector3 forward = -transform.basis[2];

// 检查长度
float length = forward.length();
if (Math::abs(length - 1.0f) > 0.01f) {
    UtilityFunctions::print("Warning: Forward vector is scaled!");
    forward = forward.normalized();  // 归一化
}

7.4 陷阱4:Basis vs Transform

cpp 复制代码
// ❌ 错误:Transform不能直接用[]访问
Vector3 forward = -transform[2];  // 编译错误!

// ✅ 正确:需要先访问basis
Vector3 forward = -transform.basis[2];

7.5 陷阱5:2D vs 3D混淆

cpp 复制代码
// 在Node2D中,没有basis概念
// 使用不同的API

class Player2D : public CharacterBody2D {
public:
    Vector2 get_forward_2d() {
        // 在2D中,使用rotation和Vector2
        float angle = get_rotation();
        return Vector2(Math::cos(angle), Math::sin(angle));
    }
};

八、调试技巧

8.1 可视化方向向量

cpp 复制代码
class DebugVectors : public Node3D {
    GDCLASS(DebugVectors, Node3D)

public:
    void _process(double delta) override {
        Transform3D transform = get_global_transform();
        
        // 提取方向向量
        Vector3 forward = -transform.basis[2];
        Vector3 up = transform.basis[1];
        Vector3 right = transform.basis[0];
        
        // 使用ImmediateMesh或DebugDraw绘制箭头
        draw_debug_arrow(transform.origin, forward, Color(0, 0, 1));  // 蓝色=前
        draw_debug_arrow(transform.origin, up, Color(0, 1, 0));       // 绿色=上
        draw_debug_arrow(transform.origin, right, Color(1, 0, 0));    // 红色=右
    }
    
private:
    void draw_debug_arrow(Vector3 start, Vector3 direction, Color color) {
        // 实现箭头绘制逻辑
        // 可以使用ImmediateMesh或第三方调试工具
    }
};

8.2 打印调试信息

cpp 复制代码
void debug_print_directions() {
    Transform3D transform = get_global_transform();
    
    Vector3 forward = -transform.basis[2];
    Vector3 up = transform.basis[1];
    Vector3 right = transform.basis[0];
    
    UtilityFunctions::print("=== Direction Vectors ===");
    UtilityFunctions::print("Forward: ", forward);
    UtilityFunctions::print("Up:      ", up);
    UtilityFunctions::print("Right:   ", right);
    
    // 验证正交性
    float dot_forward_up = forward.dot(up);
    float dot_forward_right = forward.dot(right);
    float dot_up_right = up.dot(right);
    
    UtilityFunctions::print("=== Orthogonality Check ===");
    UtilityFunctions::print("Forward·Up:    ", dot_forward_up);    // 应该≈0
    UtilityFunctions::print("Forward·Right: ", dot_forward_right); // 应该≈0
    UtilityFunctions::print("Up·Right:      ", dot_up_right);      // 应该≈0
}

8.3 单元测试示例

cpp 复制代码
#include <godot_cpp/classes/node3d.hpp>

class VectorTests {
public:
    static void test_forward_vector() {
        // 创建测试节点
        Node3D test_node;
        test_node.set_rotation(Vector3(0, 0, 0));  // 无旋转
        
        // 获取前向向量
        Vector3 forward = -test_node.get_transform().basis[2];
        
        // 验证:无旋转时应该是(0, 0, -1)
        Vector3 expected = Vector3(0, 0, -1);
        float difference = (forward - expected).length();
        
        if (difference < 0.001f) {
            UtilityFunctions::print("✓ Forward vector test passed");
        } else {
            UtilityFunctions::printerr("✗ Forward vector test failed!");
            UtilityFunctions::print("Expected: ", expected);
            UtilityFunctions::print("Got:      ", forward);
        }
    }
    
    static void test_rotation() {
        Node3D test_node;
        
        // 旋转90度(Y轴)
        test_node.rotate_y(Math_PI / 2.0f);
        
        Vector3 forward = -test_node.get_transform().basis[2];
        Vector3 expected = Vector3(1, 0, 0);  // 现在应该指向+X
        
        float difference = (forward - expected).length();
        
        if (difference < 0.001f) {
            UtilityFunctions::print("✓ Rotation test passed");
        } else {
            UtilityFunctions::printerr("✗ Rotation test failed!");
        }
    }
    
    static void run_all_tests() {
        UtilityFunctions::print("=== Running Vector Tests ===");
        test_forward_vector();
        test_rotation();
        UtilityFunctions::print("=== Tests Complete ===");
    }
};

九、性能优化

9.1 缓存频繁使用的向量

cpp 复制代码
class OptimizedController : public Node3D {
private:
    Vector3 cached_forward;
    Vector3 cached_up;
    Vector3 cached_right;
    bool cache_valid = false;

public:
    void _process(double delta) override {
        // 只在需要时重新计算
        if (!cache_valid) {
            update_direction_cache();
        }
        
        // 使用缓存的值
        move(cached_forward * delta);
    }
    
    void _on_transform_changed() {
        cache_valid = false;  // 标记缓存失效
    }
    
private:
    void update_direction_cache() {
        Transform3D transform = get_global_transform();
        cached_forward = -transform.basis[2];
        cached_up = transform.basis[1];
        cached_right = transform.basis[0];
        cache_valid = true;
    }
};

9.2 避免不必要的计算

cpp 复制代码
// ❌ 低效:每帧都重新计算
void _process(double delta) {
    for (int i = 0; i < 1000; i++) {
        Vector3 forward = -get_global_transform().basis[2];
        // 使用forward...
    }
}

// ✅ 高效:计算一次,多次使用
void _process(double delta) {
    Vector3 forward = -get_global_transform().basis[2];
    for (int i = 0; i < 1000; i++) {
        // 使用同一个forward...
    }
}

十、与其他引擎的对比

10.1 Unity vs Godot

cpp 复制代码
// Unity C#
Vector3 forward = transform.forward;      // 直接属性
Vector3 up = transform.up;
Vector3 right = transform.right;

// Godot C++
Vector3 forward = -transform.basis[2];    // 需要手动提取
Vector3 up = transform.basis[1];
Vector3 right = transform.basis[0];

10.2 Unreal Engine vs Godot

cpp 复制代码
// Unreal C++
FVector Forward = Actor->GetActorForwardVector();
FVector Up = Actor->GetActorUpVector();
FVector Right = Actor->GetActorRightVector();

// Godot C++
Vector3 forward = -get_global_transform().basis[2];
Vector3 up = get_global_transform().basis[1];
Vector3 right = get_global_transform().basis[0];

十一、完整项目示例

11.1 完整的FPS控制器

cpp 复制代码
// fps_controller.h
#ifndef FPS_CONTROLLER_H
#define FPS_CONTROLLER_H

#include <godot_cpp/classes/character_body3d.hpp>
#include <godot_cpp/classes/camera3d.hpp>

using namespace godot;

class FPSController : public CharacterBody3D {
    GDCLASS(FPSController, CharacterBody3D)

private:
    // 移动参数
    float walk_speed = 5.0f;
    float sprint_speed = 8.0f;
    float crouch_speed = 2.5f;
    float jump_velocity = 4.5f;
    float gravity = 9.8f;
    
    // 相机参数
    float mouse_sensitivity = 0.002f;
    float camera_pitch = 0.0f;
    const float MAX_PITCH = Math_PI / 2.0f - 0.01f;
    
    // 组件引用
    Camera3D* camera = nullptr;
    
    // 状态
    bool is_sprinting = false;
    bool is_crouching = false;

protected:
    static void _bind_methods();

public:
    FPSController();
    ~FPSController();
    
    void _ready() override;
    void _physics_process(double delta) override;
    void _input(const Ref<InputEvent>& event) override;
    
private:
    void handle_movement(double delta);
    void handle_mouse_look(Vector2 relative_motion);
    float get_current_speed();
};

#endif
cpp 复制代码
// fps_controller.cpp
#include "fps_controller.h"
#include <godot_cpp/classes/input.hpp>
#include <godot_cpp/classes/input_event_mouse_motion.hpp>

void FPSController::_bind_methods() {
    // 导出属性
    ClassDB::bind_method(D_METHOD("_ready"), &FPSController::_ready);
    ClassDB::bind_method(D_METHOD("_physics_process"), &FPSController::_physics_process);
}

FPSController::FPSController() {}
FPSController::~FPSController() {}

void FPSController::_ready() {
    // 获取相机节点
    camera = get_node<Camera3D>("Camera3D");
    if (!camera) {
        UtilityFunctions::printerr("Camera3D node not found!");
    }
    
    // 捕获鼠标
    Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
}

void FPSController::_physics_process(double delta) {
    // 应用重力
    Vector3 velocity = get_velocity();
    if (!is_on_floor()) {
        velocity.y -= gravity * delta;
    }
    
    // 处理跳跃
    Input* input = Input::get_singleton();
    if (input->is_action_just_pressed("jump") && is_on_floor()) {
        velocity.y = jump_velocity;
    }
    
    // 获取方向向量
    Transform3D transform = get_global_transform();
    Vector3 forward = -transform.basis[2];
    Vector3 right = transform.basis[0];
    
    // 只保留水平分量
    forward.y = 0;
    forward = forward.normalized();
    right.y = 0;
    right = right.normalized();
    
    // 计算移动输入
    Vector3 input_dir = Vector3();
    if (input->is_action_pressed("move_forward")) {
        input_dir += forward;
    }
    if (input->is_action_pressed("move_backward")) {
        input_dir -= forward;
    }
    if (input->is_action_pressed("move_left")) {
        input_dir -= right;
    }
    if (input->is_action_pressed("move_right")) {
        input_dir += right;
    }
    
    // 检查冲刺和蹲下
    is_sprinting = input->is_action_pressed("sprint");
    is_crouching = input->is_action_pressed("crouch");
    
    // 应用速度
    if (input_dir.length() > 0) {
        input_dir = input_dir.normalized();
        float current_speed = get_current_speed();
        velocity.x = input_dir.x * current_speed;
        velocity.z = input_dir.z * current_speed;
    } else {
        velocity.x = 0;
        velocity.z = 0;
    }
    
    // 移动
    set_velocity(velocity);
    move_and_slide();
}

void FPSController::_input(const Ref<InputEvent>& event) {
    // 处理鼠标移动
    Ref<InputEventMouseMotion> mouse_motion = event;
    if (mouse_motion.is_valid()) {
        handle_mouse_look(mouse_motion->get_relative());
    }
    
    // ESC退出捕获
    if (event->is_action_pressed("ui_cancel")) {
        Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
    }
}

void FPSController::handle_mouse_look(Vector2 relative_motion) {
    // 水平旋转(玩家本身)
    rotate_y(-relative_motion.x * mouse_sensitivity);
    
    // 垂直旋转(相机)
    if (camera) {
        camera_pitch -= relative_motion.y * mouse_sensitivity;
        camera_pitch = Math::clamp(camera_pitch, -MAX_PITCH, MAX_PITCH);
        
        Vector3 camera_rot = camera->get_rotation();
        camera_rot.x = camera_pitch;
        camera->set_rotation(camera_rot);
    }
}

float FPSController::get_current_speed() {
    if (is_crouching) {
        return crouch_speed;
    } else if (is_sprinting) {
        return sprint_speed;
    } else {
        return walk_speed;
    }
}

十二、总结

12.1 核心要点

  1. Godot C++完全支持方向向量获取 ,语法是 -basis[2](前向)、basis[1](上向)、basis[0](右向)

  2. 逻辑与GDScript/C#完全一致,只是访问语法略有不同

  3. 必须使用负号获取前向,因为Godot定义+Z为后向

  4. 使用全局变换获取世界空间方向,使用局部变换获取相对父节点的方向

  5. 从basis获取的向量通常已归一化,除非对象有缩放

12.2 快速参考

cpp 复制代码
// 获取方向向量的标准代码
Transform3D transform = get_global_transform();
Vector3 forward = -transform.basis[2];  // 前向
Vector3 up = transform.basis[1];        // 上向
Vector3 right = transform.basis[0];     // 右向

// 确保归一化(如果有缩放)
forward = forward.normalized();

12.3 最佳实践

  • ✅ 使用 get_global_transform() 获取世界空间方向
  • ✅ 记住前向需要负号:-basis[2]
  • ✅ 移动前将方向向量归一化
  • ✅ 缓存频繁使用的方向向量
  • ❌ 不要忘记负号得到后向而非前向
  • ❌ 不要混淆局部和全局坐标系
  • ❌ 不要直接对Transform使用[]操作符

12.4 学习资源

希望本文能帮助你在Godot C++开发中正确、高效地使用方向向量!


关键词: Godot C++ Transform Basis Forward Vector 方向向量 坐标系统 游戏开发 3D编程

相关推荐
云缘若仙9 小时前
Godot游戏开发——C# (一)
c#·godot
小-黯9 小时前
OpenGL使用C++ 绘制三角形
c++·opengl·xmake
code_ing-9 小时前
【Linux】命令行参数与环境变量
linux·c++·windows·笔记
wangjialelele9 小时前
Qt中的常用组件:QWidget篇
开发语言·前端·c++·qt
九德拉9 小时前
利用XPlaneConnect从X-Plane内读写数据
c++·飞行仿真·x-plane
小南家的青蛙10 小时前
O3DE社区发布2510.0版本
游戏引擎·图形引擎
_OP_CHEN10 小时前
C++进阶:(三)深度解析二叉搜索树原理及实现
开发语言·数据结构·c++·二叉树·二叉搜索树·键值对
郝学胜-神的一滴12 小时前
深入解析C++命令模式:设计原理与实际应用
开发语言·c++·程序人生·软件工程·命令模式
ShineSpark12 小时前
Crashpad介绍
c++·windows