плавная анимация в играх

Как создать «плавную» анимацию? Разрабатываю игру на android. Я знаю, что способ плавной анимации по умолчанию не самый лучший:

perFrame() {
    time = highrestime();
    frameTime = time - lastTime;
    lastTime = time;

    // now you could use frameTime as delta. this will lead to not real
    // smooth animations
}

Я читал некоторые документы по этой теме. Особенно, если вы используете физическое моделирование. Известная статья выглядит так: http://gafferongames.com/game-physics/fix-your-timestep/ Я создал базовый класс, который выглядит так:

public class TimeSystem {
private long mCurrentTime;
private long mAccumulator;
private Simulation mSimulation; 
private Renderer mRenderer;

private static float mSimulationDelta = 1000f / 60f;

public TimeSystem(Simulation simulation, Renderer renderer) {
    mCurrentTime = System.currentTimeMillis();
    mAccumulator = 0;

    mSimulation = simulation;
    mRenderer = renderer;
}

public void notifyNextFrame() {
    long newTime;
    long frameTime;

    newTime =  System.currentTimeMillis();
    frameTime = newTime - mCurrentTime;
    mCurrentTime = newTime;

    if(frameTime > 250)
        frameTime = 250;

    mAccumulator += frameTime;

    // while full steps of simulation steps can be simulated
    while(mAccumulator >= mSimulationDelta) {
        mSimulation.simulate(mSimulationDelta);
        mAccumulator -= mSimulationDelta;
    }

    // render
    mRenderer.render(frameTime / 1000.0f); // frameTime in seconds
}

public interface Simulation
{
    void simulate(float delta);
}

public interface Renderer
{
    void render(float delta);
}
}

Я действительно не эксперт в разработке игр, но надеюсь, что понимаю основы этой статьи. .simulate вызывается столько раз, сколько необходимо для фиксированного временного шага. Проблема в том, что анимация в сложной системе все равно не плавная. FPS в моей программе около 60FPS.

Приложение для тестирования написано на java для android:

    public float x = 40;
public float y = 40;
public float xmove = 0.1f;
public float ymove = 0.05f;

public void simulate(float delta) {
    x += xmove * delta;
    y += ymove * delta;

    if(xmove > 0)
    {
        if(x > 480)
            xmove = -xmove;
    }
    else if(xmove < 0)
    {
        if(x < 0)
            xmove = -xmove;
    }

    if(ymove > 0)
    {
        if(y > 480)
            ymove = -ymove;
    }
    else
    {
        if(y < 0)
            ymove = -ymove;
    }

    RandomSleep(8);
}

Random rnd = new Random();

public void doDraw(Canvas canvas, float delta) {
    Paint paint = new Paint();

    paint.setColor(Color.BLACK);
    paint.setStyle(Paint.Style.FILL);
    paint.setTextSize(30);

    canvas.drawColor(Color.BLUE);
    canvas.drawCircle(x, y, 10, paint);
    canvas.drawText("" + FPSConverter.getFPSFromSeconds(delta), 40, 40, paint);

    RandomSleep(5);
}

public void RandomSleep(int maxMS)
{
    try {
        Thread.sleep((int)(rnd.nextDouble()*maxMS));
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

Сумма случайных снов будет макс. 13мс. Что вроде бы крайность, но с данной системой вы не должны почувствовать разницы. (Я пытаюсь смоделировать свою игровую систему, чтобы выяснить, почему анимация в моем реальном игровом движке не плавная; поэтому я использую наихудший сценарий)

РЕДАКТИРОВАТЬ: Если нет прямого ответа, возможно, кто-то может указать, как я могу узнать, где может быть «ошибка». Что я мог сделать, чтобы правильно проанализировать это?


person Daniel Bişar    schedule 01.05.2011    source источник


Ответы (2)


Итак, наконец-то! Через две недели я нашел простое решение! Проблема заключалась в том, что я использовал System.currentTimeMillis (); Кажется, разрешение этого таймера недостаточно высокое для плавной анимации. Я переписываю класс таймера, и теперь он отлично работает. Для тех, у кого есть аналогичная проблема и кто хотел бы использовать мой код:

public class TimeSystem
{
private long mOldTime;
private long mAccumulator;
private long mNewTime;
private long mFrameTime;

private Simulation mSimulation;
private Renderer mRenderer;

private static final long mSimulationDelta = 1000 * 1000 / 10;
private static final float mSimulationDeltaInMilliseconds = mSimulationDelta / 1000;
private static final long mMaxFrameTime = mSimulationDelta;


public TimeSystem(Simulation simulation, Renderer renderer)
{
    mSimulation = simulation;
    mRenderer = renderer;

    mAccumulator = 0;
    mOldTime = System.nanoTime();
}

public void notifyNextFrame()
{
    mNewTime = System.nanoTime();
    mFrameTime = mNewTime - mOldTime;
    mOldTime = mNewTime;

    // if FPS is to slow don't produce even more CPU usage
    if (mFrameTime > mMaxFrameTime)
        mFrameTime = mMaxFrameTime;

    mAccumulator += mFrameTime;

    // while full steps of simulation steps can be simulated
    while (mAccumulator >= mSimulationDelta)
    {
        mSimulation.simulate(mSimulationDeltaInMilliseconds);
        mAccumulator -= mSimulationDelta;
    }

    // render
    mRenderer.render(mFrameTime / 1000f*1000f);
}

public interface Simulation
{
    void simulate(float delta);
}

public interface Renderer
{
    void render(float delta);
}
}

Если понравится, голосуйте :) Спасибо.

person Daniel Bişar    schedule 15.05.2011

Думаю, в этой строке допущена опечатка: frameTime = lastTime - time; должно быть frameTime = time - lastTime; хотя я думаю, что в вашем реальном коде такой проблемы нет, иначе все было бы не так.

Возникает один вопрос: сколько времени занимает этап симуляции? Вы уверены, что проблема не в этом? Может быть, симуляция просто не успевает за вашим рендерером?

person Himadri Choudhury    schedule 01.05.2011
comment
Да, вы правы, должно быть frameTime = time - lastTime; Исправил ошибку. Хм, я не уверен. Если я помню, правильно в traceview для каждого вызова (симуляции и рендеринга) показывалось что-то около 600. Вопрос в том, что 600. [мс] невозможно. Это то, что немного дольше, чем вызов System.currentTimeMillis (); но, возможно, я неправильно прочитал таблицу. Вы должны иметь в виду, что в настоящее время в симуляции всего около 3 объектов, которые почти ничего не делают, кроме нескольких вызовов функций. - person Daniel Bişar; 02.05.2011