让body执行2.5D坐标系

豆包很厉害,已经是我的老师了,我有什么不懂的就问它,它居然很快理解了意思,并且会给出解决方案,比如博主最近研究的一个2.5D坐标的问题。

cs 复制代码
using Godot;
using System;
using System.Collections.Generic;
using static System.Net.Mime.MediaTypeNames;
using Image = Godot.Image;

public partial class NPC : Node2D
{


    public CharacterBody2D body;
    public AnimatedSprite2D sprite;
    public float MoveSpeed = 50f;
    public string name = "巡逻员";
    public Label tipText = new Label();

    private List<Texture2D>[] dirFramesList = new List<Texture2D>[8]; // list数组



    // 道路路点列表(List 版本,更灵活)
    private List<Vector2> roadPoints = new List<Vector2>()
    {
        new Vector2(1000, 1000),
        new Vector2(800, 50),
        new Vector2(40, 1000),
        new Vector2(500, 500),
    };
    private int currentPointIndex = 0;



    public override void _Ready()
    {
        LoadAllSpriteFrames();
        NPCInit();

    }


    public override void _PhysicsProcess(double delta)
    {



        // 键盘输入
         Vector2 dir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down");



        /*
        // 获取目标点
        Vector2 target = roadPoints[currentPointIndex];
        Vector2 dir = (target - body.Position).Normalized();

        // 到达点 → 切换下一个
        if (body.Position.DistanceTo(target) < 10)
        {
            currentPointIndex = (currentPointIndex + 1) % roadPoints.Count;
        }
        */

        // 移动
        body.Velocity = dir.Normalized() * MoveSpeed;
        body.MoveAndSlide();



        // 精灵跟随身体
         sprite.Position = RotatePoint(body.Position);

        tipText.Text = $"巡逻员  2.5D坐标: {body.Position.Round()}, 屏幕坐标: {sprite.Position.Round()}";


        //方向->ID->名称->播放对应名称动画
        int dirIdx = GetNearDirIndex(dir);
        sprite.Play(GetAnimName(dirIdx));
    }

    //加载美工素材
    private void LoadAllSpriteFrames()
    {
        for (int d = 0; d < 8; d++)
        {
            dirFramesList[d] = new List<Texture2D>();
            for (int f = 0; f < 12; f++)
            {
                int num = 433 + d + f * 8;
                string path = $"res://巡逻员/SprMain_{num:D05}.png";

                var tex = GD.Load<Texture2D>(path);
                if (tex != null)
                {
                    dirFramesList[d].Add(FixRed(tex));
                }
            }
        }
    }
    private ImageTexture FixRed(Texture2D tex)
    {
        var img = tex.GetImage();
        Color gray = new Color(0.7f, 0.7f, 0.7f, 0.1f);

        for (int x = 0; x < img.GetWidth(); x++)
            for (int y = 0; y < img.GetHeight(); y++)
            {
                Color c = img.GetPixel(x, y);
                // 扩大匹配:只要红色为主的像素(包含边缘半透明红)全部替换
                if (c.R > 0.5f && c.G < 0.3f && c.B < 0.3f && c.A > 0.1f)
                {
                    img.SetPixel(x, y, gray);
                }
            }

        ImageTexture nt = new ImageTexture();
        nt.SetImage(img);
        return nt;
    }

    // 精灵初始化
    private void NPCInit()
    {
        // 创建角色主体
        body = new CharacterBody2D();
        AddChild(body);

        // 添加动画精灵
        sprite = new AnimatedSprite2D();
        sprite.Centered = true;
        AddChild(sprite);


        // 2.5D 偏移(只设置一次!)
     //   sprite.Position = RotatePoint(Vector2.Zero);



        // ==========================
        // 极简头顶文字(就这5行)
        // ==========================

        tipText.Text = "巡逻员";
        tipText.Position = new Vector2(-20, -50); // 头上位置
        tipText.AddThemeFontSizeOverride("font_size", 12);
        tipText.AddThemeColorOverride("font_color", Colors.White);
        sprite.AddChild(tipText); // 挂在精灵上 → 自动跟随!


        // 你的Sprite2D变量,比如叫sprite
        sprite.TextureFilter = TextureFilterEnum.Nearest;
        // 额外保险:关闭Mipmap
        sprite.TextureRepeat = TextureRepeatEnum.Disabled;

        //默认位置
        body.Position = new Vector2(500, 50);

        // 构建八方向动画
        var frames = new SpriteFrames();

        for (int d = 0; d < 8; d++)
        {
            string animName = GetAnimName(d);

            frames.AddAnimation(animName);

            foreach (var t in dirFramesList[d])
            { frames.AddFrame(animName, t); }


            frames.SetAnimationSpeed(animName, 12);
        }
        sprite.SpriteFrames = frames;

    }



    public static Vector2 RotatePoint(Vector2 origin)
    {
        double newX = origin.X - origin.Y;
        double newY = (origin.X + origin.Y) / 2;
        return (new Vector2((float)newX, (float)newY));
    }




    // 8方向向量
    private readonly Vector2[] _eightDirs = new Vector2[]
    {
        new(0, -1),   // 0 上
        new(1, -1),   // 1 右上
        new(1, 0),    // 2 右
        new(1, 1),    // 3 右下
        new(0, 1),    // 4 下
        new(-1, 1),   // 5 左下
        new(-1, 0),   // 6 左
        new(-1, -1)   // 7 左上
    };

    // 获取最近八方向
    private int GetNearDirIndex(Vector2 input)
    {
        int best = 4;
        float maxDot = -100;
        for (int i = 0; i < 8; i++)
        {
            float dot = input.Dot(_eightDirs[i].Normalized());
            if (dot > maxDot)
            {
                maxDot = dot;
                best = i;
            }
        }
        return best;
    }

    // 动画名称映射
    private string GetAnimName(int dir)
    {
        return dir switch
        {
            0 => "up",
            1 => "up_right",
            2 => "right",
            3 => "down_right",
            4 => "down",
            5 => "down_left",
            6 => "left",
            7 => "up_left",
            _ => "down"
        };
    }

}

我的问题:

在_PhysicsProcess函数中,NPC的移动使用的: body.Velocity = dir.Normalized() * MoveSpeed; body.MoveAndSlide(); 但是,我这是一个2.5D的游戏,我使用的坐标系,有一个转换函数RotatePoint(body.Position); 如果我使用 body.MoveAndSlide();,我就没有办法自己控制NPC的X,Y的坐标,怎么办呢?

豆包:

我完全明白你的问题了!你现在的核心痛点是:

2.5D 等距视角(斜 45°) 屏幕坐标 = RotatePoint(逻辑坐标)你希望 逻辑坐标(body.Position)正常移动 ,但 视觉精灵(sprite)按 2.5D 方式显示 。但你发现:一旦用 MoveAndSlide(),你无法手动修改 body.Position,因为移动被物理系统接管了。

终极正确方案

核心一句话

不要让物理系统(CharacterBody2D)帮你移动,自己写 2.5D 移动逻辑,完全控制 Position。

你不能用 MoveAndSlide() 了!因为 MoveAndSlide() 是给正常直角坐标系用的。

cs 复制代码
public override void _PhysicsProcess(double delta)
{
    // 1. 输入方向(直角坐标系)
    Vector2 inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down");
    if (inputDir.Length() < 0.01f)
    {
        sprite.Stop(); // 没输入就停动画
        return;
    }
    inputDir = inputDir.Normalized();

    // 2. 把 输入方向 → 转换成 2.5D 移动方向
    Vector2 logicDir = inputDir;
    Vector2 moveDir25D = new Vector2(
        logicDir.X - logicDir.Y,
        (logicDir.X + logicDir.Y) / 2
    ).Normalized();

    // 3. 直接修改 Position(完全自己控制 2.5D 坐标)
    body.Position += moveDir25D * MoveSpeed * (float)delta;

    // 4. Sprite 是 body 的子节点 → 自动同步!不用再转坐标!
    sprite.Position = Vector2.Zero; 

    // 5. 动画方向
    int dirIdx = GetNearDirIndex(inputDir);
    sprite.Play(GetAnimName(dirIdx));

    tipText.Text = $"2.5D坐标: {body.Position.Round()}";
}
相关推荐
郝学胜-神的一滴2 天前
[简化版 GAMES 101] 计算机图形学 10:反走样与深度缓冲核心解析
c++·unity·godot·图形渲染·three.js·unreal engine·opengl
OSwich3 天前
【 Godot 4 学习笔记】命名规范
笔记·学习·godot
相信神话20214 天前
第四章:创建《酒魂》项目与场景结构
游戏·游戏引擎·godot·2d游戏开发
winlife_5 天前
把 Godot 编辑器接入 AI:Funplay MCP for Godot 介绍
人工智能·编辑器·godot·ai编程·游戏开发·mcp
郝学胜-神的一滴6 天前
中级OpenGL教程 006:高光反射原理与 Shader 实现
c++·unity·godot·图形渲染·three.js·opengl·unreal
相信神话20218 天前
3.5《酒魂》体验与失败设计
游戏引擎·godot·godot4
刘欣的博客9 天前
Godot的Normalized()函数说明
godot·normalized函数说明
风酥糖10 天前
Godot游戏练习01-第34节-开始引入AI开发
人工智能·游戏·godot
郝学胜-神的一滴16 天前
[简化版 GAMES 101] 计算机图形学 08:三角形光栅化上
c++·unity·游戏引擎·godot·图形渲染·opengl·unreal