新增analysis用于分析游戏缺陷
This commit is contained in:
198
source/bin/framework.d.ts
vendored
198
source/bin/framework.d.ts
vendored
@@ -209,6 +209,9 @@ declare abstract class Component extends egret.DisplayObjectContainer {
|
||||
registerComponent(): void;
|
||||
deregisterComponent(): void;
|
||||
}
|
||||
declare enum CoreEvents {
|
||||
SceneChanged = 0
|
||||
}
|
||||
declare class Entity extends egret.DisplayObjectContainer {
|
||||
private static _idGenerator;
|
||||
name: string;
|
||||
@@ -297,12 +300,19 @@ declare class SceneManager {
|
||||
private static _nextScene;
|
||||
static sceneTransition: SceneTransition;
|
||||
static stage: egret.Stage;
|
||||
static activeSceneChanged: Function;
|
||||
static emitter: Emitter<CoreEvents>;
|
||||
static content: ContentManager;
|
||||
private static _instnace;
|
||||
static readonly Instance: SceneManager;
|
||||
constructor(stage: egret.Stage);
|
||||
static scene: Scene;
|
||||
static initialize(stage: egret.Stage): void;
|
||||
static update(): void;
|
||||
static render(): void;
|
||||
static startSceneTransition<T extends SceneTransition>(sceneTransition: T): T;
|
||||
static registerActiveSceneChanged(current: Scene, next: Scene): void;
|
||||
onSceneChanged(): void;
|
||||
}
|
||||
declare class Camera extends Component implements IUpdatable {
|
||||
private _zoom;
|
||||
@@ -379,6 +389,34 @@ declare class Mesh extends RenderableComponent {
|
||||
render(camera: Camera): void;
|
||||
reset(): void;
|
||||
}
|
||||
declare class SpriteRenderer extends RenderableComponent {
|
||||
private _sprite;
|
||||
protected bitmap: egret.Bitmap;
|
||||
sprite: Sprite;
|
||||
setSprite(sprite: Sprite): SpriteRenderer;
|
||||
setColor(color: number): SpriteRenderer;
|
||||
isVisibleFromCamera(camera: Camera): boolean;
|
||||
render(camera: Camera): void;
|
||||
onRemovedFromEntity(): void;
|
||||
reset(): void;
|
||||
}
|
||||
declare class TiledSpriteRenderer extends SpriteRenderer {
|
||||
protected sourceRect: Rectangle;
|
||||
protected leftTexture: egret.Bitmap;
|
||||
protected rightTexture: egret.Bitmap;
|
||||
scrollX: number;
|
||||
scrollY: number;
|
||||
constructor(sprite: Sprite);
|
||||
render(camera: Camera): void;
|
||||
}
|
||||
declare class ScrollingSpriteRenderer extends TiledSpriteRenderer {
|
||||
scrollSpeedX: number;
|
||||
scroolSpeedY: number;
|
||||
private _scrollX;
|
||||
private _scrollY;
|
||||
update(): void;
|
||||
render(camera: Camera): void;
|
||||
}
|
||||
declare class Sprite {
|
||||
texture2D: egret.Texture;
|
||||
readonly sourceRect: Rectangle;
|
||||
@@ -392,17 +430,6 @@ declare class SpriteAnimation {
|
||||
readonly frameRate: number;
|
||||
constructor(sprites: Sprite[], frameRate: number);
|
||||
}
|
||||
declare class SpriteRenderer extends RenderableComponent {
|
||||
private _sprite;
|
||||
protected bitmap: egret.Bitmap;
|
||||
sprite: Sprite;
|
||||
setSprite(sprite: Sprite): SpriteRenderer;
|
||||
setColor(color: number): SpriteRenderer;
|
||||
isVisibleFromCamera(camera: Camera): boolean;
|
||||
render(camera: Camera): void;
|
||||
onRemovedFromEntity(): void;
|
||||
reset(): void;
|
||||
}
|
||||
declare class SpriteAnimator extends SpriteRenderer implements IUpdatable {
|
||||
onAnimationCompletedEvent: Function;
|
||||
speed: number;
|
||||
@@ -436,13 +463,6 @@ declare enum State {
|
||||
paused = 2,
|
||||
completed = 3
|
||||
}
|
||||
declare class TiledSpriteRenderer extends SpriteRenderer {
|
||||
protected sourceRect: Rectangle;
|
||||
scrollX: number;
|
||||
scrollY: number;
|
||||
constructor(sprite: Sprite);
|
||||
render(camera: Camera): void;
|
||||
}
|
||||
interface ITriggerListener {
|
||||
onTriggerEnter(other: Collider, local: Collider): any;
|
||||
onTriggerExit(other: Collider, local: Collider): any;
|
||||
@@ -1076,6 +1096,17 @@ declare class ListPool {
|
||||
static obtain<T>(): Array<T>;
|
||||
static free<T>(obj: Array<T>): void;
|
||||
}
|
||||
declare const THREAD_ID: string;
|
||||
declare const setItem: any;
|
||||
declare const getItem: any;
|
||||
declare const removeItem: any;
|
||||
declare const nextTick: (fn: any) => void;
|
||||
declare class LockUtils {
|
||||
private _keyX;
|
||||
private _keyY;
|
||||
constructor(key: any);
|
||||
lock(): Promise<{}>;
|
||||
}
|
||||
declare class Pair<T> {
|
||||
first: T;
|
||||
second: T;
|
||||
@@ -1104,3 +1135,134 @@ declare class Vector2Ext {
|
||||
static transform(sourceArray: Vector2[], matrix: Matrix2D, destinationArray: Vector2[]): void;
|
||||
static round(vec: Vector2): Vector2;
|
||||
}
|
||||
declare class Layout {
|
||||
clientArea: Rectangle;
|
||||
safeArea: Rectangle;
|
||||
constructor();
|
||||
place(size: Vector2, horizontalMargin: number, verticalMargine: number, alignment: Alignment): Rectangle;
|
||||
}
|
||||
declare enum Alignment {
|
||||
none = 0,
|
||||
left = 1,
|
||||
right = 2,
|
||||
horizontalCenter = 4,
|
||||
top = 8,
|
||||
bottom = 16,
|
||||
verticalCenter = 32,
|
||||
topLeft = 9,
|
||||
topRight = 10,
|
||||
topCenter = 12,
|
||||
bottomLeft = 17,
|
||||
bottomRight = 18,
|
||||
bottomCenter = 20,
|
||||
centerLeft = 33,
|
||||
centerRight = 34,
|
||||
center = 36
|
||||
}
|
||||
declare namespace stopwatch {
|
||||
class Stopwatch {
|
||||
private readonly getSystemTime;
|
||||
private _startSystemTime;
|
||||
private _stopSystemTime;
|
||||
private _stopDuration;
|
||||
private _pendingSliceStartStopwatchTime;
|
||||
private _completeSlices;
|
||||
constructor(getSystemTime?: GetTimeFunc);
|
||||
getState(): State;
|
||||
isIdle(): boolean;
|
||||
isRunning(): boolean;
|
||||
isStopped(): boolean;
|
||||
slice(): Slice;
|
||||
getCompletedSlices(): Slice[];
|
||||
getCompletedAndPendingSlices(): Slice[];
|
||||
getPendingSlice(): Slice;
|
||||
getTime(): number;
|
||||
private calculatePendingSlice;
|
||||
private caculateStopwatchTime;
|
||||
private getSystemTimeOfCurrentStopwatchTime;
|
||||
reset(): void;
|
||||
start(forceReset?: boolean): void;
|
||||
stop(recordPendingSlice?: boolean): number;
|
||||
private recordPendingSlice;
|
||||
}
|
||||
type GetTimeFunc = () => number;
|
||||
enum State {
|
||||
IDLE = "IDLE",
|
||||
RUNNING = "RUNNING",
|
||||
STOPPED = "STOPPED"
|
||||
}
|
||||
function setDefaultSystemTimeGetter(systemTimeGetter?: GetTimeFunc): void;
|
||||
interface Slice {
|
||||
readonly startTime: number;
|
||||
readonly endTime: number;
|
||||
readonly duration: number;
|
||||
}
|
||||
}
|
||||
declare class TimeRuler {
|
||||
static readonly maxBars: number;
|
||||
static readonly maxSamples: number;
|
||||
static readonly maxNestCall: number;
|
||||
static readonly barHeight: number;
|
||||
static readonly maxSampleFrames: number;
|
||||
static readonly logSnapDuration: number;
|
||||
static readonly barPadding: number;
|
||||
static readonly autoAdjustDelay: number;
|
||||
static Instance: TimeRuler;
|
||||
private _frameKey;
|
||||
private _logKey;
|
||||
private _logs;
|
||||
private sampleFrames;
|
||||
targetSampleFrames: number;
|
||||
width: number;
|
||||
enabled: true;
|
||||
private _position;
|
||||
private _prevLog;
|
||||
private _curLog;
|
||||
private frameCount;
|
||||
private markers;
|
||||
private stopwacth;
|
||||
private _markerNameToIdMap;
|
||||
private _updateCount;
|
||||
showLog: boolean;
|
||||
private _frameAdjust;
|
||||
constructor();
|
||||
private onGraphicsDeviceReset;
|
||||
startFrame(): void;
|
||||
beginMark(markerName: string, color: number, barIndex?: number): void;
|
||||
endMark(markerName: string, barIndex?: number): void;
|
||||
getAverageTime(barIndex: number, markerName: string): number;
|
||||
resetLog(): void;
|
||||
render(position?: Vector2, width?: number): void;
|
||||
}
|
||||
declare class FrameLog {
|
||||
bars: MarkerCollection[];
|
||||
constructor();
|
||||
}
|
||||
declare class MarkerCollection {
|
||||
markers: Marker[];
|
||||
markCount: number;
|
||||
markerNests: number[];
|
||||
nestCount: number;
|
||||
}
|
||||
declare class Marker {
|
||||
markerId: number;
|
||||
beginTime: number;
|
||||
endTime: number;
|
||||
color: number;
|
||||
}
|
||||
declare class MarkerInfo {
|
||||
name: string;
|
||||
logs: MarkerLog[];
|
||||
constructor(name: any);
|
||||
}
|
||||
declare class MarkerLog {
|
||||
snapMin: number;
|
||||
snapMax: number;
|
||||
snapAvg: number;
|
||||
min: number;
|
||||
max: number;
|
||||
avg: number;
|
||||
samples: number;
|
||||
color: number;
|
||||
initialized: boolean;
|
||||
}
|
||||
|
||||
@@ -1023,6 +1023,10 @@ var Component = (function (_super) {
|
||||
};
|
||||
return Component;
|
||||
}(egret.DisplayObjectContainer));
|
||||
var CoreEvents;
|
||||
(function (CoreEvents) {
|
||||
CoreEvents[CoreEvents["SceneChanged"] = 0] = "SceneChanged";
|
||||
})(CoreEvents || (CoreEvents = {}));
|
||||
var Entity = (function (_super) {
|
||||
__extends(Entity, _super);
|
||||
function Entity(name) {
|
||||
@@ -1393,9 +1397,19 @@ var Scene = (function (_super) {
|
||||
var SceneManager = (function () {
|
||||
function SceneManager(stage) {
|
||||
stage.addEventListener(egret.Event.ENTER_FRAME, SceneManager.update, this);
|
||||
SceneManager._instnace = this;
|
||||
SceneManager.emitter = new Emitter();
|
||||
SceneManager.content = new ContentManager();
|
||||
SceneManager.stage = stage;
|
||||
SceneManager.initialize(stage);
|
||||
}
|
||||
Object.defineProperty(SceneManager, "Instance", {
|
||||
get: function () {
|
||||
return this._instnace;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(SceneManager, "scene", {
|
||||
get: function () {
|
||||
return this._scene;
|
||||
@@ -1406,10 +1420,12 @@ var SceneManager = (function () {
|
||||
if (this._scene == null) {
|
||||
this._scene = value;
|
||||
this._scene.begin();
|
||||
SceneManager.Instance.onSceneChanged();
|
||||
}
|
||||
else {
|
||||
this._nextScene = value;
|
||||
}
|
||||
this.registerActiveSceneChanged(this._scene, this._nextScene);
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
@@ -1430,12 +1446,9 @@ var SceneManager = (function () {
|
||||
}
|
||||
if (SceneManager._nextScene) {
|
||||
SceneManager._scene.end();
|
||||
for (var i = 0; i < SceneManager._scene.entities.buffer.length; i++) {
|
||||
var entity = SceneManager._scene.entities.buffer[i];
|
||||
entity.destroy();
|
||||
}
|
||||
SceneManager._scene = SceneManager._nextScene;
|
||||
SceneManager._nextScene = null;
|
||||
SceneManager._instnace.onSceneChanged();
|
||||
SceneManager._scene.begin();
|
||||
}
|
||||
}
|
||||
@@ -1471,6 +1484,12 @@ var SceneManager = (function () {
|
||||
this.sceneTransition = sceneTransition;
|
||||
return sceneTransition;
|
||||
};
|
||||
SceneManager.registerActiveSceneChanged = function (current, next) {
|
||||
if (this.activeSceneChanged)
|
||||
this.activeSceneChanged(current, next);
|
||||
};
|
||||
SceneManager.prototype.onSceneChanged = function () {
|
||||
};
|
||||
return SceneManager;
|
||||
}());
|
||||
var Camera = (function (_super) {
|
||||
@@ -1873,10 +1892,6 @@ var TiledSpriteRenderer = (function (_super) {
|
||||
},
|
||||
set: function (value) {
|
||||
this.sourceRect.x = value;
|
||||
if (this.sourceRect.x < -this.sourceRect.width)
|
||||
this.sourceRect.x = this.sourceRect.width;
|
||||
else if (this.sourceRect.x > this.sourceRect.width)
|
||||
this.sourceRect.x = -this.sourceRect.width;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
@@ -1887,10 +1902,6 @@ var TiledSpriteRenderer = (function (_super) {
|
||||
},
|
||||
set: function (value) {
|
||||
this.sourceRect.y = value;
|
||||
if (this.sourceRect.y < -this.sourceRect.height)
|
||||
this.sourceRect.y = this.sourceRect.height;
|
||||
else if (this.sourceRect.y > this.sourceRect.height)
|
||||
this.sourceRect.y = -this.sourceRect.height;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
@@ -1925,11 +1936,28 @@ var ScrollingSpriteRenderer = (function (_super) {
|
||||
return _this;
|
||||
}
|
||||
ScrollingSpriteRenderer.prototype.update = function () {
|
||||
this.scrollX += this.scrollSpeedX * Time.deltaTime;
|
||||
this.scrollY += this.scroolSpeedY * Time.deltaTime;
|
||||
this._scrollX += this.scrollSpeedX * Time.deltaTime;
|
||||
this._scrollY += this.scroolSpeedY * Time.deltaTime;
|
||||
this.sourceRect.x = this._scrollX;
|
||||
this.sourceRect.y = this._scrollY;
|
||||
};
|
||||
ScrollingSpriteRenderer.prototype.render = function (camera) {
|
||||
if (!this.sprite)
|
||||
return;
|
||||
_super.prototype.render.call(this, camera);
|
||||
var renderTexture = new egret.RenderTexture();
|
||||
var cacheBitmap = new egret.DisplayObjectContainer();
|
||||
cacheBitmap.removeChildren();
|
||||
cacheBitmap.addChild(this.leftTexture);
|
||||
cacheBitmap.addChild(this.rightTexture);
|
||||
this.leftTexture.x = this.sourceRect.x;
|
||||
this.rightTexture.x = this.sourceRect.x - this.sourceRect.width;
|
||||
this.leftTexture.y = this.sourceRect.y;
|
||||
this.rightTexture.y = this.sourceRect.y;
|
||||
cacheBitmap.cacheAsBitmap = true;
|
||||
renderTexture.drawToTexture(cacheBitmap, new egret.Rectangle(0, 0, this.sourceRect.width, this.sourceRect.height));
|
||||
this.bitmap.texture = renderTexture;
|
||||
};
|
||||
return ScrollingSpriteRenderer;
|
||||
}(TiledSpriteRenderer));
|
||||
var Sprite = (function () {
|
||||
@@ -5095,6 +5123,47 @@ var ListPool = (function () {
|
||||
ListPool._objectQueue = [];
|
||||
return ListPool;
|
||||
}());
|
||||
var THREAD_ID = Math.floor(Math.random() * 1000) + "-" + Date.now();
|
||||
var setItem = egret.localStorage.setItem.bind(localStorage);
|
||||
var getItem = egret.localStorage.getItem.bind(localStorage);
|
||||
var removeItem = egret.localStorage.removeItem.bind(localStorage);
|
||||
var nextTick = function (fn) {
|
||||
setTimeout(fn, 0);
|
||||
};
|
||||
var LockUtils = (function () {
|
||||
function LockUtils(key) {
|
||||
this._keyX = "mutex_key_" + key + "_X";
|
||||
this._keyY = "mutex_key_" + key + "_Y";
|
||||
}
|
||||
LockUtils.prototype.lock = function () {
|
||||
var _this = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
var fn = function () {
|
||||
setItem(_this._keyX, THREAD_ID);
|
||||
if (!getItem(_this._keyY) === null) {
|
||||
nextTick(fn);
|
||||
}
|
||||
setItem(_this._keyY, THREAD_ID);
|
||||
if (getItem(_this._keyX) !== THREAD_ID) {
|
||||
setTimeout(function () {
|
||||
if (getItem(_this._keyY) !== THREAD_ID) {
|
||||
nextTick(fn);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
removeItem(_this._keyY);
|
||||
}, 10);
|
||||
}
|
||||
else {
|
||||
resolve();
|
||||
removeItem(_this._keyY);
|
||||
}
|
||||
};
|
||||
fn();
|
||||
});
|
||||
};
|
||||
return LockUtils;
|
||||
}());
|
||||
var Pair = (function () {
|
||||
function Pair(first, second) {
|
||||
this.first = first;
|
||||
@@ -5240,3 +5309,423 @@ var Vector2Ext = (function () {
|
||||
};
|
||||
return Vector2Ext;
|
||||
}());
|
||||
var Layout = (function () {
|
||||
function Layout() {
|
||||
this.clientArea = new Rectangle(0, 0, SceneManager.stage.stageWidth, SceneManager.stage.stageHeight);
|
||||
this.safeArea = this.clientArea;
|
||||
}
|
||||
Layout.prototype.place = function (size, horizontalMargin, verticalMargine, alignment) {
|
||||
var rc = new Rectangle(0, 0, size.x, size.y);
|
||||
if ((alignment & Alignment.left) != 0) {
|
||||
rc.x = this.clientArea.x + (this.clientArea.width * horizontalMargin);
|
||||
}
|
||||
else if ((alignment & Alignment.right) != 0) {
|
||||
rc.x = this.clientArea.x + (this.clientArea.width * (1 - horizontalMargin)) - rc.width;
|
||||
}
|
||||
else if ((alignment & Alignment.horizontalCenter) != 0) {
|
||||
rc.x = this.clientArea.x + (this.clientArea.width - rc.width) / 2 + (horizontalMargin * this.clientArea.width);
|
||||
}
|
||||
else {
|
||||
}
|
||||
if ((alignment & Alignment.top) != 0) {
|
||||
rc.y = this.clientArea.y + (this.clientArea.height * verticalMargine);
|
||||
}
|
||||
else if ((alignment & Alignment.bottom) != 0) {
|
||||
rc.y = this.clientArea.y + (this.clientArea.height * (1 - verticalMargine)) - rc.height;
|
||||
}
|
||||
else if ((alignment & Alignment.verticalCenter) != 0) {
|
||||
rc.y = this.clientArea.y + (this.clientArea.height - rc.height) / 2 + (verticalMargine * this.clientArea.height);
|
||||
}
|
||||
else {
|
||||
}
|
||||
if (rc.left < this.safeArea.left)
|
||||
rc.x = this.safeArea.left;
|
||||
if (rc.right > this.safeArea.right)
|
||||
rc.x = this.safeArea.right - rc.width;
|
||||
if (rc.top < this.safeArea.top)
|
||||
rc.y = this.safeArea.top;
|
||||
if (rc.bottom > this.safeArea.bottom)
|
||||
rc.y = this.safeArea.bottom - rc.height;
|
||||
return rc;
|
||||
};
|
||||
return Layout;
|
||||
}());
|
||||
var Alignment;
|
||||
(function (Alignment) {
|
||||
Alignment[Alignment["none"] = 0] = "none";
|
||||
Alignment[Alignment["left"] = 1] = "left";
|
||||
Alignment[Alignment["right"] = 2] = "right";
|
||||
Alignment[Alignment["horizontalCenter"] = 4] = "horizontalCenter";
|
||||
Alignment[Alignment["top"] = 8] = "top";
|
||||
Alignment[Alignment["bottom"] = 16] = "bottom";
|
||||
Alignment[Alignment["verticalCenter"] = 32] = "verticalCenter";
|
||||
Alignment[Alignment["topLeft"] = 9] = "topLeft";
|
||||
Alignment[Alignment["topRight"] = 10] = "topRight";
|
||||
Alignment[Alignment["topCenter"] = 12] = "topCenter";
|
||||
Alignment[Alignment["bottomLeft"] = 17] = "bottomLeft";
|
||||
Alignment[Alignment["bottomRight"] = 18] = "bottomRight";
|
||||
Alignment[Alignment["bottomCenter"] = 20] = "bottomCenter";
|
||||
Alignment[Alignment["centerLeft"] = 33] = "centerLeft";
|
||||
Alignment[Alignment["centerRight"] = 34] = "centerRight";
|
||||
Alignment[Alignment["center"] = 36] = "center";
|
||||
})(Alignment || (Alignment = {}));
|
||||
var stopwatch;
|
||||
(function (stopwatch) {
|
||||
var Stopwatch = (function () {
|
||||
function Stopwatch(getSystemTime) {
|
||||
if (getSystemTime === void 0) { getSystemTime = _defaultSystemTimeGetter; }
|
||||
this.getSystemTime = getSystemTime;
|
||||
this._stopDuration = 0;
|
||||
this._completeSlices = [];
|
||||
}
|
||||
Stopwatch.prototype.getState = function () {
|
||||
if (this._startSystemTime === undefined) {
|
||||
return State.IDLE;
|
||||
}
|
||||
else if (this._stopSystemTime === undefined) {
|
||||
return State.RUNNING;
|
||||
}
|
||||
else {
|
||||
return State.STOPPED;
|
||||
}
|
||||
};
|
||||
Stopwatch.prototype.isIdle = function () {
|
||||
return this.getState() === State.IDLE;
|
||||
};
|
||||
Stopwatch.prototype.isRunning = function () {
|
||||
return this.getState() === State.RUNNING;
|
||||
};
|
||||
Stopwatch.prototype.isStopped = function () {
|
||||
return this.getState() === State.STOPPED;
|
||||
};
|
||||
Stopwatch.prototype.slice = function () {
|
||||
return this.recordPendingSlice();
|
||||
};
|
||||
Stopwatch.prototype.getCompletedSlices = function () {
|
||||
return Array.from(this._completeSlices);
|
||||
};
|
||||
Stopwatch.prototype.getCompletedAndPendingSlices = function () {
|
||||
return this._completeSlices.concat([this.getPendingSlice()]);
|
||||
};
|
||||
Stopwatch.prototype.getPendingSlice = function () {
|
||||
return this.calculatePendingSlice();
|
||||
};
|
||||
Stopwatch.prototype.getTime = function () {
|
||||
return this.caculateStopwatchTime();
|
||||
};
|
||||
Stopwatch.prototype.calculatePendingSlice = function (endStopwatchTime) {
|
||||
if (this._pendingSliceStartStopwatchTime === undefined) {
|
||||
return Object.freeze({ startTime: 0, endTime: 0, duration: 0 });
|
||||
}
|
||||
if (endStopwatchTime === undefined) {
|
||||
endStopwatchTime = this.getTime();
|
||||
}
|
||||
return Object.freeze({
|
||||
startTime: this._pendingSliceStartStopwatchTime,
|
||||
endTime: endStopwatchTime,
|
||||
duration: endStopwatchTime - this._pendingSliceStartStopwatchTime
|
||||
});
|
||||
};
|
||||
Stopwatch.prototype.caculateStopwatchTime = function (endSystemTime) {
|
||||
if (this._startSystemTime === undefined)
|
||||
return 0;
|
||||
if (endSystemTime === undefined)
|
||||
endSystemTime = this.getSystemTimeOfCurrentStopwatchTime();
|
||||
return endSystemTime - this._startSystemTime - this._stopDuration;
|
||||
};
|
||||
Stopwatch.prototype.getSystemTimeOfCurrentStopwatchTime = function () {
|
||||
return this._stopSystemTime === undefined ? this.getSystemTime() : this._stopSystemTime;
|
||||
};
|
||||
Stopwatch.prototype.reset = function () {
|
||||
this._startSystemTime = this._pendingSliceStartStopwatchTime = this._stopSystemTime = undefined;
|
||||
this._stopDuration = 0;
|
||||
this._completeSlices = [];
|
||||
};
|
||||
Stopwatch.prototype.start = function (forceReset) {
|
||||
if (forceReset === void 0) { forceReset = false; }
|
||||
if (forceReset) {
|
||||
this.reset();
|
||||
}
|
||||
if (this._stopSystemTime !== undefined) {
|
||||
var systemNow = this.getSystemTime();
|
||||
var stopDuration = systemNow - this._stopSystemTime;
|
||||
this._stopDuration += stopDuration;
|
||||
this._stopSystemTime = undefined;
|
||||
}
|
||||
else if (this._startSystemTime === undefined) {
|
||||
var systemNow = this.getSystemTime();
|
||||
this._startSystemTime = systemNow;
|
||||
this._pendingSliceStartStopwatchTime = 0;
|
||||
}
|
||||
};
|
||||
Stopwatch.prototype.stop = function (recordPendingSlice) {
|
||||
if (recordPendingSlice === void 0) { recordPendingSlice = false; }
|
||||
if (this._startSystemTime === undefined) {
|
||||
return 0;
|
||||
}
|
||||
var systemTimeOfStopwatchTime = this.getSystemTimeOfCurrentStopwatchTime();
|
||||
if (recordPendingSlice) {
|
||||
this.recordPendingSlice(this.caculateStopwatchTime(systemTimeOfStopwatchTime));
|
||||
}
|
||||
this._stopSystemTime = systemTimeOfStopwatchTime;
|
||||
return this.getTime();
|
||||
};
|
||||
Stopwatch.prototype.recordPendingSlice = function (endStopwatchTime) {
|
||||
if (this._pendingSliceStartStopwatchTime !== undefined) {
|
||||
if (endStopwatchTime === undefined) {
|
||||
endStopwatchTime = this.getTime();
|
||||
}
|
||||
var slice = this.calculatePendingSlice(endStopwatchTime);
|
||||
this._pendingSliceStartStopwatchTime = slice.endTime;
|
||||
this._completeSlices.push(slice);
|
||||
return slice;
|
||||
}
|
||||
else {
|
||||
return this.calculatePendingSlice();
|
||||
}
|
||||
};
|
||||
return Stopwatch;
|
||||
}());
|
||||
stopwatch.Stopwatch = Stopwatch;
|
||||
var State;
|
||||
(function (State) {
|
||||
State["IDLE"] = "IDLE";
|
||||
State["RUNNING"] = "RUNNING";
|
||||
State["STOPPED"] = "STOPPED";
|
||||
})(State || (State = {}));
|
||||
function setDefaultSystemTimeGetter(systemTimeGetter) {
|
||||
if (systemTimeGetter === void 0) { systemTimeGetter = Date.now; }
|
||||
_defaultSystemTimeGetter = systemTimeGetter;
|
||||
}
|
||||
stopwatch.setDefaultSystemTimeGetter = setDefaultSystemTimeGetter;
|
||||
var _defaultSystemTimeGetter = Date.now;
|
||||
})(stopwatch || (stopwatch = {}));
|
||||
var TimeRuler = (function () {
|
||||
function TimeRuler() {
|
||||
this._frameKey = 'frame';
|
||||
this._logKey = 'log';
|
||||
this.markers = [];
|
||||
this.stopwacth = new stopwatch.Stopwatch();
|
||||
this._markerNameToIdMap = new Map();
|
||||
this.showLog = false;
|
||||
TimeRuler.Instance = this;
|
||||
this._logs = new Array(2);
|
||||
for (var i = 0; i < this._logs.length; ++i)
|
||||
this._logs[i] = new FrameLog();
|
||||
this.sampleFrames = this.targetSampleFrames = 1;
|
||||
this.width = SceneManager.stage.stageWidth * 0.8;
|
||||
this.onGraphicsDeviceReset();
|
||||
}
|
||||
TimeRuler.prototype.onGraphicsDeviceReset = function () {
|
||||
var layout = new Layout();
|
||||
this._position = layout.place(new Vector2(this.width, TimeRuler.barHeight), 0, 0.01, Alignment.bottomCenter).location;
|
||||
};
|
||||
TimeRuler.prototype.startFrame = function () {
|
||||
var _this = this;
|
||||
var lock = new LockUtils(this._frameKey);
|
||||
lock.lock().then(function () {
|
||||
_this._updateCount = parseInt(egret.localStorage.getItem(_this._frameKey), 10);
|
||||
var count = _this._updateCount;
|
||||
count += 1;
|
||||
egret.localStorage.setItem(_this._frameKey, count.toString());
|
||||
if (_this.enabled && (1 < count && count < TimeRuler.maxSampleFrames))
|
||||
return;
|
||||
_this._prevLog = _this._logs[_this.frameCount++ & 0x1];
|
||||
_this._curLog = _this._logs[_this.frameCount & 0x1];
|
||||
var endFrameTime = _this.stopwacth.getTime();
|
||||
for (var barIndex = 0; barIndex < _this._prevLog.bars.length; ++barIndex) {
|
||||
var prevBar = _this._prevLog.bars[barIndex];
|
||||
var nextBar = _this._curLog.bars[barIndex];
|
||||
for (var nest = 0; nest < prevBar.nestCount; ++nest) {
|
||||
var markerIdx = prevBar.markerNests[nest];
|
||||
prevBar.markers[markerIdx].endTime = endFrameTime;
|
||||
nextBar.markerNests[nest] = nest;
|
||||
nextBar.markers[nest].markerId = prevBar.markers[markerIdx].markerId;
|
||||
nextBar.markers[nest].beginTime = 0;
|
||||
nextBar.markers[nest].endTime = -1;
|
||||
nextBar.markers[nest].color = prevBar.markers[markerIdx].color;
|
||||
}
|
||||
for (var markerIdx = 0; markerIdx < prevBar.markCount; ++markerIdx) {
|
||||
var duration = prevBar.markers[markerIdx].endTime - prevBar.markers[markerIdx].beginTime;
|
||||
var markerId = prevBar.markers[markerIdx].markerId;
|
||||
var m = _this.markers[markerId];
|
||||
m.logs[barIndex].color = prevBar.markers[markerIdx].color;
|
||||
if (!m.logs[barIndex].initialized) {
|
||||
m.logs[barIndex].min = duration;
|
||||
m.logs[barIndex].max = duration;
|
||||
m.logs[barIndex].avg = duration;
|
||||
m.logs[barIndex].initialized = true;
|
||||
}
|
||||
else {
|
||||
m.logs[barIndex].min = Math.min(m.logs[barIndex].min, duration);
|
||||
m.logs[barIndex].max = Math.min(m.logs[barIndex].max, duration);
|
||||
m.logs[barIndex].avg += duration;
|
||||
m.logs[barIndex].avg *= 0.5;
|
||||
if (m.logs[barIndex].samples++ >= TimeRuler.logSnapDuration) {
|
||||
m.logs[barIndex].snapMin = m.logs[barIndex].min;
|
||||
m.logs[barIndex].snapMax = m.logs[barIndex].max;
|
||||
m.logs[barIndex].snapAvg = m.logs[barIndex].avg;
|
||||
m.logs[barIndex].samples = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
nextBar.markCount = prevBar.nestCount;
|
||||
nextBar.nestCount = prevBar.nestCount;
|
||||
}
|
||||
_this.stopwacth.reset();
|
||||
_this.stopwacth.start();
|
||||
});
|
||||
};
|
||||
TimeRuler.prototype.beginMark = function (markerName, color, barIndex) {
|
||||
var _this = this;
|
||||
if (barIndex === void 0) { barIndex = 0; }
|
||||
var lock = new LockUtils(this._frameKey);
|
||||
lock.lock().then(function () {
|
||||
if (barIndex < 0 || barIndex >= TimeRuler.maxBars)
|
||||
throw new Error("barIndex argument out of range");
|
||||
var bar = _this._curLog.bars[barIndex];
|
||||
if (bar.markCount >= TimeRuler.maxSamples) {
|
||||
throw new Error("exceeded sample count. either set larger number to timeruler.maxsaple or lower sample count");
|
||||
}
|
||||
if (bar.nestCount >= TimeRuler.maxNestCall) {
|
||||
throw new Error("exceeded nest count. either set larger number to timeruler.maxnestcall or lower nest calls");
|
||||
}
|
||||
var markerId = _this._markerNameToIdMap.get(markerName);
|
||||
if (!markerId) {
|
||||
markerId = _this.markers.length;
|
||||
_this._markerNameToIdMap.set(markerName, markerId);
|
||||
}
|
||||
bar.markerNests[bar.nestCount++] = bar.markCount;
|
||||
bar.markers[bar.markCount].markerId = markerId;
|
||||
bar.markers[bar.markCount].color = color;
|
||||
bar.markers[bar.markCount].beginTime = _this.stopwacth.getTime();
|
||||
bar.markers[bar.markCount].endTime = -1;
|
||||
});
|
||||
};
|
||||
TimeRuler.prototype.endMark = function (markerName, barIndex) {
|
||||
var _this = this;
|
||||
if (barIndex === void 0) { barIndex = 0; }
|
||||
var lock = new LockUtils(this._frameKey);
|
||||
lock.lock().then(function () {
|
||||
if (barIndex < 0 || barIndex >= TimeRuler.maxBars)
|
||||
throw new Error("barIndex argument out of range");
|
||||
var bar = _this._curLog.bars[barIndex];
|
||||
if (bar.nestCount <= 0) {
|
||||
throw new Error("call beginMark method before calling endMark method");
|
||||
}
|
||||
var markerId = _this._markerNameToIdMap.get(markerName);
|
||||
if (!markerId) {
|
||||
throw new Error("Marker " + markerName + " is not registered. Make sure you specifed same name as you used for beginMark method");
|
||||
}
|
||||
var markerIdx = bar.markerNests[--bar.nestCount];
|
||||
if (bar.markers[markerIdx].markerId != markerId) {
|
||||
throw new Error("Incorrect call order of beginMark/endMark method. beginMark(A), beginMark(B), endMark(B), endMark(A) But you can't called it like beginMark(A), beginMark(B), endMark(A), endMark(B).");
|
||||
}
|
||||
bar.markers[markerIdx].endTime = _this.stopwacth.getTime();
|
||||
});
|
||||
};
|
||||
TimeRuler.prototype.getAverageTime = function (barIndex, markerName) {
|
||||
if (barIndex < 0 || barIndex >= TimeRuler.maxBars) {
|
||||
throw new Error("barIndex argument out of range");
|
||||
}
|
||||
var result = 0;
|
||||
var markerId = this._markerNameToIdMap.get(markerName);
|
||||
if (markerId) {
|
||||
result = this.markers[markerId].logs[barIndex].avg;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
TimeRuler.prototype.resetLog = function () {
|
||||
var _this = this;
|
||||
var lock = new LockUtils(this._logKey);
|
||||
lock.lock().then(function () {
|
||||
var count = parseInt(egret.localStorage.getItem(_this._logKey), 10);
|
||||
count += 1;
|
||||
egret.localStorage.setItem(_this._logKey, count.toString());
|
||||
_this.markers.forEach(function (markerInfo) {
|
||||
for (var i = 0; i < markerInfo.logs.length; ++i) {
|
||||
markerInfo.logs[i].initialized = false;
|
||||
markerInfo.logs[i].snapMin = 0;
|
||||
markerInfo.logs[i].snapMax = 0;
|
||||
markerInfo.logs[i].snapAvg = 0;
|
||||
markerInfo.logs[i].min = 0;
|
||||
markerInfo.logs[i].max = 0;
|
||||
markerInfo.logs[i].avg = 0;
|
||||
markerInfo.logs[i].samples = 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
TimeRuler.prototype.render = function (position, width) {
|
||||
if (position === void 0) { position = this._position; }
|
||||
if (width === void 0) { width = this.width; }
|
||||
egret.localStorage.setItem(this._frameKey, "0");
|
||||
if (!this.showLog)
|
||||
return;
|
||||
var height = 0;
|
||||
var maxTime = 0;
|
||||
this._prevLog.bars.forEach(function (bar) {
|
||||
if (bar.markCount > 0) {
|
||||
height += TimeRuler.barHeight + TimeRuler.barPadding * 2;
|
||||
maxTime = Math.max(maxTime, bar.markers[bar.markCount - 1].endTime);
|
||||
}
|
||||
});
|
||||
var frameSpan = 1 / 60 * 1000;
|
||||
var sampleSpan = this.sampleFrames * frameSpan;
|
||||
if (maxTime > sampleSpan) {
|
||||
this._frameAdjust = Math.max(0, this._frameAdjust) + 1;
|
||||
}
|
||||
else {
|
||||
this._frameAdjust = Math.min(0, this._frameAdjust) - 1;
|
||||
}
|
||||
if (Math.max(this._frameAdjust) > TimeRuler.autoAdjustDelay) {
|
||||
this.sampleFrames = Math.min(TimeRuler.maxSampleFrames, this.sampleFrames);
|
||||
this.sampleFrames = Math.max(this.targetSampleFrames, (maxTime / frameSpan) + 1);
|
||||
this._frameAdjust = 0;
|
||||
}
|
||||
var msToPs = width / sampleSpan;
|
||||
var startY = position.y - (height - TimeRuler.barHeight);
|
||||
var y = startY;
|
||||
};
|
||||
TimeRuler.maxBars = 0;
|
||||
TimeRuler.maxSamples = 256;
|
||||
TimeRuler.maxNestCall = 32;
|
||||
TimeRuler.barHeight = 8;
|
||||
TimeRuler.maxSampleFrames = 4;
|
||||
TimeRuler.logSnapDuration = 120;
|
||||
TimeRuler.barPadding = 2;
|
||||
TimeRuler.autoAdjustDelay = 30;
|
||||
return TimeRuler;
|
||||
}());
|
||||
var FrameLog = (function () {
|
||||
function FrameLog() {
|
||||
this.bars = new Array(TimeRuler.maxBars);
|
||||
for (var i = 0; i < TimeRuler.maxBars; ++i)
|
||||
this.bars[i] = new MarkerCollection();
|
||||
}
|
||||
return FrameLog;
|
||||
}());
|
||||
var MarkerCollection = (function () {
|
||||
function MarkerCollection() {
|
||||
this.markers = new Array(TimeRuler.maxSamples);
|
||||
this.markerNests = new Array(TimeRuler.maxNestCall);
|
||||
}
|
||||
return MarkerCollection;
|
||||
}());
|
||||
var Marker = (function () {
|
||||
function Marker() {
|
||||
}
|
||||
return Marker;
|
||||
}());
|
||||
var MarkerInfo = (function () {
|
||||
function MarkerInfo(name) {
|
||||
this.logs = new Array(TimeRuler.maxBars);
|
||||
this.name = name;
|
||||
}
|
||||
return MarkerInfo;
|
||||
}());
|
||||
var MarkerLog = (function () {
|
||||
function MarkerLog() {
|
||||
}
|
||||
return MarkerLog;
|
||||
}());
|
||||
|
||||
2
source/bin/framework.min.js
vendored
2
source/bin/framework.min.js
vendored
File diff suppressed because one or more lines are too long
4
source/src/ECS/CoreEvents.ts
Normal file
4
source/src/ECS/CoreEvents.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
enum CoreEvents{
|
||||
/** 当场景发生变化时触发 */
|
||||
SceneChanged,
|
||||
}
|
||||
@@ -4,10 +4,25 @@ class SceneManager {
|
||||
private static _nextScene: Scene;
|
||||
public static sceneTransition: SceneTransition;
|
||||
public static stage: egret.Stage;
|
||||
/** 订阅此事件以在活动场景发生更改时得到通知。 */
|
||||
public static activeSceneChanged: Function;
|
||||
/** 核心发射器。只发出核心级别的事件 */
|
||||
public static emitter: Emitter<CoreEvents>;
|
||||
/** 全局内容管理器加载任何应该停留在场景之间的资产 */
|
||||
public static content: ContentManager;
|
||||
/** 简化对内部类的全局内容实例的访问 */
|
||||
private static _instnace: SceneManager;
|
||||
public static get Instance(){
|
||||
return this._instnace;
|
||||
}
|
||||
|
||||
constructor(stage: egret.Stage) {
|
||||
stage.addEventListener(egret.Event.ENTER_FRAME, SceneManager.update, this);
|
||||
|
||||
SceneManager._instnace = this;
|
||||
SceneManager.emitter = new Emitter<CoreEvents>();
|
||||
SceneManager.content = new ContentManager();
|
||||
|
||||
SceneManager.stage = stage;
|
||||
SceneManager.initialize(stage);
|
||||
}
|
||||
@@ -22,9 +37,12 @@ class SceneManager {
|
||||
if (this._scene == null) {
|
||||
this._scene = value;
|
||||
this._scene.begin();
|
||||
SceneManager.Instance.onSceneChanged();
|
||||
} else {
|
||||
this._nextScene = value;
|
||||
}
|
||||
|
||||
this.registerActiveSceneChanged(this._scene, this._nextScene);
|
||||
}
|
||||
|
||||
public static initialize(stage: egret.Stage) {
|
||||
@@ -48,13 +66,9 @@ class SceneManager {
|
||||
if (SceneManager._nextScene) {
|
||||
SceneManager._scene.end();
|
||||
|
||||
for (let i = 0; i < SceneManager._scene.entities.buffer.length; i++) {
|
||||
let entity = SceneManager._scene.entities.buffer[i];
|
||||
entity.destroy();
|
||||
}
|
||||
|
||||
SceneManager._scene = SceneManager._nextScene;
|
||||
SceneManager._nextScene = null;
|
||||
SceneManager._instnace.onSceneChanged();
|
||||
|
||||
SceneManager._scene.begin();
|
||||
}
|
||||
@@ -101,4 +115,16 @@ class SceneManager {
|
||||
this.sceneTransition = sceneTransition;
|
||||
return sceneTransition;
|
||||
}
|
||||
|
||||
public static registerActiveSceneChanged(current: Scene, next: Scene){
|
||||
if (this.activeSceneChanged)
|
||||
this.activeSceneChanged(current, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在一个场景结束后,下一个场景开始之前调用
|
||||
*/
|
||||
public onSceneChanged(){
|
||||
|
||||
}
|
||||
}
|
||||
69
source/src/Utils/Analysis/Layout.ts
Normal file
69
source/src/Utils/Analysis/Layout.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 支持标题安全区的布局类。
|
||||
*/
|
||||
class Layout {
|
||||
public clientArea: Rectangle;
|
||||
public safeArea: Rectangle;
|
||||
|
||||
constructor(){
|
||||
this.clientArea = new Rectangle(0, 0, SceneManager.stage.stageWidth, SceneManager.stage.stageHeight);
|
||||
this.safeArea = this.clientArea;
|
||||
}
|
||||
|
||||
public place(size: Vector2, horizontalMargin: number, verticalMargine: number, alignment: Alignment){
|
||||
let rc = new Rectangle(0, 0, size.x, size.y);
|
||||
if ((alignment & Alignment.left) != 0){
|
||||
rc.x = this.clientArea.x + (this.clientArea.width * horizontalMargin);
|
||||
}else if((alignment & Alignment.right) != 0){
|
||||
rc.x = this.clientArea.x + (this.clientArea.width * (1 - horizontalMargin)) - rc.width;
|
||||
} else if((alignment & Alignment.horizontalCenter) != 0){
|
||||
rc.x = this.clientArea.x + (this.clientArea.width - rc.width) / 2 + (horizontalMargin * this.clientArea.width);
|
||||
}else{
|
||||
|
||||
}
|
||||
|
||||
if ((alignment & Alignment.top) != 0){
|
||||
rc.y = this.clientArea.y + (this.clientArea.height * verticalMargine);
|
||||
}else if((alignment & Alignment.bottom) != 0){
|
||||
rc.y = this.clientArea.y + (this.clientArea.height * (1 - verticalMargine)) - rc.height;
|
||||
} else if((alignment & Alignment.verticalCenter) != 0){
|
||||
rc.y = this.clientArea.y + (this.clientArea.height - rc.height) / 2 + (verticalMargine * this.clientArea.height);
|
||||
}else{
|
||||
|
||||
}
|
||||
|
||||
// 确保布局区域在安全区域内。
|
||||
if (rc.left < this.safeArea.left)
|
||||
rc.x = this.safeArea.left;
|
||||
|
||||
if (rc.right > this.safeArea.right)
|
||||
rc.x = this.safeArea.right - rc.width;
|
||||
|
||||
if (rc.top < this.safeArea.top)
|
||||
rc.y = this.safeArea.top;
|
||||
|
||||
if (rc.bottom > this.safeArea.bottom)
|
||||
rc.y = this.safeArea.bottom - rc.height;
|
||||
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
enum Alignment {
|
||||
none = 0,
|
||||
left = 1,
|
||||
right = 2,
|
||||
horizontalCenter = 4,
|
||||
top = 8,
|
||||
bottom = 16,
|
||||
verticalCenter = 32,
|
||||
topLeft = top | left,
|
||||
topRight = top | right,
|
||||
topCenter = top | horizontalCenter,
|
||||
bottomLeft = bottom | left,
|
||||
bottomRight = bottom | right,
|
||||
bottomCenter = bottom | horizontalCenter,
|
||||
centerLeft = verticalCenter | left,
|
||||
centerRight = verticalCenter | right,
|
||||
center = verticalCenter | horizontalCenter
|
||||
}
|
||||
233
source/src/Utils/Analysis/Stopwatch.ts
Normal file
233
source/src/Utils/Analysis/Stopwatch.ts
Normal file
@@ -0,0 +1,233 @@
|
||||
namespace stopwatch {
|
||||
/**
|
||||
* 记录时间的持续时间,一些设计灵感来自物理秒表。
|
||||
*/
|
||||
export class Stopwatch {
|
||||
/**
|
||||
* 秒表启动的系统时间。
|
||||
* undefined,如果秒表尚未启动,或已复位。
|
||||
*/
|
||||
private _startSystemTime: number | undefined;
|
||||
/**
|
||||
* 秒表停止的系统时间。
|
||||
* undefined,如果秒表目前没有停止,尚未开始,或已复位。
|
||||
*/
|
||||
private _stopSystemTime: number | undefined;
|
||||
/** 自上次复位以来,秒表已停止的系统时间总数。 */
|
||||
private _stopDuration: number = 0;
|
||||
/**
|
||||
* 用秒表计时,当前等待的切片开始的时间。
|
||||
* undefined,如果秒表尚未启动,或已复位。
|
||||
*/
|
||||
private _pendingSliceStartStopwatchTime: number | undefined;
|
||||
/**
|
||||
* 记录自上次复位以来所有已完成切片的结果。
|
||||
*/
|
||||
private _completeSlices: Slice[] = [];
|
||||
|
||||
constructor(private readonly getSystemTime = _defaultSystemTimeGetter) { }
|
||||
|
||||
public getState() {
|
||||
if (this._startSystemTime === undefined) {
|
||||
return State.IDLE;
|
||||
} else if (this._stopSystemTime === undefined) {
|
||||
return State.RUNNING;
|
||||
} else {
|
||||
return State.STOPPED;
|
||||
}
|
||||
}
|
||||
|
||||
public isIdle(){
|
||||
return this.getState() === State.IDLE;
|
||||
}
|
||||
|
||||
public isRunning(){
|
||||
return this.getState() === State.RUNNING;
|
||||
}
|
||||
|
||||
public isStopped(){
|
||||
return this.getState() === State.STOPPED;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public slice(){
|
||||
return this.recordPendingSlice();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自上次复位以来该秒表已完成/记录的所有片的列表。
|
||||
*/
|
||||
public getCompletedSlices(): Slice[] {
|
||||
return Array.from(this._completeSlices);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自上次重置以来该秒表已完成/记录的所有片的列表,以及当前挂起的片。
|
||||
*/
|
||||
public getCompletedAndPendingSlices(): Slice[] {
|
||||
return [...this._completeSlices, this.getPendingSlice()];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取关于这个秒表当前挂起的切片的详细信息。
|
||||
*/
|
||||
public getPendingSlice(): Slice {
|
||||
return this.calculatePendingSlice();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前秒表时间。这是这个秒表自上次复位以来运行的系统时间总数。
|
||||
*/
|
||||
public getTime(){
|
||||
return this.caculateStopwatchTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算指定秒表时间的当前挂起片。
|
||||
* @param endStopwatchTime
|
||||
*/
|
||||
private calculatePendingSlice(endStopwatchTime?: number): Slice {
|
||||
if (this._pendingSliceStartStopwatchTime === undefined){
|
||||
return Object.freeze({startTime: 0, endTime: 0, duration: 0});
|
||||
}
|
||||
|
||||
if (endStopwatchTime === undefined){
|
||||
endStopwatchTime = this.getTime();
|
||||
}
|
||||
|
||||
return Object.freeze({
|
||||
startTime: this._pendingSliceStartStopwatchTime,
|
||||
endTime: endStopwatchTime,
|
||||
duration: endStopwatchTime - this._pendingSliceStartStopwatchTime
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算指定系统时间的当前秒表时间。
|
||||
* @param endSystemTime
|
||||
*/
|
||||
private caculateStopwatchTime(endSystemTime?: number){
|
||||
if (this._startSystemTime === undefined)
|
||||
return 0;
|
||||
|
||||
if (endSystemTime === undefined)
|
||||
endSystemTime = this.getSystemTimeOfCurrentStopwatchTime();
|
||||
|
||||
return endSystemTime - this._startSystemTime - this._stopDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取与当前秒表时间等效的系统时间。
|
||||
* 如果该秒表当前停止,则返回该秒表停止时的系统时间。
|
||||
*/
|
||||
private getSystemTimeOfCurrentStopwatchTime(){
|
||||
return this._stopSystemTime === undefined ? this.getSystemTime() : this._stopSystemTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 完全重置这个秒表到它的初始状态。清除所有记录的运行持续时间、切片等。
|
||||
*/
|
||||
public reset(){
|
||||
this._startSystemTime = this._pendingSliceStartStopwatchTime = this._stopSystemTime = undefined;
|
||||
this._stopDuration = 0;
|
||||
this._completeSlices = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始(或继续)运行秒表。
|
||||
* @param forceReset
|
||||
*/
|
||||
public start(forceReset: boolean = false){
|
||||
if (forceReset){
|
||||
this.reset();
|
||||
}
|
||||
|
||||
if (this._stopSystemTime !== undefined){
|
||||
const systemNow = this.getSystemTime();
|
||||
const stopDuration = systemNow - this._stopSystemTime;
|
||||
|
||||
this._stopDuration += stopDuration;
|
||||
this._stopSystemTime = undefined;
|
||||
} else if (this._startSystemTime === undefined){
|
||||
const systemNow = this.getSystemTime();
|
||||
this._startSystemTime = systemNow;
|
||||
this._pendingSliceStartStopwatchTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param recordPendingSlice
|
||||
*/
|
||||
public stop(recordPendingSlice: boolean = false){
|
||||
if (this._startSystemTime === undefined){
|
||||
return 0;
|
||||
}
|
||||
|
||||
const systemTimeOfStopwatchTime = this.getSystemTimeOfCurrentStopwatchTime();
|
||||
|
||||
if (recordPendingSlice){
|
||||
this.recordPendingSlice(this.caculateStopwatchTime(systemTimeOfStopwatchTime));
|
||||
}
|
||||
|
||||
this._stopSystemTime = systemTimeOfStopwatchTime;
|
||||
return this.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束/记录当前挂起的片的私有实现。
|
||||
* @param endStopwatchTime
|
||||
*/
|
||||
private recordPendingSlice(endStopwatchTime?: number){
|
||||
if (this._pendingSliceStartStopwatchTime !== undefined){
|
||||
if (endStopwatchTime === undefined){
|
||||
endStopwatchTime = this.getTime();
|
||||
}
|
||||
|
||||
const slice = this.calculatePendingSlice(endStopwatchTime);
|
||||
|
||||
this._pendingSliceStartStopwatchTime = slice.endTime;
|
||||
this._completeSlices.push(slice);
|
||||
return slice;
|
||||
} else {
|
||||
return this.calculatePendingSlice();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回某个系统的“当前时间”的函数。
|
||||
* 惟一的要求是,对该函数的每次调用都必须返回一个大于或等于前一次对该函数的调用的数字。
|
||||
*/
|
||||
export type GetTimeFunc = () => number;
|
||||
|
||||
enum State {
|
||||
/** 秒表尚未启动,或已复位。 */
|
||||
IDLE = "IDLE",
|
||||
/** 秒表正在运行。 */
|
||||
RUNNING = "RUNNING",
|
||||
/** 秒表以前还在跑,但现在已经停了。 */
|
||||
STOPPED = "STOPPED"
|
||||
}
|
||||
|
||||
export function setDefaultSystemTimeGetter(systemTimeGetter: GetTimeFunc = Date.now) {
|
||||
_defaultSystemTimeGetter = systemTimeGetter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 由秒表记录的单个“薄片”的测量值
|
||||
*/
|
||||
interface Slice {
|
||||
/** 秒表显示的时间在这一片开始的时候。 */
|
||||
readonly startTime: number;
|
||||
/** 秒表在这片片尾的时间。 */
|
||||
readonly endTime: number;
|
||||
/** 该切片的运行时间 */
|
||||
readonly duration: number;
|
||||
}
|
||||
|
||||
/** 所有新实例的默认“getSystemTime”实现 */
|
||||
let _defaultSystemTimeGetter: GetTimeFunc = Date.now;
|
||||
}
|
||||
342
source/src/Utils/Analysis/TimeRuler.ts
Normal file
342
source/src/Utils/Analysis/TimeRuler.ts
Normal file
@@ -0,0 +1,342 @@
|
||||
/**
|
||||
* 通过使用这个类,您可以直观地找到瓶颈和基本的CPU使用情况。
|
||||
*/
|
||||
class TimeRuler {
|
||||
/** 最大条数 8 */
|
||||
public static readonly maxBars = 0;
|
||||
/** */
|
||||
public static readonly maxSamples = 256;
|
||||
/** 每条的最大嵌套调用 */
|
||||
public static readonly maxNestCall = 32;
|
||||
/** 条的高度(以像素为单位) */
|
||||
public static readonly barHeight = 8;
|
||||
/** 最大显示帧 */
|
||||
public static readonly maxSampleFrames = 4;
|
||||
/** 持续时间(帧数)为采取抓拍日志。 */
|
||||
public static readonly logSnapDuration = 120;
|
||||
public static readonly barPadding = 2;
|
||||
public static readonly autoAdjustDelay = 30;
|
||||
public static Instance: TimeRuler;
|
||||
private _frameKey = 'frame';
|
||||
private _logKey = 'log';
|
||||
|
||||
/** 每帧的日志 */
|
||||
private _logs: FrameLog[];
|
||||
/** 当前显示帧计数 */
|
||||
private sampleFrames: number;
|
||||
/** 获取/设置目标样本帧。 */
|
||||
public targetSampleFrames: number;
|
||||
/** 获取/设置计时器标尺宽度。 */
|
||||
public width: number;
|
||||
public enabled: true;
|
||||
/** TimerRuler画的位置。 */
|
||||
private _position: Vector2;
|
||||
/** 上一帧日志 */
|
||||
private _prevLog: FrameLog;
|
||||
/** 当前帧日志 */
|
||||
private _curLog: FrameLog;
|
||||
/** 当前帧数量 */
|
||||
private frameCount: number;
|
||||
/** */
|
||||
private markers: MarkerInfo[] = [];
|
||||
/** 秒表用来测量时间。 */
|
||||
private stopwacth: stopwatch.Stopwatch = new stopwatch.Stopwatch();
|
||||
/** 从标记名映射到标记id的字典。 */
|
||||
private _markerNameToIdMap: Map<string, number> = new Map<string, number>();
|
||||
/**
|
||||
* 你想在游戏开始时调用StartFrame更新方法。
|
||||
* 当游戏在固定时间步进模式下运行缓慢时,更新会多次调用。
|
||||
* 在这种情况下,我们应该忽略StartFrame调用。
|
||||
* 为此,我们只需一直跟踪StartFrame调用的次数,直到Draw被调用。
|
||||
*/
|
||||
private _updateCount: number;
|
||||
/** */
|
||||
public showLog = false;
|
||||
private _frameAdjust: number;
|
||||
|
||||
constructor() {
|
||||
TimeRuler.Instance = this;
|
||||
this._logs = new Array<FrameLog>(2);
|
||||
for (let i = 0; i < this._logs.length; ++i)
|
||||
this._logs[i] = new FrameLog();
|
||||
|
||||
this.sampleFrames = this.targetSampleFrames = 1;
|
||||
this.width = SceneManager.stage.stageWidth * 0.8;
|
||||
this.onGraphicsDeviceReset();
|
||||
}
|
||||
|
||||
private onGraphicsDeviceReset() {
|
||||
let layout = new Layout();
|
||||
this._position = layout.place(new Vector2(this.width, TimeRuler.barHeight), 0, 0.01, Alignment.bottomCenter).location;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public startFrame() {
|
||||
// 当这个方法被多次调用时,我们跳过重置帧。
|
||||
let lock = new LockUtils(this._frameKey);
|
||||
lock.lock().then(() => {
|
||||
this._updateCount = parseInt(egret.localStorage.getItem(this._frameKey), 10);
|
||||
let count = this._updateCount;
|
||||
count += 1;
|
||||
egret.localStorage.setItem(this._frameKey, count.toString());
|
||||
if (this.enabled && (1 < count && count < TimeRuler.maxSampleFrames))
|
||||
return;
|
||||
|
||||
// 更新当前帧日志。
|
||||
this._prevLog = this._logs[this.frameCount++ & 0x1];
|
||||
this._curLog = this._logs[this.frameCount & 0x1];
|
||||
|
||||
let endFrameTime = this.stopwacth.getTime();
|
||||
// 更新标记并创建日志。
|
||||
for (let barIndex = 0; barIndex < this._prevLog.bars.length; ++barIndex) {
|
||||
let prevBar = this._prevLog.bars[barIndex];
|
||||
let nextBar = this._curLog.bars[barIndex];
|
||||
|
||||
// 重新打开在前一帧中没有调用结束标记的标记。
|
||||
for (let nest = 0; nest < prevBar.nestCount; ++nest) {
|
||||
let markerIdx = prevBar.markerNests[nest];
|
||||
prevBar.markers[markerIdx].endTime = endFrameTime;
|
||||
nextBar.markerNests[nest] = nest;
|
||||
nextBar.markers[nest].markerId = prevBar.markers[markerIdx].markerId;
|
||||
nextBar.markers[nest].beginTime = 0;
|
||||
nextBar.markers[nest].endTime = -1;
|
||||
nextBar.markers[nest].color = prevBar.markers[markerIdx].color;
|
||||
}
|
||||
|
||||
// 更新日志标记
|
||||
for (let markerIdx = 0; markerIdx < prevBar.markCount; ++markerIdx) {
|
||||
let duration = prevBar.markers[markerIdx].endTime - prevBar.markers[markerIdx].beginTime;
|
||||
let markerId = prevBar.markers[markerIdx].markerId;
|
||||
let m = this.markers[markerId];
|
||||
|
||||
m.logs[barIndex].color = prevBar.markers[markerIdx].color;
|
||||
if (!m.logs[barIndex].initialized) {
|
||||
m.logs[barIndex].min = duration;
|
||||
m.logs[barIndex].max = duration;
|
||||
m.logs[barIndex].avg = duration;
|
||||
m.logs[barIndex].initialized = true;
|
||||
} else {
|
||||
m.logs[barIndex].min = Math.min(m.logs[barIndex].min, duration);
|
||||
m.logs[barIndex].max = Math.min(m.logs[barIndex].max, duration);
|
||||
m.logs[barIndex].avg += duration;
|
||||
m.logs[barIndex].avg *= 0.5;
|
||||
|
||||
if (m.logs[barIndex].samples++ >= TimeRuler.logSnapDuration) {
|
||||
m.logs[barIndex].snapMin = m.logs[barIndex].min;
|
||||
m.logs[barIndex].snapMax = m.logs[barIndex].max;
|
||||
m.logs[barIndex].snapAvg = m.logs[barIndex].avg;
|
||||
m.logs[barIndex].samples = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextBar.markCount = prevBar.nestCount;
|
||||
nextBar.nestCount = prevBar.nestCount;
|
||||
}
|
||||
|
||||
this.stopwacth.reset();
|
||||
this.stopwacth.start();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始测量时间。
|
||||
* @param markerName
|
||||
* @param color
|
||||
*/
|
||||
public beginMark(markerName: string, color: number, barIndex: number = 0) {
|
||||
let lock = new LockUtils(this._frameKey);
|
||||
lock.lock().then(() => {
|
||||
if (barIndex < 0 || barIndex >= TimeRuler.maxBars)
|
||||
throw new Error("barIndex argument out of range");
|
||||
|
||||
let bar = this._curLog.bars[barIndex];
|
||||
if (bar.markCount >= TimeRuler.maxSamples) {
|
||||
throw new Error("exceeded sample count. either set larger number to timeruler.maxsaple or lower sample count");
|
||||
}
|
||||
|
||||
if (bar.nestCount >= TimeRuler.maxNestCall) {
|
||||
throw new Error("exceeded nest count. either set larger number to timeruler.maxnestcall or lower nest calls");
|
||||
}
|
||||
|
||||
// 获取注册的标记
|
||||
let markerId = this._markerNameToIdMap.get(markerName);
|
||||
if (!markerId) {
|
||||
// 如果此标记未注册,则注册此标记。
|
||||
markerId = this.markers.length;
|
||||
this._markerNameToIdMap.set(markerName, markerId);
|
||||
}
|
||||
|
||||
bar.markerNests[bar.nestCount++] = bar.markCount;
|
||||
bar.markers[bar.markCount].markerId = markerId;
|
||||
bar.markers[bar.markCount].color = color;
|
||||
bar.markers[bar.markCount].beginTime = this.stopwacth.getTime();
|
||||
bar.markers[bar.markCount].endTime = -1;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param markerName
|
||||
* @param barIndex
|
||||
*/
|
||||
public endMark(markerName: string, barIndex: number = 0) {
|
||||
let lock = new LockUtils(this._frameKey);
|
||||
lock.lock().then(() => {
|
||||
if (barIndex < 0 || barIndex >= TimeRuler.maxBars)
|
||||
throw new Error("barIndex argument out of range");
|
||||
|
||||
let bar = this._curLog.bars[barIndex];
|
||||
if (bar.nestCount <= 0) {
|
||||
throw new Error("call beginMark method before calling endMark method");
|
||||
}
|
||||
|
||||
let markerId = this._markerNameToIdMap.get(markerName);
|
||||
if (!markerId) {
|
||||
throw new Error(`Marker ${markerName} is not registered. Make sure you specifed same name as you used for beginMark method`);
|
||||
}
|
||||
|
||||
let markerIdx = bar.markerNests[--bar.nestCount];
|
||||
if (bar.markers[markerIdx].markerId != markerId) {
|
||||
throw new Error("Incorrect call order of beginMark/endMark method. beginMark(A), beginMark(B), endMark(B), endMark(A) But you can't called it like beginMark(A), beginMark(B), endMark(A), endMark(B).");
|
||||
}
|
||||
|
||||
bar.markers[markerIdx].endTime = this.stopwacth.getTime();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取给定bar索引和标记名称的平均时间。
|
||||
* @param barIndex
|
||||
* @param markerName
|
||||
*/
|
||||
public getAverageTime(barIndex: number, markerName: string) {
|
||||
if (barIndex < 0 || barIndex >= TimeRuler.maxBars) {
|
||||
throw new Error("barIndex argument out of range");
|
||||
}
|
||||
let result = 0;
|
||||
let markerId = this._markerNameToIdMap.get(markerName);
|
||||
if (markerId) {
|
||||
result = this.markers[markerId].logs[barIndex].avg;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public resetLog() {
|
||||
let lock = new LockUtils(this._logKey);
|
||||
lock.lock().then(() => {
|
||||
let count = parseInt(egret.localStorage.getItem(this._logKey), 10);
|
||||
count += 1;
|
||||
egret.localStorage.setItem(this._logKey, count.toString());
|
||||
this.markers.forEach(markerInfo => {
|
||||
for (let i = 0; i < markerInfo.logs.length; ++i){
|
||||
markerInfo.logs[i].initialized = false;
|
||||
markerInfo.logs[i].snapMin = 0;
|
||||
markerInfo.logs[i].snapMax = 0;
|
||||
markerInfo.logs[i].snapAvg = 0;
|
||||
|
||||
markerInfo.logs[i].min = 0;
|
||||
markerInfo.logs[i].max = 0;
|
||||
markerInfo.logs[i].avg = 0;
|
||||
|
||||
markerInfo.logs[i].samples = 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public render(position: Vector2 = this._position, width: number = this.width){
|
||||
egret.localStorage.setItem(this._frameKey, "0");
|
||||
|
||||
if (!this.showLog)
|
||||
return;
|
||||
|
||||
let height = 0;
|
||||
let maxTime = 0;
|
||||
this._prevLog.bars.forEach(bar => {
|
||||
if (bar.markCount > 0){
|
||||
height += TimeRuler.barHeight + TimeRuler.barPadding * 2;
|
||||
maxTime = Math.max(maxTime, bar.markers[bar.markCount - 1].endTime);
|
||||
}
|
||||
})
|
||||
|
||||
const frameSpan = 1 / 60 * 1000;
|
||||
let sampleSpan = this.sampleFrames * frameSpan;
|
||||
|
||||
if (maxTime > sampleSpan){
|
||||
this._frameAdjust = Math.max(0, this._frameAdjust) + 1;
|
||||
}else{
|
||||
this._frameAdjust = Math.min(0, this._frameAdjust) - 1;
|
||||
}
|
||||
|
||||
if (Math.max(this._frameAdjust) > TimeRuler.autoAdjustDelay){
|
||||
this.sampleFrames = Math.min(TimeRuler.maxSampleFrames, this.sampleFrames);
|
||||
this.sampleFrames = Math.max(this.targetSampleFrames, (maxTime / frameSpan) + 1);
|
||||
|
||||
this._frameAdjust = 0;
|
||||
}
|
||||
|
||||
let msToPs = width / sampleSpan;
|
||||
let startY = position.y - (height - TimeRuler.barHeight);
|
||||
let y = startY;
|
||||
|
||||
// TODO: draw
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志信息
|
||||
*/
|
||||
class FrameLog {
|
||||
public bars: MarkerCollection[];
|
||||
|
||||
constructor() {
|
||||
this.bars = new Array<MarkerCollection>(TimeRuler.maxBars);
|
||||
for (let i = 0; i < TimeRuler.maxBars; ++i)
|
||||
this.bars[i] = new MarkerCollection();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记的集合
|
||||
*/
|
||||
class MarkerCollection {
|
||||
public markers: Marker[] = new Array<Marker>(TimeRuler.maxSamples);
|
||||
public markCount: number;
|
||||
public markerNests: number[] = new Array<number>(TimeRuler.maxNestCall);
|
||||
public nestCount: number;
|
||||
}
|
||||
|
||||
class Marker {
|
||||
public markerId: number;
|
||||
public beginTime: number;
|
||||
public endTime: number;
|
||||
public color: number;
|
||||
}
|
||||
|
||||
class MarkerInfo {
|
||||
public name: string;
|
||||
public logs: MarkerLog[] = new Array<MarkerLog>(TimeRuler.maxBars);
|
||||
|
||||
constructor(name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
class MarkerLog {
|
||||
public snapMin: number;
|
||||
public snapMax: number;
|
||||
public snapAvg: number;
|
||||
public min: number;
|
||||
public max: number;
|
||||
public avg: number;
|
||||
public samples: number;
|
||||
public color: number;
|
||||
public initialized: boolean;
|
||||
}
|
||||
51
source/src/Utils/LockUtils.ts
Normal file
51
source/src/Utils/LockUtils.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
const THREAD_ID = `${Math.floor(Math.random() * 1000)}-${Date.now()}`;
|
||||
const setItem = egret.localStorage.setItem.bind(localStorage);
|
||||
const getItem = egret.localStorage.getItem.bind(localStorage);
|
||||
const removeItem = egret.localStorage.removeItem.bind(localStorage);
|
||||
|
||||
const nextTick = fn => {
|
||||
setTimeout(fn, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* 利用共享区域实现快速锁
|
||||
*/
|
||||
class LockUtils {
|
||||
private _keyX: string;
|
||||
private _keyY: string;
|
||||
constructor(key){
|
||||
this._keyX = `mutex_key_${key}_X`;
|
||||
this._keyY = `mutex_key_${key}_Y`;
|
||||
}
|
||||
|
||||
public lock(){
|
||||
return new Promise((resolve, reject) => {
|
||||
const fn = () => {
|
||||
setItem(this._keyX, THREAD_ID);
|
||||
if (!getItem(this._keyY) === null){
|
||||
// restart
|
||||
nextTick(fn);
|
||||
}
|
||||
setItem(this._keyY, THREAD_ID);
|
||||
if (getItem(this._keyX) !== THREAD_ID){
|
||||
// delay
|
||||
setTimeout(()=>{
|
||||
if (getItem(this._keyY) !== THREAD_ID){
|
||||
// restart
|
||||
nextTick(fn);
|
||||
return;
|
||||
}
|
||||
// critical section
|
||||
resolve();
|
||||
removeItem(this._keyY);
|
||||
}, 10);
|
||||
} else {
|
||||
resolve();
|
||||
removeItem(this._keyY);
|
||||
}
|
||||
};
|
||||
|
||||
fn();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user