如何在 LibGDX 中实现 Sugar ORM
How can I implement Sugar ORM in LibGDX
我们是一群法国学生。我们正在开发一款需要数据库的游戏。为了简化代码,我们使用 LibGdx。
但是,Sugar ORM 似乎并未与应用程序绑定。我们不能扩展 SugarRecord。
我放了AndroidManifest.xml和build.gradle的代码(模块:Android)。请问我们如何解决这个问题?
编辑:我们在 Android 文件夹中创建 类。 Sugar Orm 未在核心中定义。
<application
android:name="com.orm.SugarApp"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/GdxTheme">
<meta-data
android:name="DATABASE"
android:value="AppName.db" />
<meta-data
android:name="VERSION"
android:value="1" />
<meta-data
android:name="QUERY_LOG"
android:value="true" />
<meta-data
android:name="DOMAIN_PACKAGE_NAME"
android:value="com.AppName" />
build.gradle(模块:Android):
dependencies {
compile 'com.github.satyan:sugar:1.3'
//other dependencies
}
谢谢!
您好!我将做一些假设(如果他们错了请纠正我,我们将在下面完善答案):
- 问题是您正在尝试扩展
SugarRecord<T>
,而您的 compiler/IDE 看不到 class。
- 您尝试扩展
SugarRecord<T>
的 class 位于 core/src/...
,而不是 android/src/...
有什么问题?
当您编写软件时(如果是游戏则无关紧要),您需要注意如何将软件划分为多个部分以及这些部分如何交互。特别是,您希望能够知道对一个部分的更改何时会破坏另一部分。
libGDX 根据平台将其代码分成几部分(这就是为什么你有一个 core 项目和一个 desktop 项目和一个 android 项目)。 core 应该拥有独立于平台的所有代码(即无论您在 PC 还是移动设备上都是相同的东西),而您的其他项目负责平台具体内容。
Sugar ORM 是 android 特定的东西,因此您(正确地)将其放入 android gradle 项目依赖项中。但是,这意味着只有 android/src
文件夹下的代码知道 Sugar ORM 并可以使用它。我很确定这就是导致您出现问题的原因。问题是,你要保存的 classes 几乎肯定在 core/src
下,它们 属于 那里,那么我们如何解决它?
修复的简单方法
如果您只打算让您的程序在 android 上 运行(并且您的项目的截止日期很紧 :wink: ),您可以将依赖项从 Android gradle 项目到您的核心 gradle 项目。这将允许您在任何地方使用那些 classes,但是当您尝试构建 Desktop/iOS/HTML 项目时,这将意味着麻烦。
修复的正确方法
如果你想以 正确的 方式修复它(也许你疯狂的编码能力会给你的教授留下深刻印象),你需要使用一种叫做 Dependency Injection 的东西。依赖注入是当我们获取代码需要的东西,并在 运行 时将其提供给代码。这意味着我们可以即时决定是传递 android 数据库还是 iOS 数据库。正如 @Arctic45 在评论中所说,libGDX Wiki 对这项技术进行了简要概述,但我们会更详细地介绍。
对于这个例子,我假设一个简单的游戏 Monster
class 看起来像这样:
// Lives in core
public class Monster {
public String name; // prénom
public int hitpoints; // points de dommage
public Monster() {
this.name = "Tim the Unnamed";
this.hitpoints = 1;
}
public Monster(String name, int hitpoints) {
this.name = name;
this.hitpoints = hitpoints;
}
@Override
public String toString() {
return String.format("{name: '%s', hitpoints: %n}", this.name, this.hitpoints);
}
public void attack(Monster other) {
// Game specific logic...
}
}
现在我们希望能够将其保存到数据库中,但我们不知道它是 Android 数据库还是 iOS 数据库,甚至可能是一个数据库在网络上的某个地方(比如 Firebase)。我们如何处理?
我们所做的是给核心一个DatabaseWrapper
接口。这个接口提供了我们需要的方法,但不包括它们是如何实现的——它就像一个承诺。 core 可以计划使用这些方法,然后我们会在知道我们在哪个平台上后提供它们。下面是一个示例应用程序,它说明了这种技术:
// Lives in core
// Replace with your application
public class LibGDXTestbed extends ApplicationAdapter {
DatabaseWrapper database;
public LibGDXTestbed() { } // For platforms that don't have databases to inject.
public LibGDXTestbed(DatabaseWrapper database) {
this.database = database;
}
/**
* For demo purposes, add a new randomized monster to the database, then log a list of all the
* monsters created to date.
*/
@Override
public void create () {
if(database != null) {
createMonster();
printMonsters();
} else {
Gdx.app.error("WARNING", "No database provided. Load/Save Functionality Disabled.");
}
}
// Helper method
private void createMonster() {
// Create a set of names we can use for new monsters.
String[] names = {"Fred", "Mary", "Jean", "Tim"};
String randomName = new Array<String>(names).random();
int randomHP = MathUtils.random(100);
database.saveMonster(new Monster(randomName, randomHP));
}
// Helper method
private void printMonsters() {
for(Monster monster : database.getMonsters()) {
Gdx.app.log("DEBUG", monster.toString());
}
}
}
请注意,以上内容对 Sugar ORM 一无所知,也不对数据库的工作方式做出任何假设。
包装器本身看起来像这样:
// Located in core
public interface DatabaseWrapper {
public void saveMonster(Monster monster);
public List<Monster> getMonsters();
}
现在这有点做作(可以重构为更通用),但它说明了这一点。
接下来,我们创建实现此数据库所需的 android 特定代码。首先,我们将创建一个扩展 SugarRecord
的 SugarMonster
class(因为我们不想用我们的核心 Monster
class 本身来做):
// Lives in android/src
public class SugarMonster extends SugarRecord<SugarMonster> {
public String name; // prénom
public int hitpoints; // points de dommage
public SugarMonster() {
}
public SugarMonster(Monster monster) {
this.name = monster.name;
this.hitpoints = monster.hitpoints;
}
}
我们还需要一个 SugarWrapper
class 来实现我们的 DatabaseWrapper
class 在幕后使用 Sugar ORM:
// Lives in android/src
public class SugarWrapper implements DatabaseWrapper {
@Override
public void saveMonster(Monster monster) {
SugarMonster data = new SugarMonster(monster);
data.save();
}
@Override
public List<Monster> getMonsters() {
List<SugarMonster> records = SugarMonster.listAll(SugarMonster.class);
ArrayList<Monster> monsters = new ArrayList<>();
for(SugarMonster record : records) {
monsters.add(new Monster(record.name, record.hitpoints));
}
return monsters;
}
}
最后,我们需要更新 AndroidLauncher
class 以注入我们的数据库包装器:
// Lives in android/src
public class AndroidLauncher extends AndroidApplication {
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
initialize(new LibGDXTestbed(new SugarWrapper()), config);
}
}
奖金
另一件很酷的事情是,如果您以 "right" 方式实现它,它会为测试提供一些很酷的可能性。如果您想针对您的代码编写单元测试,您可以创建一个 TestWrapper
实现 DatabaseWrapper
并使用静态数据模拟数据库功能:
public class TestWrapper implements DatabaseWrapper {
List<Monster> monsters;
public TestWrapper() {
this.monsters = new ArrayList<>();
this.monsters.add(new Monster("Tim the Tester", 123));
}
@Override
public void saveMonster(Monster monster) {
this.monsters.add(monster);
}
@Override
public List<Monster> getMonsters() {
return this.monsters;
}
}
我们是一群法国学生。我们正在开发一款需要数据库的游戏。为了简化代码,我们使用 LibGdx。 但是,Sugar ORM 似乎并未与应用程序绑定。我们不能扩展 SugarRecord。
我放了AndroidManifest.xml和build.gradle的代码(模块:Android)。请问我们如何解决这个问题?
编辑:我们在 Android 文件夹中创建 类。 Sugar Orm 未在核心中定义。
<application
android:name="com.orm.SugarApp"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/GdxTheme">
<meta-data
android:name="DATABASE"
android:value="AppName.db" />
<meta-data
android:name="VERSION"
android:value="1" />
<meta-data
android:name="QUERY_LOG"
android:value="true" />
<meta-data
android:name="DOMAIN_PACKAGE_NAME"
android:value="com.AppName" />
build.gradle(模块:Android):
dependencies {
compile 'com.github.satyan:sugar:1.3'
//other dependencies
}
谢谢!
您好!我将做一些假设(如果他们错了请纠正我,我们将在下面完善答案):
- 问题是您正在尝试扩展
SugarRecord<T>
,而您的 compiler/IDE 看不到 class。 - 您尝试扩展
SugarRecord<T>
的 class 位于core/src/...
,而不是android/src/...
有什么问题?
当您编写软件时(如果是游戏则无关紧要),您需要注意如何将软件划分为多个部分以及这些部分如何交互。特别是,您希望能够知道对一个部分的更改何时会破坏另一部分。
libGDX 根据平台将其代码分成几部分(这就是为什么你有一个 core 项目和一个 desktop 项目和一个 android 项目)。 core 应该拥有独立于平台的所有代码(即无论您在 PC 还是移动设备上都是相同的东西),而您的其他项目负责平台具体内容。
Sugar ORM 是 android 特定的东西,因此您(正确地)将其放入 android gradle 项目依赖项中。但是,这意味着只有 android/src
文件夹下的代码知道 Sugar ORM 并可以使用它。我很确定这就是导致您出现问题的原因。问题是,你要保存的 classes 几乎肯定在 core/src
下,它们 属于 那里,那么我们如何解决它?
修复的简单方法
如果您只打算让您的程序在 android 上 运行(并且您的项目的截止日期很紧 :wink: ),您可以将依赖项从 Android gradle 项目到您的核心 gradle 项目。这将允许您在任何地方使用那些 classes,但是当您尝试构建 Desktop/iOS/HTML 项目时,这将意味着麻烦。
修复的正确方法
如果你想以 正确的 方式修复它(也许你疯狂的编码能力会给你的教授留下深刻印象),你需要使用一种叫做 Dependency Injection 的东西。依赖注入是当我们获取代码需要的东西,并在 运行 时将其提供给代码。这意味着我们可以即时决定是传递 android 数据库还是 iOS 数据库。正如 @Arctic45 在评论中所说,libGDX Wiki 对这项技术进行了简要概述,但我们会更详细地介绍。
对于这个例子,我假设一个简单的游戏 Monster
class 看起来像这样:
// Lives in core
public class Monster {
public String name; // prénom
public int hitpoints; // points de dommage
public Monster() {
this.name = "Tim the Unnamed";
this.hitpoints = 1;
}
public Monster(String name, int hitpoints) {
this.name = name;
this.hitpoints = hitpoints;
}
@Override
public String toString() {
return String.format("{name: '%s', hitpoints: %n}", this.name, this.hitpoints);
}
public void attack(Monster other) {
// Game specific logic...
}
}
现在我们希望能够将其保存到数据库中,但我们不知道它是 Android 数据库还是 iOS 数据库,甚至可能是一个数据库在网络上的某个地方(比如 Firebase)。我们如何处理?
我们所做的是给核心一个DatabaseWrapper
接口。这个接口提供了我们需要的方法,但不包括它们是如何实现的——它就像一个承诺。 core 可以计划使用这些方法,然后我们会在知道我们在哪个平台上后提供它们。下面是一个示例应用程序,它说明了这种技术:
// Lives in core
// Replace with your application
public class LibGDXTestbed extends ApplicationAdapter {
DatabaseWrapper database;
public LibGDXTestbed() { } // For platforms that don't have databases to inject.
public LibGDXTestbed(DatabaseWrapper database) {
this.database = database;
}
/**
* For demo purposes, add a new randomized monster to the database, then log a list of all the
* monsters created to date.
*/
@Override
public void create () {
if(database != null) {
createMonster();
printMonsters();
} else {
Gdx.app.error("WARNING", "No database provided. Load/Save Functionality Disabled.");
}
}
// Helper method
private void createMonster() {
// Create a set of names we can use for new monsters.
String[] names = {"Fred", "Mary", "Jean", "Tim"};
String randomName = new Array<String>(names).random();
int randomHP = MathUtils.random(100);
database.saveMonster(new Monster(randomName, randomHP));
}
// Helper method
private void printMonsters() {
for(Monster monster : database.getMonsters()) {
Gdx.app.log("DEBUG", monster.toString());
}
}
}
请注意,以上内容对 Sugar ORM 一无所知,也不对数据库的工作方式做出任何假设。
包装器本身看起来像这样:
// Located in core
public interface DatabaseWrapper {
public void saveMonster(Monster monster);
public List<Monster> getMonsters();
}
现在这有点做作(可以重构为更通用),但它说明了这一点。
接下来,我们创建实现此数据库所需的 android 特定代码。首先,我们将创建一个扩展 SugarRecord
的 SugarMonster
class(因为我们不想用我们的核心 Monster
class 本身来做):
// Lives in android/src
public class SugarMonster extends SugarRecord<SugarMonster> {
public String name; // prénom
public int hitpoints; // points de dommage
public SugarMonster() {
}
public SugarMonster(Monster monster) {
this.name = monster.name;
this.hitpoints = monster.hitpoints;
}
}
我们还需要一个 SugarWrapper
class 来实现我们的 DatabaseWrapper
class 在幕后使用 Sugar ORM:
// Lives in android/src
public class SugarWrapper implements DatabaseWrapper {
@Override
public void saveMonster(Monster monster) {
SugarMonster data = new SugarMonster(monster);
data.save();
}
@Override
public List<Monster> getMonsters() {
List<SugarMonster> records = SugarMonster.listAll(SugarMonster.class);
ArrayList<Monster> monsters = new ArrayList<>();
for(SugarMonster record : records) {
monsters.add(new Monster(record.name, record.hitpoints));
}
return monsters;
}
}
最后,我们需要更新 AndroidLauncher
class 以注入我们的数据库包装器:
// Lives in android/src
public class AndroidLauncher extends AndroidApplication {
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
initialize(new LibGDXTestbed(new SugarWrapper()), config);
}
}
奖金
另一件很酷的事情是,如果您以 "right" 方式实现它,它会为测试提供一些很酷的可能性。如果您想针对您的代码编写单元测试,您可以创建一个 TestWrapper
实现 DatabaseWrapper
并使用静态数据模拟数据库功能:
public class TestWrapper implements DatabaseWrapper {
List<Monster> monsters;
public TestWrapper() {
this.monsters = new ArrayList<>();
this.monsters.add(new Monster("Tim the Tester", 123));
}
@Override
public void saveMonster(Monster monster) {
this.monsters.add(monster);
}
@Override
public List<Monster> getMonsters() {
return this.monsters;
}
}