Scala JPanel 渲染同步
Scala JPanel rendering synchronisation
我正在 Scala 中做一个模拟程序,我试图通过覆盖 paintComponent 在 JPanel 中渲染模拟:
override def paintComponent(g: Graphics2D) = {
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
super.paintComponent(g)
tx1 = g.getTransform()
g.setColor(new Color(0,0,0))
simulator.getVehicles foreach{vehc =>
g.translate(vehc.getPos.x,vehc.getPos.y)
g.draw(new Ellipse2D.Double(-Vehicle.rad, -Vehicle.rad, Vehicle.diam, Vehicle.diam))
g.drawLine(0,0,(Vehicle.rad*vehc.getDir.x).toInt,(Vehicle.rad*vehc.getDir.y).toInt)
g.setTransform(tx1)
}
}
我在另一个线程上有模拟本身 运行:
def run{
//logic loop
time = System.currentTimeMillis();
dt = 1000/60
while(loop)
{
getVehicles.foreach{
_.move
}
collider.solvecollisions()
Thread.sleep(dt- (time - System.currentTimeMillis()))
time = System.currentTimeMillis();
}}
GetVehicles returns 所有模拟车辆的 Buffer[Vehicle]。
我的问题是渲染中存在抖动。我的意思是,有时某些车辆的渲染时间比其他车辆晚。
我认为发生这种情况是因为模拟循环在渲染循环获取位置的同时更新位置,并且存在一些重叠。即当渲染从时间步 n 开始时,一半的车辆被渲染,然后时间步 n+1 发生,其余车辆进一步渲染一个时间步。起初我认为这是一个需要用双缓冲来解决的问题,但由于 paintComponent 已经这样做了,我认为情况并非如此。
任何想法如何解决这一问题?我尝试简单地渲染 getVehicles.clone 但这没有帮助,因为对车辆的引用仍然相同。
谢谢!
您的车辆模型似乎是可变的 (_.move
)。那么如果在不同的线程中模拟和绘画运行,在Swing中得不到一致的世界观也就不足为奇了。
根据您的要求,我可以看到以下解决方案:
- 运行 事件调度线程上的模拟更新。优点:根本不需要更改代码。缺点:如果模拟很重,可能会使 GUI 变慢
- 创建一个全局 "world" 锁,您
synchronize
。优点:需要对代码进行很少的更改。缺点:除非 GUI 更新速度很慢,否则模拟和渲染会相互阻塞。如果 GUI 更新是模拟率的一小部分,可能会有用。
- 采用不可变模型,然后您的模拟将在每一步中创建一个一致的更新世界。优点:渲染和模拟会自动保持一致。可能是最快的解决方案。缺点:你需要重写你的模拟。 可能是最佳解决方案。
- 将您的
var
可变状态更改为 STM 参考单元格。如果 GUI 速率与模拟速率相比较低,则可能会很好地工作,因为这种 "optimistic" 方法可能适用于相对较少的回滚。我不确定 Scala-STM 和渲染器只进行读取访问是如何工作的。也许您需要一个完整的多版本 STM 来避免回滚。
概述不可变变体:
trait Vehicle {
def move: Vehicle // return updated model
}
trait Collisions {
def solve(in: Seq[Vehicle]): Seq[Vehicle] // return corrected models
}
trait World {
def vehicles: Seq[Vehicle]
}
trait Simulator {
protected def coll: Collisions
// create updated world
def run(prev: World): World = new World {
val vehicles = coll.solve(prev.vehicles.map(_.move))
}
}
我正在 Scala 中做一个模拟程序,我试图通过覆盖 paintComponent 在 JPanel 中渲染模拟:
override def paintComponent(g: Graphics2D) = {
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
super.paintComponent(g)
tx1 = g.getTransform()
g.setColor(new Color(0,0,0))
simulator.getVehicles foreach{vehc =>
g.translate(vehc.getPos.x,vehc.getPos.y)
g.draw(new Ellipse2D.Double(-Vehicle.rad, -Vehicle.rad, Vehicle.diam, Vehicle.diam))
g.drawLine(0,0,(Vehicle.rad*vehc.getDir.x).toInt,(Vehicle.rad*vehc.getDir.y).toInt)
g.setTransform(tx1)
}
}
我在另一个线程上有模拟本身 运行:
def run{
//logic loop
time = System.currentTimeMillis();
dt = 1000/60
while(loop)
{
getVehicles.foreach{
_.move
}
collider.solvecollisions()
Thread.sleep(dt- (time - System.currentTimeMillis()))
time = System.currentTimeMillis();
}}
GetVehicles returns 所有模拟车辆的 Buffer[Vehicle]。
我的问题是渲染中存在抖动。我的意思是,有时某些车辆的渲染时间比其他车辆晚。 我认为发生这种情况是因为模拟循环在渲染循环获取位置的同时更新位置,并且存在一些重叠。即当渲染从时间步 n 开始时,一半的车辆被渲染,然后时间步 n+1 发生,其余车辆进一步渲染一个时间步。起初我认为这是一个需要用双缓冲来解决的问题,但由于 paintComponent 已经这样做了,我认为情况并非如此。 任何想法如何解决这一问题?我尝试简单地渲染 getVehicles.clone 但这没有帮助,因为对车辆的引用仍然相同。
谢谢!
您的车辆模型似乎是可变的 (_.move
)。那么如果在不同的线程中模拟和绘画运行,在Swing中得不到一致的世界观也就不足为奇了。
根据您的要求,我可以看到以下解决方案:
- 运行 事件调度线程上的模拟更新。优点:根本不需要更改代码。缺点:如果模拟很重,可能会使 GUI 变慢
- 创建一个全局 "world" 锁,您
synchronize
。优点:需要对代码进行很少的更改。缺点:除非 GUI 更新速度很慢,否则模拟和渲染会相互阻塞。如果 GUI 更新是模拟率的一小部分,可能会有用。 - 采用不可变模型,然后您的模拟将在每一步中创建一个一致的更新世界。优点:渲染和模拟会自动保持一致。可能是最快的解决方案。缺点:你需要重写你的模拟。 可能是最佳解决方案。
- 将您的
var
可变状态更改为 STM 参考单元格。如果 GUI 速率与模拟速率相比较低,则可能会很好地工作,因为这种 "optimistic" 方法可能适用于相对较少的回滚。我不确定 Scala-STM 和渲染器只进行读取访问是如何工作的。也许您需要一个完整的多版本 STM 来避免回滚。
概述不可变变体:
trait Vehicle {
def move: Vehicle // return updated model
}
trait Collisions {
def solve(in: Seq[Vehicle]): Seq[Vehicle] // return corrected models
}
trait World {
def vehicles: Seq[Vehicle]
}
trait Simulator {
protected def coll: Collisions
// create updated world
def run(prev: World): World = new World {
val vehicles = coll.solve(prev.vehicles.map(_.move))
}
}