воскресенье, 18 августа 2013 г.

LWJGL. Таймер и анимация.

Привет, гость!
В этот раз тебе очень повезло. Ты узнаешь о таймере и о анимации которая так сильно нужна в современных приложениях.




Что же подразумевается под словом "таймер"?
Таймер - некий отдельный класс или программа для измерения времени для анимации или для изменения позиции, например, квадрата или другого объекта на экране.
На счастье, в библиотеке LWJGL уже есть все необходимое для создания таймера и поэтому начнем!

System.currentTimeMillis(); в Java имеет точность в размере от 1 до 55 миллисекунд в зависимости от операционной системы, но для более точного и быстрого измерения времени, мы будем использовать Sys.getTime();
Этот lwjglковский таймер умеет измерять время в наносекундах, микросекундах, миллисекундах, и т.д.
Нам нужны миллисекунды, для этого нужно метод Sys.getTimer(); умножить на 1000.

Во многих играх целочисленное значение используется для хранения количество миллисекунд, прошедших с последнего кадра. Это значение может использоваться для перемещения объектов в игре в кадре независимым образом. Это означает, что независимо от того, быстро или медленно FPS все должно двигаться с постоянной скоростью.

Чтобы ограничить FPS в библиотеке существует метод

Display.sync(int fps);

Для чего это нужно?
Этот метод позволяет игровой цикл работать на заданной частоте кадров (например, 60 кадров в секунду) и спать в течение дополнительного времени для облягчения нагрузки на видеокарту или процессор.

Теперь по поводу анимации.
В LWJGL библиотеке есть встроенные методы для обработки нажатия клавиш на клавиатуре или кнопок на мыше.
Это классы Keyboard и Mouse.
Для того, чтобы обработать нажатие кнопки "F" на клавиатуре, нужно делать так:

if (Keyboard.isKeyDown(Keyboard.KEY_F))
{
    System.out.println("нажат клавиша F");
}

Или для мыши

if (Mouse.isButtonDown(0)) // 0 - левая кнопка, 1 - правая.
{
    System.out.println("нажат левая кнопка мыши");
}

Для того, чтобы обрабатывать нажатие кнопок всего один раз, нужно создать еще один цикл:
while (Keyboard.next())
{
    // и в нем обрабатывать нажатие.
    if (Keyboard.isKeyDown(Keyboard.KEY_F))
    {
        System.out.println("нажат клавиша F");
    }
    if (Keyboard.isKeyDown(Keyboard.KEY_W))
    {
        System.out.println("нажат клавиша W");
    }
}
То же самое и для мыши:

while(Mouse.next())
{
    if (Mouse.isButtonDown(...) {...}
}

Теперь нам нужно при нажатии кнопок - перемещать квадрат по экрану...
float x = 0.0F, y = 0.0F;
float movementSpeed = 0.1F;

if (Keyboard.isKeyDown(Keyboard.KEY_W))
{
    y += movementSpeed * delta; // delta - время, за которое произойдет перемещение позиции.
}
if (Keyboard.isKeyDown(Keyboard.KEY_S))
{
    y -= movementSpeed * delta;
}
if (Keyboard.isKeyDown(Keyboard.KEY_D))

{
    x += movementSpeed * delta;
}
if (Keyboard.isKeyDown(Keyboard.KEY_A))
{
    x -= movementSpeed * delta;
}
Ну вот и все!
Теперь код:

import java.util.logging.Level;
import java.util.logging.Logger;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;

public final class SimpleProgram
{
    private float x, y;
    private float movementSpeed;
    private float rotation;
    private long lastFrame;
    private long lastFPS;
    private int fps;
    private int delta;

    private SimpleProgram()
    {
        this.x = 400.0F;
        this.y = 300.0F;
        this.movementSpeed = 0.1F;
        this.rotation = 0.0F;
        this.lastFrame = 0;
        this.fps = 0;
        this.lastFPS = 0;
        this.delta = 0;
    }

    private void initialize()
    {
        this.createDisplay(800, 600, "SimpleProgram");
        this.createMatrixOrtho();
        this.running();
    }

    private void createDisplay(int width, int height, String title)
    {
        try
        {
            Display.setDisplayMode(new DisplayMode(width, height));
            Display.setTitle(title);
            Display.create();
        }
        catch (LWJGLException ex)
        {
            Logger.getLogger(SimpleProgram.class.getName()).log(Level.SEVERE, null, ex);
            this.clearResources(true);
        }
    }

    private void createMatrixOrtho()
    {
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glLoadIdentity();
        GL11.glOrtho(0.0D, (double) Display.getWidth(), 0.0D, (double) Display.getHeight(), -1.0D, 1.0D);
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glLoadIdentity();
    }

    private void running()
    {
        this.bindResources();
        while (!Display.isCloseRequested())
        {
            this.displayUpdate();
        }
        this.clearResources(false);
    }

    private void bindResources()
    {
        this.getDelta();
        this.lastFPS = this.getTime();
    }

    private void keyInput()
    {
        if (Keyboard.isKeyDown(Keyboard.KEY_W))
        {
            this.y += this.movementSpeed * this.delta; // delta - время, за которое произойдет перемещение позиции.
        }
        if (Keyboard.isKeyDown(Keyboard.KEY_S))
        {
            this.y -= this.movementSpeed * this.delta;
        }
        if (Keyboard.isKeyDown(Keyboard.KEY_D))
        {
            this.x += this.movementSpeed * this.delta;
        }
        if (Keyboard.isKeyDown(Keyboard.KEY_A))
        {
            this.x -= this.movementSpeed * this.delta;
        }
        this.rotation += 0.1F * this.delta;
    }

    private void drawQuad(float x, float y, float rotate, float width, float height)
    {
        GL11.glPushMatrix();
        GL11.glTranslatef(x, y, 0.0F);
        GL11.glRotatef(rotate, 0.0F, 0.0F, 1.0F); // повернуть объект.
        GL11.glTranslatef(-width / 2, -height / 2, 0.0F); // сдвиг по координате x на половину для вращения в центре.
        GL11.glBegin(GL11.GL_QUADS);
        GL11.glVertex2f(0.0F, 0.0F);
        GL11.glVertex2f(width, 0.0F);
        GL11.glVertex2f(width, height);
        GL11.glVertex2f(0.0F, height);
        GL11.glEnd();
        GL11.glPopMatrix();
    }

    private void displayUpdate()
    {
        // обновление значение delta.
        this.delta = this.getDelta();
        // обработка событий.
        this.keyInput();
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
        GL11.glLoadIdentity();

        // рисование квадрата.
        this.drawQuad(this.x, this.y, this.rotation, 50.0F, 50.0F);

        GL11.glPushMatrix();
        GL11.glTranslatef(100.0F, 200.0F, 0.0F);
        GL11.glBegin(GL11.GL_QUADS);
        GL11.glColor3f(1.0F, 0.0F, 0.0F);
        GL11.glVertex2f(0.0F, 0.0F);
        GL11.glColor3f(0.0F, 1.0F, 0.0F);
        GL11.glVertex2f(50.0F, 0.0f);
        GL11.glColor3f(0.0F, 0.0F, 1.0F);
        GL11.glVertex2f(50.0F, 50.0F);
        GL11.glColor3f(1.0F, 1.0F, 1.0F);
        GL11.glVertex2f(0.0F, 50.0F);
        GL11.glEnd();
        GL11.glPopMatrix();
        GL11.glColor3f(1.0F, 1.0F, 1.0F);

        this.updateFPS(); // обновление FPS.
        Display.update();
        Display.sync(60);
    }

    private void clearResources(boolean hasCrash)
    {
        System.out.println(GLU.gluErrorString(GL11.glGetError()));
        Display.destroy();
        System.exit(hasCrash ? 1 : 0);
    }

    private void updateFPS()
    {
        if (this.getTime() - this.lastFPS > 1000)
        {
            Display.setTitle("FPS: " + this.fps); // показ FPS.
            this.fps = 0;
            this.lastFPS += 1000;
        }
        this.fps++;
    }

    // получить delta.
    private int getDelta()
    {
        long time = this.getTime();
        int delta2 = (int) (time - this.lastFrame);
        this.lastFrame = time;

        return delta2;
    }

    // получить время в миллисекундах.
    public long getTime()
    {
        return (Sys.getTime() * 1000) / Sys.getTimerResolution();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        new SimpleProgram().initialize();
    }
}

Комментариев нет:

Отправить комментарий