Commit 4906038f authored by 熊东起's avatar 熊东起

game

parents
Pipeline #302404 failed with stages
in 0 seconds
# project ignores
node_modules
released
.DS_Store
scripts/copyJs.js
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>泡泡浴</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" />
<!-- <meta name="viewport" content="width=device-width,minimum-scale=1.0,user-scalable=no"> -->
<!-- <script src="https://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script> -->
<!-- 小程序分享得用这个 -->
<!-- <script src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"></script> -->
<!-- 易盾js -->
<!-- <script type="text/javascript" src="//cstaticdun.126.net/load.min.js"></script> -->
<!-- <script src="libs/zepto.min.js"></script> -->
<!-- <script src="libs/p2.js"></script> -->
<script src="libs/fyge.min.js"></script>
<script src="libs/p2.min.js"></script>
<script src="libs/svgaParser.min.js"></script>
<style>
html,
body {
padding: 0;
margin: 0;
border: 0;
width: 100%;
height: 100%;
overflow: hidden;
position: absolute;
/* background-color: #000000; */
}
</style>
</head>
<body>
<script src="output.js"></script>
<!-- <div id="__loading__" style="position:absolute;left:50%;top:50%;margin-left:-45px;color:#ffffff">拼命加载中...</div> -->
<!-- <img src="" id="img" /> -->
<div id="cusEngine" style="line-height:0;font-size:0">
<canvas id="canvas" style="width: 100%;height: 100%"></canvas>
</div>
</body>
<!-- 帧率检测 -->
<!-- <script src="libs/stats.js"></script> -->
<script>
window.addEventListener("load", function() {
//是否完成新手
const scheduleFinished =0;
//目标分数
const targetScore = 120;
//是否音乐
const isMusic = true;
//时间
let tickSeconds = 30;
//获取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 BubbleBathMain(canvas,{scheduleFinished,targetScore,isMusic,tickSeconds});
// console.log(main.stage)
var mouseEvent = main.stage.onMouseEvent.bind(main.stage);
canvas.addEventListener("touchstart", mouseEvent, false);
canvas.addEventListener('touchmove', mouseEvent, false);
canvas.addEventListener('touchend', mouseEvent, false);
// localStorage.clear();
})
</script>
</html>
\ No newline at end of file
import { Main } from './Main';
Page({
//暂时先不用吧,有问题
data: {
},
onLoad(query) {
// 页面加载
console.info(`Page onLoad with query: ${JSON.stringify(query)}`);
},
onReady() {
return
my.development = true;
// my._createCanvas({
// id: 'canvas',
// success: (ccc) => {
// const dpr = my.getSystemInfoSync().pixelRatio
// const windowWidth = my.getSystemInfoSync().windowWidth;
// const windowHeight = my.getSystemInfoSync().windowHeight;
// ccc.width = windowWidth * dpr;
// ccc.height = windowHeight * dpr;
// // const context = canvas.getContext("2d");
// console.log(ccc.width, ccc.height, dpr)
// //初始化
// FYGE.initedByCanvas(ccc);
// //测试
// var aa = ccc.createImage();
// aa.src = "https://yun.duiba.com.cn/db_games/activity/etc/optionImages/%E5%8D%8E%E4%B8%BAP30.jpg";
// console.log(aa)
// //帧率控制
// FYGE.Stage.addFPS("canva4stats")
// this.main = new Main(ccc)
// console.log(ccc.width, ccc.height)
// }
// })
},
onCanvasReady() {
my.development = true;
my._createCanvas({
id: 'canvas',
success: (ccc) => {
const dpr = my.getSystemInfoSync().pixelRatio
const windowWidth = my.getSystemInfoSync().windowWidth;
const windowHeight = my.getSystemInfoSync().windowHeight;
ccc.width = windowWidth * dpr;
ccc.height = windowHeight * dpr;
this.main = new Main(ccc)
}
})
},
onShow() {
// 页面显示
// FYGE.Stage.pause = false
if (this.main) this.main.run();
},
onHide() {
// 页面隐藏
// FYGE.Stage.pause = true
if (this.main) this.main.pause();
},
onUnload() {
// 页面被关闭
// Stage.stop()
this.main.destroy();
},
onTitleClick() {
// 标题被点击
},
onPullDownRefresh() {
// 页面被下拉
},
onReachBottom() {
// 页面被拉到底部
},
onShareAppMessage() {
// 返回自定义分享信息
return {
title: 'My App',
desc: 'My App description',
path: 'pages/index/index',
};
},
log(e) {
if (this.main) this.main.stage.onMouseEvent(e)
},
});
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 source diff could not be displayed because it is too large. You can view the blob instead.
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 source diff could not be displayed because it is too large. You can view the blob instead.
const path = require('path');
const config = {
"/projectx/projectId/scoring_1/submit.do": {
data: './json/finalSubmit.json'
}
}
for (let item in config) {
/* let key = item.replace(new RegExp("{", "g"), "");
key = key.replace(new RegExp("}", "g"), "");
config[key] = config[item]; */
if (config.hasOwnProperty(item))
config[item].path = path.resolve(__dirname, config[item].data);
}
module.exports = config;
\ No newline at end of file
{
"success": true,
"code": "000",
"data": {
"score": 3456
},
"message": "no"
}
\ No newline at end of file
This diff is collapsed.
import { destroyWaiting } from "./waitingCtrl";
import { Panel } from "../views/Panel";
import PanelCtrl from "./panelCtrl";
import SceneCtrl from "./sceneCtrl";
import { Scene } from "../views/Scene";
import { destroyToast } from "./toastCtrl";
export { showToast } from "./toastCtrl";
export * from "./waitingCtrl";
/**
* 展示弹框
* @param panel 弹框类
* @param data 数据
*/
export const showPanel = (panel: any, data?: any) => {
PanelCtrl.instance.show(panel, data)
}
/**
* 关闭所有弹框
*/
export const closeAllPanels = () => {
PanelCtrl.instance.closeAll();
}
/**
* 关闭当前弹框
*/
export const closeCurrentPanel = () => {
PanelCtrl.instance.closeCurrent();
}
/**
* 替换场景
* @param scene
* @param data
*/
export const changeScene = (scene: any, data?: any) => {
SceneCtrl.instance.change(scene, data)
}
/**
* 获取当前场景
*/
export function getCurrentScene(): any {
return SceneCtrl.instance.currentScene
}
/**
* 淘宝小程序的alert
* @param {string} title
* @param {string} content
*/
export const showAlert = (title?: string, content?: string) => {
//@ts-ignore
if (my) {
//@ts-ignore
my.alert({
title: title || "",
content: content || ""
});
} else {
console.log(title, content)
}
}
/**
* 替换setTimeout 因为页面销毁时setTimeout不会停
* 所以干脆用Tween的
* @param {Function} callback
* @param {number} time 毫秒计
*/
export function wait(callback: () => void, time: number): {} {
let obj = {};
FYGE.Tween.get(obj)
.wait(time)
.call(callback)
return obj
}
export function clearWait(obj={}) {
FYGE.Tween.removeTweens(obj);
}
/**
* 销毁方法
*/
export function destroyAllCtrls() {
destroyToast();
destroyWaiting();
PanelCtrl.instance.destroy();
SceneCtrl.instance.destroy();
}
\ No newline at end of file
import { Panel } from "../views/Panel";
import { layers } from "../views/layers";
import { showWaiting, hideWaiting } from "./waitingCtrl";
import { showToast } from "./toastCtrl";
export default class PanelCtrl {
/**
* 父级容器
*/
private _parent: FYGE.Container;
/**
* 半透明黑色背景
*/
private _bg: FYGE.Graphics;
/**
* 所有的弹框
*/
private stacks: Panel[] = [];
private static _instance: PanelCtrl;
static get instance() {
return PanelCtrl._instance || (PanelCtrl._instance = new PanelCtrl())
}
init(parent: FYGE.Container) {
this._parent = parent;
let bg = new FYGE.Graphics();
bg.beginFill(0, 1);
bg.drawRect(//引用适配
layers.stageOffsetX - parent.x,
layers.stageOffsetY - parent.y,
layers.stageWidth,
layers.stageHeight
);
bg.endFill();
bg.visible = false;
this._parent.addChild(bg);
this._bg = bg;
}
/**
* 关闭所有弹框
*/
closeAll() {
this.stacks.forEach(e => e.hidePanel());
}
show<T extends Panel>(cls: any, data?: any): T {
showWaiting()
const panel: T = new cls(data);
this.add(panel);
this.stacks.push(panel);
panel.onLoaded = () => {
hideWaiting();
this.updateView();
//start只执行一边
panel.start(data);
}
//资源加载失败时
panel.onLoadError = () => {
hideWaiting();
showToast("资源加载失败")
panel.removeEventListener('onDestroy', this.onPanelHide, this);
this.remove(panel);
}
return panel;
}
private updateView() {
if (!this.stacks.length) {
this._bg.visible = false;
this._current = null;
this._parent.visible = false;
} else {
//显示弹框层
this._parent.visible = true;
//如果首次出现弹框,加个动画
if (this._bg.visible === false) {
this._bg.visible = true;
this._bg.alpha = 0;
FYGE.Tween.removeTweens(this._bg);
FYGE.Tween.get(this._bg).to({ alpha: 0.7 }, 200, FYGE.Ease.cubicOut)
}
}
for (let i = 0; i < this.stacks.length; i++) {
if (i < this.stacks.length - 1) {
this.stacks[i].visible = false;
} else {
this.stacks[i].visible = true;
this.stacks[i].showAni();
this._current = this.stacks[i];
}
}
}
/**
* 添加进父级并添加事件
* @param panel
*/
private add(panel: Panel) {
this._parent.addChild(panel);
panel.addEventListener('onDestroy', this.onPanelHide, this);
}
/**
* 移除
* @param panel
*/
private remove(panel: Panel) {
this._parent.removeChild(panel);
this.stacks = this.stacks.filter(e => e != panel);
}
/**
* 弹框移除时执行
* @param e
*/
private onPanelHide(e: FYGE.Event) {
const panel = e.target as Panel;
panel.removeEventListener('onDestroy', this.onPanelHide, this);
this.remove(panel);
this.updateView();
}
//当前弹框
private _current: Panel;
/**
* 关闭当前弹框
*/
closeCurrent() {
if (this._current) {
this._current.hidePanel();
// this._current.removeEventListener('onDestroy', this.onPanelHide, this);
// this.remove(this._current);
// this.updateView();
}
}
destroy() {
PanelCtrl._instance = null;
this.stacks = null;
this._current = null;
this._parent = null;
this._bg.destroy();
this._bg = null;
}
}
\ No newline at end of file
import { Panel } from "../views/Panel";
import { layers } from "../views/layers";
import { showWaiting, hideWaiting } from "./waitingCtrl";
import { showToast } from "./toastCtrl";
export default class PanelCtrl {
/**
* 父级容器
*/
private _parent: FYGE.Container;
/**
* 半透明黑色背景
*/
private _bg: FYGE.Graphics;
/**
* 所有的弹框
*/
private stacks: Panel[] = [];
private static _instance: PanelCtrl;
static get instance() {
return PanelCtrl._instance || (PanelCtrl._instance = new PanelCtrl())
}
init(parent: FYGE.Container) {
this._parent = parent;
let bg = new FYGE.Graphics();
bg.beginFill(0, 1);
bg.drawRect(//引用适配
layers.stageOffsetX - parent.x,
layers.stageOffsetY - parent.y,
layers.stageWidth,
layers.stageHeight
);
bg.endFill();
bg.visible = false;
this._parent.addChild(bg);
this._bg = bg;
}
/**
* 关闭所有弹框
*/
closeAll() {
this.stacks.forEach(e => e.hidePanel());
}
show<T extends Panel>(cls: any, data?: any): T {
this._bg.alpha = 0.7;
this._bg.visible = true;
showWaiting()
const panel: T = new cls(data);
this.add(panel);
this.stacks.push(panel);
panel.onLoaded = () => {
hideWaiting();
this.updateView();
//start只执行一边
panel.start(data);
}
//资源加载失败时
panel.onLoadError = () => {
hideWaiting();
showToast("资源加载失败")
panel.removeEventListener('onDestroy', this.onPanelHide, this);
this.remove(panel);
}
return panel;
}
private bgAni: "hide" | "show";
private updateView() {
//没有弹框的时候
if (!this.stacks.length) {
// this._bg.visible = false;
// this._current = null;
// this._parent.visible = false;
if (this._bg.visible) {//原先背景存在时,待测试
this.bgAni = "hide"
FYGE.Tween.removeTweens(this._bg);
FYGE.Tween.get(this._bg)
.to({ alpha: 0 }, 200, FYGE.Ease.cubicOut)
.call(() => {
this._bg.visible = false;
this._current = null;
this._parent.visible = false;
})
}
} else {
//显示弹框层
this._parent.visible = true;
if (this.bgAni == "hide") {//如果正在执行蒙层消失动画,
this.bgAni = "show"
FYGE.Tween.removeTweens(this._bg);
this._bg.alpha = 0.7;
}
//如果首次出现弹框,加个动画
if (this._bg.visible === false) {
this._bg.visible = true;
this._bg.alpha = 0;
FYGE.Tween.get(this._bg).to({ alpha: 0.7 }, 200, FYGE.Ease.cubicOut)
}
}
for (let i = 0; i < this.stacks.length; i++) {
if (i < this.stacks.length - 1) {
this.stacks[i].visible = false;
} else {
this.stacks[i].visible = true;
this.stacks[i].showAni();
this._current = this.stacks[i];
}
}
}
/**
* 添加进父级并添加事件
* @param panel
*/
private add(panel: Panel) {
this._parent.addChild(panel);
panel.addEventListener('onDestroy', this.onPanelHide, this);
}
/**
* 移除
* @param panel
*/
private remove(panel: Panel) {
this._parent.removeChild(panel);
this.stacks = this.stacks.filter(e => e != panel);
}
/**
* 弹框移除时执行
* @param e
*/
private onPanelHide(e: FYGE.Event) {
const panel = e.target as Panel;
panel.removeEventListener('onDestroy', this.onPanelHide, this);
this.remove(panel);
this.updateView();
}
//当前弹框
private _current: Panel;
/**
* 关闭当前弹框
*/
closeCurrent() {
if (this._current) {
this._current.hidePanel();
// this._current.removeEventListener('onDestroy', this.onPanelHide, this);
// this.remove(this._current);
// this.updateView();
}
}
destroy() {
PanelCtrl._instance = null;
this.stacks = null;
this._current = null;
this._parent = null;
this._bg = null;
}
}
\ No newline at end of file
import { Scene } from "../views/Scene";
import { showWaiting, hideWaiting } from "./waitingCtrl";
import { showToast } from "./toastCtrl";
export default class SceneCtrl {
private _parent: FYGE.Container;
private _currentScene: Scene;
private static _instance: SceneCtrl;
static get instance() {
return SceneCtrl._instance || (SceneCtrl._instance = new SceneCtrl())
}
init(parent: FYGE.Container) {
this._parent = parent;
}
change(cls: any, data?: any) {
//如果是同一个场景,考虑是替换还是return
if (this._currentScene && this._currentScene instanceof cls) return;
let scene: Scene = new cls(data);
scene.visible = false;
showWaiting();
let preScene: Scene = this._currentScene;
scene.onLoaded = () => {
// console.log(1231666)
hideWaiting();
scene.showAni(() => {
if (preScene) preScene.destroy();
})
scene.visible = true;
//start里可能处理资源信息,所以在onLoaded后执行
scene.start();
}
//加载失败,继续用之前的场景,移除scene
scene.onLoadError = () => {
hideWaiting();
showToast("资源加载失败")
this._currentScene = preScene || null;
this._parent.removeChild(scene);
}
this._currentScene = scene;
this._parent.addChild(scene);
}
get currentScene() {
return this._currentScene
}
destroy() {
SceneCtrl._instance = null;
this._currentScene = null;
this._parent = null;
}
}
\ No newline at end of file
import { layers } from "../views/layers";
import { RES } from "../RES";
let inited = false;
let _toast: Toast;
let _parent: FYGE.Container;
let startY: number
let endY: number
const initToast = () => {
if (!inited) {
inited = true;
_toast = new Toast();
_parent = layers.toastLayer;
_toast.alpha = 0;
//_toast.x = layers.stageOffsetX - _parent.x + (layers.stageWidth - _toast.width) / 2;
var h = _toast.height;
var y = layers.stageOffsetY - _parent.y;
startY = y - h;
endY = y + (layers.stageHeight - h) / 2;
}
}
export const showToast = (msg: string) => {
if(!msg){
console.log('msg为空');
return ;
}
initToast();
_toast.show(msg)
_parent.addChild(_toast);
FYGE.Tween.removeTweens(_toast);
FYGE.Tween.get(_toast)//动画看需求
.set({ y: startY, alpha: 1 })
.to({ y: endY }, 500, FYGE.Ease.quartOut)
.wait(800)
.to({ alpha: 0 }, 300)
.call(() => {
_parent.removeChild(_toast);
})
}
/**
* 对于之前淘宝小程序遇到的问题,需要销毁,否则会出问题
*/
export const destroyToast = () => {
if (inited && _toast && !_toast.destroyed) {
_toast.destroy();
_toast = null;
_parent = null;
inited = false;
}
}
/**
* toast类,不对外导出,适配居中有问题,有时间改
* 自身居中,
*/
class Toast extends FYGE.Container {
msg: FYGE.TextField;
bg: FYGE.Sprite;
PADDING = 40;
constructor() {
super();
this.mouseChildren = false;
this.mouseEnable = false;
var toastBgTexture: FYGE.Texture = RES.getRes("toastBg.png");
this.bg = new FYGE.Sprite(toastBgTexture);
// this.bg.x = (750 - 460) / 2// (layers.stageWidth - this.bg.width) / 2
this.addChild(this.bg);
this.msg = new FYGE.TextField();
this.msg.size = 28;
this.msg.fillColor = "0xffffff";
this.msg.text = "";
this.msg.verticalAlign = FYGE.VERTICAL_ALIGN.MIDDLE;
this.msg.textHeight = toastBgTexture.height;
this.msg.textAlign = FYGE.TEXT_ALIGN.CENTER;
this.addChild(this.msg)
}
/**
* 显示时调用
* @param msg
*/
show(msg: string) {
this.msg.text = msg;
//是否需要根据文本宽度缩放背景
this.bg.width = Math.max(this.msg.textWidth + this.PADDING * 2, 523);
//文本居中适配
this.msg.x = (layers.stageWidth - this.msg.textWidth) / 2//(layers.stageWidth - this.msg.textWidth) / 2;
//背景居中适配,由于上面一行注释,那这行就构造函数里只执行一次吧
this.bg.x = (layers.stageWidth - this.bg.width) / 2
}
destroy() {
super.destroy();
this.msg = null
this.bg = null;
}
}
\ No newline at end of file
import { RES } from "../RES";
import { layers } from "../views/layers";
import { showAlert } from ".";
let inited = false;
let _waiting: Waiting;
let _parent: FYGE.Container
const initWaiting = () => {
if (!inited) {
inited = true;
const waiting = new Waiting();
_parent = layers.topLayer;
_waiting = waiting;
//居中偏移
var offX = (layers.stageWidth - 160/*_waiting.width*/) / 2;
var offY = (layers.stageHeight - _waiting.height) / 2;
//位置适配
_waiting.x = layers.stageOffsetX - _parent.x + offX;
_waiting.y = layers.stageOffsetY - _parent.y + offY;
//阻止事件用
var bg: FYGE.Graphics = new FYGE.Graphics()
.beginFill(0x000000)
.drawRect(-offX, -offY, layers.stageWidth, layers.stageHeight)
.endFill();
bg.alpha = 0;
_waiting.addChildAt(bg, 0);
}
}
/**
* 显示菊花圈
* @param msg 尽量三个字
*/
export const showWaiting = (msg?: string) => {
initWaiting();
_waiting.show(msg)
_parent.addChild(_waiting);
}
/**
* 隐藏菊花圈
*/
export const hideWaiting = () => {
_parent.removeChild(_waiting);
}
export const destroyWaiting = () => {
if (inited && _waiting && !_waiting.destroyed) {
_waiting.destroy();
_waiting = null;
_parent = null;
inited = false;
}
}
/**
* 菊花圈,有机会重写,应该适应所有场景居中
*/
class Waiting extends FYGE.Container {
msg: FYGE.TextField;
constructor() {
super();
//圆角矩形背景
var rectBgTexture: FYGE.Texture = RES.getRes("waitingBg.png")
var rectBg = new FYGE.Sprite(rectBgTexture);
this.addChild(rectBg);
var rotTexture: FYGE.Texture = RES.getRes("waitingRot.png")
let rot = new FYGE.Sprite(rotTexture);
rot.x = (rectBgTexture.width - rotTexture.width) / 2
rot.y = 47//533;
rot.anchorX = rotTexture.width / 2;
rot.anchorY = rotTexture.height / 2;
this.addChild(rot);
let count = 0;
rot.addEventListener(FYGE.Event.ENTER_FRAME, () => {
count++;
if (count % 30 == 0) rot.rotation += 45;
}, this)
this.msg = new FYGE.TextField();
this.msg.y = 125;
this.msg.textWidth = rectBgTexture.width;
this.msg.textAlign = FYGE.TEXT_ALIGN.CENTER;
this.msg.size = 26
this.msg.fillColor = "#ffffff";
this.addChild(this.msg);
}
show(msg: string = "加载中") {
this.msg.text = msg;
}
destroy() {
super.destroy();
this.msg = null;
}
}
import { vec2 } from '../math/vec2';
var tmp = vec2.create();
/**
* 左下角为坐标原点,向上向右为正
* 最简单的无旋转矩形式碰撞检测;
* Axis aligned bounding box class.
* @class AABB
* @constructor
* @param {Object} [options]
* @param {Array} [options.upperBound]
* @param {Array} [options.lowerBound]
* @example
* var aabb = new AABB({
* upperBound: [1, 1],
* lowerBound: [-1, -1]
* });
*/
export class AABB {
/**
* 左下角点
* The lower bound of the bounding box.
* @property lowerBound
* @type {Array}
*/
lowerBound: Float32Array;
/**
* 右上角点
* The upper bound of the bounding box.
* @property upperBound
* @type {Array}
*/
upperBound: Float32Array;
constructor(options?) {
options = options || {};
this.lowerBound = options.lowerBound ? vec2.clone(options.lowerBound) : vec2.create();
this.upperBound = options.upperBound ? vec2.clone(options.upperBound) : vec2.create();
}
/**
* 计算一组点的AABB,提供 点,位置,旋转
* Set the AABB bounds from a set of points, transformed by the given position and angle.
* @method setFromPoints
* @param {Array} points An array of vec2's. vec2的数组.如[[0,1],[0,6],[2,1]]
* @param {Array} position 偏移信息,所有点的
* @param {number} [angle=0] 旋转信息,所有点的
* @param {number} [skinSize=0] Some margin to be added to the AABB.
*/
setFromPoints(points, position, angle = 0, skinSize = 0) {
var l = this.lowerBound,
u = this.upperBound;
// Set to the first point
if (angle !== 0) {
vec2.rotate(l, points[0], angle);
} else {
vec2.copy(l, points[0]);
}
vec2.copy(u, l);
// Compute cosines and sines just once
var cosAngle = Math.cos(angle),
sinAngle = Math.sin(angle);
for (var i = 1; i < points.length; i++) {
var p = points[i];
if (angle !== 0) {
var x = p[0],
y = p[1];
tmp[0] = cosAngle * x - sinAngle * y;
tmp[1] = sinAngle * x + cosAngle * y;
p = tmp;
}
for (var j = 0; j < 2; j++) {
if (p[j] > u[j]) {
u[j] = p[j];
}
if (p[j] < l[j]) {
l[j] = p[j];
}
}
}
// Add offset
if (position) {
vec2.add(l, l, position);
vec2.add(u, u, position);
}
if (skinSize) {
l[0] -= skinSize;
l[1] -= skinSize;
u[0] += skinSize;
u[1] += skinSize;
}
};
/**
* 复制
* Copy bounds from an AABB to this AABB
* @method copy
* @param {AABB} aabb
*/
copy(aabb) {
vec2.copy(this.lowerBound, aabb.lowerBound);
vec2.copy(this.upperBound, aabb.upperBound);
};
/**
* 扩展AABB
* Extend this AABB so that it covers the given AABB too.
* @method extend
* @param {AABB} aabb
*/
extend(aabb: AABB) {
var lower = this.lowerBound,
upper = this.upperBound;
// Loop over x and y
var i = 2;
while (i--) {
// Extend lower bound
var l = aabb.lowerBound[i];
if (lower[i] > l) {
lower[i] = l;
}
// Upper
var u = aabb.upperBound[i];
if (upper[i] < u) {
upper[i] = u;
}
}
};
/**
* 判断与所给AABB是否重叠
* Returns true if the given AABB overlaps this AABB.
* @method overlaps
* @param {AABB} aabb
* @return {Boolean}
*/
overlaps(aabb: AABB): boolean {
var l1 = this.lowerBound,
u1 = this.upperBound,
l2 = aabb.lowerBound,
u2 = aabb.upperBound;
// l2 u2
// |---------|
// |--------|
// l1 u1
return ((l2[0] <= u1[0] && u1[0] <= u2[0]) || (l1[0] <= u2[0] && u2[0] <= u1[0])) &&
((l2[1] <= u1[1] && u1[1] <= u2[1]) || (l1[1] <= u2[1] && u2[1] <= u1[1]));
};
/**
* 判断是否包含某点
* @method containsPoint
* @param {Array} point
* @return {boolean}
*/
containsPoint(point) {
var l = this.lowerBound,
u = this.upperBound;
return l[0] <= point[0] && point[0] <= u[0] && l[1] <= point[1] && point[1] <= u[1];
};
/**
* 判断射线碰撞
* Check if the AABB is hit by a ray.
* @method overlapsRay
* @param {Ray} ray
* @return {number} -1 if no hit, a number between 0 and 1 if hit, indicating the position between the "from" and "to" points.
* @example
* var aabb = new AABB({
* upperBound: [1, 1],
* lowerBound: [-1, -1]
* });
* var ray = new Ray({
* from: [-2, 0],
* to: [0, 0]
* });
* var fraction = aabb.overlapsRay(ray); // fraction == 0.5
*/
overlapsRay(ray) {
// ray.direction is unit direction vector of ray
var dirFracX = 1 / ray.direction[0];
var dirFracY = 1 / ray.direction[1];
// this.lowerBound is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner
var from = ray.from;
var lowerBound = this.lowerBound;
var upperBound = this.upperBound;
var t1 = (lowerBound[0] - from[0]) * dirFracX;
var t2 = (upperBound[0] - from[0]) * dirFracX;
var t3 = (lowerBound[1] - from[1]) * dirFracY;
var t4 = (upperBound[1] - from[1]) * dirFracY;
var tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4)));
var tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4)));
// if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behing us
if (tmax < 0) {
//t = tmax;
return -1;
}
// if tmin > tmax, ray doesn't intersect AABB
if (tmin > tmax) {
//t = tmax;
return -1;
}
return tmin / ray.length;
};
}
\ No newline at end of file
import { vec2 } from '../math/vec2';
import { Body } from '../objects/Body';
/**
* 粗略全局检测碰撞
* 子类,两种,一种原生检测(时间复杂度高),一种排序后检测
*/
export class Broadphase {
/**
* Axis aligned bounding box type.
* @static
* @property {Number} AABB
*/
static AABB = 1;
/**
* Bounding circle type.
* @static
* @property {Number} BOUNDING_CIRCLE
*/
static BOUNDING_CIRCLE = 2;
static NAIVE = 1;
static SAP = 2;
/**
* Check whether the bounding radius of two bodies overlap.
* @method boundingRadiusCheck
* @param {Body} bodyA
* @param {Body} bodyB
* @return {Boolean}
*/
static boundingRadiusCheck(bodyA, bodyB) {
var d2 = vec2.squaredDistance(bodyA.position, bodyB.position),
r = bodyA.boundingRadius + bodyB.boundingRadius;
return d2 <= r * r;
};
/**
* Check whether the AABB of two bodies overlap.
* @method aabbCheck
* @param {Body} bodyA
* @param {Body} bodyB
* @return {Boolean}
*/
static aabbCheck(bodyA, bodyB) {
return bodyA.getAABB().overlaps(bodyB.getAABB());
};
/**
* Check whether two bodies are allowed to collide at all.
* @method canCollide
* @param {Body} bodyA
* @param {Body} bodyB
* @return {Boolean}
*/
static canCollide(bodyA, bodyB) {
var KINEMATIC = Body.KINEMATIC;
var STATIC = Body.STATIC;
var typeA = bodyA.type;
var typeB = bodyB.type;
// Cannot collide static bodies
if (typeA === STATIC && typeB === STATIC) {
return false;
}
// Cannot collide static vs kinematic bodies
if ((typeA === KINEMATIC && typeB === STATIC) ||
(typeA === STATIC && typeB === KINEMATIC)) {
return false;
}
// Cannot collide kinematic vs kinematic
if (typeA === KINEMATIC && typeB === KINEMATIC) {
return false;
}
// Cannot collide both sleeping bodies
if (bodyA.sleepState === Body.SLEEPING && bodyB.sleepState === Body.SLEEPING) {
return false;
}
// Cannot collide if one is static and the other is sleeping
if ((bodyA.sleepState === Body.SLEEPING && typeB === STATIC) ||
(bodyB.sleepState === Body.SLEEPING && typeA === STATIC)) {
return false;
}
return true;
};
type: any;
/**
* The resulting overlapping pairs. Will be filled with results during .getCollisionPairs().
* @property result
* @type {Array}
*/
result: any[];
/**
* The world to search for collision pairs in. To change it, use .setWorld()
* @property world
* @type {World}
* @readOnly
*/
world: any;
/**
* The bounding volume type to use in the broadphase algorithms. Should be set to Broadphase.AABB or Broadphase.BOUNDING_CIRCLE.
* @property {Number} boundingVolumeType
*/
boundingVolumeType: number;
/**
* Base class for broadphase implementations. Don't use this class directly.
* @class Broadphase
* @constructor
*/
constructor(type) {
this.type = type;
this.result = [];
this.world = null;
this.boundingVolumeType = Broadphase.AABB;
}
/**
* Set the world that we are searching for collision pairs in.
* @method setWorld
* @param {World} world
*/
setWorld(world) {
this.world = world;
};
/**
* Get all potential intersecting body pairs.
* @method getCollisionPairs
* @param {World} world The world to search in.
* @return {Array} An array of the bodies, ordered in pairs. Example: A result of [a,b,c,d] means that the potential pairs are: (a,b), (c,d).
*/
getCollisionPairs(world) { };
/**
* Check whether the bounding volumes of two bodies overlap.
* @method boundingVolumeCheck
* @param {Body} bodyA
* @param {Body} bodyB
* @return {Boolean}
*/
boundingVolumeCheck(bodyA, bodyB) {
var result;
switch (this.boundingVolumeType) {
case Broadphase.BOUNDING_CIRCLE:
result = Broadphase.boundingRadiusCheck(bodyA, bodyB);
break;
case Broadphase.AABB:
result = Broadphase.aabbCheck(bodyA, bodyB);
break;
default:
throw new Error('Bounding volume type not recognized: ' + this.boundingVolumeType);
}
return result;
};
/**
* Returns all the bodies within an AABB.
* @method aabbQuery
* @param {World} world
* @param {AABB} aabb
* @param {array} result An array to store resulting bodies in.
* @return {array}
*/
aabbQuery(world, aabb, result) {
// To be implemented in subclasses
};
}
import { Broadphase } from '../collision/Broadphase';
/**
* 直接检测
* Naive broadphase implementation. Does N^2 tests.
* @class NaiveBroadphase
* @constructor
* @extends Broadphase
*/
export class NaiveBroadphase extends Broadphase {
constructor() {
super(Broadphase.NAIVE)
}
/**
* Get the colliding pairs
* @method getCollisionPairs
* @param {World} world
* @return {Array}
*/
getCollisionPairs(world) {
var bodies = world.bodies,
result = this.result;
result.length = 0;
for (var i = 0, Ncolliding = bodies.length; i !== Ncolliding; i++) {
var bi = bodies[i];
for (var j = 0; j < i; j++) {
var bj = bodies[j];
if (Broadphase.canCollide(bi, bj) && this.boundingVolumeCheck(bi, bj)) {
result.push(bi, bj);
}
}
}
return result;
};
/**
* Returns all the bodies within an AABB.
* @method aabbQuery
* @param {World} world
* @param {AABB} aabb
* @param {array} result An array to store resulting bodies in.
* @return {array}
*/
aabbQuery(world, aabb, result) {
result = result || [];
var bodies = world.bodies;
for (var i = 0; i < bodies.length; i++) {
var b = bodies[i];
if (b.aabbNeedsUpdate) {
b.updateAABB();
}
if (b.aabb.overlaps(aabb)) {
result.push(b);
}
}
return result;
};
}
\ No newline at end of file
This diff is collapsed.
import { vec2 } from '../math/vec2';
var intersectBody_worldPosition = vec2.create();
/**
* A line with a start and end point that is used to intersect shapes. For an example, see {{#crossLink "World/raycast:method"}}World.raycast{{/crossLink}}
* @class Ray
* @constructor
* @param {object} [options]
* @param {array} [options.from]
* @param {array} [options.to]
* @param {boolean} [options.checkCollisionResponse=true]
* @param {boolean} [options.skipBackfaces=false]
* @param {number} [options.collisionMask=-1]
* @param {number} [options.collisionGroup=-1]
* @param {number} [options.mode=Ray.ANY]
* @param {Function} [options.callback]
*/
export class Ray {
/**
* This raycasting mode will make the Ray traverse through all intersection points and only return the closest one.
* @static
* @property {Number} CLOSEST
*/
public static CLOSEST = 1;
/**
* This raycasting mode will make the Ray stop when it finds the first intersection point.
* @static
* @property {Number} ANY
*/
public static ANY = 2;
/**
* This raycasting mode will traverse all intersection points and executes a callback for each one.
* @static
* @property {Number} ALL
*/
public static ALL = 4;
/**
* Ray start point.
* @property {array} from
*/
from: Float32Array;
/**
* Ray end point
* @property {array} to
*/
to: Float32Array;
/**
* Set to true if you want the Ray to take .collisionResponse flags into account on bodies and shapes.
* @property {Boolean} checkCollisionResponse
*/
checkCollisionResponse: boolean;
/**
* If set to true, the ray skips any hits with normal.dot(rayDirection) < 0.
* @property {Boolean} skipBackfaces
*/
skipBackfaces: boolean;
/**
* @property {number} collisionMask
* @default -1
*/
collisionMask: number;
/**
* @property {number} collisionGroup
* @default -1
*/
collisionGroup: number;
/**
*
* The intersection mode. Should be {{#crossLink "Ray/ANY:property"}}Ray.ANY{{/crossLink}}, {{#crossLink "Ray/ALL:property"}}Ray.ALL{{/crossLink}} or {{#crossLink "Ray/CLOSEST:property"}}Ray.CLOSEST{{/crossLink}}.
* @property {number} mode
*/
mode: number;
/**
* Current, user-provided result callback. Will be used if mode is Ray.ALL.
* @property {Function} callback
*/
callback: Function;
/**
* @readOnly
* @property {array} direction
*/
direction: Float32Array;
/**
* Length of the ray
* @readOnly
* @property {number} length
*/
length: number;
_currentBody: any;
_currentShape: any;
constructor(options) {
options = options || {};
this.from = options.from ? vec2.clone(options.from) : vec2.create();
this.to = options.to ? vec2.clone(options.to) : vec2.create();
this.checkCollisionResponse = options.checkCollisionResponse !== undefined ? options.checkCollisionResponse : true;
this.skipBackfaces = !!options.skipBackfaces;
this.collisionMask = options.collisionMask !== undefined ? options.collisionMask : -1;
this.collisionGroup = options.collisionGroup !== undefined ? options.collisionGroup : -1;
this.mode = options.mode !== undefined ? options.mode : Ray.ANY;
this.callback = options.callback || function (/*result*/) { };
this.direction = vec2.create();
this.length = 1;
this.update();
}
/**
* Should be called if you change the from or to point.
* @method update
*/
update() {
// Update .direction and .length
var d = this.direction;
vec2.subtract(d, this.to, this.from);
this.length = vec2.length(d);
vec2.normalize(d, d);
};
/**
* @method intersectBodies
* @param {Array} bodies An array of Body objects.
*/
intersectBodies(result, bodies) {
for (var i = 0, l = bodies.length; !result.shouldStop(this) && i < l; i++) {
var body = bodies[i];
var aabb = body.getAABB();
if (aabb.overlapsRay(this) >= 0 || aabb.containsPoint(this.from)) {
this.intersectBody(result, body);
}
}
};
/**
* Shoot a ray at a body, get back information about the hit.
* @method intersectBody
* @private
* @param {Body} body
*/
intersectBody(result, body) {
var checkCollisionResponse = this.checkCollisionResponse;
if (checkCollisionResponse && !body.collisionResponse) {
return;
}
var worldPosition = intersectBody_worldPosition;
for (var i = 0, N = body.shapes.length; i < N; i++) {
var shape = body.shapes[i];
if (checkCollisionResponse && !shape.collisionResponse) {
continue; // Skip
}
if ((this.collisionGroup & shape.collisionMask) === 0 || (shape.collisionGroup & this.collisionMask) === 0) {
continue;
}
// Get world angle and position of the shape
vec2.rotate(worldPosition, shape.position, body.angle);
vec2.add(worldPosition, worldPosition, body.position);
var worldAngle = shape.angle + body.angle;
this.intersectShape(
result,
shape,
worldAngle,
worldPosition,
body
);
if (result.shouldStop(this)) {
break;
}
}
};
/**
* @method intersectShape
* @private
* @param {Shape} shape
* @param {number} angle
* @param {array} position
* @param {Body} body
*/
intersectShape(result, shape, angle, position, body) {
var from = this.from;
// Checking radius
var distance = distanceFromIntersectionSquared(from, this.direction, position);
if (distance > shape.boundingRadius * shape.boundingRadius) {
return;
}
this._currentBody = body;
this._currentShape = shape;
shape.raycast(result, this, position, angle);
this._currentBody = this._currentShape = null;
};
/**
* Get the AABB of the ray.
* @method getAABB
* @param {AABB} aabb
*/
getAABB(result) {
var to = this.to;
var from = this.from;
vec2.set(
result.lowerBound,
Math.min(to[0], from[0]),
Math.min(to[1], from[1])
);
vec2.set(
result.upperBound,
Math.max(to[0], from[0]),
Math.max(to[1], from[1])
);
};
/**
* @method reportIntersection
* @private
* @param {number} fraction
* @param {array} normal
* @param {number} [faceIndex=-1]
* @return {boolean} True if the intersections should continue
*/
reportIntersection(result, fraction, normal, faceIndex) {
var shape = this._currentShape;
var body = this._currentBody;
// Skip back faces?
if (this.skipBackfaces && vec2.dot(normal, this.direction) > 0) {
return;
}
switch (this.mode) {
case Ray.ALL:
result.set(
normal,
shape,
body,
fraction,
faceIndex
);
this.callback(result);
break;
case Ray.CLOSEST:
// Store if closer than current closest
if (fraction < result.fraction || !result.hasHit()) {
result.set(
normal,
shape,
body,
fraction,
faceIndex
);
}
break;
case Ray.ANY:
// Report and stop.
result.set(
normal,
shape,
body,
fraction,
faceIndex
);
break;
}
};
}
var v0 = vec2.create(),
intersect = vec2.create();
function distanceFromIntersectionSquared(from, direction, position) {
// v0 is vector from from to position
vec2.subtract(v0, position, from);
var dot = vec2.dot(v0, direction);
// intersect = direction * dot + from
vec2.scale(intersect, direction, dot);
vec2.add(intersect, intersect, from);
return vec2.squaredDistance(position, intersect);
}
import { vec2 } from '../math/vec2';
import { Ray } from '../collision/Ray';
import { Shape } from "../shapes/Shape";
/**
* 记录射线碰撞结果
* 主要有normal,shape,body,faceIndex,fraction
* 还有方法等
*/
export class RaycastResult {
/**
* 碰撞点的法向量,在全局内
* The normal of the hit, oriented in world space.
* @property {array} normal
*/
normal: Float32Array;
/**
* The hit shape, or null.
* @property {Shape} shape
*/
shape: Shape;
/**
* The hit body, or null.
* @property {Body} body
*/
body: any;
/**
* shape中的三角形索引
* The index of the hit triangle, if the hit shape was indexable.
* @property {number} faceIndex
* @default -1
*/
faceIndex: number;
/**
* 0靠近起点,1靠近终点
* Distance to the hit, as a fraction. 0 is at the "from" point, 1 is at the "to" point. Will be set to -1 if there was no hit yet.
* @property {number} fraction
* @default -1
*/
fraction: number;
/**
* 是否穿透
* If the ray should stop traversing.
* @readonly
* @property {Boolean} isStopped
*/
isStopped: boolean;
/**
* Storage for Ray casting hit data.
* @class RaycastResult
* @constructor
*/
constructor() {
this.normal = vec2.create();
this.shape = null;
this.body = null;
this.faceIndex = -1;
this.fraction = -1;
this.isStopped = false;
}
/**
* 重置
* Reset all result data. Must be done before re-using the result object.
* @method reset
*/
reset() {
vec2.set(this.normal, 0, 0);
this.shape = null;
this.body = null;
this.faceIndex = -1;
this.fraction = -1;
this.isStopped = false;
};
/**
* 获取到碰撞点的距离
* Get the distance to the hit point.
* @method getHitDistance
* @param {Ray} ray
* @return {number}
*/
getHitDistance(ray:Ray) {
return vec2.distance(ray.from, ray.to) * this.fraction;
};
/**
* 是否碰撞
* Returns true if the ray hit something since the last reset().
* @method hasHit
* @®eturn {boolean}
*/
hasHit() {
return this.fraction !== -1;
};
/**
* 获取全局碰撞点
* Get world hit point.
* @method getHitPoint
* @param {array} out 输出量
* @param {Ray} ray
*/
getHitPoint(out, ray) {
vec2.lerp(out, ray.from, ray.to, this.fraction);
};
/**
* 停止继续寻找碰撞点
* Can be called while iterating over hits to stop searching for hit points.
* @method stop
*/
stop() {
this.isStopped = true;
};
/**
* @method shouldStop
* @private
* @param {Ray} ray
* @return {boolean}
*/
shouldStop(ray) {
return this.isStopped || (this.fraction !== -1 && ray.mode === Ray.ANY);
};
/**
* @method set
* @private
* @param {array} normal
* @param {Shape} shape
* @param {Body} body
* @param {number} fraction
* @param {number} faceIndex
*/
set(
normal,
shape,
body,
fraction,
faceIndex
) {
vec2.copy(this.normal, normal);
this.shape = shape;
this.body = body;
this.fraction = fraction;
this.faceIndex = faceIndex;
};
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
import { Constraint } from './Constraint'
import { AngleLockEquation } from '../equations/AngleLockEquation'
import { Utils } from '../utils/Utils';
export class GearConstraint extends Constraint {
/**
* The gear ratio.
* @property ratio
* @type {Number}
*/
ratio: any;
/**
* The relative angle
* @property angle
* @type {Number}
*/
angle: any;
/**
* 齿轮约束
* Constrains the angle of two bodies to each other to be equal. If a gear ratio is not one, the angle of bodyA must be a multiple of the angle of bodyB.
* @class GearConstraint
* @constructor
* @author schteppe
* @param {Body} bodyA
* @param {Body} bodyB
* @param {Object} [options]
* @param {Number} [options.angle=0] Relative angle between the bodies. Will be set to the current angle between the bodies (the gear ratio is accounted for).
* @param {Number} [options.ratio=1] Gear ratio.
* @param {Number} [options.maxTorque] Maximum torque to apply.
* @extends Constraint
*
* @example
* var constraint = new GearConstraint(bodyA, bodyB);
* world.addConstraint(constraint);
*
* @example
* var constraint = new GearConstraint(bodyA, bodyB, {
* ratio: 2,
* maxTorque: 1000
* });
* world.addConstraint(constraint);
*/
constructor(bodyA, bodyB, options) {
options = options || {};
super(bodyA, bodyB, Constraint.GEAR, options);
this.ratio = options.ratio !== undefined ? options.ratio : 1;
this.angle = options.angle !== undefined ? options.angle : bodyB.angle - this.ratio * bodyA.angle;
// Send same parameters to the equation
var angleLockOptions: any = Utils.shallowClone(options);
angleLockOptions.angle = this.angle;
angleLockOptions.ratio = this.ratio;
this.equations = [
new AngleLockEquation(bodyA, bodyB, angleLockOptions),
];
// Set max torque
if (options.maxTorque !== undefined) {
this.setMaxTorque(options.maxTorque);
}
}
update() {
var eq = this.equations[0];
var ratio = this.ratio;
if (eq["ratio"] !== ratio) {
eq["setRatio"](ratio);
}
eq["angle"] = this.angle;
};
/**
* Set the max torque for the constraint.
* @method setMaxTorque
* @param {Number} torque
*/
setMaxTorque(torque:number):void {
this.equations[0]["setMaxTorque"](torque);
};
/**
* Get the max torque for the constraint.
* @method getMaxTorque
* @return {Number}
*/
getMaxTorque():number {
return this.equations[0].maxForce;
};
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
{
"type": "activity",
"name": "kickball-feile"
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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