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

Java Applet中scale变量作用及viewCenter设置的技术咨询

SeekFlee Applet代码分析与核心疑问解答

这是一个老版Java Applet程序,核心功能是模拟追逐-逃离的AI行为:一个角色(Seeker)会朝着屏幕中心移动,另一个角色会远离屏幕中心;当两个角色接触后,等待15帧就会重置它们的位置,重新开始模拟。

核心疑问解答

1. this.scale变量的作用

scale = 15.0F 是虚拟世界坐标与屏幕像素坐标之间的缩放转换因子:

  • 程序里的所有位置、速度计算都是在一个"虚拟世界空间"里进行的,这个空间的单位和屏幕像素不直接对应;
  • 当需要把世界空间的坐标绘制到屏幕上时,会用世界坐标 / scale得到屏幕像素坐标(从updateSeekers里调用draw(this.canvasG, this.scale)可以推断);
  • 反过来,把屏幕像素坐标转换成世界坐标时,就需要乘以scale,这也是viewCenter赋值的逻辑基础。

2. viewCenter的赋值逻辑与作用

this.viewCenter = new Vector3(this.wxsize / 2.0F * this.scale, this.wysize / 2.0F * this.scale, 0.0F);

这行代码是把屏幕中心的像素坐标转换成虚拟世界空间的坐标:

  • wxsize/2.0F是屏幕水平方向的中心像素值,乘以scale后得到世界空间的X坐标;
  • wysize/2.0F是屏幕垂直方向的中心像素值,乘以scale后得到世界空间的Y坐标;
  • Z设为0是因为这是纯2D模拟,Z轴没有实际作用。

viewCenter的核心作用是作为两个角色的行为目标:

  • 追逐者(seeker)会朝着这个点移动;
  • 逃离者(fleer)会远离这个点;
  • 重置角色位置时,也是以这个点为中心,在周围随机生成初始位置。

完整代码整理

SeekFlee类代码

import java.applet.Applet;
import java.awt.Color;
import java.awt.Event;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;

public class SeekFlee extends Applet implements Runnable {
  private Thread update;

  Seeker seeker;

  Seeker fleer;

  float scale = 15.0F;

  Vector3 viewCenter;

  int framesSinceTouch;

  Image canvasimage;

  Graphics canvasG;

  Graphics myG;

  int wxsize;

  int wysize;

  public void init() {
    super.init();
    this.wxsize = (getSize()).width;
    this.wysize = (getSize()).height;
    resize(this.wxsize, this.wysize);
    this.canvasimage = createImage(this.wxsize, this.wysize);
    this.canvasG = this.canvasimage.getGraphics();
    this.myG = getGraphics();
    this.viewCenter = new Vector3(this.wxsize / 2.0F * this.scale, this.wysize / 2.0F * this.scale, 0.0F);
    this.seeker = new Seeker();
    this.fleer = new Seeker();
    resetSeekers();
  }

  public boolean handleEvent(Event event) {
    boolean returnValue = false;
    return returnValue;
  }

  public static void main(String[] args) {
    Frame f = new Frame();
    SeekFlee app = new SeekFlee();
    app.init();
    app.start();
    f.add("Center", app);
    f.resize(515, 300);
    f.show();
  }

  public void start() {
    if (this.update == null) {
      this.update = new Thread(this);
      this.update.start();
    } 
  }

  public void stop() {
    if (this.update != null && this.update.isAlive())
      this.update.stop(); 
    this.update = null;
  }

  public void run() {
    while (true) {
      repaint();
      try {
        Thread.sleep(33L);
      } catch (InterruptedException interruptedException) {}
    } 
  }

  private void showbuffer() {
    this.myG.drawImage(this.canvasimage, 0, 0, this);
  }

  static final Color bgColor = new Color(0.9F, 0.9F, 0.6F);

  public void paint(Graphics g) {
    this.canvasG.setColor(bgColor);
    this.canvasG.fillRect(0, 0, this.wxsize, this.wysize);
    updateSeekers();
    this.canvasG.setColor(Color.black);
    this.canvasG.drawRect(0, 0, this.wxsize - 1, this.wysize - 1);
    showbuffer();
  }

  public void updateSeekers() {
    this.seeker.update();
    this.seeker.draw(this.canvasG, this.scale);
    this.fleer.update();
    this.fleer.draw(this.canvasG, this.scale);
    this.fleer.touch = this.seeker.touch;
    this.framesSinceTouch = this.seeker.touch ? (this.framesSinceTouch + 1) : 0;
    if (this.framesSinceTouch > 15)
      resetSeekers(); 
  }

  public void resetSeekers() {
    this.seeker.position.setUnitRandom();
    this.seeker.position.setScale(17.0F, this.seeker.position);
    this.seeker.position.setSum(this.viewCenter, this.seeker.position);
    this.seeker.position.z = 0.0F;
    this.seeker.velocity.setUnitRandom();
    this.seeker.velocity.setScale(this.seeker.maxSpeed, this.seeker.velocity);
    this.seeker.velocity.z = 0.0F;
    this.seeker.target.set(this.viewCenter);
    this.seeker.touch = false;
    this.fleer.seek = false;
    this.fleer.position.set(this.seeker.position);
    this.fleer.velocity.set(this.seeker.velocity);
    this.fleer.target.set(this.viewCenter);
    this.fleer.touch = false;
  }

  public void update(Graphics g) {
    paint(g);
  }
}

Vector3类代码

import java.util.Random;

public class Vector3 {
  public float x;

  public float y;

  public float z;

  public static final Vector3 zero = new Vector3();

  public Vector3() {
    setZero();
  }

  public Vector3(float x, float y, float z) {
    set(x, y, z);
  }

  public void set(Vector3 that) {
    this.x = that.x;
    this.y = that.y;
    this.z = that.z;
  }

  public void set(float x, float y, float z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }

  public void setZero() {
    set(0.0F, 0.0F, 0.0F);
  }

  public boolean equals(Vector3 that) {
    if (this.x != that.x || 
      this.y != that.y || 
      this.z != that.z)
      return false; 
    return true;
  }

  public boolean equalsZero() {
    return !(this.x != 0.0F || this.y != 0.0F || this.z != 0.0F);
  }

  public float magnitude() {
    return (float)Math.sqrt(magnitudeSquared());
  }

  public float magnitudeSquared() {
    return this.x * this.x + 
      this.y * this.y + 
      this.z * this.z;
  }

  public float dot(Vector3 that) {
    return this.x * that.x + 
      this.y * that.y + 
      this.z * that.z;
  }

  static Random generator = new Random();

  public void setUnitRandom() {
    do {
      this.x = generator.nextFloat() * 2.0F - 1.0F;
      this.y = generator.nextFloat() * 2.0F - 1.0F;
      this.z = generator.nextFloat() * 2.0F - 1.0F;
    } while (magnitudeSquared() > 1.0F);
  }

  public float approximateLength() {
    float a = this.x;
    if (a < 0.0F)
      a = -a; 
    float b = this.y;
    if (b < 0.0F)
      b = -b; 
    float c = this.z;
    if (c < 0.0F)
      c = -c; 
    if (a < b) {
      float t = a;
      a = b;
      b = t;
    } 
    if (a < c) {
      float t = a;
      a = c;
      c = t;
    } 
    return a * 0.9375F + (b + c) * 0.375F;
  }

  private static Vector3 distTemp = new Vector3();

  public float distance(Vector3 v) {
    synchronized (distTemp) {
      distTemp.setDiff(this, v);
      return distTemp.magnitude();
    } 
  }

  public float approximateDistance(Vector3 v) {
    synchronized (distTemp) {
      distTemp.setDiff(this, v);
      return distTemp.approximateLength();
    } 
  }

  public void setSum(Vector3 a, Vector3 b) {
    a.x += b.x;
    a.y += b.y;
    a.z += b.z;
  }

  public void setDiff(Vector3 a, Vector3 b) {
    a.x -= b.x;
    a.y -= b.y;
    a.z -= b.z;
  }

  public void setScale(float a, Vector3 b) {
    this.x = a * b.x;
    this.y = a * b.y;
    this.z = a * b.z;
  }

  public void setCross(Vector3 a, Vector3 b) {
    set(a.y * b.z - a.z * b.y, 
        a.z * b.x - a.x * b.z, 
        a.x * b.y - a.y * b.x);
  }

  public void setInterp(float blend, Vector3 v0, Vector3 v1) {
    v0.x += blend * (v1.x - v0.x);
    v0.y += blend * (v1.y - v0.y);
    v0.z += blend * (v1.z - v0.z);
  }

  public void setNormalize() {
    float m = magnitude();
    if (m != 0.0F)
      setScale(1.0F / m, this); 
  }

  public void setApproximateNormalize() {
    float m = approximateLength();
    if (m != 0.0F)
      setScale(1.0F / m, this); 
  }

  public void setApproximateTruncate(float threshold) {
    float length = approximateLength();
    if (length > threshold)
      setScale(threshold / length, this); 
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 17:58:13