Как анимировать картинку, не заставляя ее мерцать, используя wxWidgets в erlang?

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

"draw_asteroids" вызывается каждые 100 мс, так как я хочу, чтобы движение изображения было как можно более непрерывным, плюс у меня есть много других элементов, которые также перемещаются на экране (код похож на космический корабль).

мой код:

start->
Wx = wx:new(),
Frame = wxFrame:new(Wx, -1, "Main Game Frame", [{size, {?max_x, ?max_y}}]),    
MenuBar = wxMenuBar:new(),
wxFrame:setMenuBar(Frame, MenuBar),
Panel = wxPanel:new(Frame),
wxFrame:connect(Panel, paint),
Image = wxImage:new("asteroid.png"),
wxFrame:show(Frame),
loop(Panel, {0,0}, Image).



loop(Panel, {X,Y},  Image)->
receive
after 100 ->
{NewX,NewY} =claculateNewPosition({X,Y}),
clearScreen(Frame, Panel),
draw_asteroids(Panel, {NewX,NewY}, Image),
loop(Panel, {NewX,NewY},  Image)
end.



draw_asteroids(Panel, {X, Y}, Image) ->

Pos = {round(X), round(Y)},
ClientDC = wxClientDC:new(Panel),
Bitmap = wxBitmap:new(Image),
wxDC:drawBitmap(ClientDC, Bitmap, Pos),
wxBitmap:destroy(Bitmap),
wxClientDC:destroy(ClientDC).


clearScreen(Frame, Panel)->

NewPanel = wxPanel:new(Frame),
wxWindow:setSize(Frame, {?max_x+1, ?max_y+1}),
wxWindow:setSize(Frame, {?max_x, ?max_y}),
NewPanel.

1 ответ

Я решил это в Windows и Linux, выполнив следующие действия:

в функции init: создайте растровое изображение ожидаемого размера, а memoryDC сформируйте его:

Bitmap = wxBitmap:new(W,H),
MemoryDC = wxMemoryDC:new(Bitmap),
...

Я также определяю область для размещения растрового изображения и подключаю его к какому-либо событию, по крайней мере, для рисования:

Panel = wxPanel:new(Board, [{size,{W,H}}]),
wxPanel:connect(Panel, paint, [callback]),
wxPanel:connect(Panel, left_up, []),
wxPanel:connect(Panel, right_down, []),
wxPanel:connect(Panel, middle_down, []),

Я использую для хранения запроса на изменение в списке, который будет обработан позже.

затем я использую внешнее событие для обновления экрана, которое запускает обновление окна. Параметр {eraseBackground,false} очень важен, чтобы избежать мерцания:

do_refresh(Cb,Z,PMa,PFe,PM,BMa,BFe,BM,P,MemoryDC) ->
    wx:batch(fun ()  ->
        Cb1 = lists:reverse(Cb),
        [cell(MemoryDC,PMa,PFe,PM,BMa,BFe,BM,State,Cell,Sex,Z) || {State,Cell,Sex} <- Cb1],
        wxWindow:refresh(P,[{eraseBackground,false}])
    end).

Я использую это внешнее событие по двум причинам:

  • есть регулярная частота обновления, которую я осваиваю
  • разрешить многим процессам самостоятельно обновлять растровое изображение (сохраняя запрос в списке)

Я использую внешний таймер, который вызывает функцию обновления, которая собирает список изменений, запрошенных другими процессами, выполняет их по порядку в memoryDC, а затем запускает событие рисования с помощью функции window:refresh/2. Вся функция выполняется в пакете для улучшения производительности.

А в событии перерисовки я просто "перетаскиваю" панель (обратите внимание, что эта функция может обновлять часть панели, чтобы сократить время выполнения, сначала я не использовал эту оптимизацию, и результат был достаточно быстрым и плавным, поэтому я сохранил мой исходный код):

handle_sync_event(#wx{event = #wxPaint{}}, _wxObj, #state{panel=Panel, memoryDC=MDC, w=W, h=H}) ->
    redraw(Panel,MDC,W,H).

redraw(Panel, MemoryDC, W, H) ->
    DC = wxPaintDC:new(Panel),  
    wxDC:blit(DC,{0,0},{W,H},MemoryDC,{0,0}),
    wxPaintDC:destroy(DC).

Кажется, необходимо создать и уничтожить PaintDC.

Этот код не управляет изменением размера окна (в моем приложении это не имеет смысла), но его легко расширить.

Другие вопросы по тегам