Como construir desfazer/refazer em um ambiente multiplayer

By | Junho 9, 2022

Desfazer/refazer é um recurso amplamente utilizado e essencial em softwares de criação de conteúdo atualmente. Construir essa funcionalidade em um ambiente colaborativo é teoricamente bastante simples, mas ainda é notoriamente difícil de construir. Isso está se tornando ainda mais complexo em um mundo multiplayer onde muitas pessoas podem fazer alterações em tempo real.

Mas o que torna o desenvolvimento do recurso desfazer/refazer tão complexo em um ambiente colaborativo e como lidar com isso de forma adequada?

Os principais desafios no desenvolvimento de um recurso de desfazer/refazer em um ambiente colaborativo incluem desfazer/repetir específico do cliente, exibir todas as alterações de outros usuários em tempo real e manter o estado de outros recursos no documento, exceto o conteúdo anterior.

Vamos mergulhar em como lidamos com desfazer/repetir multiplayer em Liveblocks.

O cancelamento deve ser local para cada cliente

Uma das abordagens mais usadas para lidar com desfazer/refazer é salvar os estados anteriores do aplicativo e retroceder por eles quando o usuário clica em desfazer. Essa técnica também é conhecida como padrão de memória e popularizou desfazer a implementação da história a popular Biblioteca Estadual Redux.

Uma visualização interativa mostrando a tela e seus vários estados ao longo do tempo.

Ao contrário de situações não colaborativas, manter o estado anterior do aplicativo desse usuário e retornar a ele não funciona. Isso ocorre porque vários usuários podem alterar o estado de um documento e desfazê-lo pode excluir o trabalho de outros.

Uma visualização interativa que mostra uma tela com dois usuários e como retroceder para estados anteriores leva ao conflito.

Ver? Alguém acabou de perder o emprego. Não é uma ótima experiência multiplayer e poderia ser muito pior em um cenário do mundo real, onde alguém poderia perder horas de seu tempo em um piscar de olhos.

Em vez de salvar estados anteriores, como fazem a maioria dos aplicativos não colaborativos, é melhor ter processos de desfazer/refazer específicos do cliente para que o usuário possa desfazer ou refazer apenas suas alterações. Para isso, podemos contar com formulário de comando onde o comando oposto de cada usuário é armazenado para ser aplicado posteriormente quando as pessoas usarem desfazer e refazer.

Uma visualização interativa que exibe uma tela e linhas do tempo individuais para cada usuário para evitar conflitos.

Estamos no caminho certo agora! No entanto, existem várias deficiências desse novo modelo baseado em comando; sob certas circunstâncias, os conflitos são difíceis de resolver.

Uma visualização interativa mostrando a tela e como alguns conflitos incomuns ainda podem ocorrer.

Mal, não é? Precisamos recriar o formulário excluído? Devemos ignorar a anulação? Devemos desfazer a próxima operação de pilha?

O Figma e o Google Slides resolvem da mesma maneira – nada acontece. No entanto, o Pitch resolve essa situação entrando em um estado inválido.

Um vídeo mostrando o que Figma, Google Slides e Pitch desfazem em formulários que não existem mais.

Felizmente, mostrar a presença de pessoas para coisas como seleções e cursores é algo que provavelmente não acontecerá no mundo real. Então nós da Liveblocks decidimos resolver isso da mesma forma que o Figma e o Google Slides fazem. Se você tem alguma ideia de como melhorar isso, Por favor nos informe!!

Os comandos intermediários devem ser agrupados

As operações que afetam vários usuários precisam ser exibidas em tempo real para manter todos em sincronia, caso contrário a sensação de estarmos juntos na mesma sala começa a piorar.

Uma visualização interativa que mostra duas telas paralelas e usuários que não veem as mesmas mudanças acontecendo em sincronia.

Em vez disso, o que queremos fazer é mostrar os estados de mediação à medida que eles acontecem.

Uma visualização interativa que mostra duas telas paralelas e usuários que veem as mesmas alterações o tempo todo.

Não é muito melhor? Mas com isso vem os desafios de desfazer/repetir. Vejamos o que aconteceria se alguém desfizesse as alterações depois de arrastar uma camada.

Uma visualização interativa que mostra a tela e o usuário tendo que desfazer várias vezes após mover a forma.

Certamente não é a melhor experiência. Imagine ter que desfazer 20 vezes para voltar para onde estava alguns segundos atrás. Isso seria tedioso!

Se estivéssemos em um ambiente não cooperativo usando um padrão de memória, isso poderia ser facilmente resolvido pulando estados de mediação. Em um sistema de desfazer/repetir baseado em comandos multijogador, também podemos resolver isso pausando e retomando um monte de histórico no momento certo. Mas para que isso funcione quando o usuário clicar em desfazer, precisamos aplicar todos os comandos que aconteceram nesse meio tempo de uma só vez.

Nesse cenário, pausaríamos o histórico quando abaixamos o mouse quando o usuário começa a arrastar e o retomamos quando arrastamos o mouse para cima quando ele termina de arrastar.

Uma visualização interativa que mostra a tela e o usuário tendo que desfazer apenas uma vez após mover a forma.

Como você pode ver na ilustração acima, o usuário pode retornar rapidamente ao seu estado inicial, mantendo tudo tranquilo.

Desfazer/refazer deve afetar mais do que o conteúdo do documento

Dependendo do caso de uso, o status de recursos como seleção do usuário, seleção da página do usuário, configuração de zoom do usuário etc. podem ser incluídos na pilha de desfazer/refazer para proporcionar uma ótima experiência. Um bom exemplo de um caso de uso real pode ser visto no Figma, onde os usuários podem navegar entre as páginas e depois desfazer para retornar a uma página selecionada anteriormente. Esses estados devem ser incluídos na pilha de histórico para que, ao desfazer ou repetir operações, a seleção ou configuração do usuário atual seja consistente com o estado atual da página.

Na ferramenta de design, por exemplo, se o usuário tiver selecionado anteriormente uma forma, você deseja garantir que a forma permaneça selecionada ao desfazê-la. O estado de seleção do usuário é vital para uma ótima experiência de desfazer/refazer, pois mantém o fluxo do sistema e os mantém totalmente imersos no trabalho que realizam.

Uma visualização interativa que mostra duas telas próximas uma da outra e usuários que perdem suas seleções depois que outros clicam em cancelar ou refazer.

A experiência acima não é ideal, não é? O usuário deve agora selecionar novamente a camada para selecionar a camada. Isso pode não parecer muito, mas quando você está criando algo, é importante que a ferramenta nunca atrapalhe o que o usuário está tentando criar.

Uma visualização interativa que mostra duas telas paralelas e usuários que retêm suas seleções depois que outros clicam em cancelar ou refazer.

Muito melhor – mas isso é difícil de implementar. É por isso que nós da Liveblocks criamos APIs que permitem que os desenvolvedores incluam recursos do usuário, como selecionar vários produtos de desfazer/refazer que eles criam. Isso garante que as pessoas que usam esses produtos possam ter a melhor experiência de classe que os mantém sempre atualizados.

Deixe o Liveblocks ser seu herói desconhecido

Embora tenhamos nos concentrado no uso de ferramentas criativas, vale a pena notar que esses padrões se aplicam a todos os produtos multijogador — portanto, agora você deve ter conhecimento suficiente para criar sua própria solução de desfazer/refazer multijogador.

Se você não quer o incômodo, também pode usar o Liveblocks diretamente e continuaremos trabalhando na construção da infraestrutura de colaboração em tempo real que sempre desejamos.

Ao usar Liveblocks, vários utilitários de histórico podem ser acessados room.history a partir de @liveblocks/client implemente desfazer/refazer multijogador da maneira certa em segundos: undo(), redo(), pause()e resume().

E se você usa Liveblocks com React, os mesmos utilitários podem ser acessados ​​via useHistory gancho de @liveblocks/react.

Na Liveblocks, criamos APIs para desenvolvedores criarem aplicativos multijogador e adoramos resolver problemas complexos. O Liveblocks está ao seu lado – deixe-nos ser seus campeões nos bastidores para que você possa se concentrar em seus principais recursos.

Se você é apaixonado por tornar a web mais colaborativa e adora esses tipos de desafios de engenharia e UX, nós empregamos!! Você também pode siga e contribua com o GitHub.

Deixe uma resposta

O seu endereço de email não será publicado.