Базовым классом приложения теперь является класс Game входящий в пакет gram.engine содержащий в себе все остальные компоненты. Рассмотрим его:
Код
public class Game {
private Frame frame=null;
private Graphics gFrame=null;
private BufferStrategy bufferStrategy=null;
private GraphicsDevice device=null;
private FrameBuffer buffer;
private Settings settings;
private Camera camera;
private World world;
private GLFont font;
//private boolean isIdle;
private boolean exit=false;
private InputMap inputMap;
private int fps;
private int lastFps;
private int totalFps;
public Game(Settings settings)
{
this.settings = settings;
}
public void Run()
{
LoadResources();
InitJPCT(settings);
loop();
}
private void LoadResources()
{
font = new GLFont( new Font("Default",Font.TRUETYPE_FONT,16),GLFont.ENGLISH+GLFont.RUSSIAN);
}
private void InitJPCT(Settings settings)
{
if (settings.fullscreen) {
GraphicsEnvironment env=GraphicsEnvironment.getLocalGraphicsEnvironment();
device=env.getDefaultScreenDevice();
GraphicsConfiguration gc=device.getDefaultConfiguration();
frame=new JFrame(gc);
frame.setUndecorated(true);
frame.setIgnoreRepaint(true);
device.setFullScreenWindow(frame);
if (device.isDisplayChangeSupported())
{
device.setDisplayMode(new DisplayMode(settings.width, settings.heigth, settings.bitt, 0));
}
frame.createBufferStrategy(2);
bufferStrategy=frame.getBufferStrategy();
Graphics g=bufferStrategy.getDrawGraphics();
bufferStrategy.show();
g.dispose();
Config.glFullscreen = true;
}
else {
frame=new JFrame();
frame.setTitle(settings.windowTitle);
frame.pack();
Insets insets = frame.getInsets();
settings.titleBarHeight=insets.top;
settings.leftBorderWidth=insets.left;
frame.setSize(settings.width+settings.leftBorderWidth+insets.right, settings.heigth+settings.titleBarHeight+insets.bottom);
frame.setResizable(false);
frame.show();
gFrame=frame.getGraphics();
}
frame.addWindowListener(new WindowAdapter(){});
buffer=new FrameBuffer(settings.width, settings.heigth, settings.mode);
if (!settings.useOPENGL) {
buffer.disableRenderer(IRenderer.RENDERER_OPENGL);
buffer.enableRenderer(IRenderer.RENDERER_SOFTWARE, IRenderer.MODE_OPENGL);
if (settings.fullscreen)
{
device.setFullScreenWindow(null);
}
}
else {
frame.hide();
buffer.enableRenderer(IRenderer.RENDERER_OPENGL, IRenderer.MODE_OPENGL);
buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
}
Config.glMipmap = settings.useMipmap;
Config.maxPolysVisible= settings.MaxPoligons;
Config.glTrilinear= settings.useTrilinear;
buffer.optimizeBufferAccess();
world = new World();
world.setAmbientLight(150, 150, 150);
inputMap = new InputMap(buffer);
}
private void loop()
{
final long delay = 50;
long previous = System.currentTimeMillis();
World.setDefaultThread(Thread.currentThread());
Timer fpsTimer=new Timer(1000);
fpsTimer.start();
while (!exit) {
final long current = System.currentTimeMillis();
if( current - previous > delay )
{
inputMap.evaluteInput();
}
buffer.clear(java.awt.Color.WHITE);
world.renderScene(buffer);
world.draw(buffer);
//вывод счетчика fps
font.blitString(buffer, Integer.toString(totalFps), 10, 15, 1, Color.BLACK);
font.blitString(buffer, "Это пример ", 10, 30, 1, Color.black);
font.blitString(buffer, "вывода текста ", 90, 50, 1, Color.CYAN);
font.blitString(buffer, "на экран ", 200, 70, 1, Color.blue);
buffer.update();
display();
fps++;
if (fpsTimer.getElapsedTicks()>0) {
totalFps=(fps-lastFps);
lastFps=fps;
}
if (org.lwjgl.opengl.Display.isCloseRequested())
{
exit=true;
}
Thread.yield();
}
if (!settings.useOPENGL && settings.fullscreen)
{
device.setFullScreenWindow(null);
}
System.exit(0);
}
private void display()
{
if (!settings.useOPENGL) {
if (!settings.fullscreen) {
buffer.display(gFrame, settings.leftBorderWidth, settings.titleBarHeight);
} else {
Graphics g=bufferStrategy.getDrawGraphics();
g.drawImage(buffer.getOutputBuffer(), 0, 0, null);
bufferStrategy.show();
g.dispose();
}
} else {
buffer.displayGLOnly();
}
}
private class Timer {
private long ticks=0;
private long granularity=0;
public Timer(int granularity) {
this.granularity=granularity;
}
public void start() {
ticks=System.currentTimeMillis();
}
public void reset() {
start();
}
public long getElapsedTicks() {
long cur=System.currentTimeMillis();
long l=cur-ticks;
if (l>=granularity) {
ticks=cur-(l%granularity);
return l/granularity;
}
return 0;
}
}
}
В данной реализации в конструктор класса передается экземпляр класса Settings который содержит все настройки, а в его конструкторе заданы параметры по умолчанию. Это позволяет менять их в одном месте и в последующем реализовать файл настроек для их хранения.
Метод Run() используется для запуска приложения в нем последовательно выполняется загрузка ресурсов, настройка JPCT и запуск игрового цикла. Также в классе определен приватный подкласс Timer в котором реализован функционал таймера, который в данном примере используется для расчета количества рисуемых кадров.
Серьезное изменение претерпела функция настройки JPCT. В ней теперь мы сами создаем и настраиваем окно приложения, задаем глубину цвета а также включаем различные возможности. В ней также стало возможно использовать режим программного рендеринга(я не тестировал его на корректную работу т.к. я им не планирую пользоваться, но такую возможность оставил).
Функция игрового цикла не претерпела сильных изменений. В ней добавился вызов функции display() которая выполняет отображения графики в зависимости от заданного режима работы. А также расчет и вывод на экран счетчика кадров.
Также в проект был добавлен файл run_java для запуска приложения вне NetBeans.
Для запуска обязательно необходимо наличие dll библиотек в папке lib.
В дальнейшем я планирую использовать и развивать данный проект в своих статьях.