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
相关产品推荐
相关产品推荐

