鸿蒙开源第三方组件—游戏框架JustWeEngine

开发 项目管理
JustWeEngine是托管在Gitee 的一个开源的Ohos原生开发框架,可以让Ohos的开发人员非常便捷,无需切换语言和编译器的制作Ohos原生游戏。

[[414844]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

JustWeEngine

本项目是基于开源项目JustWeEngine进行ohos化的移植和开发的,可以通过项目标签以及github地址(https://github.com/lfkdsk/JustWeEngine )追踪到原项目版本

项目介绍

  • 项目名称:JustWeEngine
  • 所属系列:ohos的第三方组件适配移植
  • 功能:JustWeEngine是托管在Gitee 的一个开源的Ohos原生开发框架,可以让Ohos的开发人员非常便捷,无需切换语言和编译器的制作Ohos原生游戏
  • 项目移植状态:完成
  • 调用差异:无
  • 原项目基线版本:v1.13
  • 编程语言:Java

效果展示

安装教程

方法1.

  1. 编译har包JustWeEngine.har。
  2. 启动 DevEco Studio,将编译的har包,导入工程目录“entry->libs”下。
  3. 在moudle级别下的build.gradle文件中添加依赖,在dependences标签中增加对libs目录下har包的引用。
dependencies { 
    implementation fileTree(dir: 'libs', include: ['*.jar''*.har']) 
    …… 

  • 1.
  • 2.
  • 3.
  • 4.

      4.在导入的har包上点击右键,选择“Add as Library”对包进行引用,选择需要引用的模块,并点击“OK”即引用成功。

方法2.

1.在工程的build.gradle的allprojects中,添加HAR所在的Maven仓地址(等maven发布后进行修改)

repositories { 
    maven { 
        url 'https://s01.oss.sonatype.org/content/repositories/releases/'  
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

2.在应用模块的build.gradle的dependencies闭包中,添加如下代码:(等maven发布后进行修改)

dependencies { 
    implementation 'io.github.dzsf:just-we-engine:1.0.1' 

  • 1.
  • 2.
  • 3.

使用说明

1.基础功能

  • 1.1继承引擎核心类
  • 1.2绘制文字
  • 1.3绘制图片
  • 1.4使用精灵
  • 1.5使用按钮

2.动画系统

  • 2.1绑定在BaseSub物品及精灵基类上的动画类
  • 2.2绑定在Button上的动画类

3.物体分组碰撞检测和死亡判定

4.屏幕扫描模式

5.工具类

6.音频系统

  • 6.1播放短音效
  • 6.2播放音频
  • 6.3通过短音效编曲

进阶应用

7.使用网络

8.使用状态机精灵

9.CrashHandler崩溃守护

10.使用蓝牙

  • 10.1开启、关闭服务
  • 10.2扫描设备
  • 10.3发送消息

11.SQLite数据库

  • 11.1创建表
  • 11.2增删查改

基础功能

继承引擎核心类:

由于框架全部使用SurfaceView进行绘制,不使用诸如Button、Layout等原生控件,所以应该首先新建类继承引擎核心类Engine,负责游戏的流程,注释中已有明确的标明功能。

public class MainAbility extends Engine implements Runnable, Component.TouchEventListener, Component.DrawTask { 
    // 一般在构造函数完成变量的初始化 
    public MainAbility() { 
         
    } 
 
    public void onStart(Intent intent) { 
        // 调用初始化方法 
        init(); 
    } 
     
    // 载入一些UI参数和设置屏幕放向,默认背景色,设定屏幕的扫描方式等 
    public void init() { 
        // 初始化UI默认参数,必须调用该方法,其中有一些用于多机型适配的参数需要初始化 
        UIdefaultData.init(this); 
    } 
 
    // 通常用于精灵,背景,图片等物体的设置和载入 
    public void load() { 
 
    } 
 
    // draw 和 update 在线程中进行不断的循环刷新 
    // draw 负责绘制 update 负责更新数据和对象信息 
    public void draw() { 
 
    } 
 
    public void update() { 
 
    } 
     
    // 将touch 事件传回 功能和所设定的屏幕扫描方式有关 
    public void touch(TouchEvent event) { 
 
    } 
     
    // 将碰撞事件传回 传回精灵和物品的基类  
    // 用于处理碰撞事件 默认使用矩形碰撞 
    public void collision(BaseSub baseSub) { 
 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.

绘制文字:

使用GamePrinter进行文字绘制,除此以外还有多种方法绘制:

@Override 
  public void draw() { 
      Canvas canvas = getCanvas(); 
      GameTextPrinter printer = new GameTextPrinter(canvas); 
      printer.drawText("哈哈哈", 100, 100); 
  } 
   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

绘制图片:

建议图片存放在Resources中:

GameTexture texture = new GameTexture(this); 
    texture.loadFromAsset("resources/base/graphic/shoot.png"
    texture.draw(canvas, 100, 100); 
  • 1.
  • 2.
  • 3.

另外也可使用loadFromAssetStripFrame从一个大的图片中取出对应位置的图片。

/** 
    * get bitmap from a big bitmap 
    * 
    * @param filename 
    * @param x 
    * @param y 
    * @param width 
    * @param height 
    * @return 
    */ 
   public boolean loadFromAssetStripFrame(String filename, 
                                          int x, int y, 

                                          int width, int height) 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

PicUtils中提供了在Bitmap处理中很有用的各种特效和压缩方法,大家可以一试。

使用精灵:

使用精灵可以使用BaseSprite也可以继承该类使用,BaseSprite封装了很多方法供各种动画使用,这些功能很多都是需要结合动画系统来使用的,动画系统会在后面介绍。

新建精灵:

1.简单初始化:

sprite = new BaseSprite(this); 
  • 1.

2.初始化连续帧动画:

连续帧的初始化需要连续的帧图片。

GameTexture texture = new GameTexture(this); 
texture.loadFromAsset("resources/base/graphic/flare.png"); 
// 长宽以及列数 
sprite = new BaseSprite(this, 96, 96, 8); 
sprite.setTexture(texture); 
sprite.setPosition(100, 100); 
sprite.setDipScale(100, 100); 
// 帧切换动画是关键 
sprite.addAnimation(new FrameAnimation(0, 63, 1)); 
addToSpriteGroup(sprite); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

3.使用从大图取出的多帧图片:

// 新建图片资源(此图为上图的大图) 
        GameTexture texture = new GameTexture(this); 
        texture.loadFromAsset("resources/base/graphic/shoot.png"); 
        // 初始化设定模式和长宽 
        ship = new BaseSprite(this, 100, 124, FrameType.COMMON); 
        // 设定资源 
        ship.setTexture(texture); 
        // 从大图中取出两帧 
        ship.addRectFrame(0, 100, 100, 124); 
        ship.addRectFrame(167, 361, 100, 124); 
        ship.addAnimation(new FrameAnimation(0, 1, 1)); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

4.一些重要的其他设定:

// 图片资源 
      ship.setTexture(texture); 
      // 大图取帧模式 
      ship.addRectFrame(0, 100, 100, 124); 
      // 设定位置 
      ship.setPosition(x, y); 
      // 设置dp大小 
    ship.setDipScale(96, 96); 
    // 设定dp位置 
 ship.setDipPosition(x, y); 
 // 设定透明度 
 ship.setAlpha(...); 
 // 只有将精灵添加到SpriteGroup中框架才会自行绘制,否则需要手动调用 
 addToSpriteGroup(ship); 
 ... 
  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

使用按钮:

使用的按钮可以继承BaseButton进行拓展,也可以直接使用TextureButton和TextButton进行使用。

Button设定功能的方式和原生一样,通过设定接口回调的方式进行:

button.setOnClickListener(new OnClickListener() { 
          @Override 
          public void onClick() { 
              Log.e("button""onClick"); 
          } 
        }); 
         
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

TextureButton:

TextureButton button; 
     // 初始化并设定名字 
     button = new TextureButton(this, "logo"); 
  texture = new GameTexture(this); 
     texture.loadFromAsset("resources/base/graphic/logo.jpg"); 
     // 添加图片资源 
     button.setTexture(texture); 
     // button的接口回调,不是View的那个接口 
     button.setOnClickListener(new OnClickListener() { 
         @Override 
         public void onClick() { 
             Log.e("button""onClick"); 
         } 
       }); 
     button.setPosition(200, 300); 
     button.setDipScale(100, 150); 
     // 添加到ButtonGroup进行绘制和处理 
     addToButtonGroup(button); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

结合PicUtil中的各种Bitmap处理方法可以很容易的做出各种样式的Button。

TextButton:

TextButton button;

TextButton button;   
    button = new TextButton(this, "logo"); 
    button.setText("刘丰恺"); 
    addToButtonGroup(button); 
    // 余略见源码 
 ... 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

动画系统

目前的动画系统可以使用已经封装好的继承了BaseAnimation的动画,也可以继承BaseAnim进行自我定义动画类进行使用。

绑定在BaseSub物品及精灵基类上的动画类

AnimType中保存了Animation的应用类型。

绑定动画分为两类,ListAnimation和FixedAnimation,ListAnimation将动画存储到固定的一个List中,用于重复更新的动画,

而FixedAnimation存储在Map中,使用名字进行调用,用于点击或者非自动更新的动画。

比如前面精灵类动画的就是添加到ListAnimation。

下面的这种写法就是FixedAnimation,这个动画是飞机入场,因为只使用了一次,所以使用了FixedAnimation。

ship.addfixedAnimation("start"
               new MoveAnimation(UIdefaultData.centerInHorizontalX -   ship.getWidthWithScale() / 2, 
                       UIdefaultData.screenHeight - 2 * ship.getHeightWidthScale(), new Float2(10, 10))); 
            
  • 1.
  • 2.
  • 3.
  • 4.

绑定在Button上的动画类

BaseButtonAnimation是BaseButton的动画类继承了BaseAnim的动画基类,通过提供Button的状态,设定Button的动画。

为Button设定放缩动画:

// 设定中心放缩 
   button.setZoomCenter(true); 
   // 三个参数 初始值/放大值/帧数 
   button.setAnimation(new ZoomCenterButtonAnim(10, 30, 3)); 
  • 1.
  • 2.
  • 3.
  • 4.

为Button设定颜色动画:

// 初始颜色 / 按下颜色 
button.setAnimation( 
      new ColorAnimation(UIdefaultData.colorAccent, 
      UIdefaultData.colorPressed)); 
  • 1.
  • 2.
  • 3.
  • 4.

物体分组碰撞检测和死亡判定

使用设置ID和Name进行物体分组,通过物体分组,框架核心类会对对象进行分类处理。

final int SHIP = 0; 
    ship.setName("SHIP"); 
    ship.setIdentifier(SHIP); 
  • 1.
  • 2.
  • 3.

只要使用了addToSpriteGroup(sprite)的精灵对象就会自动进行碰撞检测,而对碰撞检测的结果会从

collision中进行发回。

@Override 
  public void collision(BaseSub baseSub) { 
    // 获取与之碰撞的对象 
      BaseSprite other = (BaseSprite) baseSub.getOffender(); 
      // 获取ID分组处理 
      if (baseSub.getIdentifier() == BULLET && 
              other.getIdentifier() == ENEMY) { 
          // 设定死亡 
          other.setAlive(false); 
          // 回收 
          removeFromSpriteGroup(other); 
          addToRecycleGroup(other); 
      } 
  } 
   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

其中getOffender()获得与之碰撞的对象,通过getIdentifier()获取设定的对象分组,实行逻辑判断。

开启Debug模式会看见碰撞线。

屏幕扫描模式

屏幕扫描模式是用来优先响应屏幕点击、Button点击、和多点触控而设的,放置在不同情况下都能优化屏幕的刷新。

// 检测单一移动 
SINGLE, 
// 检测Button 
 BUTTON, 
 // 多点检测 
 FULL
 // 单击+Button 
 SINGLE_BUTTON 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

并且通过如下方式进行设置:

super.setTouchMode(TouchMode.BUTTON); 
  • 1.

工具类

  • NetUtils 网络状态工具类
  • PicUtils 图片处理工具类
  • ServiceUtils 服务工具类
  • ImageHelper 图型处理类
  • DisplayUtils 数据转换类
  • SpUtils Sp简化工具类(可存储list和map)
  • ValidatorsUtils 正则表达式处理类

音频系统

播放短音效

播放短音效,首先初始化SoundManager用以加载音效。

// 接收实例和Manager的尺寸 
   SoundManager manager = new SoundManager(this, 5); 
   // 从assets加载音频 同时加载路径也会作为音效名进行存储 
manager.addSound("resource/rawfile/open.mid"); 
// 通过加载名进行播放 
manager.play("resource/rawfile/open.mid"); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

完成以上步骤就可以播放了,当然尽量只向其中放置较短的音效,如背景音乐的长音频,请见播放音频。

public void removeSound(String musicName) // 移除 
public void play(String musicName, float volume) // 播放 + 音量 
public boolean containSoundID(int soundID) // 判断音频是否存在 
public int getSoundID(String soundName)  // 获取ID 
... 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

播放音频

播放音频适合例如背景音乐一样的音乐。

// 传入两个参数 上下文和文件名 
    MusicPlayer player = new MusicPlayer(this, "resources/rawfile/open.mp3"); 
    player.play(); 
  • 1.
  • 2.
  • 3.

以上的就能实现播放了,下面还有一些其他的方法。

public void dispose() // 清理 
    public void setLooping(boolean isLooping) // 是否循环 
    public void setVolume(float volume) // 设定音量 
    ... 
  • 1.
  • 2.
  • 3.
  • 4.

 通过短音效编曲

从SoundManager中导入多段音频,快速播放达成音效的效果。

SoundManager manager = new SoundManager(this, 5); 
  manager.addSound("resources/rawfile/media/1.mid"); 
  manager.addSound("resources/rawfile/media/2.mid"); 
  SoundPlayer player = new SoundPlayer(manager, 500, 16); 
  player.addSound("resources/rawfile/media/1.mid"); 
  player.addSound("resources/rawfile/media/2.mid"); 
  ...  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

使用player.play();进行播放。

使用网络

网络的使用可参考JustWe-WebServer中的介绍。

按照介绍操作就可以通过:

server.apply("/lfk", new OnWebStringResult() { 
    @Override 
    public String OnResult() { 
        return "======="
    } 
}); 
 
server.apply("/main", new OnWebFileResult() { 
    @Override 
    public File returnFile() { 
        return new File(WebServerDefault.WebServerFiles+"/"+"welcome.html"); 
    } 
}); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

这样的简单方式绑定路由,而get/post数据可以直接使用http协议的get和post进行。

使用状态机精灵

// 为状态机添加一个任务 
sprite.addState(new StateFinder() { 
    @Override 
    public boolean isContent(BaseSub baseSub) { 
        return Math.abs(zom.s_position.x - baseSub.s_position.x) > 50; 
    } 
}, new FrameAnimation(0, 63, 1)); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

可以通过上述的addState方法为状态机精灵添加一个任务,只有当第一个参数接口回调的返回值为真的时候,

才会去运行第二个参数提供的指令,如果返回为假则会运行第二项状态的判断。

状态的优先级由加入顺序提供。

CrashHandler崩溃守护

CrashHandler用于处理游戏的意外崩溃事件,初始化推荐在Application中进行。

CrashHandler可以自动保存机型和异常日志,以便让开发者找到问题所在。

CrashHandler.getInstance().init(this); 
  • 1.

使用以上语句即可自动保存错误日志。

还可以:

CrashHandler.getInstance().setRestartActivity(MainActivity.class); // 重启的Activity 
   // 添加崩溃回调 
   CrashHandler.getInstance().addCrashListener(new AfterCrashListener() { 
       @Override 
       public void AfterCrash() {  // 设定保存项目 
           ... 
       } 
   });     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

使用蓝牙

开启、关闭服务

使用蓝牙需要新建BlueToothServer对象,传入上下文和MessageBack接口。

      blueToothServer = new BlueToothServer(this, new OnMessageBack() { 
          @Override 
          public void getMessage(String msg) { 
              Log.e("L", msg); 
          } 
 
          @Override 
          public void sendMessage(String msg) { 
              Log.e("L", msg); 
          } 
 
          @Override 
          public void getDevice(ArrayList<String> msg) { 
              Log.e("L", msg.size() + ""); 
          } 
      }); 
 
// 使用如下语句进行初始化 
      blueToothServer.init(); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

服务初始化之后如未打开蓝牙,系统会自动提示应用要求蓝牙开启。

通过MessageBack接口可以接收到发送、接收、以及扫描设备信息,采取对应操作就可以获得数据。

关闭服务时请使用blueToothServer.unBindService();关闭服务。

扫描设备

使用blueToothServer.doDiscovery();进行设备扫描,返回结果在OnMessageBack()接口的

getDevice()方法接收。

使用blueToothServer.ensureDiscoverable();允许被扫描。

使用blueToothServer.getPairedDevices();返回已配对的设备列表。

发送消息

在配对成功之后就可以使用blueToothServer.sendMessage(String msg);发送消息了。

同时,消息的接收也可以从getMessage()接口中获得。

SQLite数据库

SQLite使用了IOC的模式。

创建表

新建的创建表需要继承Node并且写出注解类。

// 表名 
@TableName(tableName = "lfkdsk"
public class User extends Node { 
 
// 主键自增 INTEGER型 
   @LabelName(autoincrement = true
           type = LabelName.Type.INTEGER
           columnName = "name"
           generatedId = true
   private int name
 
// TEXT型 栏名为user_name 
   @LabelName(type = LabelName.Type.TEXT, 
           columnName = "user_name"
   private String user_name; 
 
// 自增主键所以只需要提供其他信息 
   public User(String user_name) { 
       super(user_name); 
       this.user_name = user_name; 
   } 
 
   public User(int name, String user_name) { 
       super(name, user_name); 
       this.name = name
       this.user_name = user_name; 
   } 
 
   public int getName() { 
       return name
   } 
 
   public void setName(int name) { 
       this.name = name
   } 
 
   public String getUser_name() { 
       return user_name; 
   } 
 
   public void setUser_name(String user_name) { 
       this.user_name = user_name; 
   } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
// 通过这种方式获取数据库   表名 
    private DataBase dataBase = DataBase.initAndOpen("user"User.class); 
  • 1.
  • 2.

增删查改

// add 
    database.insert(User user); 
    // find 
    database.get(int position); 
    // delete 
    database.delete(int position); 
    // update 
    database.update(User user); 
    ... 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

相关资料

项目地址:https://gitee.com/openneusoft/just-we-engine

IDE官方下载地址:https://developer.harmonyos.com/cn/develop/deveco-studio

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2021-03-10 15:03:40

鸿蒙HarmonyOS应用

2021-04-29 14:32:24

鸿蒙HarmonyOS应用

2021-08-26 16:07:46

鸿蒙HarmonyOS应用

2021-03-24 09:30:49

鸿蒙HarmonyOS应用

2021-03-01 14:00:11

鸿蒙HarmonyOS应用

2021-03-03 09:42:26

鸿蒙HarmonyOS图片裁剪

2021-07-06 18:21:31

鸿蒙HarmonyOS应用

2021-08-30 17:55:58

鸿蒙HarmonyOS应用

2021-08-05 15:06:30

鸿蒙HarmonyOS应用

2021-04-08 14:57:52

鸿蒙HarmonyOS应用

2021-04-20 15:06:42

鸿蒙HarmonyOS应用

2021-01-27 10:04:46

鸿蒙HarmonyOS动画

2021-11-02 14:54:21

鸿蒙HarmonyOS应用

2021-11-17 15:37:43

鸿蒙HarmonyOS应用

2021-07-20 15:20:40

鸿蒙HarmonyOS应用

2021-04-15 17:47:38

鸿蒙HarmonyOS应用

2021-03-12 16:35:33

鸿蒙HarmonyOS应用

2017-12-11 15:53:56

2021-08-10 15:23:08

鸿蒙HarmonyOS应用

2021-10-19 10:04:51

鸿蒙HarmonyOS应用
点赞
收藏

51CTO技术栈公众号