R Shiny 可以同时为两个用户显示不同的视图,相互交互吗?

Can R Shiny display different views for two simultaneous users, interacting with one another?

通常情况下,Shiny 服务器会为单个用户生成单独的实例,以便多个人可以同时单独使用同一个应用程序。 shows how to make a simple multi-user chat room using Shiny, and this answer 解释多个用户如何通过直接 IP 连接到同一个会话。我让聊天示例正常工作,两个用户都可以在发送消息时立即看到消息,因此可以互相聊天。

我想知道是否有可能将 Shiny 用于一种(实验)场景,在这种场景中,两个用户相互交互,会在他们的屏幕上看到 不同的 GUI 元素各自的屏幕和不同的输出,取决于轮到谁 "play"。例如,如果 user1 是 "starting player",他会看到三个按钮并单击其中一个,会为 user2 弹出一个相关图像(而不是 user1),user2 单击一个按钮(他认为与图像匹配) ,然后为user1弹出相关图片,user1点击"correct"/"incorrect"按钮发送反馈;他们不应该看到谁点击了哪个按钮,也不应该看到另一个人看到的图像(如果实际上隐藏 GUI 元素很棘手,间歇性地使它们变灰 out/disabling 也很好,只要他们看不到另一个人在做什么) .

或更形象地说明:

round 1
user1                   user2
director                guesser

what they see, step by step:
1. [three buttons]      [ (blank) ]
2. [clicks one]         [ ]
3  [ ]                  [sees an image & 3 buttons]
4. [ ]                  [clicks a button]
5. [sees image,2 butns] [ ]
6. [clicks button]      [ ]
7. [ ]                  [sees the message "correct" or "incorrect"]

round 2
user1                   user2
guesser                 director
1. [ ]                  [three buttons]
...
...

下一轮他们交换角色,以此类推,进行多轮。

我见过使用 Javascript(jsPsych、nodegame)和 Python(psychopy、oTree)实现的类似实验场景,但我希望了解是否可以在有光泽,如果有,如何。

几年前,当我将“风险”(棋盘游戏)实现为一个闪亮的应用程序时,我遇到了同样的挑战。

简要概述我当时是如何处理它的:

如果您在服务器函数中使用 session 参数,您可以在该会话中/为该用户创建一个 local/secret reactiveValue()

接下来您可以在服务器功能之外设置 reactiveValues() 以获取跨会话访问的“全局信息”。

后一种方法可能更令人惊讶,因为我们通常“被迫”在服务器函数中定义 reactive 行为。但它有效,请参见下面的示例。

可重现的例子:

library(shiny)

ui <- fluidPage({
  uiOutput("moreControls")
})

global <- reactiveValues(info  = "public info: I can be seen by everyone", amountUser = 0, userIdToPlay = 1)

server <- function(input, output, session) {
  local <- reactiveValues(secret = paste0("My secret number is ", sample(6, 1)))
  
  observe({
    isolate(global$amountUser <-  global$amountUser + 1)
    isolate(local$userId <- global$amountUser)
  })
  
  observeEvent(input$finish,{
    global$userIdToPlay <- 3 - global$userIdToPlay # assumes two players (for MVE)
  })
  
  output$moreControls <- renderUI({
    global$userIdToPlay
    isolate({
      if(local$userId == global$userIdToPlay){
        return(
          tagList(
            h2("my time to play"),
            selectInput("a", "b", letters),
            actionButton("finish", "finish")
          )
        )
      }else{
        return(
          h2("not my time to play")
        )
      }
    })
  })
  
}
shinyApp(ui, server)