2013-07-19 2 views
0

Итак, я работаю над созданием 2D-игры на Java, и у меня действительно не слишком много опыта работы с Java. В настоящее время я использую очень простой цикл с помощью поворотно таймера выполняется каждые 10 мс или так, что выглядит примерно так:Обновление и рендеринг для 2D-игры в Java

public void actionPerformed(ActionEvent e) { 
update(); 
repaint(); 
} 

Однако, мне нужно что-то более практичное, по понятным причинам. Эти причины включают в себя тот факт, что большее отставание означает меньше FPS и более медленное перемещение/другое обновление. Я нашел следующий код в учебнике для 3D-игры Java here. Он начнет работать, когда начнется программа, и я достаточно понимаю, чтобы знать, что это сработает. Тем не менее, я до конца не понимаю: (клеща() является программой обновления, визуализации() делает экран)

long currenttime; 
    long previoustime = System.nanoTime(); 
    long passedtime; 
    int frames = 0; 
    double unprocessedseconds = 0; 
    double secondspertick = 1/60.0; 
    int tickcount = 0; 
    boolean ticked = false;  

    while (gameIsRunning) { 
     currenttime = System.nanoTime(); 
     passedtime = currenttime - previoustime; 
     previoustime = currenttime; 
     unprocessedseconds += passedtime/1000000000.0; 

     while (unprocessedseconds > secondspertick) { 
      tick(); 
      unprocessedseconds -= secondspertick; 
      ticked = true; 
      tickcount++; 
      System.out.println(tickcount); 
      if (tickcount % 60 == 0) { 
       System.out.println(frames + " FPS"); 
       previoustime += 1000; 
       frames = 0; 
      } 
     } 
     if (ticked) { 
      render(); 
      frames++; 
     } 
     render();   
     frames++; 
    } 

Этот код не был объяснен в учебнике я нашел его в Может кто-то пожалуйста, разорвать этот вниз. и объяснить это? Я также посмотрел here на идеи, и последний фрагмент кода на этой странице с потоком рендеринга и потоком обновлений имеет для меня большой смысл. Какой метод использовать? Один из вышеперечисленных, или что-то совершенно другое? Кроме того, вы, вероятно, можете сказать, что это мой первый вопрос здесь, в stackoverflow. Спасибо заранее, Джош

ответ

1

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

Существует популярная статья в Интернете, в которой объясняется, почему это так, и почему использование фиксированного timestep является правильной процедурой. Check it out.

Каждое обновление игры продвинуто в 1/60-й секунде (так 60 кадров в секунду). Это повторяется до тех пор, пока в агрегате не останется меньше 1/60 секунды. Агрегат - просто причудливое слово для суммы.

Затем снимок текущего состояния игры отображается на экране.

Я не буду углубляться в него, но на самом деле этот код должен инсерполировать позицию каждого объекта на оставшееся время в совокупности во время render().

long currenttime; 
long previoustime = System.nanoTime(); 
long passedtime; 
int frames = 0; 
//this is an aggregate, games usually step in fixed units of time. 
//this is usually because physics simulations can't handle too large of time steps. 
double unprocessedseconds = 0; 
double secondspertick = 1/60.0; 
int tickcount = 0; 
boolean ticked = false;  

while (gameIsRunning) { 
    //get elapsed nano seconds from the epoch (january 1st, 1970) 
    currenttime = System.nanoTime(); 
    //take difference of current time in nanos and previous time in nanos 
    passedtime = currenttime - previoustime; 
    previoustime = currenttime; 
    //divide to get the elapsed time in seconds. 
    unprocessedseconds += passedtime/1000000000.0; 

    while (unprocessedseconds > secondspertick) { 
     tick(); 
     unprocessedseconds -= secondspertick; 
     ticked = true; 
     tickcount++; 
     System.out.println(tickcount); 
     if (tickcount % 60 == 0) { 
      System.out.println(frames + " FPS"); 
      previoustime += 1000; 
      frames = 0; 
     } 
    } 
    if (ticked) { 
     render(); 
     frames++; 
    } 
    render();   
    frames++; 
} 

Удачи Джошу.

Edit:

У меня нет опыта работы с играми, используя одну нить для обновлений, и один для рендеринга. По этой причине я не могу давать советы по этим вопросам. Если у вас мало или вообще нет опыта многопоточности, я бы избегал этого, поскольку только сложные игры, вероятно, потребуют такого подхода, а многопоточность добавит множество проблем, с которыми вы, вероятно, не хотите иметь дело.

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

Написание многопоточного игрового движка было бы хорошим введением в потоки. Это может вас многому научить. Зависит от того, что вы хотите выбраться из этого.

Если вы делаете 2D-игру, я чувствую себя еще увереннее, что вам не понадобится один поток для обновления и один для рендеринга.

Если вы действительно хотите заниматься этим, here's the approach I'd take.

Вам не нужно больше, чем время цикла для управления рендеринга.

+0

Благодарим за быстрый ответ, но, к сожалению, я уже почти ничего не знал о коде. –

+0

Что конкретно вы хотели бы узнать? Я объяснил все важные моменты в опубликованном коде. Есть ли что-то еще, что вы хотели бы, чтобы я просмотрел? –

+0

Возможно, вам нужно сравнить многопоточные обновления игр и обновления одной нити? –

0

То, как я делаю свои двигатели, так же, как объяснялось ранее, я многопоточно. В принципе, если вы разделите работу по обработке и рисованию игры на два сегмента, она станет быстрее за счет использования большего количества ресурсов. Я делаю что-то вроде этого:

public class Engine implements Runnable { 

    //Sets classes 
    Engine tick = new Engine(true); 
    Engine render = new Engine(false); 
    Thread tickThread = new Thread(tick); 
    Thread renderThread = new Thread(render); 

    boolean job; 
    boolean isRunning = false; 

    long sleepTime = 5L; 

    public Engine(boolean job) { 
     //Sets what the thread does 
     this.job = job; 
    } 

    public static void startEngine() { 
     //Starts Engine 
     isRunning = true; 
     tickThread.start(); 
     renderThread.start(); 
    } 

    public void tick() { 
     //Process things 
    } 

    public void render() { 
     //Draw things 
    } 

    public void run() { 
     //Do engine base things 
     while(isRunning) { 
       if(job) { 
        tick(); 
       } else { 
        render(); 
       } 
       Thread.sleep(sleepTime); 
     } 
    } 

} 

Это далеко не передовой. Это просто пример того, как будет выглядеть простой многопоточный игровой движок. Честно говоря, я использовал этот точный код, когда начинал играть в игры. Это можно использовать, но необходимо внести некоторые корректировки в зависимости от того, для чего вы его используете. Я имею в виду, что позволяет сказать, что у вас есть объект, который движется и его визуализируется в одно и то же время. Если позиция объектов равна 50 и увеличивается, и метод визуализации рисует ее, тогда объект может перейти на 51, затем 52, а затем снова отобразить. Обычно обработка выполняется быстрее, чем чертеж. Другой пример: Допустим, у вас есть ArrayList и постоянно удаляются и добавляются к нему объекты. Иногда вы можете удалить объект так же, как метод рендеринга вот-вот нарисовать его и вызвать исключение нулевого указателя, потому что он пытается нарисовать что-то, что не существует. (Я использовал «if (object.get (i)! = Null)» и работал вокруг него таким образом) Надеюсь, это помогло хотя бы немного (два года спустя, LOL) и помогло вам получить основу того, Threading похожа (если вы этого еще не сделали).

Смежные вопросы