豆包很厉害,已经是我的老师了,我有什么不懂的就问它,它居然很快理解了意思,并且会给出解决方案,比如博主最近研究的一个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()}";
}