Я не мог понять ваш первый абзац, поэтому мой ответ касается вашего второго абзаца, если я правильно его понял. ;)
Swing следует модели с одним потоком. Таким образом, вы должны обновлять пользовательский интерфейс из потока рассылки событий (EDT). Этот поток также отвечает за доставку событий в ваш код, отсюда и название. Если вы постоянно обновляете пользовательский интерфейс в цикле, это приведет к тому, что EDT будет занят и заблокирован. Конечным эффектом будет пользовательский интерфейс, который не реагирует на пользовательские события. Это потому, что события ставятся в очередь, и EDT может выбрать их и доставить в ваш код, когда он станет бесплатным.
Игры обычно сталкиваются с таким сценарием. Вы могли заметить, что игры обычно имеют одну фиксированную частоту обновления, которую они называют FPS (кадров в секунду). Обычно достаточно поддерживать 60 кадров в секунду. То есть вам нужно отрисовывать свой пользовательский интерфейс 50 раз в секунду, но сейчас кажется, что ваш цикл рендеринга (который обновляет пользовательский интерфейс) работает непрерывно.
Вам нужно, чтобы отдельный поток постоянно работал, который отвечает за отрисовку пользовательского интерфейса. Это должно отрисовываться в буфер (Image
). А затем вызовите repaint()
для элемента пользовательского интерфейса, который нужно обновить. paintComponent()
этого элемента пользовательского интерфейса необходимо переопределить, чтобы он мог копировать изображение в буфер Image
и рисовать его в графическом контексте.
Теперь начинается настоящая хитрость. Цикл, который вызывает repaint()
, должен выполнить некоторые арифметические действия, чтобы убедиться, что он не выходит за пределы отрисовки 60 раз, т. е. зацикливания 60 раз в секунду. Если и когда это произойдет, то он должен вызвать Thread.sleep(sleepTime)
, где sleepTime
— количество миллисекунд, оставшихся в секунду после 60-кратного цикла. Может случиться так, что вашему циклу потребуется более секунды для завершения 60 итераций, тогда не просто переходите к следующей итерации, а вызываете Thread.yield()
. Это даст другим потокам возможность использовать ЦП, например. может быть, ваш EDT. Чтобы усложнить ситуацию, не продолжайте уступать всегда, поэтому, возможно, стоит добавить некоторую логику, чтобы убедиться, что уступка выполняется только x последовательных раз. Этот последний сценарий должен быть очень редким, если вообще. Этот сценарий означает, что система находится под большой нагрузкой.
Помните, что repaint()
является потокобезопасным и может вызываться из любого потока. Он планирует вызов paint()
по восточноевропейскому времени. Таким образом, вызов repaint()
не гарантирует краску. Таким образом, вы можете поэкспериментировать с различными значениями FPS, чтобы найти то, которое вам подходит.
Кстати, прием рендеринга в память Image
технически называется двойным буфером. Это дает нам возможность рендерить красивую плавную анимацию.
Дальнейшее чтение:-
person
AppleGrew
schedule
28.01.2012