Browse Source

first commit

master
song 4 years ago
commit
8de69bfb6d
  1. 113
      .gitignore
  2. 13
      .prettierrc
  3. 375
      README.md
  4. 34
      gulpfile.js
  5. 5132
      package-lock.json
  6. 29
      package.json
  7. BIN
      public/images/_preloader.gif
  8. BIN
      public/images/source_code_atlas_1.png
  9. BIN
      public/images/source_code_atlas_2.png
  10. BIN
      public/images/跨栏背景_.png
  11. 42
      public/index.html
  12. 19
      public/libs/1.0.0/createjs.min.js
  13. BIN
      public/sounds/amazing.mp3
  14. BIN
      public/sounds/audio1.mp3
  15. BIN
      public/sounds/bgmMusic.mp3
  16. BIN
      public/sounds/excitationMusic.mp3
  17. BIN
      public/sounds/unbelievable.mp3
  18. 80
      public/source-code.html
  19. 4261
      public/source-code.js
  20. BIN
      public/跨栏(右脚)_HTML5 Canvas.fla
  21. 47
      src/classes/again.js
  22. 32
      src/classes/back.js
  23. 102
      src/classes/count.js
  24. 47
      src/classes/demo.js
  25. 65
      src/classes/end.js
  26. 34
      src/classes/level.js
  27. 86
      src/classes/main.js
  28. 47
      src/classes/sound.js
  29. 55
      src/classes/suspend.js
  30. 126
      src/classes/time.js
  31. 56
      src/custom.js
  32. 50
      src/index.js
  33. 132
      src/message.js
  34. 57
      src/test.js

113
.gitignore

@ -0,0 +1,113 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
.vscode/
.idea/
yarn.lock
dist
dist/*
.~README.md

13
.prettierrc

@ -0,0 +1,13 @@
{
"printWidth": 140,
"singleQuote": true,
"semi": true,
"trailingComma": "all",
"arrowParens": "avoid",
"tabWidth": 2,
"useTabs": false,
"bracketSpacing": true,
"jsxBracketSameLine": false,
"proseWrap": "always",
"endOfLine": "lf"
}

375
README.md

@ -0,0 +1,375 @@
# 鸟妈妈回家
# 运行步骤
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的Go Live
6. 部署服务器 dist目录
## 游戏介绍
“鸟妈妈回家”游戏用于 康复训练,上肢训练
游戏使用Adobe Animate制作,游戏包含如下几个模块:
+ 主体:鸟、背景(山、云朵、树木等)、成功次数(也能代表得分)
+ 开始倒计时:开始面板、倒计时文本
+ 返回按钮
+ 难度级别
+ 游戏计时
+ 结束modal:界面面板、得分文本
+ 测试模块
该游戏源文件,使用旧版的flash软件制作,转换成animate支持的canvas模式。
游戏原有动画均在图层中制作。包括游戏动作成功的字数,也在动画中制作。游戏动作设计调整难度较大,次数修改成本较高。目前是固定的20次,一般情况是够用。
## 目录结构说明
通过如下命令输出的目录结构:
```bash
tree -I "node_modules|package-lock.json|yarn.lock"
```
```bash
│ .gitignore
│ .prettierrc
│ .svrxrc.js
│ dist.zip
│ gulpfile.js
│ package-lock.json
│ package.json
│ README.md
├─dist
│ │ bird.js
│ │ index.html
│ │ main.js
│ │
│ ├─images
│ │ Bitmap1.png
│ │ Bitmap2.png
│ │ Bitmap3.png
│ │ Bitmap4.png
│ │ Bitmap5.png
│ │ level.png
│ │ stopDemo.png
│ │ suspend.png
│ │ _preloader.gif
│ │ 任务完成_.png
│ │ 倒计时_.png
│ │ 再来一次_.png
│ │ 树林前景_.png
│ │ 计时_.png
│ │ 返回按钮_.png
│ │
│ ├─libs
│ │ └─1.0.0
│ │ createjs.min.js
│ │
│ └─sounds
│ amazing.mp3
│ bgmMusic.mp3
│ excitationMusic.mp3
│ unbelievable.mp3
├─public
│ │ bird.html
│ │ bird.js
│ │ index.html
│ │ 鸟妈妈回家_HTML5 Canvas.fla
│ │
│ ├─images
│ │ Bitmap1.png
│ │ Bitmap2.png
│ │ Bitmap3.png
│ │ Bitmap4.png
│ │ Bitmap5.png
│ │ level.png
│ │ stopDemo.png
│ │ suspend.png
│ │ _preloader.gif
│ │ 任务完成_.png
│ │ 倒计时_.png
│ │ 再来一次_.png
│ │ 树林前景_.png
│ │ 计时_.png
│ │ 返回按钮_.png
│ │
│ ├─libs
│ │ └─1.0.0
│ │ createjs.min.js
│ │
│ └─sounds
│ amazing.mp3
│ bgmMusic.mp3
│ excitationMusic.mp3
│ unbelievable.mp3
└─src
│ custom.js
│ index.js
│ test.js
└─classes
again.js
back.js
count.js
demo.js
end.js
level.js
main.js
sound.js
suspend.js
time.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文件的整合引入。其他不动
```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调用,如下:
```js
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整个调用各个类,注意类的调用有先后顺序之分。图层顺序在上的后调用
```js
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,以回调函数的形式传参使用
+ 尽最大可能减少全局变量,外部变量的引用,保证函数的引用透明,让他纯,减少副作用,提高复用性
```js
/**
* 游戏时长 计时
* @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工程化打包构建等,避免源代码暴露,代码体积、模块导入引用等都能得到好的提升

34
gulpfile.js

@ -0,0 +1,34 @@
const { task, series, parallel, src, dest, watch } = require('gulp');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
const concat = require('gulp-concat');
const clean = require('gulp-clean');
const livereload = require('gulp-livereload');
const cleanTask = function () {
return src('dist/*', { read: false }).pipe(clean());
};
const script = function () {
return src('src/**/*.js')
.pipe(babel({ presets: ['@babel/env'] }))
.pipe(uglify())
.pipe(concat('main.js'))
.pipe(dest('dist/'))
.pipe(livereload({ start: true, port: 3001, reloadPage: 'dist/index.html' }));
};
const copy = function () {
src('./public/index.html').pipe(dest('dist/'));
src('./public/source-code.js').pipe(dest('dist/'));
src('./public/images/*').pipe(dest('dist/images'));
src('./public/libs/**/*').pipe(dest('dist/libs'));
src('./public/sounds/*').pipe(dest('dist/sounds'));
};
const watchTask = function () {
livereload.listen();
watch('src/*', script);
};
exports.default = parallel(copy, script, watchTask);

5132
package-lock.json

File diff suppressed because it is too large

29
package.json

@ -0,0 +1,29 @@
{
"name": "bird-go-home",
"version": "1.0.0",
"description": "",
"main": ".svrxrc.js",
"scripts": {
"dev": "gulp"
},
"repository": {
"type": "git",
"url": "gitea@dd.tall.wiki:ccsens_fe/bird-go-home.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.15.5",
"@babel/preset-env": "^7.15.6",
"gulp": "^4.0.2",
"gulp-babel": "^8.0.0",
"gulp-clean": "^0.4.0",
"gulp-concat": "^2.6.1",
"gulp-livereload": "^4.0.2",
"gulp-uglify": "^3.0.2"
},
"dependencies": {
"vinyl-paths": "^4.0.0"
}
}

BIN
public/images/_preloader.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
public/images/source_code_atlas_1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
public/images/source_code_atlas_2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

BIN
public/images/跨栏背景_.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

42
public/index.html

@ -0,0 +1,42 @@
<!DOCTYPE html>
<!--
NOTES:
1. All tokens are represented by '$' sign in the template.
2. You can write your code only wherever mentioned.
3. All occurrences of existing tokens will be replaced by their appropriate values.
4. Blank lines will be removed automatically.
5. Remove unnecessary comments before creating your template.
-->
<html>
<head>
<meta charset="UTF-8">
<meta name="authoring-tool" content="Adobe_Animate_CC">
<title>source-code</title>
<!-- write your code here -->
<style>
#animation_container, #_preload_div_ {
position:absolute;
margin:auto;
left:0;right:0;
top:0;bottom:0;
}
</style>
<script src="libs/1.0.0/createjs.min.js"></script>
<script src="source-code.js"></script>
<script src="main.js"></script>
<script>
</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>
<!-- 音频 -->
<audio src="sounds/audio1.mp3" id="audio1" controls="controls" hidden="true"></audio>
</body>
</html>

19
public/libs/1.0.0/createjs.min.js

File diff suppressed because one or more lines are too long

BIN
public/sounds/amazing.mp3

Binary file not shown.

BIN
public/sounds/audio1.mp3

Binary file not shown.

BIN
public/sounds/bgmMusic.mp3

Binary file not shown.

BIN
public/sounds/excitationMusic.mp3

Binary file not shown.

BIN
public/sounds/unbelievable.mp3

Binary file not shown.

80
public/source-code.html

@ -0,0 +1,80 @@
<!DOCTYPE html>
<!--
NOTES:
1. All tokens are represented by '$' sign in the template.
2. You can write your code only wherever mentioned.
3. All occurrences of existing tokens will be replaced by their appropriate values.
4. Blank lines will be removed automatically.
5. Remove unnecessary comments before creating your template.
-->
<html>
<head>
<meta charset="UTF-8">
<meta name="authoring-tool" content="Adobe_Animate_CC">
<title>source-code</title>
<!-- write your code here -->
<style>
#animation_container, #_preload_div_ {
position:absolute;
margin:auto;
left:0;right:0;
top:0;bottom:0;
}
</style>
<script src="libs/1.0.0/createjs.min.js"></script>
<script src="source-code.js"></script>
<script>
var canvas, stage, exportRoot, anim_container, dom_overlay_container, fnStartAnimation;
function init() {
canvas = document.getElementById("canvas");
anim_container = document.getElementById("animation_container");
dom_overlay_container = document.getElementById("dom_overlay_container");
var comp=AdobeAn.getComposition("903396EEDEAE5046BAF0A48EB7A12FC3");
var lib=comp.getLibrary();
var loader = new createjs.LoadQueue(false);
loader.addEventListener("fileload", function(evt){handleFileLoad(evt,comp)});
loader.addEventListener("complete", function(evt){handleComplete(evt,comp)});
var lib=comp.getLibrary();
loader.loadManifest(lib.properties.manifest);
}
function handleFileLoad(evt, comp) {
var images=comp.getImages();
if (evt && (evt.item.type == "image")) { images[evt.item.id] = evt.result; }
}
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; 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);
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();
}
</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>
</html>

4261
public/source-code.js

File diff suppressed because it is too large

BIN
public/跨栏(右脚)_HTML5 Canvas.fla

Binary file not shown.

47
src/classes/again.js

@ -0,0 +1,47 @@
/**
* 再玩一次类
* @property {object} lib 库对象
* @property {object} btnAgain 面板元素对象
*/
function Again() {
this.lib = library;
this.btnAgain = null;
}
Again.of = (function () {
let instance = null;
return function () {
if (!instance) {
instance = new Again();
}
instance.init();
return instance;
};
})();
// 初始化
Again.prototype.init = function () {
this.showAgainBtn();
};
// 渲染背景面板
Again.prototype.showAgainBtn = function () {
const lib = this.lib;
const btnAgain = new lib.btnAgain();
btnAgain.x = lib.properties.width / 2;
btnAgain.y = lib.properties.height / 2 + 150;
this.btnAgain = btnAgain;
this.btnAgain.addEventListener(
'click',
function () {
if (config.mode === 0) {
againMessage();
} else {
location.reload();
}
},
false,
);
stage.addChild(btnAgain);
};

32
src/classes/back.js

@ -0,0 +1,32 @@
function Back() {
this.lib = library;
this.back = null;
}
Back.of = (function () {
let instance = null;
return function () {
if (!instance) {
instance = new Back();
}
instance.init();
return instance;
};
})();
Back.prototype.init = function () {
const target = new this.lib.btnBack();
target.x = 40;
target.y = 80;
target.scaleX = 1.3;
target.scaleY = 1.3;
target.addEventListener('click', this.goHome, false);
this.back = target;
stage.addChild(target);
};
// 返回主菜单 TODO:
Back.prototype.goHome = function () {
console.log('goHome');
};

102
src/classes/count.js

@ -0,0 +1,102 @@
// Count.of(startTime, defaultCount) 调用即可
/**
* 倒计时类
* @param {number} startTime 开始倒计时的时间
* @param {number} defaultCount 倒计时时长 TODO: const startTime = +gameInfo.startTime;
* @property {object} modal 面板对象
* @property {object} text 倒计时文本对象
* @property {number} default 倒计时值
* @property {number} timer 计时器id
* @property {object} lib 库对象
* @property {function} countEndCallback 结束回调函数
*/
function Count(countEndCallback, startTime, defaultCount = config.count || 5) {
this.default = defaultCount;
this.startTime = startTime;
this.modal = null;
this.text = null;
this.timer = null;
this.lib = library;
this.countEndCallback = countEndCallback;
}
/**
* 静态方法 封装new init方法
* 使用时直接调用此方法
* @param {function} countEndCallback 倒计时结束的回调函数
* @param {number} defaultCount 倒计时时长
* @returns
*/
Count.of = (function () {
let instance = null;
return function (countEndCallback, startTime = Date.now(), defaultCount) {
if (!instance) {
instance = new Count(countEndCallback, startTime, defaultCount);
}
instance.init();
return instance;
};
})();
// 初始化方法
Count.prototype.init = function () {
this.renderModal();
this.renderContent();
};
// 渲染面板
Count.prototype.renderModal = function () {
const target = new this.lib.bgModalBegin();
target.x = this.lib.properties.width / 2;
target.y = this.lib.properties.height / 2;
this.modal = target;
stage.addChild(target); // 显示开始游戏的倒计时面板
};
// 渲染倒计时文本内容
Count.prototype.renderContent = function () {
const endCountTime = this.startTime + this.default * 1000;
let leftCount = Math.round((endCountTime - Date.now()) / 1000);
this.update(leftCount); // 开始开始游戏的 倒计时
this.timer = setInterval(() => {
leftCount = Math.round((endCountTime - Date.now()) / 1000);
stage.removeChild(this.text);
this.update(leftCount);
}, 1000);
};
// 更新倒计时文本
Count.prototype.update = function (time) {
if (time <= 0) {
// 发送倒计时结束的消息
clearInterval(this.timer);
stage.removeChild(this.text);
stage.removeChild(this.modal);
// sendEndCountRequest(); // 发送倒计时结束的消息
this.countEnd();
return;
}
this.renderText(time);
};
// 渲染文本
Count.prototype.renderText = function (time) {
const text = new createjs.Text(time, 'bold 100px Arial', '#87431c');
text.x = this.lib.properties.width / 2;
text.y = this.lib.properties.height / 2 + 20;
text.textAlign = 'center';
text.textBaseline = 'alphabetic';
this.text = text;
stage.addChild(text);
};
// 结束倒计时 开始游戏
Count.prototype.countEnd = function () {
// 执行结束后的回调
this.countEndCallback();
};

47
src/classes/demo.js

@ -0,0 +1,47 @@
/**
* 演示模式类
* @property {object} lib 库对象
* @property {object} stopDemo 面板元素对象
*/
function Demo() {
this.lib = library;
this.stopDemo = null;
}
Demo.of = (function () {
let instance = null;
return function () {
if (!instance) {
instance = new Demo();
}
instance.init();
return instance;
};
})();
// 初始化
Demo.prototype.init = function () {
this.showStopDemo();
};
// 显示演示模式按钮
Demo.prototype.showStopDemo = function () {
const lib = this.lib;
const stopDemo = new lib.stopDemo();
stopDemo.x = 50;
stopDemo.y = lib.properties.height - 100;
this.stopDemo = stopDemo;
// 关闭演示模式
this.stopDemo.addEventListener(
'click',
function () {
stage.removeChild(stopDemo);
location.hash = '';
location.reload();
isDemo = false;
},
false,
);
stage.addChild(stopDemo);
};

65
src/classes/end.js

@ -0,0 +1,65 @@
/**
* 游戏结束
* @param {number} score 最终得分
*
* @property {object} lib 资源对象
* @property {number} score 最终得分
* @property {object} modal 面板元素对象
* @property {object} text 文本元素对象
*/
function End(score) {
this.lib = library;
this.score = score;
this.modal = null;
this.text = null;
}
/**
* 静态方法 封装new init 返回实例
* @param {number} score 最终得分
* @returns
*/
End.of = (function () {
let instance = null;
return function (score) {
if (!instance) {
instance = new End(score);
}
instance.init();
return instance;
};
})();
// 初始化
End.prototype.init = function () {
this.renderModal();
this.renderText(this.score);
Again.of();
};
// 渲染背景面板
End.prototype.renderModal = function () {
const lib = this.lib;
const target = new lib.bgModalEnd();
target.x = lib.properties.width / 2;
target.y = lib.properties.height / 2;
this.modal = target;
stage.addChild(target);
};
/**
* 渲染得分文本对象
* @param {number} score 最终得分
*/
End.prototype.renderText = function (score) {
const lib = this.lib;
const target = new createjs.Text(score, 'bold 100px Arial', '#793b18');
target.x = lib.properties.width / 2;
target.y = lib.properties.height / 2 + 70;
target.textAlign = 'center';
target.textBaseline = 'alphabetic';
this.text = target;
stage.addChild(target);
};

34
src/classes/level.js

@ -0,0 +1,34 @@
/**
* 难度等级类
* @param {number} level 等级数字代码
*/
function Level(level) {
this.lib = library;
this.level = level;
}
/**
* 静态方法 封装new init 返回实例
* @param {number} level 等级数值
* @returns
*/
Level.of = function (level) {
const instance = new Level(level);
instance.init();
return instance;
};
// 初始化 渲染
// 更新config中的level属性
Level.prototype.init = function () {
const initX = this.lib.properties.width / 2;
const initY = 80;
const target = new this.lib.Level();
target.x = initX;
target.y = initY;
target.gotoAndStop(`level-${this.level}`);
config.level = this.level;
stage.addChild(target);
};

86
src/classes/main.js

@ -0,0 +1,86 @@
/**
* 游戏主体
* @param {function} endCallback 游戏结束的回调函数
* @param {number} max 最多运动次数
*
* @property {object} lib 资源对象
* @property {object} element 主体元素对象
* @property {number} prevTime 上次完成的时间ms
* @property {number} max 最多运动(play)次数
* @property {number} times 当前运动次数 play依次+1
* @property {function} endCallback 结束后 调用的回调函数
*/
function Main(endCallback, max) {
this.lib = library;
this.element = null;
this.prevTime = 0;
this.max = max;
this.times = 0;
this.endCallback = endCallback;
}
/**
* 静态方法 封装new init 返回实例
* @param {function} endCallback 游戏结束的回调函数
* @param {number} max 最多运动次数
*/
Main.of = (function () {
let instance = null;
return function (endCallback, max = config.times) {
if (!instance) {
instance = new Main(endCallback, max);
}
instance.init();
return instance;
};
})();
// 初始方法
Main.prototype.init = function () {
const target = new this.lib.Main();
this.element = target;
stage.addChild(target);
};
// play
// 限制了两次动作间隔时间不能少于2s
// 游戏状态在进行中才能触发
// play次数 >= 最多完成次数 调用结束的callback
Main.prototype.play = function (direction) {
if (Date.now() - this.prevTime <= 1200 || state !== 1) return;
// if (state !== 1) return;
this.element.play();
this.times += 1;
this.computeScore(this.times, direction);
this.setMusic(direction);
this.prevTime = Date.now();
if (this.times >= this.max) {
this.times = this.max;
this.endCallback();
}
};
/**
* 计算当前的次数与分值 并发送给父窗口
* @param {number} times 当前动作执行成功的次数
* @param {number} direction play code
*/
Main.prototype.computeScore = function (times, direction = 0) {
const directionTarget = config.config.scores.find(item => item.direction === direction);
config.currentTimes = times;
config.currentScore += directionTarget.score;
if (config.mode === 0) {
sendMessage({ event: 'play', data: { currentTimes: times, currentScore: config.currentScore } });
}
};
Main.prototype.setMusic = function (direction) {
let audio1 = document.getElementById('audio1');
if (direction === 0) {
audio1.play();
}
};

47
src/classes/sound.js

@ -0,0 +1,47 @@
// 音频处理类;
function Sound() {
this.lib = library;
this.music = {};
}
Sound.of = (function () {
let instance = null;
return function () {
if (!instance) {
instance = new Sound();
}
instance.init();
return instance;
};
})();
Sound.prototype.init = function () {
createjs.Sound.alternateExtensions = ['mp3'];
const sounds = [{ src: 'sounds/bgmMusic.mp3', id: 'bgm' }];
const _this = this;
createjs.Sound.addEventListener('fileload', function (event) {
_this.music[event.id] = createjs.Sound.createInstance(event.id);
});
createjs.Sound.registerSounds(sounds, './');
this.playBgm();
};
Sound.prototype.playBgm = function () {
this.music.bgm && this.music.bgm.play({ loop: -1, volume: 0.3 }); // 播放背景音乐
};
// let music = {};
// function initSound() {
// createjs.Sound.alternateExtensions = ['mp3'];
// const sounds = [
// { src: 'public/sounds/bgmMusic.mp3', id: 'bgm' },
// { src: 'public/sounds/excitationMusic.mp3', id: 'excitation' },
// { src: 'public/sounds/amazing.mp3', id: 'amazing' },
// { src: 'public/sounds/unbelievable.mp3', id: 'unbelievable' },
// ];
// createjs.Sound.addEventListener('fileload', function (event) {
// music[event.id] = createjs.Sound.createInstance(event.id);
// });
// createjs.Sound.registerSounds(sounds, '../../');
// }

55
src/classes/suspend.js

@ -0,0 +1,55 @@
/**
* 暂停游戏类暂停继续游戏
* @property {object} lib 库对象
* @property {object} suspend 面板元素对象
*/
function Suspend() {
this.lib = library;
this.suspend = null;
}
Suspend.of = (function () {
let instance = null;
return function () {
if (!instance) {
instance = new Suspend();
}
instance.init();
return instance;
};
})();
// 初始化
Suspend.prototype.init = function () {
this.suspendGame();
};
// 暂停游戏按钮
Suspend.prototype.suspendGame = function () {
const lib = this.lib;
const suspend = new lib.suspend();
suspend.x = 50;
suspend.y = lib.properties.height - 100;
this.suspend = suspend;
const _this = this;
// 继续游戏
this.suspend.addEventListener(
'click',
function () {
_this.hide();
continueMessage();
},
false,
);
// stage.addChild(this.suspend);
// stage.removeChild(this.suspend);
};
Suspend.prototype.hide = function () {
stage.removeChild(this.suspend);
};
Suspend.prototype.show = function () {
stage.addChild(this.suspend);
};

126
src/classes/time.js

@ -0,0 +1,126 @@
/**
* 游戏时长 计时
* @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 () {
let instance = null;
return function (endCallback, duration = config.duration || 60, count = config.count || 5) {
if (!instance) {
instance = new Time(endCallback, duration, count);
}
instance.init();
return instance;
};
})();
// 初始化
Time.prototype.init = function () {
this.renderBg();
this.renderText(this.duration);
};
// 设置时长
Time.prototype.setDuration = function (duration) {
this.duration = duration;
this.renderText(this.duration);
};
// 开始游戏 开始倒计时
Time.prototype.start = function (startTime = Date.now()) {
this.started = true;
state = 1;
this.startTime = startTime;
this.duration = leftDuration ? leftDuration : this.duration;
this.update();
main.play();
};
// 暂停游戏 暂停倒计时
Time.prototype.pause = function () {
this.started = false;
state = 3;
clearTimeout(this.timerId);
this.timerId = null;
};
// 结束游戏 结束倒计时
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) {
if (this.text) stage.removeChild(this.text);
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.duration * 1000;
this.timerId = setInterval(() => {
// let leftTime = Math.round((endTime - Date.now()) / 1000);
this.duration--;
leftDuration = this.duration;
this.text && stage.removeChild(this.text);
if (this.duration <= 0) {
this.duration = 0;
this.end();
}
this.renderText(this.duration);
}, 1000);
};

56
src/custom.js

@ -0,0 +1,56 @@
const config = {
count: 5, // 默认倒计时时长
duration: 60, // 总时长 s
total: 100, // 总分
times: 15, // 动作次数
level: 1, // 游戏难度级别
mode: 1, // 模式 0-> 正常模式 1-> 演示模式
currentScore: 0, // 当前得分
currentTimes: 0, // 当前次数
config: {
scores: [
{ direction: 0, score: 5 },
{ direction: 1, score: 10 },
],
directions: [0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0],
}, // 得分配置
};
let library = null;
let state = 0; // 游戏状态 0->未开始 1->进行中 2->结束 3->暂停
let isDemo = false; // 是不是演示模式
let leftDuration = null; // 暂停时的时间
function initStage(lib) {
library = lib;
window.main = Main.of(gameOver); // 初始化鸟等
window.timeInstance = Time.of(gameOver); // 初始化游戏时间
window.soundInstance = Sound.of(); // 初始化音频
Level.of(config.level); // 游戏难度级别
Back.of(); // 返回按钮
isHash();
window.suspend = Suspend.of();
window.addEventListener(
'click',
function () {
if (!window.soundInstance) return;
window.soundInstance.playBgm();
},
false,
);
}
// 游戏结束 显示结束得分面板
function gameOver() {
state = 2;
const times = main.times;
setTimeout(() => {
End.of(config.currentScore || 0);
if (config.mode === 0) {
finishMessage(config.currentScore, times);
}
}, 2000);
}

50
src/index.js

@ -0,0 +1,50 @@
var canvas, stage, exportRoot, anim_container, dom_overlay_container, fnStartAnimation;
function init() {
canvas = document.getElementById('canvas');
anim_container = document.getElementById('animation_container');
dom_overlay_container = document.getElementById('dom_overlay_container');
var comp = AdobeAn.getComposition('903396EEDEAE5046BAF0A48EB7A12FC3');
var lib = comp.getLibrary();
var loader = new createjs.LoadQueue(false);
loader.addEventListener('fileload', function (evt) {
handleFileLoad(evt, comp);
});
loader.addEventListener('complete', function (evt) {
handleComplete(evt, comp);
});
var lib = comp.getLibrary();
loader.loadManifest(lib.properties.manifest);
}
function handleFileLoad(evt, comp) {
var images = comp.getImages();
if (evt && evt.item.type == 'image') {
images[evt.item.id] = evt.result;
}
}
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 (let 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);
stage.enableMouseOver();
//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();
}

132
src/message.js

@ -0,0 +1,132 @@
window.addEventListener(
'message',
function (e) {
const res = e.data;
console.log('子->接受: ', res);
switch (res.event) {
case 'start':
startGame(res.data);
return;
case 'play':
playGame(res.data);
return;
case 'pause':
pauseGame(res.data);
return;
case 'continue':
continueGame(res.data);
return;
default:
finishGame(res.data);
return;
}
function startGame(data) {
if (state === 2) {
location.reload();
return;
}
const { count, game, status, param } = data;
config.count = count.duration;
config.duration = game.duration;
config.total = game.totalScore;
config.times = game.totalTimes;
config.level = game.level;
config.config = game.config;
config.mode = game.mode;
window.timeInstance.setDuration(game.duration);
Level.of(config.level);
if (config.mode === 0) {
// 开始倒计时
Count.of(countOver);
} else {
sevenClick();
}
if (!window.soundInstance) return;
window.soundInstance.playBgm();
}
function playGame(data) {
if (state !== 1) return;
const { status, param } = data;
// config.currentScore = score;
// config.currentTimes = times;
state = status;
if (config.config.directions[config.currentTimes] === param.direction) {
main.play(param.direction);
} else {
alert('动作不匹配');
}
}
function pauseGame(data) {
if (state !== 1) return;
state = data.status;
window.suspend.show();
window.timeInstance.pause();
}
function continueGame(data) {
if (state !== 3) return;
state = data.status;
window.suspend.hide();
window.timeInstance.start();
}
function finishGame(data) {
const { score, times, status, param } = data;
state = status;
// config.total = score;
// config.times = times;
End.of(score || 0);
window.timeInstance.setDuration(0);
}
},
false,
);
// 发消息
function sendMessage(data) {
if (!data) {
return alert('错误: 发送消息数据为空');
}
console.log('子->发送: ', data);
top.postMessage(data, document.referrer);
}
// 发送继续游戏消息
function continueMessage() {
const data = {
event: 'continue',
data: {
status: 1, // 1 -> 进行中
},
};
sendMessage(data);
}
// 发送游戏结束消息
function finishMessage(score, times) {
const data = {
event: 'finish',
data: {
score: score, // 得分
times: times, // 次数
status: 2, // 游戏状态 0 1 2
param: {}, // 额外个性化参数
},
};
sendMessage(data);
}
// 发送再来一次消息
function againMessage() {
const data = {
event: 'again',
data: {
param: {}, // 额外个性化参数
},
};
sendMessage(data);
}

57
src/test.js

@ -0,0 +1,57 @@
// 判断是否在演示模式 能否直接开始游戏
function isHash() {
if (location.hash && location.hash === '#p') {
isDemo = true;
Demo.of();
Count.of(countOver);
} else {
// 连点7次触发倒计时
startGame();
}
}
function startGame() {
var count = 0,
timer;
document.onclick = function () {
if (isDemo || config.mode !== 1) return;
if (count < 6) {
if (timer) {
clearTimeout(timer);
}
count++;
timer = setTimeout(function () {
count = 0;
}, 300);
} else if (count === 6) {
count = 0;
clearTimeout(timer);
sevenClick();
}
};
}
function sevenClick() {
isDemo = true;
Count.of(countOver);
addHash();
}
// 倒计时结束 开始游戏
function countOver() {
timeInstance.start();
}
// 添加hash值
function addHash() {
location.hash = '#p';
Demo.of();
}
function test() {
document.addEventListener('click', () => {
if (!isDemo) return;
main.play(config.config.directions[config.currentTimes]);
});
}
test();
Loading…
Cancel
Save