Commit 3f7249ab authored by 熊东起's avatar 熊东起

flex:--11

parents
# project ignores
node_modules
released
.DS_Store
.idea
output.js
output.js.map
\ No newline at end of file
# 推箱子 游戏解析
推箱子小游戏是很多人的童年回忆.
今天是 6.1,我们就用简单的代码来实现一下.
## 推箱子需求
思考:
1、需要一个代码开发环境.
2、需要一个 canvas 引擎.
3、需要准备一个人物来做我们的主人公,一个箱子,一个箱子推放的终点,一个燃烧的箱子,还有我们的游戏地图(墙体).
## 一、简单搭建一个 TS 环境
TS 不能直接在浏览器中运行,我们需要使用`ts-loader`.
使用`webpack`打包.
```js
entry: './src/main.ts',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
output: {
filename: 'output.js',
path: __dirname,
libraryTarget: 'umd',
}
```
指定根目录下一个`tsconfig.json`,来进行项目的一些配置.
```js
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"noImplicitAny": false,
"sourceMap": true,
"removeComments": true,
"noEmitOnError":true,
"outDir":"dist",
"lib": [
"es5",
"dom",
"es2015.promise"
]
},
"exclude": [
"node_modules"
]
}
```
具体配置这里不展开细讲.
至此一个简单的 TS 开发环境就完成了.
## 二、引入游戏引擎
游戏引擎只是为了我们更好的使用`canvas`.
有兴趣的同学可以了解下`pixi`, `egret`,`phaser`,`laya`,`cocos`等优秀的游戏引擎.其应用但不局限于游戏.
这里我们使用可梦引擎`fyge`.(兑吧专用).
使用方法:
1、使用 CDN 引入
```js
<script
src="//yun.duiba.com.cn/db_games/libs0924/fyge2020.min.js"
crossorigin="anonymous"
></script>
```
2、使用本地文件
```js
<script src="libs/fyge.min.js"></script>
```
3、使用 npm
```js
npm i fyge //具体名字问大师兄
```
我们这里使用本地文件.
引入`FYGE.d.ts`便于查找 API.
## 三、搭建简单的舞台
创建一个入口类`Main`;
初始化我们的`canvas`画布.
```js
var stage = new FYGE.Stage(
canvas,
750, //设计宽度,按设计搞给的就行
1624, //设计高度
document.body.clientWidth,
document.body.clientHeight,
FYGE.RENDERER_TYPE.WEBGL,
false, //视窗居中裁切
)
```
舞台创建完成之后,我们需要每一帧更新舞台,使舞台动起来.
```js
stage.flush()
```
在舞台`stage`加载完成之后就可以加载我们的游戏场景了.
游戏场景中包括一些显示对象和一些游戏逻辑.
```js
const gameScene: GameScene = this.stage.addChild(new GameScene())
```
## 四、开始我们的游戏场景
首先,完成一个推箱子游戏,需要些什么?
1、准备素材
墙体,箱子,着火的箱子,箱子的终点,人物
![素材](//yun.duiba.com.cn/spark/assets/453d81424cef82ca68d29dec7861d2c9028a0685.png '素材')
2、编辑场景地图
为了可扩展,我们选用 20\*20 的二维数组来编辑我们的地图.
定义数据映射关系.
0 -- 背景
1 -- 墙壁
2 -- 地板
3 -- 箱子
4 -- 终点
5 -- 人
6 -- 着火的箱子
```js
/** 格子 0 无 1 墙壁 2 地板 3 箱子 4 箱子终点 5 人 6 着火的箱子 */
export enum BLOCK {
wall = 1,
ground = 2,
box = 3,
end = 4,
player = 5,
badBox = 6
}
```
我们先来编辑第一关的地图.
```js
[
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 3, 2, 3, 4, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 4, 2, 3, 5, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]
```
地图编辑完成之后,我们在舞台上渲染场景如下:
```js
/** 使用精灵图 */
this.mapContainer.addChild(FYGE.Sprite.fromUrl(RES.box))
```
![场景](//yun.duiba.com.cn/spark/assets/bd0e11ef7c3abb1f790b2c58cd98854c004e3d2b.png '场景')
这里我们绘制场景,使用数据映射关系,每一个格子`80*80`;
其实我们推箱子的原理就是更新数据结构.
每走一步,就是更新我们的地图,通过新的二维数组,渲染新的地图,达到视觉上推箱子的效果.
## 五、开始推箱子
这里我们简单点,直接监听键盘事件`document.onkeydown`.
通过`e.key`判断上`w`、下`s`、左`a`、右`d`.
我们判断一个格子是否可以更新(推动),需要判断下一个位置和下下个位置.
所以我们需要记录这两个数组位置:
```js
let p1 = {
x: 0,
y: 0,
}
let p2 = {
x: 0,
y: 0,
}
```
1、什么样的情况我们推不动?
- 推动方向是墙体
- 推动方向是箱子,箱子同一个方向的下一个位置是箱子,墙体(这里分为着火的箱子和目标箱子)
2、什么样的情况下我们可以移动?
- 下一个位置是空地
- 下一个位置是箱子终点
- 下一个位置是箱子,下下个位置是空地
- 下一个位置是箱子,下下个位置是箱子终点(分俩箱子)
3、注意
- 当前人物位置是箱子终点时,需要还原
思考完成之后我们就可以写代码了.
```js
const { x: x1, y: y1 } = p1;
const { x: x2, y: y2 } = p2;
const { x, y } = this.playerPointer;
//不能移动 墙体
if (this.curMap[x1][y1] == BLOCK.wall) {
return false;
}
...
// 可以移动 地板
if (this.curMap[x1][y1] == BLOCK.ground || this.curMap[x1][y1] == BLOCK.end) {
this.curMap[x][y] = BLOCK.ground;
this.curMap[x1][y1] = BLOCK.player;
}
....
if (LEVEL[`LV_${this.lv}`][x][y] == BLOCK.end) {
this.curMap[x][y] = BLOCK.end;
}
```
然后使用新的二维数组去渲染出场景.
这个时候我们的推箱子基本就要大功告成了.
## 六、判断结果
怎么样算是过关呢?
比较关卡数组和当前数组.
当每一个箱子的终点都变成箱子的时候就过关了.
当然,我们也可以加上步数限制.
```js
/** 判断是否结束 */
private gameOver() {
const mapList = LEVEL[`LV_${this.lv}`];
for (let i = 0; i < mapList.length; i++) {
for (let j = 0; j < mapList[i].length; j++) {
if (mapList[i][j] == BLOCK.end && this.curMap[i][j] != BLOCK.box) {
return false;
}
}
}
return true;
}
```
还可以加上关卡和步数显示.
## 七、切换关卡
怎么到下一关呢?
其实也是更新数据啦.
数据关系映射好,直接进行关卡重新绘制即可.
同样,可以加个按钮,点击可以重玩本关.
```js
/** 初始化游戏 */
initGame() {
this.step = 0;
this.curMap = GUtils.copyArray(LEVEL[`LV_${this.lv}`]);
this.renderMap();
}
/** 重新开始本关 */
resetGame() {
this.initGame();
}
```
至此,大功告成.
六一写个推箱子助助兴,回忆童年时光.
\ No newline at end of file
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Document</title>
<meta name="viewport"
content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="full-screen" content="true" />
<meta name="screen-orientation" content="portrait" />
<meta name="x5-fullscreen" content="true" />
<meta name="360-fullscreen" content="true" />
<!-- 渲染引擎 -->
<!-- <script src="//yun.duiba.com.cn/db_games/libs0924/fyge2020.min.js" crossorigin="anonymous"></script> -->
<!-- svga解析库 -->
<!-- <script src="//yun.duiba.com.cn/db_games/libs0924/svgaParser.minWeb.js" crossorigin="anonymous"></script> -->
<!-- 本地的js,需要souremap调试的用本地js -->
<script src="libs/fyge.min.js"></script>
<script src="//yun.duiba.com.cn/db_games/libs0924/howler.min.js"></script>
<!-- <script src="libs/svgaParser.min.js"></script> -->
<!-- <script src="libs/svgaParser.min1.js"></script> -->
<style>
html,
body {
padding: 0;
margin: 0;
border: 0;
width: 100%;
height: 100%;
overflow: hidden;
position: absolute;
background-color: #73ad45;
/* background: linear-gradient(#93dbb7,#ff0,#b5d89a); */
/* background: linear-gradient(#93dbb7,#b5d89a); */
/* 背景图片,解决加载太慢,白屏问题,加了这个下面的__loading__可以删掉了 */
/* background-size: 100%;
background-position: center;
background-image: url("https://yun.duiba.com.cn/db_games/activity/game/1550472986/resource/assets/playscene/playscenebg.jpg"); */
}
</style>
</head>
<body>
<div id="cusEngine" style="line-height:0;font-size:0;position: absolute;">
<canvas id="canvas" style="width: 100%;height: 100%"></canvas>
</div>
<!-- 帧率检测 -->
<!-- <script src="//yun.duiba.com.cn/db_games/libs0924/stats.js"></script> -->
<!-- <script src="https://yun.duiba.com.cn/db_games/libs0126/stats.js"></script> -->
<script src="libs/matter.min.js"></script>
<script type="text/javascript" src="//yun.duiba.com.cn/js-libs/vConsole/3.3.4/vconsole.min.js"
crossorigin="anonymous"></script>
<script>
var vConsole = new VConsole();
window.addEventListener("load", function () {
//获取canvas
var canvas = document.getElementById("canvas");
canvas.width = document.body.clientWidth * (window.devicePixelRatio || 1);
canvas.height = document.body.clientHeight * (window.devicePixelRatio || 1);
var main = new Main(canvas);
var mouseEvent = main.stage.onMouseEvent.bind(main.stage);
canvas.addEventListener("touchstart", mouseEvent, false);
canvas.addEventListener('touchmove', mouseEvent, false);
canvas.addEventListener('touchend', mouseEvent, false);
window.stage = main.stage
})
</script>
<!-- 构建的js -->
<script src="output.js" crossorigin="anonymous"></script>
</body>
</html>
\ No newline at end of file
This diff is collapsed.
declare module SvgaParser {
/**
* 加载方法
* @param url 资源路径
* @param success
* @param failure
*/
export function loadSvga(url: string, success: (videoItem: VideoEntity) => void, failure?: (err: string) => void): void;
/**
* 导出只是当作类型接口用
*/
export interface VideoEntity {
/**
* SVGA 文件版本
*/
version: string;
/**
* 影片尺寸
*/
videoSize: {
width: number;
height: number;
};
/**
* 帧率,60,30等每秒
*/
FPS: number;
/**
* 总帧数
*/
frames: number;
/**
* base64图片数据记录
*/
images: {
[key: string]: string
};
/**
* 图片是否已被缓存,缓存全局,注意名字覆盖
*/
hasBeenCached: boolean;
/**
* sprite对象数据
*/
sprites: SpriteEntity[];
}
interface SpriteEntity {
/**
* 标识
*/
matteKey: string;
/**
* 图片key值
*/
imageKey: string;
/**
* 帧数据数组
*/
frames: FrameEntity[];
}
/**
* 还有很多其他数据,暂不需要,比如矢量路径和遮罩路径暂时都无
*/
interface FrameEntity {
/**
* 透明度
*/
alpha: number;
/**
* 2维矩阵数据
*/
transform: {
a: number,
b: number,
c: number,
d: number,
tx: number,
ty: number,
};
}
}
declare module "svga-parser" { export = SvgaParser; }
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
// Type definitions for howler.js v2.1.1
// Project: https://github.com/goldfire/howler.js
// Definitions by: Pedro Casaubon <https://github.com/xperiments>
// Alexander Leon <https://github.com/alien35>
// Nicholas Higgins <https://github.com/nicholashza>
// Carlos Urango <https://github.com/cjurango>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
interface HowlerGlobal {
mute(muted: boolean): this;
volume(): number;
volume(volume: number): this;
codecs(ext: string): boolean;
unload(): this;
usingWebAudio: boolean;
html5PoolSize: number;
noAudio: boolean;
autoUnlock: boolean;
autoSuspend: boolean;
ctx: AudioContext;
masterGain: GainNode;
stereo(pan: number): this;
pos(x: number, y: number, z: number): this | void;
orientation(x: number, y: number, z: number, xUp: number, yUp: number, zUp: number): this | void;
}
declare let Howler: HowlerGlobal;
interface IHowlSoundSpriteDefinition {
[name: string]: [number, number] | [number, number, boolean]
}
interface IHowlProperties {
src: string | string[];
volume?: number;
html5?: boolean;
loop?: boolean;
preload?: boolean;
autoplay?: boolean;
mute?: boolean;
sprite?: IHowlSoundSpriteDefinition;
rate?: number;
pool?: number;
format?: string[] | string;
xhrWithCredentials?: boolean;
onload?: () => void;
onloaderror?: (soundId: number, error: any) => void;
onplay?: (soundId: number) => void;
onplayerror?: (soundId: number, error: any) => void;
onend?: (soundId: number) => void;
onpause?: (soundId: number) => void;
onstop?: (soundId: number) => void;
onmute?: (soundId: number) => void;
onvolume?: (soundId: number) => void;
onrate?: (soundId: number) => void;
onseek?: (soundId: number) => void;
onfade?: (soundId: number) => void;
onunlock?: (soundId: number) => void;
}
interface Howl {
play(spriteOrId?: string | number): number; // .play() is not chainable; the other methods are
pause(id?: number): this;
stop(id?: number): this;
mute(): boolean;
mute(muted: boolean, id?: number): this;
volume(): number;
volume(idOrSetVolume: number): this | number;
volume(volume: number, id: number): this;
fade(from: number, to: number, duration: number, id?: number): this;
rate(): number;
rate(idOrSetRate: number): this | number;
rate(rate: number, id: number): this;
seek(seek?: number, id?: number): this | number;
loop(id?: number): boolean;
loop(loop: boolean, id?: number): this;
playing(id?: number): boolean;
duration(id?: number): number;
state(): 'unloaded' | 'loading' | 'loaded';
load(): this;
unload(): void;
on(event: 'load', callback: () => void, id?: number): this;
on(event: 'loaderror', callback: (soundId: number, error: any) => void, id?: number): this;
on(event: 'play', callback: (soundId: number) => void, id?: number): this;
on(event: 'playerror', callback: (soundId: number, error: any) => void, id?: number): this;
on(event: 'end', callback: (soundId: number) => void, id?: number): this;
on(event: 'pause', callback: (soundId: number) => void, id?: number): this;
on(event: 'stop', callback: (soundId: number) => void, id?: number): this;
on(event: 'mute', callback: (soundId: number) => void, id?: number): this;
on(event: 'volume', callback: (soundId: number) => void, id?: number): this;
on(event: 'rate', callback: (soundId: number) => void, id?: number): this;
on(event: 'seek', callback: (soundId: number) => void, id?: number): this;
on(event: 'fade', callback: (soundId: number) => void, id?: number): this;
on(event: string, callback: Function, id?: number): this;
on(event: 'unlock', callback: (soundId: number) => void, id?: number): this;
once(event: 'load', callback: () => void, id?: number): this;
once(event: 'loaderror', callback: (soundId: number, error: any) => void, id?: number): this;
once(event: 'play', callback: (soundId: number) => void, id?: number): this;
once(event: 'playerror', callback: (soundId: number, error: any) => void, id?: number): this;
once(event: 'end', callback: (soundId: number) => void, id?: number): this;
once(event: 'pause', callback: (soundId: number) => void, id?: number): this;
once(event: 'stop', callback: (soundId: number) => void, id?: number): this;
once(event: 'mute', callback: (soundId: number) => void, id?: number): this;
once(event: 'volume', callback: (soundId: number) => void, id?: number): this;
once(event: 'rate', callback: (soundId: number) => void, id?: number): this;
once(event: 'seek', callback: (soundId: number) => void, id?: number): this;
once(event: 'fade', callback: (soundId: number) => void, id?: number): this;
once(event: string, callback: Function, id?: number): this;
once(event: 'unlock', callback: (soundId: number) => void, id?: number): this;
off(event: string, callback?: Function, id?: number): this;
off(): this;
stereo(pan: number, id?: number): this | void;
pos(x: number, y: number, z: number, id?: number): this | void;
orientation(x: number, y: number, z: number, xUp: number, yUp: number, zUp: number): this | void;
pannerAttr(o: {
coneInnerAngle?: number,
coneOuterAngle?: number, coneOuterGain?: number,
distanceModel: 'inverse' | 'linear', maxDistance: number,
panningModel: 'HRTF' | 'equalpower', refDistance: number, rolloffFactor: number
}, id?: number): this;
}
interface HowlStatic {
new(properties: IHowlProperties): Howl;
}
declare let Howl: HowlStatic;
declare module "howler" {
export let Howler: HowlerGlobal;
export let Howl: HowlStatic;
}
This diff is collapsed.
This diff is collapsed.
var Stats = function () {
var startTime = Date.now(), prevTime = startTime;
var ms = 0, msMin = Infinity, msMax = 0;
var fps = 0, fpsMin = Infinity, fpsMax = 0;
var frames = 0, mode = 0;
var container = document.createElement('div');
container.id = 'stats';
container.addEventListener('mousedown', function (event) { event.preventDefault(); setMode(++mode % 2) }, false);
container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer';
var fpsDiv = document.createElement('div');
fpsDiv.id = 'fps';
fpsDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#002';
container.appendChild(fpsDiv);
var fpsText = document.createElement('div');
fpsText.id = 'fpsText';
fpsText.style.cssText = 'color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px';
fpsText.innerHTML = 'FPS';
fpsDiv.appendChild(fpsText);
var fpsGraph = document.createElement('div');
fpsGraph.id = 'fpsGraph';
fpsGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0ff';
fpsDiv.appendChild(fpsGraph);
while (fpsGraph.children.length < 74) {
var bar = document.createElement('span');
bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#113';
fpsGraph.appendChild(bar);
}
var msDiv = document.createElement('div');
msDiv.id = 'ms';
msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;display:none';
container.appendChild(msDiv);
var msText = document.createElement('div');
msText.id = 'msText';
msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px';
msText.innerHTML = 'MS';
msDiv.appendChild(msText);
var msGraph = document.createElement('div');
msGraph.id = 'msGraph';
msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0';
msDiv.appendChild(msGraph);
while (msGraph.children.length < 74) {
var bar = document.createElement('span');
bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131';
msGraph.appendChild(bar);
}
var setMode = function (value) {
mode = value;
switch (mode) {
case 0:
fpsDiv.style.display = 'block';
msDiv.style.display = 'none';
break;
case 1:
fpsDiv.style.display = 'none';
msDiv.style.display = 'block';
break;
}
}
var updateGraph = function (dom, value) {
var child = dom.appendChild(dom.firstChild);
child.style.height = value + 'px';
}
return {
REVISION: 11,
domElement: container,
setMode: setMode,
begin: function () {
startTime = Date.now();
},
end: function () {
var time = Date.now();
ms = time - startTime;
msMin = Math.min(msMin, ms);
msMax = Math.max(msMax, ms);
msText.textContent = ms + ' MS (' + msMin + '-' + msMax + ')';
updateGraph(msGraph, Math.min(30, 30 - (ms / 200) * 30));
frames++;
if (time > prevTime + 1000) {
fps = Math.round((frames * 1000) / (time - prevTime));
fpsMin = Math.min(fpsMin, fps);
fpsMax = Math.max(fpsMax, fps);
fpsText.textContent = fps + ' FPS (' + fpsMin + '-' + fpsMax + ')';
updateGraph(fpsGraph, Math.min(30, 30 - (fps / 100) * 30));
prevTime = time;
frames = 0;
}
return time;
},
update: function () {
startTime = this.end();
}
}
};
//执行
var stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
document.body.appendChild(stats.domElement);
aa();
function aa() {
stats.update();
requestAnimationFrame(aa)
}
\ No newline at end of file
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "babylon_demo1",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack --config webpack.prod.js",
"dev": "webpack-dev-server --open --config webpack.dev.js --host 0.0.0.0"
},
"author": "",
"license": "ISC",
"devDependencies": {
"ts-loader": "4.0.0",
"typescript": "^4.2.4",
"webpack": "^4.1.0",
"webpack-cli": "^3.1.1",
"webpack-dev-server": "^3.1.0",
"webpack-merge": "^4.1.2"
},
"dependencies": {
}
}
{
"type": "activity",
"name": "template"
}
\ No newline at end of file
/**
* Created by _xdq on 2021/04/07.
* 公共方法
* @export
* @class GUtils
*/
export class GUtils {
/** 随机数 */
static getRandom(max: number, min: number): number {
return Number((Math.random() * (max - min) + min).toFixed(1));
}
/** 获取角度 */
static getAngle(dy: number, dx: number): number {
const angle = Math.atan2(dy, dx) / Math.PI * 180;
return (angle + 360) % 360;
}
/** 判断整数 */
static isInteger(obj: any) {
return typeof obj === 'number' && obj % 1 === 0;
}
/** 拷贝数组 */
static copyArray(arr: Array<any>) {
let newArr = [];
for (var i = 0; i < arr.length; i++) {
newArr[i] = arr[i].concat();
}
return newArr;
}
}
\ No newline at end of file
/** 资源配置 */
export const RES = {
wall: "//yun.duiba.com.cn/spark/assets/5fe60952f141c695902a1a7428bc4bb1c254627c.jpeg",
}
/**
* Created by _xdq on 2021/04/07.
* 静态方法
* @export
* @class GameCfg
*/
export class GameCfg {
private static _instance: GameCfg;
public static get Ins(): GameCfg {
if (!this._instance)
this._instance = new GameCfg();
return this._instance;
}
/**
* 获得texture纹理
* @param {string} url
* @memberof GameCfg
*/
createTexture(url: string) {
let _texture: FYGE.Texture = url ? FYGE.Texture.fromUrl(url) : null;
_texture.baseTexture.premultipliedAlpha = false;
return _texture;
}
}
\ No newline at end of file
/**
* Created by _xdq on 2021/06/01.
* 推箱子场景
*/
import { GUtils } from "./GUtils";
import { DIR, BLOCK } from "./types";
export class GameScene extends FYGE.Container {
}
\ No newline at end of file
export const SoundType = {
boom: "//yun.duiba.com.cn/aurora/assets/e3c91bdea3e14eff8cc042a7481f61355edb8b13.mp3",
light: "//yun.duiba.com.cn/aurora/assets/739c5aef1331b32894473ec888d7b7d5220b4ede.mp3",
pick: "//yun.duiba.com.cn/aurora/assets/b4250ad1e151dd796a62cc94ebb704e65bdc3f8a.mp3"
}
let _isFlag = false;
/**
* 提前加载音频
* @param type
*/
export function preloadSound() {
for (let item in SoundType) {
soundHash[SoundType[item]] = new Howl({
src: SoundType[item],
preload: true,
});
}
}
/**
* 根据路径记录
*/
const soundHash: { [key: string]: Howl } = {};
export function playSound(src: string, loop: boolean = false) {
console.log("999---2222",_isFlag);
if (_isFlag) return;
//console.log('测试音效',src);
let sound: Howl;
//循环的,且有缓存,取缓存的
if (soundHash[src] && loop) sound = soundHash[src]
//没有就新建
if (!sound) sound = new Howl({ src: [src], autoplay: false, loop });
//记录下,方便停止
soundHash[src] = sound;
//不循环删除缓存
if (!loop) sound.on('stop', function () {
delete soundHash[src]
});
//播放
sound.play();
//console.log('测试是否播放音效',src);
//返回一个,可以自行控制
return sound;
}
export function stopSound(src: string) {
if (soundHash[src]) soundHash[src].stop();
}
export function stopAllSound() {
_isFlag = true;
console.log("999---1111",_isFlag);
for (let key in soundHash) soundHash[key].stop();
}
export function playAllSound() {
_isFlag = false;
// for (let key in soundHash) soundHash[key].play();
}
//设置隐藏属性和改变可见属性的事件的名称
let hidden: string, visibilityChange: string;
if (typeof document.hidden !== 'undefined') {
hidden = 'hidden';
visibilityChange = 'visibilitychange';
} else if (typeof document['msHidden'] !== 'undefined') {
hidden = 'msHidden';
visibilityChange = 'msvisibilitychange';
} else if (typeof document['webkitHidden'] !== 'undefined') {
hidden = 'webkitHidden';
visibilityChange = 'webkitvisibilitychange';
}
const handleVisibilityChange = (e) => {
if (document.visibilityState == "visible") {
playAllSound();
console.log("网页显示")
} else if (document.visibilityState == "hidden") {
stopAllSound()
console.log("网页隐藏")
}
};
document.addEventListener(
visibilityChange,
handleVisibilityChange,
false
);
window.onbeforeunload = function () {
//发接口
}
/** 格子 0 无 1 墙壁 2 地板 3 箱子 4 箱子终点 5 人 */
export enum BLOCK {
wall = 1,
ground = 2,
box = 3,
end = 4,
player = 5,
badBox = 6
}
/** 方向 */
export enum DIR {
UP = 1,
DOWN = 2,
LEFT = 3,
RIGHT = 4
}
\ No newline at end of file
import { GameScene } from "./GameScene/GameScene";
export class Main {
//主舞台
stage: FYGE.Stage;
private requestID;
private _pause: boolean;
private canvas: HTMLCanvasElement;
constructor(canvas: HTMLCanvasElement) {
//建舞台
var stage = new FYGE.Stage(
canvas,
750, //设计宽度,按设计搞给的就行
1624, //设计高度
document.body.clientWidth,
document.body.clientHeight,
FYGE.RENDERER_TYPE.WEBGL,
false //视窗居中裁切
);
this.stage = stage;
this.canvas = canvas; //赋值下,为了下面的destroy的cancelAnimationFrame
//stage初始化
stage.addEventListener(FYGE.Event.INIT_STAGE, this.onAddToStage, this);
//循环
var self = this;
loop();
function loop() {
if (!self._pause) {
FYGE.Tween.flush();
stage.flush();
}
//为了兼容多page的canvas
self.requestID = window.requestAnimationFrame(loop);
}
}
private async onAddToStage() {
const gameScene: GameScene = this.stage.addChild(new GameScene());
}
destroy() {
//Tween都移除,注意吧,可能原先的也被移除,,对于多page时注意,会把其他页面的也去掉
FYGE.Tween.removeAllTweens();
//停掉计时器
//@ts-ignore 为了兼容多page的canvas
window.cancelAnimationFrame(this.requestID);
//舞台销毁
this.stage.destroy();
}
}
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"noImplicitAny": false,
"sourceMap": true,
"removeComments": true,
"noEmitOnError":true,
"outDir":"dist",
"lib": [
"es5",
"dom",
"es2015.promise"
]
},
"exclude": [
"node_modules"
]
}
\ No newline at end of file
const path = require('path');
module.exports = {
entry: './src/main.ts',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
output: {
filename: 'output.js',
path: __dirname,
libraryTarget: 'umd',
}
};
\ No newline at end of file
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: "development",
devtool: 'eval-source-map',
devServer: {
contentBase: '.',
port: 9000,
hot: true
}
});
\ No newline at end of file
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const webpack = require('webpack');
module.exports = merge(common, {
mode: "production",
devtool: 'source-map',
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
]
});
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment