You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于A*图节点的Processing游戏:玩家移动状态检测问题求助

解决思路与代码修改

作为编程新手,你当前遇到的核心问题是Player类的移动逻辑用while循环导致瞬间完成移动,以及没有把路径节点和到达检测逻辑结合起来。下面是一步步的实操方案:

1. 修复Player的平滑移动逻辑

你现在drawPlayer()里的while (dir.mag() > 1.0)会让玩家瞬间跳到目标节点,这不仅看不到移动过程,还逼得你用时间延迟模拟"到达"。我们改成逐帧移动,贴合Processing的动画机制:

void drawPlayer() {
  fill(circleColour);
  circle(xPos,yPos,35);
  
  float speed = 5.0; // 调整这个值控制移动速度,不用100这么大,否则还是太快
  PVector dir = new PVector(xTar - xPos, yTar - yPos);
  
  // 把while改成if,每帧只移动一小步
  if (dir.mag() > 1.0) {
    dir.normalize();
    dir.mult(speed); // 直接用speed控制每帧移动距离,更直观
    xPos += dir.x;
    yPos += dir.y;
    isPlayerStopped = false;
  } else {
    // 到达目标,校准位置到节点坐标(避免微小误差)
    xPos = xTar;
    yPos = yTar;
    isPlayerStopped = true;
  }
}

2. 让Player管理完整路径,跟踪当前节点

我们需要让Player知道整条路径的节点,以及当前走到了哪一步。修改Player类,添加路径跟踪的变量和方法:

class Player {
  int lastHitting;
  int side;
  float xPos;
  float yPos;
  float xTar;
  float yTar;
  color circleColour = color(255,0,0);
  boolean isPlayerStopped;
  // 新增:跟踪路径节点和当前索引
  GraphNode[] pathNodes;
  int currentPathIndex;

  Player(int lastHitting, int side) {
    this.lastHitting = lastHitting;
    this.side = side;
    if (this.side == 0) {
      circleColour = color(0,0,255);
      xPos = 180;
      yPos = 900;
    } else if (this.side == 1) {
      circleColour = color(255,0,0);
      xPos = 990;
      yPos = 125;
    }
    // 初始化路径索引
    currentPathIndex = 0;
    isPlayerStopped = true;
  }

  // 新增:设置完整路径
  void setPath(GraphNode[] path) {
    this.pathNodes = path;
    if (path != null && path.length > 0) {
      // 设置第一个目标节点(如果路径不为空)
      currentPathIndex = 0;
      setTargetPosition(pathNodes[currentPathIndex].xf(), pathNodes[currentPathIndex].yf());
      isPlayerStopped = false;
    }
  }

  // 新增:切换到下一个路径节点
  boolean goToNextNode() {
    if (pathNodes == null || currentPathIndex >= pathNodes.length - 1) {
      // 已经到达最后一个节点,没有下一个了
      return false;
    }
    currentPathIndex++;
    GraphNode nextNode = pathNodes[currentPathIndex];
    setTargetPosition(nextNode.xf(), nextNode.yf());
    isPlayerStopped = false;
    return true;
  }

  // 新增:获取当前所在节点的ID
  int getCurrentNodeId() {
    if (pathNodes != null && currentPathIndex < pathNodes.length) {
      return pathNodes[currentPathIndex].id();
    }
    return -1; // 表示没有路径或未到达任何节点
  }

  // 保留你原有的movementCheck、getXPos、getYPos方法
  boolean movementCheck() {
    return isPlayerStopped;
  }

  float getXPos() {
    return xPos;
  }

  float getYPos() {
    return yPos;
  }

  void setTargetPosition(float targetX, float targetY) {
    xTar = targetX;
    yTar = targetY;
  }
}

3. 调整主程序逻辑,去掉时间延迟,监听到达事件

在setup()里直接把A*生成的route传给Player,不用单独存xPos/yPos:

void setup() {
  // ... 保留你原有的其他代码 ...
  midBlue = new Player(lastHittingBlue, blueSide);
  midRed = new Player(lastHittingRed, redSide);
  // 给蓝色玩家设置路径
  midBlue.setPath(route);
}

然后修改draw()函数,去掉runtime延迟,改为检测玩家是否到达当前节点:

void draw() {
  background(0);
  text((float)millis()/1000, 10,height/6);
  text(start/1000, 10,height/3);
  runtime = millis() - start;
  text(runtime/1000, 10,height/2);

  drawGraph();
  drawPath();

  // 检测蓝色玩家是否到达当前节点
  if (midBlue.movementCheck()) {
    // 到达节点,打印当前节点ID
    int currentNodeId = midBlue.getCurrentNodeId();
    println("蓝色玩家到达节点: " + currentNodeId);
    // 尝试切换到下一个节点
    boolean hasNext = midBlue.goToNextNode();
    if (!hasNext) {
      println("蓝色玩家已到达最终目标!");
      // 可以在这里添加到达终点后的逻辑,比如等待用户输入新目标
    }
  }

  midBlue.drawPlayer();
  midRed.drawPlayer(); // 红色玩家的逻辑可以参照蓝色玩家处理

  text( "x: " + mouseX + " y: " + mouseY, mouseX + 2, mouseY );
}

4. 优化路径绘制逻辑(可选)

你当前的drawPath()里while (firstRun)会重复添加节点,其实直接用route数组绘制即可:

void drawPath() {
  if (route == null || route.length < 2) return;
  
  strokeWeight(10);
  stroke(200, 255, 200, 160);
  for (int i = 1; i < route.length; i++) {
    GraphNode from = route[i-1];
    GraphNode to = route[i];
    line(from.xf(), from.yf(), to.xf(), to.yf());
  }
  
  // 统计路径长度只需要一次
  if (nodeCount == true) {
    count = route.length;
    nodeCount = false;
  }
}
关键说明
  • 为什么把while改成if?因为Processing的draw()是逐帧循环调用的(默认60帧/秒),每帧移动一小步才能实现平滑动画,同时不会阻塞程序。
  • 让Player管理路径节点,能直接获取节点ID,不需要单独存坐标,逻辑更清晰。
  • 用dir.mag() < 1.0做到达检测是合理的,当玩家和目标的距离小于1像素时,我们认为已经到达,再校准位置到节点坐标,避免微小误差。

这样修改后,你的玩家会平滑地沿路径逐节点移动,到达每个节点时都会打印当前节点ID,自动切换下一个节点,直到到达终点,完全不需要时间延迟啦!

内容的提问来源于stack exchange,提问作者Xarigizel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 11:08:13