通过EEG控制视频游戏(第2部分)

这篇文章是上一篇文章的继续:通过EEG进行视频游戏控制(第1部分),并且紧随其后,我们将继续从Emotiv耳机中获取数据。

获得心理命令的过程

正如cortex文档所指出的,我们需要以下过程来获取给定动作的心理命令:

  1. 验证用户
  2. 建立会议
  3. 订阅sys事件
  4. 设置训练中立的动作
  5. 接受或拒绝培训
  6. 订阅com活动

验证用户

要接收授权令牌,我们需要定义一个Authorize方法,该方法将clientize方法和客户端密码作为参数,将authorize方法发送到皮质。

 无效Authorize() 
{
字典 authorizeDictionary =新字典(); authorizeDictionary.Add(“ client_id”,CLIENT_ID);
authorizeDictionary.Add(“ client_secret”,CLIENT_SECRET);
w.SendString(
CortexJsonUtility.GetMethodJSON

“授权”,
(int)MethodsID.Authorize,//将授权添加到枚举中
authorizeDictionary
));
}

发送登录方法后,在初始化协程中添加对此函数的调用。

  …授权();… 

要接收授权令牌,我们需要创建一个新的字符串变量,并将另一种情况添加到GetReply开关。

  …字符串_auth;…案例方法ID.Authorize: 
_auth = CortexJsonUtility.GetFieldFromJSONObject(replyObj,“ _auth”);
打破;

这会在来自皮质的回复中在结果对象内寻找一个名为“ _auth”的字段,并将其分配给_auth变量,该变量将在下一部分中用于创建会话。

建立会议

是时候创建一个会话并告诉皮质开始管理来自Emotiv的数据流了。

  IEnumerator CreateSession() 
{
而(_auth == null)
{
收益率返回0;
}

Dictionary sessionDictionary = new Dictionary ();
sessionDictionary.Add(“ _ auth”,_auth);
sessionDictionary.Add(“ status”,“ open”);
w.SendString(
CortexJsonUtility.GetMethodJSON

“ createSession”,
(int)MethodsID.CreateSession,//将CreateSession添加到枚举
sessionDictionary
));
}

可以在Initialize协程内部的Authorize方法之后调用此协程,并在为_auth变量分配非null值后立即向皮质发送createSession方法。

在这一点上,如果我们玩游戏并输入登录按钮,则只有在连接了将在其上创建会话的Emotiv设备的情况下,它才起作用。

订阅事件

要从会话中接收数据,您必须使用subscribe方法对其进行订阅。 除了要订阅的会话外,还必须指定感兴趣的流。每个流表示来自耳机的不同类型的数据,这一次,我们将预订2个可用流。 “ sys”流是用于了解训练阶段的系统事件,而“ com”流是每次在耳机经过训练后感知到某项动作时都会调用的心理命令事件“ com”。

  IEnumerator SubscribeStreams() 
{
而(_auth == null)
{
收益率返回0;
}
w.SendString(
CortexJsonUtility.GetSuscribtionJson

(int)MethodsID.Suscribe,
_auth,
新字符串[] {“ sys”,“ com”}
));
}

可以在初始化协程结束时启动CreateSession协程之后调用此协程。

训练动作

现在该创建另一个画布来训练我们的两个动作(中立和正确)了。

这是我们代码的主要部分,由两部分组成,一个是开始对某个动作进行训练的初始代码,最后一个是接受或拒绝该训练的代码。

这个想法是,用户单击将要训练的动作,然后只要动机决定它是成功还是失败,用户就会想到该动作,然后会弹出一条消息,让用户接受或拒绝该操作,如果用户接受,则该给定操作的“ com”事件将开始弹出。

我们需要的第一件事是2个布尔变量来控制训练过程,isTrainingComplete将避免同时调用多个训练消息,而isTrainingComplete将使协程保持该过程直到用户完成训练。 因此,我们将它们添加到“静态字段”部分中。

 静态布尔isTrainingDone;静态布尔isTrainingComplete = true; 

由于游戏将在成功完成训练后显示一条带有2个选项的消息,并且根据选项,将使用不同的参数调用新的训练方法,因此我们应该定义一个方法,以仅通过传递的参数来发送消息。

 公共无效的SendTrainMessage(Dictionary 参数) 
{
w.SendString(
CortexJsonUtility.GetMethodJSON

“训练”,
(int)MethodsID.Training,
参数
));
}

然后,我们为整个过程定义一个协程,该协程需要2个字符串参数,检测类型和要训练的动作。

  IEnumerator InitializeTraining(字符串detectionType,字符串操作) 
{
}

所有代码将位于if语句内部,该语句将检查isTrainingComplete是否为true,因此不再同时调用其他训练方法。

  …如果(isTrainingComplete) 
{
}…

现在,我们使用训练方法所需的参数创建字典。

  Dictionary  initialParams = new Dictionary (); initialParams.Add(“ _ auth”,_auth); 
initialParams.Add(“ detection”,detectionType);
initialParams.Add(“ action”,action);
initialParams.Add(“ status”,“ start”);

之后,我们调用之前创建的方法,将控制变量设置为false并暂停该过程,直到完成训练为止。

  SendTrainMessage(initialParams); 
isTrainingDone = false;
isTrainingComplete = false;
而(!isTrainingDone)
收益率返回0;

要知道训练是成功还是失败,Cortex发送可以在GetReply函数上处理的“ sys”事件。

 JSONObject sysArray = replyObj.GetField(“ sys”); if(sysArray) 
{
开关(sysArray [1] .str)
{
案例“ MC_成功”:
isTrainingDone = true;
打破;
情况“ MC_Completed”:
isTrainingComplete = true;
打破;
情况“ MC_Rejected”:
isTrainingComplete = true;
打破;
案件 ”
MC_Failed “:
isTrainingComplete = true;
打破;
}
}

在重新获得InitializeTraining协程之前,我们将需要一个类来处理向用户显示的消息,该消息使用户可以选择拒绝或接受培训,创建新脚本并添加以下代码。

 使用System.Collections; 
使用System.Collections.Generic;
使用UnityEngine;
使用UnityEngine.UI;
公共类MessageQuestionSelection
{
///
///显示带有给定文本的菜单,并让用户按下两个按钮之一。
///这些按钮被分配给作为
参数。 最后,使用作为参数传递的适当参数调用委托上的此函数。
///
///
///要显示的文字。
///
///包含将在按下按钮后执行的功能的委托。
///
///带有执行委托中的方法所需的accept参数的字符串数组。
///
///具有在委托中执行方法所需的带有拒绝参数的字符串数组。
公共静态无效ShowMessageWithSelection(字符串messageQuestion,DelegateSelectionMenu答案,字典 acceptParams,字典 rejectParams)
{
GameObject messageWithSelection = GameObject.Find(“ Canvas_Selection_Message”);
if(messageWithSelection == null)
{
messageWithSelection =
GameObject.Instantiate(Resources.Load(“ Canvas_Selection_Message”,typeof(GameObject)))作为GameObject;
messageWithSelection.name =“ Canvas_Selection_Message”;
}
messageWithSelection.SetActive(true); messageWithSelection.transform.GetChild(0)
.GetChild(0).GetComponent ()。text = messageQuestion;
messageWithSelection.transform.GetChild(0)
.GetChild(1).GetComponent

要使用该类,需要构造一个预制件并将其保存在名为Resources的文件夹中,该文件夹名为Canvas_Selection_Message,它应具有以下子级。

它还定义了一个委托,将SendTrainMessage方法分配给每个按钮,这就是我们现在要做的事情,在InitializeTraining协程的最后,创建DelegateSelectionMenu的实例并分配该方法。

  DelegateSelectionMenu RepresentativeSelection = SendTrainMessage; 

需要构建拒绝和接受参数,以便我们可以在MessageQuestionSelection类中调用ShowMessageWithSelection函数。

  …Dictionary  acceptParams =新字典(); acceptParams.Add(“ _ auth”,_auth); 
acceptParams.Add(“ detection”,detectionType);
acceptParams.Add(“ action”,action);
acceptParams.Add(“状态”,“接受”);
字典 rejectParams =新字典(); rejectParams.Add(“ _ auth”,_auth);
rejectParams.Add(“ detection”,detectionType);
rejectParams.Add(“ action”,action);
rejectParams.Add(“状态”,“拒绝”);
MessageQuestionSelection.ShowMessageWithSelection

“您接受“ + action.ToUpper()+”吗?
proxySelection,
acceptParams,
拒绝参数
);

这将完成培训过程,现在我们需要一个公共功能来在按下按钮时启动协程。

 公共无效训练(弦乐动作) 
{
StartCoroutine(InitializeTraining(“ mentalCommand”,操作));
}

最后,在成功创建会话之后,需要显示火车面板,因此将以下变量添加到EmotivTest类,并在检查器上为其分配画布。

  [SerializeField] 
GameObject trainingCanvas;

在开始场景之前,仅登录画布应该处于活动状态。

在GetReply开关上,为MethodsID:CreateSession添加一个外壳,然后可以激活画布。

 案例MethodsID.CreateSession: 
trainingCanvas.SetActive(true);
打破;

还请记住,当用户注销时隐藏面板,在HandleLogoutSuccess函数中添加该代码。

 私有void HandleLogoutSuccess() 
{
loginCanvas.SetActive(true);
userInfoCanvas.SetActive(false);
trainingCanvas.SetActive(false);
}

在动作上移动对象

我们几乎完成了整个过程,直到这一点,皮质每次注册一个已经受过训练的动作时都会发送一条消息,类似这样。

  {“ com”:[“ push”,0.673717498779297],“ sid”:“ 46d18597–7034–40ab-9d6e-d617a89a24ce”,“时间”:245.356536865234} 

因此,剩下的就是在每次注册动作时告诉可移动对象移动。

在这里,一个事件将非常有用,将其添加到EmotivTest类中。

  //事件公共委托void MentalCommandEvent(string action);公共静态事件MentalCommandEvent OnMentalCommandEvent; 

并在GetReply函数中收到“ com”事件时调用它,在sys行之后添加以下行。

  JSONObject comArray = replyObj.GetField(“ com”); if(comArray) 
{
如果(OnMentalCommandEvent!= null)
{
OnMentalCommandEvent(comArray [0] .str);
}
}

现在创建一个Player类,并将其分配给多维数据集或要作为组件移动的任何对象。

 使用System.Collections; 
使用System.Collections.Generic;
使用UnityEngine;
公共阶层球员:MonoBehaviour
{
私人无效OnEnable()
{
EmotivTest.OnMentalCommandEvent + =移动;
}
私人无效OnDisable()
{
EmotivTest.OnMentalCommandEvent-=移动;
}
无效移动(字符串方向)
{
如果(方向==“正确”)
transform.Translate(Vector3.right * Time.deltaTime);
否则if(transform.position.x> 0)
transform.Translate(Vector3.left * Time.deltaTime);
}
}

在其上,您为刚创建的EmotivTest事件分配了一个函数,该函数接收作为字符串的mental命令,在本例中为单词“ right”。 如果收到的动作等于“向右”,则此函数将对象向右移动,如果不是,则对象将开始移回到初始位置。

这就是关键时刻,连接Emotiv耳机,运行游戏,登录,训练中立和正确的动作至少3次,然后在场景中移动立方体。

如果要查看代码的最终版本,可以在Cortex-Implementation-For-Unity存储库中的Cortex / Example / Scripts文件夹中找到它。