工具包详细解析
(三) 渲染处理工具类
FrameRate类
FrameRate类是计算帧速率的工具类,在所有需要每帧绘制的程序中都可以引用。
修饰符 类型 名称 说明 private String frameRate 表示帧速率的字符串 private long lastTime 上一帧的时间 private long delta 所有帧的总时间,大于1秒时清零 private int frameCount 表示帧速率的数值
修饰符 返回值 函数名 参数 说明 public void initialize () 初始化,程序开始时调用一次 public void calculate () 计算帧速率,每帧调用 public String getFrameRate () 返回表示帧速率的字符串
package Rendering.utils;
/**
* This class is used to calculate the FPS(Frames Per Second).
*
* At first, you need to call the function initialize() to
* initialize the parameters.
* Then call the function calculate() every time when rendering.
* Finally, call the function getFrameRate() when you need to use the FPS.
* @author Mortal
* @since 1.0
*/
public class FrameRate {
/**
* The message String that is needed to display the FPS.
*/
private String frameRate;
/**
* Last time to call the function calculate() or initialize().
*/
private long lastTime;
/**
* Add the millisecond every time when call the function calculate(),
* When it comes to one second, update the message String
* and reset it to zero.
*/
private long delta;
/**
* The count of calling calculate() in one second.
*/
private int frameCount;
/**
* Initialize the parameters.
*/
public void initialize() {
lastTime = System.currentTimeMillis();
frameRate = "FPS 0";
}
/**
* Add the millisecond every time and update the message String
* when it comes to one second.
*/
public void calculate() {
long current = System.currentTimeMillis();
delta += current - lastTime;
lastTime = current;
frameCount++;
if (delta > 1000) {
delta -= 1000;
frameRate = String.format("FPS %s", frameCount);
frameCount = 0;
}
}
/**
* To get to message String of FPS(Frames Per Second).
* @return The message String of FPS(Frames Per Second).
*/
public String getFrameRate() {
return frameRate;
}
}
Framework类
Framework类是表示窗口的抽象类,具有一个窗口所需的所用功能及接口(API)。它继承了JFrame类 ,实现了Runnable接口。调用时使run方法在 AWT 事件指派线程上异步执行。 Framework类是采用了抽象方法的设计模式 ,在父类中定义方法调用的抽象方法和一些接口(API),而在子类,即主程序类中再根据具体的方法来进行实现,Framework类本身可以引用到任何的窗口程序中。
修饰符 类型 名称 说明 private BufferStrategy bufferStrategy 缓冲策略(双缓冲) private volatile boolean running 线程是否正在运行 private Thread gameThread 主线程 protected int vx 视图窗口的x位置 protected int vy 视图窗口的y位置 protected int vw 视图窗口的宽度 protected int vh 视图窗口的高度 protected FrameRate frameRate 计算帧速率的工具 protected RelativeMouseInput mouse 鼠标输入处理类 protected SafeKeyboardInput keyboard 键盘输入处理类 protected Color appBackground 窗口的背景颜色 protected Color appBorder 窗口的边框颜色 protected Color appFPSColor 显示帧速率的颜色 protected Font appFont 窗口的字体 protected String appTitle 窗口的题目 protected float appBorderScale 视图窗口在主窗口的占比 protected int appWidth 主窗口的宽度 protected int appHeight 主窗口的高度 protected float appWorldWidth 世界地图的宽度 protected float appWorldHeight 世界地图的高度 protected long appSleep 每帧的休眠时间 protected boolean appMaintainRatio 视图窗口是否占据整个主窗口 protected boolean appDisableCursor 鼠标光标是否可见
修饰符 返回值 函数名 参数 说明 public Framework () 默认构造函数 protected abstract void createFramework () 创建整个窗口 protected abstract void renderFrame (Graphics g) 渲染整个窗口 public abstract int getScreenWidth () 返回视图窗口的宽度 public abstract int getScreenHeight () 返回视图窗口的高度 protected void createAndShowGUI () 创建整个窗口并运行主线程 protected void setupInput (Component component) 为指定组件设置键盘事件处理和鼠标事件处理 protected void createBufferStrategy (Canvas component) 根据窗口组件构造双缓冲策略 protected void createBufferStrategy (Window window) 根据屏幕窗口构造双缓冲策略(全屏时调用) protected void setupViewport (int sw,int sh) 根据主窗口的大小和比例设置视图窗口的位置和大小 protected Matrix3x3f getViewportTransform () 返回世界坐标到视图窗口坐标的转换矩阵 protected Matrix3x3f getReverseViewportTransform () 返回视图窗口坐标到世界坐标的转换矩阵 public Vector2f getWorldMousePosition () 返回映射到世界坐标上的鼠标绝对位置 protected Vector2f getRelativeWorldMousePosition () 返回映射到世界坐标上的鼠标相对上一帧的位置 public void run () 初始化并运行主线程 protected void initialize () 执行初始化操作 protected void terminate () 执行资源的释放操作 private void gameLoop (float delta) 主线程中的主循环 private void renderFrame () 从双缓冲策略中获取需要绘制的Graphics实例并进行绘制 private void sleep (long sleep) 主线程休眠指定时间 protected void processInput (float delta) 处理输入 protected void updateObject (float delta) 更新每个需要绘制的图形对象 protected void render (Graphics g) 每帧渲染视图窗口 private void disableCursor () 设置鼠标光标不可见 protected void shutDown () 关闭窗口 protected void onShutDown () 关闭窗口后需要执行的操作 protected static void launchApp (final Frameworkapp) 使用该静态执行主程序,使run方法在 AWT 事件指派线程上异步执行
package Rendering.utils;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
public abstract class Framework extends JFrame implements Runnable {
private BufferStrategy bufferStrategy;
private volatile boolean running;
private Thread gameThread;
protected int vx;
protected int vy;
protected int vw;
protected int vh;
protected FrameRate frameRate;
protected RelativeMouseInput mouse;
protected SafeKeyboardInput keyboard;
protected Color appBackground = Color.BLACK;
protected Color appBorder = Color.LIGHT_GRAY;
protected Color appFPSColor = Color.GREEN;
protected Font appFont = new Font("Courier New", Font.PLAIN, 14);
protected String appTitle = "TBD-Title";
protected float appBorderScale = 0.8f;
protected int appWidth = 640;
protected int appHeight = 640;
protected float appWorldWidth = 2.0f;
protected float appWorldHeight = 2.0f;
protected long appSleep = 10L;
protected boolean appMaintainRatio = false;
protected boolean appDisableCursor = false;
public Framework() {}
protected abstract void createFramework();
protected abstract void renderFrame(Graphics g);
public abstract int getScreenWidth();
public abstract int getScreenHeight();
protected void createAndShowGUI() {
createFramework();
if (appDisableCursor) {
disableCursor();
}
gameThread = new Thread(this);
gameThread.start();
}
protected void setupInput(Component component) {
keyboard = new SafeKeyboardInput();
component.addKeyListener(keyboard);
mouse = new RelativeMouseInput(component);
component.addMouseListener(mouse);
component.addMouseMotionListener(mouse);
component.addMouseWheelListener(mouse);
}
protected void createBufferStrategy(Canvas component) {
component.createBufferStrategy(2);
bufferStrategy = component.getBufferStrategy();
}
protected void createBufferStrategy(Window window) {
window.createBufferStrategy(2);
bufferStrategy = window.getBufferStrategy();
}
protected void setupViewport(int sw, int sh) {
int w = (int) (sw * appBorderScale);
int h = (int) (sh * appBorderScale);
vw = w;
vh = (int) (w * appWorldHeight / appWorldWidth);
if (vh > h) {
vw = (int) (h * appWorldWidth / appWorldHeight);
vh = h;
}
vx = (sw - vw) / 2;
vy = (sh - vh) / 2;
}
protected Matrix3x3f getViewportTransform() {
return Utility.createViewport(
appWorldWidth, appWorldHeight,
getScreenWidth(), getScreenHeight());
}
protected Matrix3x3f getReverseViewportTransform() {
return Utility.createReverseViewport(
appWorldWidth, appWorldHeight,
getScreenWidth(), getScreenHeight());
}
public Vector2f getWorldMousePosition() {
Matrix3x3f screenToWorld = getReverseViewportTransform();
Point mousePos = mouse.getPosition();
Vector2f screenPos = new Vector2f(mousePos.x, mousePos.y);
return screenToWorld.mul(screenPos);
}
protected Vector2f getRelativeWorldMousePosition() {
float sx = appWorldWidth / (getScreenWidth() - 1);
float sy = appWorldHeight / (getScreenHeight() - 1);
Matrix3x3f viewport = Matrix3x3f.scale(sx, -sy);
Point p = mouse.getPosition();
return viewport.mul(new Vector2f(p.x, p.y));
}
public void run() {
running = true;
initialize();
long curTime = System.nanoTime();
long lastTime = curTime;
double nsPerTime;
while (running) {
curTime = System.nanoTime();
nsPerTime = curTime - lastTime;
gameLoop((float) (nsPerTime / 1.0E9));
lastTime = curTime;
}
terminate();
}
protected void initialize() {
frameRate = new FrameRate();
frameRate.initialize();
}
protected void terminate() {}
private void gameLoop(float delta) {
processInput(delta);
updateObject(delta);
renderFrame();
sleep(appSleep);
}
private void renderFrame() {
do{
do{
Graphics g = null;
try{
g = bufferStrategy.getDrawGraphics();
renderFrame(g);
}finally {
if(g!=null){
g.dispose();
}
}
}while(bufferStrategy.contentsRestored());
bufferStrategy.show();
}while(bufferStrategy.contentsLost());
}
private void sleep(long sleep) {
try {
Thread.sleep(sleep);
} catch (InterruptedException ex) {}
}
protected void processInput(float delta) {
keyboard.poll();
mouse.poll();
}
protected void updateObject(float delta) {}
protected void render(Graphics g) {
g.setFont(appFont);
g.setColor(appFPSColor);
frameRate.calculate();
g.drawString(frameRate.getFrameRate(),20, 20);
}
private void disableCursor() {
Toolkit tk = Toolkit.getDefaultToolkit();
Image image = tk.createImage("");
Point point = new Point(0, 0);
String name = "CanBeAnything";
Cursor cursor = tk.createCustomCursor(image, point, name);
setCursor(cursor);
}
protected void shutDown() {
if (Thread.currentThread() != gameThread) {
try {
running = false;
gameThread.join();
onShutDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.exit(0);
} else {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
shutDown();
}
});
}
}
protected void onShutDown() {}
protected static void launchApp(final Framework app) {
app.addWindowListener(new WindowAdapter() {
/**
* Invoked when a window is in the process of being closed.
* The close operation can be overridden at this point.
*
* @param e
*/
@Override
public void windowClosing(WindowEvent e) {
app.shutDown();
}
});
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
app.createAndShowGUI();
}
});
}
}
SwingFramework类
SwingFramework类是使用Swing实现的Framework类,主窗口和视图窗口为JPanel实例,在视图窗口的Canvas实例上进行图形的绘制。
修饰符 类型 名称 说明 protected Canvas canvas 进行图形绘制的画布 private JPanel mainPanel 主窗口 private JPanel centerPanel 视图窗口
修饰符 返回值 函数名 参数 说明 protected JPanel getMainPanel () 返回主窗口 private JPanel getCenterPanel () 返回视图窗口 private Canvas getCanvas () 返回绘制的画布 private void setUpLookAndFeel () 设置主题 protected void onCreateAndShowGUI () 创建窗口细节 protected void createFramework () 创建整个窗口 protected void onComponentResized (ComponentEvent e) 窗口大小改变时调用,重新绘制画布 protected void renderFrame (Graphics g) 渲染整个窗口 public int getScreenWidth () 返回视图窗口的宽度,即画布的宽度 public int getScreenHeight () 返回视图窗口的高度,即画布的高度
package Rendering.utils;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
public class SwingFramework extends Framework {
protected Canvas canvas;
private JPanel mainPanel;
private JPanel centerPanel;
protected JPanel getMainPanel() {
if (mainPanel == null) {
mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
mainPanel.add(getCenterPanel(), BorderLayout.CENTER);
}
return mainPanel;
}
private JPanel getCenterPanel() {
if (centerPanel == null) {
centerPanel = new JPanel();
centerPanel.setBackground(appBorder);
centerPanel.setLayout(null);
centerPanel.add(getCanvas());
}
return centerPanel;
}
private Canvas getCanvas() {
if (canvas == null) {
canvas = new Canvas();
canvas.setBackground(appBackground);
}
return canvas;
}
private void setUpLookAndFeel() {
try {
UIManager.setLookAndFeel(
"com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"
);
} catch (Exception e) {
e.printStackTrace();
}
}
protected void onCreateAndShowGUI() {}
@Override
protected void createFramework() {
setUpLookAndFeel();
getContentPane().add(getMainPanel());
setLocationByPlatform(true);
setSize(appWidth, appHeight);
setTitle(appTitle);
getContentPane().setBackground(appBorder);
getContentPane().addComponentListener(new ComponentAdapter() {
/**
* Invoked when the component's size changes.
*
* @param e
*/
@Override
public void componentResized(ComponentEvent e) {
onComponentResized(e);
}
});
setupInput(getCanvas());
onCreateAndShowGUI();
setVisible(true);
createBufferStrategy(getCanvas());
getCanvas().requestFocus();
}
protected void onComponentResized(ComponentEvent e) {
Dimension size = getContentPane().getSize();
setupViewport(size.width, size.height);
getCanvas().setLocation(vx, vy);
getCanvas().setSize(vw, vh);
//改变大小时重新绘制
getCanvas().repaint();
}
@Override
protected void renderFrame(Graphics g) {
g.clearRect(0, 0, getScreenWidth(), getScreenHeight());
render(g);
}
@Override
public int getScreenWidth() {
return getCanvas().getWidth();
}
@Override
public int getScreenHeight() {
return getCanvas().getHeight();
}
public static void main(String[] args) {
launchApp(new SwingFramework());
}
}
更多:
第一篇 第二篇 第三篇 第四篇 第五篇 第六篇 第七篇 最终篇 源代码