Magic Leap开发指南(6)-- Application File System

许多应用程序需要在Magic Leap One上读写用户数据、记录日志、游戏分数等。所以本篇文章展示如何使用应用程序文件系统存储用户数据。在应用程序的私有沙箱文件系统中,可以根据安全设置将文件保存到不同的位置。无论具体位置如何,任何应用程序的私有文件夹都是:

  • documents/C1/ (writable_dir_path_locked_and_unlocked)

  • documents/C2/ (writable_dir_path)

  • tmp/ (tmp_dir_path)

关于这部分我们需要掌握:

  1. 使用Lumin Runtime Editor将UITextEdit对象(用于输入文本的区域)应用到项目中。

  2. 从系统键盘接收输入数据。

  3. 检索应用程序将写入的路径。

  4. 从文件中读取数据。

  5. 当用户点击控件的bumper按钮时,将数据存储到文件中。

实现效果预览:

 

Step 1: 在Lumin Runtime Editor设置项目

 

  1. Package Manager启动Lumin Runtime Editor

  2. 在Lumin Runtime Editor中创建一个新项目。

  3. 将其命名为AppStorage,然后单击Create Project。

  4. 在场景层次结构下,右键单击根节点。

  5. 单击 Insert > UI Nodes > UITextEdit。UITextEdit提供了一种使用系统键盘输入文本的简单方法。

  6. 将UITextEdit的名称更改为TextEdit,并应用以下设置。

  7.  

  8. 设置Scale值为 (3.0, 3.0, 3.0)。

  9. 保存场景。

场景效果如下图所示:

 

Step 2: 在Visual Studio (Windows)设置项目

  1. 启动Microsoft Visual Studio

  2. 点击File > New > Import Magic Leap Mabu Projects

  3. 在导入Magic Leap项目中单击Browse,并选择Lumin Runtime Editor项目的文件夹

  4. 点击Import

在解决方案资源管理器下,AppStorage项目应该如下图所示:

 

Step 3: 在Visual Studio Code (Windows / macOS)设置项目

  1. 打开Visual Studio Code。

  2. 点击左侧的图标

  3. 在Lumin SDK窗口标题中,单击图标,设置Lumin SDK的路径(如果还没有设置)。通常是:/Users/user/MagicLeap/mlsdk/v0.x.x。

  4. 在签名证书窗口标题中,单击图标,然后设置.cert包签名证书文件的路径(如果还没有设置)。

  5. 回到Lumin Runtime Editor,在项目菜单上,单击Code Generation > Open code in External Editor

  6. 出现下面窗口时单击OK即可。

项目文件夹如下图:

Step 4:脚本处理

Source Files文件夹(或Visual Studio code中的code/src文件夹)下,打开AppStorage.cpp脚本。Lumin Runtime已经为我们生成了大量的代码。下面我们将重点讨论需要修改的部分。

Directives, Namespaces and Globals

在脚本的顶部添加以下指令,在最后一个#include指令之后:

#include <lumin/event/KeyInputEventData.h>
#include <lumin/ui/UiKit.h>
#include <fstream>

接下来,添加以下指令和声明:

using namespace lumin;
using namespace lumin::ui;
using namespace std;

namespace {
	UiTextEdit* textEdit;
}

 

  • 命名空间lumin、lumin::ui和std简化了代码。

  • UiTextEdit* textEdit会将我们场景的TextEdit链接到脚本中的textEdit对象。

glm::vec3 AppStorage::getInitialPrismSize()更改为:

const glm::vec3 AppStorage::getInitialPrismSize() const {
  return glm::vec3(2.0f, 2.0f, 0.5f);
}

初始化

int AppStorage::init() {

  ML_LOG(Debug, "AppStorage Initializing.");

  createInitialPrism();
  lumin::ui::Cursor::SetScale(prism_, 0.03f);
  spawnInitialScenes();
  //cast the TextEdit node
  textEdit = static_cast<UiTextEdit*>(prism_->findNode("TextEdit", prism_->getRootNode()));

  //retrieve the writeable path and the prepare the data.txt file to be read
  ifstream myfilein(BaseApp::getWritablePath() + "data.txt");
  string line, text;
  text = "";

  //if the file is open read its contents line by line
  if (myfilein.is_open()) {
	  while (getline(myfilein, line)) {
		  text += line + '\n';
	  }
	  myfilein.close();
  }

  //set the text of the textEdit to become the file's contents
  textEdit->setText(text);
  
  return 0;
}
  • createInitialPrism()创建Prism。

  • ui::Cursor::SetScale(prism_, 0.03f)设置Prism (prism_)光标的比例。

  • spawnInitialScenes() 实例化我们唯一的场景。

  • textEdit = static_cast<UiTextEdit*>(prism->findNode("TextEdit", prism->getRootNode()))表示并转换场景的textEdit节点。

  • BaseApp::getWritablePath()检索应用程序写入的私有路径。

  • while (getline(myfilein, line))循环读取文件的内容,并逐渐将它们添加到文本字符串中。

  • textEdit->setText(text)将textEdit的文本更改为存储在文件中的内容,如果文件不存在,则更改为空字符串。

注意:不要在应用程序中硬编码路径。路径会随着安全设置的不同而变化。使用getWritablePath()回调即可。

bool AppStorage::eventListener(lumin::ServerEvent* anEvent)方法在运行时监听事件并接收bumper按钮点击。

bool AppStorage::eventListener(lumin::ServerEvent* anEvent) {
	if (anEvent->isInputEventType()) {
		InputEventData* inputEventData = static_cast<InputEventData*>(anEvent);
		KeyInputEventData* keyEventData = static_cast<KeyInputEventData*>(inputEventData);
		if (keyEventData->keyCode() == input::KeyCodes::AKEYCODE_EX_BUMPER) {
			ofstream myfileout(BaseApp::getWritablePath() + "data.txt");
			myfileout << textEdit->getText() + "\n";
			myfileout.close();
		}
	}

	return false;
}

此方法将每个事件捕获为ServerEvent* anEvent。如果事件是InputEvent(引用输入的事件),则执行以下操作:

  • 将InputEventData转换为InputEventData

  • 将相关的KeyInputEventData转换为keyEventData

  • 检查keyEventData是否引用AKEYCODE_EX_BUMPER事件。

如果bumper被点击了,则:

  • ofstream myfileout(BaseApp::getWritablePath() + "data.txt")备编写相同的data.txt文件。

  • myfileout << textEdit->getText() + "\n"将用户的输入写入文件。

  • myfileout.close()关闭文件。

Step 5:Build

构建签名.mpk文件并将其安装到设备上的过程取决于使用的IDE。

Visual Studio (Windows)

  1. 使用USB-C电缆将Magic Leap One连接到计算机。

  2. 选择调试配置和ML目标。

  3. 切换到Magic Leap调试器。

  4. 运行该应用程序。

  5. 如果需要调试,单击Continue继续执行。

Visual Studio Code (Windows / macOS)

  1. 把你的设备插入电脑。

  2. 单击左边的图标

  3. 将调试目标设置为Lumin OS Debug

  4. 单击三角形图标开始调试。

Step 6:总结

  1. 如果这是你第一次在设备上部署应用程序,则会打开一个安装证书的通知。在这种情况下,接受证书安装应用程序即可。

  2. 一旦证书被接受,地球模型就会出现。用OK手势让它旋转,或者用手指手势(看到你指尖上的一个小立方体)。长时间按住触发器移动整个Prism的位置。

  3. 如果应用程序无法识别手的姿势,关闭所有应用程序(长时间按住Home按钮),重新启动我们的应用程序。

  4. 如果你仍然什么也没看到,看看你周围的一切,因为设备启动时的方向决定了它在世界上的起始方向。

#include <AppStorage.h>
#include <lumin/node/RootNode.h>
#include <lumin/ui/Cursor.h>
#include <ml_logging.h>
#include <scenes.h>
#include <PrismSceneManager.h>

#include <lumin/event/KeyInputEventData.h>
#include <lumin/ui/UiKit.h>
#include <fstream>

using namespace lumin;
using namespace lumin::ui;
using namespace std;


//using namespace lumin
namespace {
	UiTextEdit* textEdit;
}

AppStorage::AppStorage() {
  ML_LOG(Debug, "AppStorage Constructor.");

  // Place your constructor implementation here.
}

AppStorage::~AppStorage() {
  ML_LOG(Debug, "AppStorage Destructor.");

  // Place your destructor implementation here.
}

const glm::vec3 AppStorage::getInitialPrismSize() const {
  return glm::vec3(2.0f, 2.0f, 0.5f);
}

void AppStorage::createInitialPrism() {
  prism_ = requestNewPrism(getInitialPrismSize());
  if (!prism_) {
    ML_LOG(Error, "AppStorage Error creating default prism.");
    abort();
  }
  prismSceneManager_ = new PrismSceneManager(prism_);
}

int AppStorage::init() {

  ML_LOG(Debug, "AppStorage Initializing.");

  createInitialPrism();
  lumin::ui::Cursor::SetScale(prism_, 0.03f);
  spawnInitialScenes();
  //cast the TextEdit node
  textEdit = static_cast<UiTextEdit*>(prism_->findNode("TextEdit", prism_->getRootNode()));

  //retrieve the writeable path and the prepare the data.txt file to be read
  ifstream myfilein(BaseApp::getWritablePath() + "data.txt");
  string line, text;
  text = "";

  //if the file is open read its contents line by line
  if (myfilein.is_open()) {
	  while (getline(myfilein, line)) {
		  text += line + '\n';
	  }
	  myfilein.close();
  }

  //set the text of the textEdit to become the file's contents
  textEdit->setText(text);
 
  return 0;
}

int AppStorage::deInit() {
  ML_LOG(Debug, "AppStorage Deinitializing.");

  // Place your deinitialization here.

  return 0;
}

void AppStorage::spawnInitialScenes() {

  // Iterate over all the exported scenes
  for (auto& exportedSceneEntry : scenes::externalScenes ) {

    // If this scene was marked to be instanced at app initialization, do it
    const SceneDescriptor &sd = exportedSceneEntry.second;
    if (sd.getInitiallySpawned()) {
      lumin::Node* const spawnedRoot = prismSceneManager_->spawn(sd);
      if (spawnedRoot) {
        if (!prism_->getRootNode()->addChild(spawnedRoot)) {
          ML_LOG(Error, "AppStorage Failed to add spawnedRoot to the prism root node");
          abort();
        }
      }
    }
  }
}

bool AppStorage::updateLoop(float fDelta) {

  // Place your update here.

  // Return true for your app to continue running, false to terminate the app.
  return true;
}

bool AppStorage::eventListener(lumin::ServerEvent* anEvent) {
	if (anEvent->isInputEventType()) {
		InputEventData* inputEventData = static_cast<InputEventData*>(anEvent);
		KeyInputEventData* keyEventData = static_cast<KeyInputEventData*>(inputEventData);
		if (keyEventData->keyCode() == input::KeyCodes::AKEYCODE_EX_BUMPER) {
			ofstream myfileout(BaseApp::getWritablePath() + "data.txt");
			myfileout << textEdit->getText() + "\n";
			myfileout.close();
		}
	}

	return false;
}

公众号[三次方AIRX]:三次方•数字化人才在线教育平台。帮助Z时代大学生和0-5年职场人获得混合现实、人工智能、游戏开发、大数据等能力。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值