康复游戏-鸟妈妈回家
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
song 1dffec2665 添加暂停按钮 4 years ago
public 添加暂停按钮 4 years ago
src 添加暂停按钮 4 years ago
.gitignore doc: 文件目录tree 4 years ago
.prettierrc oop 4 years ago
.svrxrc.js oop 4 years ago
README.md 连点7次进入演示模式 4 years ago
dist.zip 添加关闭演示模式按钮 4 years ago
gulpfile.js gulp构建 4 years ago
package-lock.json 连点7次进入演示模式 4 years ago
package.json gulp构建 4 years ago

README.md

鸟妈妈回家

运行步骤

  1. git clone https://dd.tall.wiki/gitea/ccsens_fe/bird-go-home.git
  2. 全局安装gulp npm i gulp -g
  3. npm i 或者 yarn
  4. 运行gulp
  5. 运行vscode的live server
  6. 部署服务器 dist目录

游戏介绍

“鸟妈妈回家”游戏用于 康复训练,上肢训练

游戏使用Adobe Animate制作,游戏包含如下几个模块:

  • 主体:鸟、背景(山、云朵、树木等)、成功次数(也能代表得分)
  • 开始倒计时:开始面板、倒计时文本
  • 返回按钮
  • 难度级别
  • 游戏计时
  • 结束modal:界面面板、得分文本
  • 测试模块

该游戏源文件,使用旧版的flash软件制作,转换成animate支持的canvas模式。

游戏原有动画均在图层中制作。包括游戏动作成功的字数,也在动画中制作。游戏动作设计调整难度较大,次数修改成本较高。目前是固定的20次,一般情况是够用。

目录结构说明

.
├── README.md
├── dist
│   ├── bird.js
│   ├── images
│   │   ├── Bitmap1.png
│   │   ├── Bitmap2.png
│   │   ├── Bitmap3.png
│   │   ├── Bitmap4.png
│   │   ├── Bitmap5.png
│   │   ├── _preloader.gif
│   │   ├── level.png
│   │   ├── 任务完成_.png
│   │   ├── 倒计时_.png
│   │   ├── 树林前景_.png
│   │   ├── 计时_.png
│   │   └── 返回按钮_.png
│   ├── index.html
│   ├── libs
│   │   └── 1.0.0
│   │       └── createjs.min.js
│   ├── main.js
│   └── sounds
│       ├── amazing.mp3
│       ├── bgmMusic.mp3
│       ├── excitationMusic.mp3
│       └── unbelievable.mp3
├── gulpfile.js
├── package.json
├── public
│   ├── bird.html
│   ├── bird.js
│   ├── images
│   │   ├── Bitmap1.png
│   │   ├── Bitmap2.png
│   │   ├── Bitmap3.png
│   │   ├── Bitmap4.png
│   │   ├── Bitmap5.png
│   │   ├── _preloader.gif
│   │   ├── level.png
│   │   ├── 任务完成_.png
│   │   ├── 倒计时_.png
│   │   ├── 树林前景_.png
│   │   ├── 计时_.png
│   │   └── 返回按钮_.png
│   ├── index.html
│   ├── libs
│   │   └── 1.0.0
│   │       └── createjs.min.js
│   ├── sounds
│   │   ├── amazing.mp3
│   │   ├── bgmMusic.mp3
│   │   ├── excitationMusic.mp3
│   │   └── unbelievable.mp3
│   └── 鸟妈妈回家_HTML5 Canvas.fla
└── src
    ├── classes
    │   ├── back.js
    │   ├── count.js
    │   ├── end.js
    │   ├── level.js
    │   ├── main.js
    │   ├── sound.js
    │   └── time.js
    ├── custom.js
    └── index.js
文件 说明 备注
bird.html animate自动生成的html,不编辑使用index.html代替
bird.js animate自动生成,index.html有引用
images/* animate生成的图片资源 这里没有使用雪碧图,这个文件fla导出雪碧图就卡死
index.html 入口界面
js/custom.js 自定义初始化整合js 可作为模板复制修改,config及initStage
js/index.js animate生成的内嵌到bird.html中的js提取,并添加了初始化入口函数initStage
js/test.js 测试程序,正式版可删除
js/classes/* 封装的各个模块类
js/classes/back.js 返回按钮类封装 通用
js/classes/bird.js 游戏主体类封装 不通用
js/classes/count.js 开始时倒计时组件类封装 通用,配合custom里的配置使用
js/classes/end.js 游戏结束,得分面板 通用,配合custom里的config总分,总次数使用
js/classes/level.js 游戏级别组件封装类 通用,配合custom里的config里的level使用
js/classes/sound.js 游戏音频封装类 通用,资源可能需要替换
js/classes/time.js 游戏时长、计时组件封装类 通用,配合custom里的config里的time使用
libs/* animate导出的createjs文件 不用动
sounds/*.mp3 音频资源
*.fla animate源文件

具体代码说明

index.html

入口界面,依据animate生成的 bird.html 修改而来。主要添加了测试功能区,以及js文件的整合引入。其他不动

<script src="libs/1.0.0/createjs.min.js"></script>
<script src="bird.js"></script>
<script src="main.js"></script>
<!-- write your code here -->
</head>
<body onload="init();" style="margin: 0px;">
	<div id="animation_container" style="background-color:rgba(255, 255, 255, 1.00); width:1280px; height:720px">
		<canvas id="canvas" width="1280" height="720" style="position: absolute; display: none; background-color:rgba(255, 255, 255, 1.00);"></canvas>
		<div id="dom_overlay_container" style="pointer-events:none; overflow:hidden; width:1280px; height:720px; position: absolute; left: 0px; top: 0px; display: none;">
		</div>
	</div>
	<div id='_preload_div_' style='position:absolute; top:0; left:0; display: inline-block; height:720px; width: 1280px; text-align: center;'>	<span style='display: inline-block; height: 100%; vertical-align: middle;'></span>	<img src=images/_preloader.gif style='vertical-align: middle; max-height: 100%'/></div>
</body>

index.js

将原bird.html中的内嵌js部分抽离,并添加initStage调用,如下:

function handleComplete(evt, comp) {
  //This function is always called, irrespective of the content. You can use the variable "stage" after it is created in token create_stage.
  var lib = comp.getLibrary();
  var ss = comp.getSpriteSheet();
  var queue = evt.target;
  var ssMetadata = lib.ssMetadata;
// for循环 i=0; 前面需要加 let, 否则编译会报错
  for (i = 0; i < ssMetadata.length; i++) {
    ss[ssMetadata[i].name] = new createjs.SpriteSheet({ images: [queue.getResult(ssMetadata[i].name)], frames: ssMetadata[i].frames });
  }
  var preloaderDiv = document.getElementById('_preload_div_');
  preloaderDiv.style.display = 'none';
  canvas.style.display = 'block';
  exportRoot = new lib.鸟妈妈回家_HTML5Canvas();
  stage = new lib.Stage(canvas);
  //Registers the "tick" event listener.
  fnStartAnimation = function () {
    stage.addChild(exportRoot);

    initStage(lib);

    createjs.Ticker.framerate = lib.properties.fps;
    createjs.Ticker.addEventListener('tick', stage);
  };
  //Code to support hidpi screens and responsive scaling.
  AdobeAn.makeResponsive(true, 'both', true, 1, [canvas, preloaderDiv, anim_container, dom_overlay_container]);
  AdobeAn.compositionLoaded(lib.properties.id);
  fnStartAnimation();
}

custom.js

配置,全局变量 及initStage整个调用各个类,注意类的调用有先后顺序之分。图层顺序在上的后调用

const config = {
  count: 5, // 默认倒计时时长
  duration: 60, // 总时长 s
  level: 1, // 游戏难度级别
  total: 100, // 总分
  times: 20, // 动作次数
};

let library = null;
let state = 0; // 游戏状态 0->未开始  1->进行中  2->结束

function initStage(lib) {
  library = lib;

  window.main = Main.of(gameOver); // 初始化鸟等
  window.timeInstance = Time.of(gameOver); // 初始化游戏时间
  // window.soundInstance = Sound.of(); // 初始化音频
  Level.of(2); // 游戏难度级别
  Back.of(); // 返回按钮
}

// 游戏结束 显示结束得分面板
function gameOver() {
  state = 2;
  const times = main.times;
  const score = parseInt((config.total / config.times) * times);
  End.of(score);
}

游戏模块组件类说明

以time.js 游戏计时模块为例:

每个类除了自身的功能定义、调用之外,

  • 统一添加了 of 静态方法,封装了 new 操作(不喜欢看到new)、调用了初始化 init 函数、并返回示例,因此调用时只需要Time.of(...) 就可以
  • 如果有涉及到跟其他类的关联调用,如end方法中的 endCallback,以回调函数的形式传参使用
  • 尽最大可能减少全局变量,外部变量的引用,保证函数的引用透明,让他纯,减少副作用,提高复用性
/**
 * 游戏时长 计时
 * @param {function} endCallback 倒计时结束的回调函数
 * @param {number} duration 游戏时长
 * @param {number} count 游戏倒计时时长
 *
 * @property {number} duration 游戏时长
 * @property {number} count 游戏倒计时时长
 * @property {number} startTime 游戏开始是ms
 * @property {boolean} started 游戏是否开始
 * @property {object} lib 资源库
 * @property {object} bg 时间倒计时背景
 * @property {object} text 倒计时文本
 * @property {number} timerId 定时器id
 * @property {function} endCallback 结束回调函数
 */
function Time(endCallback, duration, count) {
  this.duration = duration;
  this.count = count;
  this.startTime = Date.now();

  this.started = false; // 是否开始游戏

  this.lib = library;
  this.bg = null;
  this.text = null;
  this.timerId = null;
  this.endCallback = endCallback;
}

/**
 * 静态方法 封装new init 返回实例
 * @param {function} endCallback 倒计时结束的回调函数
 * @param {number} duration 游戏时长
 * @param {number} count 游戏倒计时时长
 * @returns
 */
Time.of = function (endCallback, duration = config.duration || 60, count = config.count || 5) {
  const instance = new Time(endCallback, duration, count);
  instance.init();
  return instance;
};

// 初始化
Time.prototype.init = function () {
  this.renderBg();
  this.renderText(this.duration);
};

// 开始游戏 开始倒计时
Time.prototype.start = function (startTime = Date.now()) {
  this.started = true;
  state = 1;
  this.startTime = startTime;
  this.update();
};

// 开始游戏 开始倒计时
Time.prototype.end = function () {
  this.started = false;
  this.timerId && clearTimeout(this.timerId);
  this.timerId = null;
  // 执行结束后的回调
  this.endCallback();
};

// 渲染背景
Time.prototype.renderBg = function () {
  const initX = this.lib.properties.width - 100;
  const initY = 80;
  const instance = new this.lib.timeBg();
  instance.x = initX;
  instance.y = initY;
  this.bg = instance;
  stage.addChild(instance);
};

// 渲染文本
Time.prototype.renderText = function (time) {
  const text = new createjs.Text(time, 'bold 40px Arial', '#823d16');
  text.x = this.lib.properties.width - 100;
  text.y = 100;
  text.textAlign = 'center';
  text.textBaseline = 'alphabetic';
  this.text = text;
  stage.addChild(text);
};

// 更新文本
Time.prototype.update = function () {
  if (this.timerId || !this.started) return;
  // const endTime = this.startTime + this.count * 1000 + this.duration * 1000;
  const endTime = this.startTime + this.duration * 1000;
  // console.log('endTime: ', endTime);
  this.timerId = setInterval(() => {
    let leftTime = Math.round((endTime - Date.now()) / 1000);
    console.log('leftTime: ', leftTime);
    this.text && stage.removeChild(this.text);
    if (leftTime <= 0) {
      leftTime = 0;
      this.end();
    }
    this.renderText(leftTime);
  }, 1000);
};

**特别说明:**时间关系,还有很多需要完善的,比如上文中的endCallback就没有做类型判断。后续除了程序的健壮性优化提升之外,还须添加webpack工程化打包构建等,避免源代码暴露,代码体积、模块导入引用等都能得到好的提升