全部移动至es模块
This commit is contained in:
@@ -1,101 +1,103 @@
|
||||
///<reference path="./PriorityQueueNode.ts" />
|
||||
/**
|
||||
* 计算路径给定的IAstarGraph和开始/目标位置
|
||||
*/
|
||||
class AStarPathfinder {
|
||||
module es {
|
||||
/**
|
||||
* 尽可能从开始到目标找到一条路径。如果没有找到路径,则返回null。
|
||||
* @param graph
|
||||
* @param start
|
||||
* @param goal
|
||||
* 计算路径给定的IAstarGraph和开始/目标位置
|
||||
*/
|
||||
public static search<T>(graph: IAstarGraph<T>, start: T, goal: T){
|
||||
let foundPath = false;
|
||||
let cameFrom = new Map<T, T>();
|
||||
cameFrom.set(start, start);
|
||||
export class AStarPathfinder {
|
||||
/**
|
||||
* 尽可能从开始到目标找到一条路径。如果没有找到路径,则返回null。
|
||||
* @param graph
|
||||
* @param start
|
||||
* @param goal
|
||||
*/
|
||||
public static search<T>(graph: IAstarGraph<T>, start: T, goal: T){
|
||||
let foundPath = false;
|
||||
let cameFrom = new Map<T, T>();
|
||||
cameFrom.set(start, start);
|
||||
|
||||
let costSoFar = new Map<T, number>();
|
||||
let frontier = new PriorityQueue<AStarNode<T>>(1000);
|
||||
frontier.enqueue(new AStarNode<T>(start), 0);
|
||||
let costSoFar = new Map<T, number>();
|
||||
let frontier = new PriorityQueue<AStarNode<T>>(1000);
|
||||
frontier.enqueue(new AStarNode<T>(start), 0);
|
||||
|
||||
costSoFar.set(start, 0);
|
||||
costSoFar.set(start, 0);
|
||||
|
||||
while (frontier.count > 0){
|
||||
let current = frontier.dequeue();
|
||||
while (frontier.count > 0){
|
||||
let current = frontier.dequeue();
|
||||
|
||||
if (JSON.stringify(current.data) == JSON.stringify(goal)){
|
||||
foundPath = true;
|
||||
break;
|
||||
if (JSON.stringify(current.data) == JSON.stringify(goal)){
|
||||
foundPath = true;
|
||||
break;
|
||||
}
|
||||
|
||||
graph.getNeighbors(current.data).forEach(next => {
|
||||
let newCost = costSoFar.get(current.data) + graph.cost(current.data, next);
|
||||
if (!this.hasKey(costSoFar, next) || newCost < costSoFar.get(next)){
|
||||
costSoFar.set(next, newCost);
|
||||
let priority = newCost + graph.heuristic(next, goal);
|
||||
frontier.enqueue(new AStarNode<T>(next), priority);
|
||||
cameFrom.set(next, current.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
graph.getNeighbors(current.data).forEach(next => {
|
||||
let newCost = costSoFar.get(current.data) + graph.cost(current.data, next);
|
||||
if (!this.hasKey(costSoFar, next) || newCost < costSoFar.get(next)){
|
||||
costSoFar.set(next, newCost);
|
||||
let priority = newCost + graph.heuristic(next, goal);
|
||||
frontier.enqueue(new AStarNode<T>(next), priority);
|
||||
cameFrom.set(next, current.data);
|
||||
}
|
||||
});
|
||||
return foundPath ? this.recontructPath(cameFrom, start, goal) : null;
|
||||
}
|
||||
|
||||
return foundPath ? this.recontructPath(cameFrom, start, goal) : null;
|
||||
}
|
||||
private static hasKey<T>(map: Map<T, number>, compareKey: T){
|
||||
let iterator = map.keys();
|
||||
let r: IteratorResult<T>;
|
||||
while (r = iterator.next() , !r.done) {
|
||||
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||
return true;
|
||||
}
|
||||
|
||||
private static hasKey<T>(map: Map<T, number>, compareKey: T){
|
||||
let iterator = map.keys();
|
||||
let r: IteratorResult<T>;
|
||||
while (r = iterator.next() , !r.done) {
|
||||
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
private static getKey<T>(map: Map<T, T>, compareKey: T){
|
||||
let iterator = map.keys();
|
||||
let valueIterator = map.values();
|
||||
let r: IteratorResult<T>;
|
||||
let v: IteratorResult<T>;
|
||||
while (r = iterator.next(), v = valueIterator.next(), !r.done) {
|
||||
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||
return v.value;
|
||||
}
|
||||
|
||||
private static getKey<T>(map: Map<T, T>, compareKey: T){
|
||||
let iterator = map.keys();
|
||||
let valueIterator = map.values();
|
||||
let r: IteratorResult<T>;
|
||||
let v: IteratorResult<T>;
|
||||
while (r = iterator.next(), v = valueIterator.next(), !r.done) {
|
||||
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||
return v.value;
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
/**
|
||||
* 从cameFrom字典重新构造路径
|
||||
* @param cameFrom
|
||||
* @param start
|
||||
* @param goal
|
||||
*/
|
||||
public static recontructPath<T>(cameFrom: Map<T, T>, start: T, goal: T): T[]{
|
||||
let path = [];
|
||||
let current = goal;
|
||||
path.push(goal);
|
||||
|
||||
while (current != start){
|
||||
current = this.getKey(cameFrom, current);
|
||||
path.push(current);
|
||||
}
|
||||
|
||||
path.reverse();
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从cameFrom字典重新构造路径
|
||||
* @param cameFrom
|
||||
* @param start
|
||||
* @param goal
|
||||
* 使用PriorityQueue需要的额外字段将原始数据封装在一个小类中
|
||||
*/
|
||||
public static recontructPath<T>(cameFrom: Map<T, T>, start: T, goal: T): T[]{
|
||||
let path = [];
|
||||
let current = goal;
|
||||
path.push(goal);
|
||||
export class AStarNode<T> extends PriorityQueueNode {
|
||||
public data: T;
|
||||
|
||||
while (current != start){
|
||||
current = this.getKey(cameFrom, current);
|
||||
path.push(current);
|
||||
constructor(data: T){
|
||||
super();
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
path.reverse();
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用PriorityQueue需要的额外字段将原始数据封装在一个小类中
|
||||
*/
|
||||
class AStarNode<T> extends PriorityQueueNode {
|
||||
public data: T;
|
||||
|
||||
constructor(data: T){
|
||||
super();
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
@@ -1,72 +1,74 @@
|
||||
/**
|
||||
* 基本静态网格图与A*一起使用
|
||||
* 将walls添加到walls HashSet,并将加权节点添加到weightedNodes
|
||||
*/
|
||||
class AstarGridGraph implements IAstarGraph<Vector2> {
|
||||
public dirs: Vector2[] = [
|
||||
new Vector2(1, 0),
|
||||
new Vector2(0, -1),
|
||||
new Vector2(-1, 0),
|
||||
new Vector2(0, 1)
|
||||
];
|
||||
|
||||
public walls: Vector2[] = [];
|
||||
public weightedNodes: Vector2[] = [];
|
||||
public defaultWeight: number = 1;
|
||||
public weightedNodeWeight = 5;
|
||||
|
||||
private _width;
|
||||
private _height;
|
||||
private _neighbors: Vector2[] = new Array(4);
|
||||
|
||||
constructor(width: number, height: number){
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
}
|
||||
|
||||
module es {
|
||||
/**
|
||||
* 确保节点在网格图的边界内
|
||||
* @param node
|
||||
* 基本静态网格图与A*一起使用
|
||||
* 将walls添加到walls HashSet,并将加权节点添加到weightedNodes
|
||||
*/
|
||||
public isNodeInBounds(node: Vector2): boolean {
|
||||
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._height;
|
||||
export class AstarGridGraph implements IAstarGraph<Vector2> {
|
||||
public dirs: Vector2[] = [
|
||||
new Vector2(1, 0),
|
||||
new Vector2(0, -1),
|
||||
new Vector2(-1, 0),
|
||||
new Vector2(0, 1)
|
||||
];
|
||||
|
||||
public walls: Vector2[] = [];
|
||||
public weightedNodes: Vector2[] = [];
|
||||
public defaultWeight: number = 1;
|
||||
public weightedNodeWeight = 5;
|
||||
|
||||
private _width;
|
||||
private _height;
|
||||
private _neighbors: Vector2[] = new Array(4);
|
||||
|
||||
constructor(width: number, height: number){
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保节点在网格图的边界内
|
||||
* @param node
|
||||
*/
|
||||
public isNodeInBounds(node: Vector2): boolean {
|
||||
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._height;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查节点是否可以通过。walls是不可逾越的。
|
||||
* @param node
|
||||
*/
|
||||
public isNodePassable(node: Vector2): boolean {
|
||||
return !this.walls.firstOrDefault(wall => JSON.stringify(wall) == JSON.stringify(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用AStarPathfinder.search的快捷方式
|
||||
* @param start
|
||||
* @param goal
|
||||
*/
|
||||
public search(start: Vector2, goal: Vector2){
|
||||
return AStarPathfinder.search(this, start, goal);
|
||||
}
|
||||
|
||||
public getNeighbors(node: Vector2): Vector2[] {
|
||||
this._neighbors.length = 0;
|
||||
|
||||
this.dirs.forEach(dir => {
|
||||
let next = new Vector2(node.x + dir.x, node.y + dir.y);
|
||||
if (this.isNodeInBounds(next) && this.isNodePassable(next))
|
||||
this._neighbors.push(next);
|
||||
});
|
||||
|
||||
return this._neighbors;
|
||||
}
|
||||
|
||||
public cost(from: Vector2, to: Vector2): number {
|
||||
return this.weightedNodes.find((p)=> JSON.stringify(p) == JSON.stringify(to)) ? this.weightedNodeWeight : this.defaultWeight;
|
||||
}
|
||||
|
||||
public heuristic(node: Vector2, goal: Vector2) {
|
||||
return Math.abs(node.x - goal.x) + Math.abs(node.y - goal.y);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查节点是否可以通过。walls是不可逾越的。
|
||||
* @param node
|
||||
*/
|
||||
public isNodePassable(node: Vector2): boolean {
|
||||
return !this.walls.firstOrDefault(wall => JSON.stringify(wall) == JSON.stringify(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用AStarPathfinder.search的快捷方式
|
||||
* @param start
|
||||
* @param goal
|
||||
*/
|
||||
public search(start: Vector2, goal: Vector2){
|
||||
return AStarPathfinder.search(this, start, goal);
|
||||
}
|
||||
|
||||
public getNeighbors(node: Vector2): Vector2[] {
|
||||
this._neighbors.length = 0;
|
||||
|
||||
this.dirs.forEach(dir => {
|
||||
let next = new Vector2(node.x + dir.x, node.y + dir.y);
|
||||
if (this.isNodeInBounds(next) && this.isNodePassable(next))
|
||||
this._neighbors.push(next);
|
||||
});
|
||||
|
||||
return this._neighbors;
|
||||
}
|
||||
|
||||
public cost(from: Vector2, to: Vector2): number {
|
||||
return this.weightedNodes.find((p)=> JSON.stringify(p) == JSON.stringify(to)) ? this.weightedNodeWeight : this.defaultWeight;
|
||||
}
|
||||
|
||||
public heuristic(node: Vector2, goal: Vector2) {
|
||||
return Math.abs(node.x - goal.x) + Math.abs(node.y - goal.y);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
/**
|
||||
* graph的接口,可以提供给AstarPathfinder.search方法
|
||||
*/
|
||||
interface IAstarGraph<T> {
|
||||
module es {
|
||||
/**
|
||||
* getNeighbors方法应该返回从传入的节点可以到达的任何相邻节点
|
||||
* @param node
|
||||
* graph的接口,可以提供给AstarPathfinder.search方法
|
||||
*/
|
||||
getNeighbors(node: T): Array<T>;
|
||||
/**
|
||||
* 计算从从from到to的成本
|
||||
* @param from
|
||||
* @param to
|
||||
*/
|
||||
cost(from: T, to: T): number;
|
||||
/**
|
||||
* 计算从node到to的启发式。参见WeightedGridGraph了解常用的Manhatten方法。
|
||||
* @param node
|
||||
* @param goal
|
||||
*/
|
||||
heuristic(node: T, goal: T);
|
||||
}
|
||||
export interface IAstarGraph<T> {
|
||||
/**
|
||||
* getNeighbors方法应该返回从传入的节点可以到达的任何相邻节点
|
||||
* @param node
|
||||
*/
|
||||
getNeighbors(node: T): Array<T>;
|
||||
/**
|
||||
* 计算从从from到to的成本
|
||||
* @param from
|
||||
* @param to
|
||||
*/
|
||||
cost(from: T, to: T): number;
|
||||
/**
|
||||
* 计算从node到to的启发式。参见WeightedGridGraph了解常用的Manhatten方法。
|
||||
* @param node
|
||||
* @param goal
|
||||
*/
|
||||
heuristic(node: T, goal: T);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,234 +1,236 @@
|
||||
/**
|
||||
* 使用堆实现最小优先级队列 O(1)复杂度
|
||||
* 这种查找速度比使用字典快5-10倍
|
||||
* 但是,由于IPriorityQueue.contains()是许多寻路算法中调用最多的方法,因此尽可能快地实现它对于我们的应用程序非常重要。
|
||||
*/
|
||||
class PriorityQueue<T extends PriorityQueueNode> {
|
||||
private _numNodes: number;
|
||||
private _nodes: T[];
|
||||
private _numNodesEverEnqueued;
|
||||
|
||||
module es {
|
||||
/**
|
||||
* 实例化一个新的优先级队列
|
||||
* @param maxNodes 允许加入队列的最大节点(执行此操作将导致undefined的行为)
|
||||
* 使用堆实现最小优先级队列 O(1)复杂度
|
||||
* 这种查找速度比使用字典快5-10倍
|
||||
* 但是,由于IPriorityQueue.contains()是许多寻路算法中调用最多的方法,因此尽可能快地实现它对于我们的应用程序非常重要。
|
||||
*/
|
||||
constructor(maxNodes: number) {
|
||||
this._numNodes = 0;
|
||||
this._nodes = new Array(maxNodes + 1);
|
||||
this._numNodesEverEnqueued = 0;
|
||||
}
|
||||
export class PriorityQueue<T extends PriorityQueueNode> {
|
||||
private _numNodes: number;
|
||||
private _nodes: T[];
|
||||
private _numNodesEverEnqueued;
|
||||
|
||||
/**
|
||||
* 从队列中删除每个节点。
|
||||
* O(n)复杂度 所有尽可能少调用该方法
|
||||
*/
|
||||
public clear() {
|
||||
this._nodes.splice(1, this._numNodes);
|
||||
this._numNodes = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回队列中的节点数。
|
||||
* O(1)复杂度
|
||||
*/
|
||||
public get count() {
|
||||
return this._numNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回可同时进入此队列的最大项数。一旦你达到这个数字(即。一旦Count == MaxSize),尝试加入另一个项目将导致undefined的行为
|
||||
* O(1)复杂度
|
||||
*/
|
||||
public get maxSize() {
|
||||
return this._nodes.length - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回(在O(1)中)给定节点是否在队列中
|
||||
* O (1)复杂度
|
||||
* @param node
|
||||
*/
|
||||
public contains(node: T): boolean {
|
||||
if (!node){
|
||||
console.error("node cannot be null");
|
||||
return false;
|
||||
/**
|
||||
* 实例化一个新的优先级队列
|
||||
* @param maxNodes 允许加入队列的最大节点(执行此操作将导致undefined的行为)
|
||||
*/
|
||||
constructor(maxNodes: number) {
|
||||
this._numNodes = 0;
|
||||
this._nodes = new Array(maxNodes + 1);
|
||||
this._numNodesEverEnqueued = 0;
|
||||
}
|
||||
|
||||
if (node.queueIndex < 0 || node.queueIndex >= this._nodes.length){
|
||||
console.error("node.QueueIndex has been corrupted. Did you change it manually? Or add this node to another queue?");
|
||||
return false;
|
||||
/**
|
||||
* 从队列中删除每个节点。
|
||||
* O(n)复杂度 所有尽可能少调用该方法
|
||||
*/
|
||||
public clear() {
|
||||
this._nodes.splice(1, this._numNodes);
|
||||
this._numNodes = 0;
|
||||
}
|
||||
|
||||
return (this._nodes[node.queueIndex] == node);
|
||||
}
|
||||
/**
|
||||
* 返回队列中的节点数。
|
||||
* O(1)复杂度
|
||||
*/
|
||||
public get count() {
|
||||
return this._numNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将节点放入优先队列 较低的值放在前面 先入先出
|
||||
* 如果队列已满,则结果undefined。如果节点已经加入队列,则结果undefined。
|
||||
* O(log n)
|
||||
* @param node
|
||||
* @param priority
|
||||
*/
|
||||
public enqueue(node: T, priority: number) {
|
||||
node.priority = priority;
|
||||
this._numNodes++;
|
||||
this._nodes[this._numNodes] = node;
|
||||
node.queueIndex = this._numNodes;
|
||||
node.insertionIndex = this._numNodesEverEnqueued++;
|
||||
this.cascadeUp(this._nodes[this._numNodes]);
|
||||
}
|
||||
/**
|
||||
* 返回可同时进入此队列的最大项数。一旦你达到这个数字(即。一旦Count == MaxSize),尝试加入另一个项目将导致undefined的行为
|
||||
* O(1)复杂度
|
||||
*/
|
||||
public get maxSize() {
|
||||
return this._nodes.length - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除队列头(具有最小优先级的节点;按插入顺序断开连接),并返回它。如果队列为空,结果undefined
|
||||
* O(log n)
|
||||
*/
|
||||
public dequeue(): T {
|
||||
let returnMe = this._nodes[1];
|
||||
this.remove(returnMe);
|
||||
return returnMe;
|
||||
}
|
||||
/**
|
||||
* 返回(在O(1)中)给定节点是否在队列中
|
||||
* O (1)复杂度
|
||||
* @param node
|
||||
*/
|
||||
public contains(node: T): boolean {
|
||||
if (!node){
|
||||
console.error("node cannot be null");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从队列中删除一个节点。节点不需要是队列的头。如果节点不在队列中,则结果未定义。如果不确定,首先检查Contains()
|
||||
* O(log n)
|
||||
* @param node
|
||||
*/
|
||||
public remove(node: T) {
|
||||
if (node.queueIndex == this._numNodes) {
|
||||
this._nodes[this._numNodes] = null;
|
||||
if (node.queueIndex < 0 || node.queueIndex >= this._nodes.length){
|
||||
console.error("node.QueueIndex has been corrupted. Did you change it manually? Or add this node to another queue?");
|
||||
return false;
|
||||
}
|
||||
|
||||
return (this._nodes[node.queueIndex] == node);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将节点放入优先队列 较低的值放在前面 先入先出
|
||||
* 如果队列已满,则结果undefined。如果节点已经加入队列,则结果undefined。
|
||||
* O(log n)
|
||||
* @param node
|
||||
* @param priority
|
||||
*/
|
||||
public enqueue(node: T, priority: number) {
|
||||
node.priority = priority;
|
||||
this._numNodes++;
|
||||
this._nodes[this._numNodes] = node;
|
||||
node.queueIndex = this._numNodes;
|
||||
node.insertionIndex = this._numNodesEverEnqueued++;
|
||||
this.cascadeUp(this._nodes[this._numNodes]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除队列头(具有最小优先级的节点;按插入顺序断开连接),并返回它。如果队列为空,结果undefined
|
||||
* O(log n)
|
||||
*/
|
||||
public dequeue(): T {
|
||||
let returnMe = this._nodes[1];
|
||||
this.remove(returnMe);
|
||||
return returnMe;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从队列中删除一个节点。节点不需要是队列的头。如果节点不在队列中,则结果未定义。如果不确定,首先检查Contains()
|
||||
* O(log n)
|
||||
* @param node
|
||||
*/
|
||||
public remove(node: T) {
|
||||
if (node.queueIndex == this._numNodes) {
|
||||
this._nodes[this._numNodes] = null;
|
||||
this._numNodes--;
|
||||
return;
|
||||
}
|
||||
|
||||
let formerLastNode = this._nodes[this._numNodes];
|
||||
this.swap(node, formerLastNode);
|
||||
delete this._nodes[this._numNodes];
|
||||
this._numNodes--;
|
||||
return;
|
||||
|
||||
this.onNodeUpdated(formerLastNode);
|
||||
}
|
||||
|
||||
let formerLastNode = this._nodes[this._numNodes];
|
||||
this.swap(node, formerLastNode);
|
||||
delete this._nodes[this._numNodes];
|
||||
this._numNodes--;
|
||||
/**
|
||||
* 检查以确保队列仍然处于有效状态。用于测试/调试队列。
|
||||
*/
|
||||
public isValidQueue(): boolean {
|
||||
for (let i = 1; i < this._nodes.length; i++) {
|
||||
if (this._nodes[i]) {
|
||||
let childLeftIndex = 2 * i;
|
||||
if (childLeftIndex < this._nodes.length && this._nodes[childLeftIndex] &&
|
||||
this.hasHigherPriority(this._nodes[childLeftIndex], this._nodes[i]))
|
||||
return false;
|
||||
|
||||
this.onNodeUpdated(formerLastNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查以确保队列仍然处于有效状态。用于测试/调试队列。
|
||||
*/
|
||||
public isValidQueue(): boolean {
|
||||
for (let i = 1; i < this._nodes.length; i++) {
|
||||
if (this._nodes[i]) {
|
||||
let childLeftIndex = 2 * i;
|
||||
if (childLeftIndex < this._nodes.length && this._nodes[childLeftIndex] &&
|
||||
this.hasHigherPriority(this._nodes[childLeftIndex], this._nodes[i]))
|
||||
return false;
|
||||
|
||||
let childRightIndex = childLeftIndex + 1;
|
||||
if (childRightIndex < this._nodes.length && this._nodes[childRightIndex] &&
|
||||
this.hasHigherPriority(this._nodes[childRightIndex], this._nodes[i]))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private onNodeUpdated(node: T) {
|
||||
// 将更新后的节点按适当的方式向上或向下冒泡
|
||||
let parentIndex = Math.floor(node.queueIndex / 2);
|
||||
let parentNode = this._nodes[parentIndex];
|
||||
|
||||
if (parentIndex > 0 && this.hasHigherPriority(node, parentNode)) {
|
||||
this.cascadeUp(node);
|
||||
} else {
|
||||
// 注意,如果parentNode == node(即节点是根),则将调用CascadeDown。
|
||||
this.cascadeDown(node);
|
||||
}
|
||||
}
|
||||
|
||||
private cascadeDown(node: T) {
|
||||
// 又名Heapify-down
|
||||
let newParent: T;
|
||||
let finalQueueIndex = node.queueIndex;
|
||||
while (true) {
|
||||
newParent = node;
|
||||
let childLeftIndex = 2 * finalQueueIndex;
|
||||
|
||||
// 检查左子节点的优先级是否高于当前节点
|
||||
if (childLeftIndex > this._numNodes) {
|
||||
// 这可以放在循环之外,但是我们必须检查newParent != node两次
|
||||
node.queueIndex = finalQueueIndex;
|
||||
this._nodes[finalQueueIndex] = node;
|
||||
break;
|
||||
}
|
||||
|
||||
let childLeft = this._nodes[childLeftIndex];
|
||||
if (this.hasHigherPriority(childLeft, newParent)) {
|
||||
newParent = childLeft;
|
||||
}
|
||||
|
||||
// 检查右子节点的优先级是否高于当前节点或左子节点
|
||||
let childRightIndex = childLeftIndex + 1;
|
||||
if (childRightIndex <= this._numNodes) {
|
||||
let childRight = this._nodes[childRightIndex];
|
||||
if (this.hasHigherPriority(childRight, newParent)) {
|
||||
newParent = childRight;
|
||||
let childRightIndex = childLeftIndex + 1;
|
||||
if (childRightIndex < this._nodes.length && this._nodes[childRightIndex] &&
|
||||
this.hasHigherPriority(this._nodes[childRightIndex], this._nodes[i]))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果其中一个子节点具有更高(更小)的优先级,则交换并继续级联
|
||||
if (newParent != node) {
|
||||
// 将新的父节点移动到它的新索引
|
||||
// 节点将被移动一次,这样做比调用Swap()少一个赋值操作。
|
||||
this._nodes[finalQueueIndex] = newParent;
|
||||
return true;
|
||||
}
|
||||
|
||||
let temp = newParent.queueIndex;
|
||||
newParent.queueIndex = finalQueueIndex;
|
||||
finalQueueIndex = temp;
|
||||
private onNodeUpdated(node: T) {
|
||||
// 将更新后的节点按适当的方式向上或向下冒泡
|
||||
let parentIndex = Math.floor(node.queueIndex / 2);
|
||||
let parentNode = this._nodes[parentIndex];
|
||||
|
||||
if (parentIndex > 0 && this.hasHigherPriority(node, parentNode)) {
|
||||
this.cascadeUp(node);
|
||||
} else {
|
||||
// 参见上面的笔记
|
||||
node.queueIndex = finalQueueIndex;
|
||||
this._nodes[finalQueueIndex] = node;
|
||||
break;
|
||||
// 注意,如果parentNode == node(即节点是根),则将调用CascadeDown。
|
||||
this.cascadeDown(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当没有内联时,性能会稍微好一些
|
||||
* @param node
|
||||
*/
|
||||
private cascadeUp(node: T) {
|
||||
// 又名Heapify-up
|
||||
let parent = Math.floor(node.queueIndex / 2);
|
||||
while (parent >= 1) {
|
||||
let parentNode = this._nodes[parent];
|
||||
if (this.hasHigherPriority(parentNode, node))
|
||||
break;
|
||||
private cascadeDown(node: T) {
|
||||
// 又名Heapify-down
|
||||
let newParent: T;
|
||||
let finalQueueIndex = node.queueIndex;
|
||||
while (true) {
|
||||
newParent = node;
|
||||
let childLeftIndex = 2 * finalQueueIndex;
|
||||
|
||||
// 节点具有较低的优先级值,因此将其向上移动到堆中
|
||||
// 出于某种原因,使用Swap()比使用单独的操作更快,如CascadeDown()
|
||||
this.swap(node, parentNode);
|
||||
// 检查左子节点的优先级是否高于当前节点
|
||||
if (childLeftIndex > this._numNodes) {
|
||||
// 这可以放在循环之外,但是我们必须检查newParent != node两次
|
||||
node.queueIndex = finalQueueIndex;
|
||||
this._nodes[finalQueueIndex] = node;
|
||||
break;
|
||||
}
|
||||
|
||||
parent = Math.floor(node.queueIndex / 2);
|
||||
let childLeft = this._nodes[childLeftIndex];
|
||||
if (this.hasHigherPriority(childLeft, newParent)) {
|
||||
newParent = childLeft;
|
||||
}
|
||||
|
||||
// 检查右子节点的优先级是否高于当前节点或左子节点
|
||||
let childRightIndex = childLeftIndex + 1;
|
||||
if (childRightIndex <= this._numNodes) {
|
||||
let childRight = this._nodes[childRightIndex];
|
||||
if (this.hasHigherPriority(childRight, newParent)) {
|
||||
newParent = childRight;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果其中一个子节点具有更高(更小)的优先级,则交换并继续级联
|
||||
if (newParent != node) {
|
||||
// 将新的父节点移动到它的新索引
|
||||
// 节点将被移动一次,这样做比调用Swap()少一个赋值操作。
|
||||
this._nodes[finalQueueIndex] = newParent;
|
||||
|
||||
let temp = newParent.queueIndex;
|
||||
newParent.queueIndex = finalQueueIndex;
|
||||
finalQueueIndex = temp;
|
||||
} else {
|
||||
// 参见上面的笔记
|
||||
node.queueIndex = finalQueueIndex;
|
||||
this._nodes[finalQueueIndex] = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当没有内联时,性能会稍微好一些
|
||||
* @param node
|
||||
*/
|
||||
private cascadeUp(node: T) {
|
||||
// 又名Heapify-up
|
||||
let parent = Math.floor(node.queueIndex / 2);
|
||||
while (parent >= 1) {
|
||||
let parentNode = this._nodes[parent];
|
||||
if (this.hasHigherPriority(parentNode, node))
|
||||
break;
|
||||
|
||||
// 节点具有较低的优先级值,因此将其向上移动到堆中
|
||||
// 出于某种原因,使用Swap()比使用单独的操作更快,如CascadeDown()
|
||||
this.swap(node, parentNode);
|
||||
|
||||
parent = Math.floor(node.queueIndex / 2);
|
||||
}
|
||||
}
|
||||
|
||||
private swap(node1: T, node2: T) {
|
||||
// 交换节点
|
||||
this._nodes[node1.queueIndex] = node2;
|
||||
this._nodes[node2.queueIndex] = node1;
|
||||
|
||||
// 交换他们的indicies
|
||||
let temp = node1.queueIndex;
|
||||
node1.queueIndex = node2.queueIndex;
|
||||
node2.queueIndex = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果higher的优先级高于lower,则返回true,否则返回false。
|
||||
* 注意,调用HasHigherPriority(节点,节点)(即。两个参数为同一个节点)将返回false
|
||||
* @param higher
|
||||
* @param lower
|
||||
*/
|
||||
private hasHigherPriority(higher: T, lower: T) {
|
||||
return (higher.priority < lower.priority ||
|
||||
(higher.priority == lower.priority && higher.insertionIndex < lower.insertionIndex));
|
||||
}
|
||||
}
|
||||
|
||||
private swap(node1: T, node2: T) {
|
||||
// 交换节点
|
||||
this._nodes[node1.queueIndex] = node2;
|
||||
this._nodes[node2.queueIndex] = node1;
|
||||
|
||||
// 交换他们的indicies
|
||||
let temp = node1.queueIndex;
|
||||
node1.queueIndex = node2.queueIndex;
|
||||
node2.queueIndex = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果higher的优先级高于lower,则返回true,否则返回false。
|
||||
* 注意,调用HasHigherPriority(节点,节点)(即。两个参数为同一个节点)将返回false
|
||||
* @param higher
|
||||
* @param lower
|
||||
*/
|
||||
private hasHigherPriority(higher: T, lower: T) {
|
||||
return (higher.priority < lower.priority ||
|
||||
(higher.priority == lower.priority && higher.insertionIndex < lower.insertionIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
class PriorityQueueNode {
|
||||
/**
|
||||
* 插入此节点的优先级。在将节点添加到队列之前必须设置
|
||||
*/
|
||||
public priority: number = 0;
|
||||
/**
|
||||
* 由优先级队列使用-不要编辑此值。表示插入节点的顺序
|
||||
*/
|
||||
public insertionIndex: number = 0;
|
||||
/**
|
||||
* 由优先级队列使用-不要编辑此值。表示队列中的当前位置
|
||||
*/
|
||||
public queueIndex: number = 0;
|
||||
}
|
||||
module es {
|
||||
export class PriorityQueueNode {
|
||||
/**
|
||||
* 插入此节点的优先级。在将节点添加到队列之前必须设置
|
||||
*/
|
||||
public priority: number = 0;
|
||||
/**
|
||||
* 由优先级队列使用-不要编辑此值。表示插入节点的顺序
|
||||
*/
|
||||
public insertionIndex: number = 0;
|
||||
/**
|
||||
* 由优先级队列使用-不要编辑此值。表示队列中的当前位置
|
||||
*/
|
||||
public queueIndex: number = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,43 @@
|
||||
/**
|
||||
* 计算路径给定的IUnweightedGraph和开始/目标位置
|
||||
*/
|
||||
class BreadthFirstPathfinder {
|
||||
public static search<T>(graph: IUnweightedGraph<T>, start: T, goal: T): T[]{
|
||||
let foundPath = false;
|
||||
let frontier = [];
|
||||
frontier.unshift(start);
|
||||
module es {
|
||||
/**
|
||||
* 计算路径给定的IUnweightedGraph和开始/目标位置
|
||||
*/
|
||||
export class BreadthFirstPathfinder {
|
||||
public static search<T>(graph: IUnweightedGraph<T>, start: T, goal: T): T[]{
|
||||
let foundPath = false;
|
||||
let frontier = [];
|
||||
frontier.unshift(start);
|
||||
|
||||
let cameFrom = new Map<T, T>();
|
||||
cameFrom.set(start, start);
|
||||
let cameFrom = new Map<T, T>();
|
||||
cameFrom.set(start, start);
|
||||
|
||||
while (frontier.length > 0){
|
||||
let current = frontier.shift();
|
||||
if (JSON.stringify(current) == JSON.stringify(goal)){
|
||||
foundPath = true;
|
||||
break;
|
||||
while (frontier.length > 0){
|
||||
let current = frontier.shift();
|
||||
if (JSON.stringify(current) == JSON.stringify(goal)){
|
||||
foundPath = true;
|
||||
break;
|
||||
}
|
||||
|
||||
graph.getNeighbors(current).forEach(next => {
|
||||
if (!this.hasKey(cameFrom, next)){
|
||||
frontier.unshift(next);
|
||||
cameFrom.set(next, current);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
graph.getNeighbors(current).forEach(next => {
|
||||
if (!this.hasKey(cameFrom, next)){
|
||||
frontier.unshift(next);
|
||||
cameFrom.set(next, current);
|
||||
}
|
||||
});
|
||||
return foundPath ? AStarPathfinder.recontructPath(cameFrom, start, goal) : null;
|
||||
}
|
||||
|
||||
return foundPath ? AStarPathfinder.recontructPath(cameFrom, start, goal) : null;
|
||||
}
|
||||
private static hasKey<T>(map: Map<T, T>, compareKey: T){
|
||||
let iterator = map.keys();
|
||||
let r: IteratorResult<T>;
|
||||
while (r = iterator.next() , !r.done) {
|
||||
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||
return true;
|
||||
}
|
||||
|
||||
private static hasKey<T>(map: Map<T, T>, compareKey: T){
|
||||
let iterator = map.keys();
|
||||
let r: IteratorResult<T>;
|
||||
while (r = iterator.next() , !r.done) {
|
||||
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
interface IUnweightedGraph<T>{
|
||||
/**
|
||||
* getNeighbors方法应该返回从传入的节点可以到达的任何相邻节点。
|
||||
* @param node
|
||||
*/
|
||||
getNeighbors(node: T): T[];
|
||||
}
|
||||
module es {
|
||||
export interface IUnweightedGraph<T>{
|
||||
/**
|
||||
* getNeighbors方法应该返回从传入的节点可以到达的任何相邻节点。
|
||||
* @param node
|
||||
*/
|
||||
getNeighbors(node: T): T[];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
/**
|
||||
* 一个未加权图的基本实现。所有的边都被缓存。这种类型的图最适合于非基于网格的图。
|
||||
* 作为边添加的任何节点都必须在边字典中有一个条目作为键。
|
||||
*/
|
||||
class UnweightedGraph<T> implements IUnweightedGraph<T> {
|
||||
public edges: Map<T, T[]> = new Map<T, T[]>();
|
||||
module es {
|
||||
/**
|
||||
* 一个未加权图的基本实现。所有的边都被缓存。这种类型的图最适合于非基于网格的图。
|
||||
* 作为边添加的任何节点都必须在边字典中有一个条目作为键。
|
||||
*/
|
||||
export class UnweightedGraph<T> implements IUnweightedGraph<T> {
|
||||
public edges: Map<T, T[]> = new Map<T, T[]>();
|
||||
|
||||
public addEdgesForNode(node: T, edges: T[]){
|
||||
this.edges.set(node, edges);
|
||||
return this;
|
||||
}
|
||||
public addEdgesForNode(node: T, edges: T[]){
|
||||
this.edges.set(node, edges);
|
||||
return this;
|
||||
}
|
||||
|
||||
public getNeighbors(node: T){
|
||||
return this.edges.get(node);
|
||||
public getNeighbors(node: T){
|
||||
return this.edges.get(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +1,63 @@
|
||||
///<reference path="../../../Math/Vector2.ts" />
|
||||
/**
|
||||
* 基本的未加权网格图形用于BreadthFirstPathfinder
|
||||
*/
|
||||
class UnweightedGridGraph implements IUnweightedGraph<Vector2> {
|
||||
private static readonly CARDINAL_DIRS: Vector2[] = [
|
||||
new Vector2(1, 0),
|
||||
new Vector2(0, -1),
|
||||
new Vector2(-1, 0),
|
||||
new Vector2(0, -1)
|
||||
];
|
||||
module es {
|
||||
/**
|
||||
* 基本的未加权网格图形用于BreadthFirstPathfinder
|
||||
*/
|
||||
export class UnweightedGridGraph implements IUnweightedGraph<Vector2> {
|
||||
private static readonly CARDINAL_DIRS: Vector2[] = [
|
||||
new Vector2(1, 0),
|
||||
new Vector2(0, -1),
|
||||
new Vector2(-1, 0),
|
||||
new Vector2(0, -1)
|
||||
];
|
||||
|
||||
private static readonly COMPASS_DIRS = [
|
||||
new Vector2(1, 0),
|
||||
new Vector2(1, -1),
|
||||
new Vector2(0, -1),
|
||||
new Vector2(-1, -1),
|
||||
new Vector2(-1, 0),
|
||||
new Vector2(-1, 1),
|
||||
new Vector2(0, 1),
|
||||
new Vector2(1, 1),
|
||||
];
|
||||
private static readonly COMPASS_DIRS = [
|
||||
new Vector2(1, 0),
|
||||
new Vector2(1, -1),
|
||||
new Vector2(0, -1),
|
||||
new Vector2(-1, -1),
|
||||
new Vector2(-1, 0),
|
||||
new Vector2(-1, 1),
|
||||
new Vector2(0, 1),
|
||||
new Vector2(1, 1),
|
||||
];
|
||||
|
||||
public walls: Vector2[] = [];
|
||||
public walls: Vector2[] = [];
|
||||
|
||||
private _width: number;
|
||||
private _hegiht: number;
|
||||
private _width: number;
|
||||
private _hegiht: number;
|
||||
|
||||
private _dirs: Vector2[];
|
||||
private _neighbors: Vector2[] = new Array(4);
|
||||
private _dirs: Vector2[];
|
||||
private _neighbors: Vector2[] = new Array(4);
|
||||
|
||||
constructor(width: number, height: number, allowDiagonalSearch: boolean = false) {
|
||||
this._width = width;
|
||||
this._hegiht = height;
|
||||
this._dirs = allowDiagonalSearch ? UnweightedGridGraph.COMPASS_DIRS : UnweightedGridGraph.CARDINAL_DIRS;
|
||||
constructor(width: number, height: number, allowDiagonalSearch: boolean = false) {
|
||||
this._width = width;
|
||||
this._hegiht = height;
|
||||
this._dirs = allowDiagonalSearch ? UnweightedGridGraph.COMPASS_DIRS : UnweightedGridGraph.CARDINAL_DIRS;
|
||||
}
|
||||
|
||||
public isNodeInBounds(node: Vector2): boolean {
|
||||
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._hegiht;
|
||||
}
|
||||
|
||||
public isNodePassable(node: Vector2): boolean {
|
||||
return !this.walls.firstOrDefault(wall => JSON.stringify(wall) == JSON.stringify(node));
|
||||
}
|
||||
|
||||
public getNeighbors(node: Vector2) {
|
||||
this._neighbors.length = 0;
|
||||
|
||||
this._dirs.forEach(dir => {
|
||||
let next = new Vector2(node.x + dir.x, node.y + dir.y);
|
||||
if (this.isNodeInBounds(next) && this.isNodePassable(next))
|
||||
this._neighbors.push(next);
|
||||
});
|
||||
|
||||
return this._neighbors;
|
||||
}
|
||||
|
||||
public search(start: Vector2, goal: Vector2): Vector2[] {
|
||||
return BreadthFirstPathfinder.search(this, start, goal);
|
||||
}
|
||||
}
|
||||
|
||||
public isNodeInBounds(node: Vector2): boolean {
|
||||
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._hegiht;
|
||||
}
|
||||
|
||||
public isNodePassable(node: Vector2): boolean {
|
||||
return !this.walls.firstOrDefault(wall => JSON.stringify(wall) == JSON.stringify(node));
|
||||
}
|
||||
|
||||
public getNeighbors(node: Vector2) {
|
||||
this._neighbors.length = 0;
|
||||
|
||||
this._dirs.forEach(dir => {
|
||||
let next = new Vector2(node.x + dir.x, node.y + dir.y);
|
||||
if (this.isNodeInBounds(next) && this.isNodePassable(next))
|
||||
this._neighbors.push(next);
|
||||
});
|
||||
|
||||
return this._neighbors;
|
||||
}
|
||||
|
||||
public search(start: Vector2, goal: Vector2): Vector2[] {
|
||||
return BreadthFirstPathfinder.search(this, start, goal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
interface IWeightedGraph<T>{
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
*/
|
||||
getNeighbors(node: T): T[];
|
||||
module es {
|
||||
export interface IWeightedGraph<T>{
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
*/
|
||||
getNeighbors(node: T): T[];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param from
|
||||
* @param to
|
||||
*/
|
||||
cost(from: T, to: T): number;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param from
|
||||
* @param to
|
||||
*/
|
||||
cost(from: T, to: T): number;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +1,69 @@
|
||||
///<reference path="../../../Math/Vector2.ts" />
|
||||
/**
|
||||
* 支持一种加权节点的基本网格图
|
||||
*/
|
||||
class WeightedGridGraph implements IWeightedGraph<Vector2> {
|
||||
public static readonly CARDINAL_DIRS = [
|
||||
new Vector2(1, 0),
|
||||
new Vector2(0, -1),
|
||||
new Vector2(-1, 0),
|
||||
new Vector2(0, 1)
|
||||
];
|
||||
module es {
|
||||
/**
|
||||
* 支持一种加权节点的基本网格图
|
||||
*/
|
||||
export class WeightedGridGraph implements IWeightedGraph<Vector2> {
|
||||
public static readonly CARDINAL_DIRS = [
|
||||
new Vector2(1, 0),
|
||||
new Vector2(0, -1),
|
||||
new Vector2(-1, 0),
|
||||
new Vector2(0, 1)
|
||||
];
|
||||
|
||||
private static readonly COMPASS_DIRS = [
|
||||
new Vector2(1, 0),
|
||||
new Vector2(1, -1),
|
||||
new Vector2(0, -1),
|
||||
new Vector2(-1, -1),
|
||||
new Vector2(-1, 0),
|
||||
new Vector2(-1, 1),
|
||||
new Vector2(0, 1),
|
||||
new Vector2(1, 1),
|
||||
];
|
||||
private static readonly COMPASS_DIRS = [
|
||||
new Vector2(1, 0),
|
||||
new Vector2(1, -1),
|
||||
new Vector2(0, -1),
|
||||
new Vector2(-1, -1),
|
||||
new Vector2(-1, 0),
|
||||
new Vector2(-1, 1),
|
||||
new Vector2(0, 1),
|
||||
new Vector2(1, 1),
|
||||
];
|
||||
|
||||
public walls: Vector2[] = [];
|
||||
public weightedNodes: Vector2[] = [];
|
||||
public defaultWeight = 1;
|
||||
public weightedNodeWeight = 5;
|
||||
public walls: Vector2[] = [];
|
||||
public weightedNodes: Vector2[] = [];
|
||||
public defaultWeight = 1;
|
||||
public weightedNodeWeight = 5;
|
||||
|
||||
private _width: number;
|
||||
private _height: number;
|
||||
private _dirs: Vector2[];
|
||||
private _neighbors: Vector2[] = new Array(4);
|
||||
private _width: number;
|
||||
private _height: number;
|
||||
private _dirs: Vector2[];
|
||||
private _neighbors: Vector2[] = new Array(4);
|
||||
|
||||
constructor(width: number, height: number, allowDiagonalSearch: boolean = false){
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._dirs = allowDiagonalSearch ? WeightedGridGraph.COMPASS_DIRS : WeightedGridGraph.CARDINAL_DIRS;
|
||||
constructor(width: number, height: number, allowDiagonalSearch: boolean = false){
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._dirs = allowDiagonalSearch ? WeightedGridGraph.COMPASS_DIRS : WeightedGridGraph.CARDINAL_DIRS;
|
||||
}
|
||||
|
||||
public isNodeInBounds(node: Vector2){
|
||||
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._height;
|
||||
}
|
||||
|
||||
public isNodePassable(node: Vector2): boolean {
|
||||
return !this.walls.firstOrDefault(wall => JSON.stringify(wall) == JSON.stringify(node));
|
||||
}
|
||||
|
||||
public search(start: Vector2, goal: Vector2){
|
||||
return WeightedPathfinder.search(this, start, goal);
|
||||
}
|
||||
|
||||
public getNeighbors(node: Vector2): Vector2[]{
|
||||
this._neighbors.length = 0;
|
||||
|
||||
this._dirs.forEach(dir => {
|
||||
let next = new Vector2(node.x + dir.x, node.y + dir.y);
|
||||
if (this.isNodeInBounds(next) && this.isNodePassable(next))
|
||||
this._neighbors.push(next);
|
||||
});
|
||||
|
||||
return this._neighbors;
|
||||
}
|
||||
|
||||
public cost(from: Vector2, to: Vector2): number{
|
||||
return this.weightedNodes.find(t => JSON.stringify(t) == JSON.stringify(to)) ? this.weightedNodeWeight : this.defaultWeight;
|
||||
}
|
||||
}
|
||||
|
||||
public isNodeInBounds(node: Vector2){
|
||||
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._height;
|
||||
}
|
||||
|
||||
public isNodePassable(node: Vector2): boolean {
|
||||
return !this.walls.firstOrDefault(wall => JSON.stringify(wall) == JSON.stringify(node));
|
||||
}
|
||||
|
||||
public search(start: Vector2, goal: Vector2){
|
||||
return WeightedPathfinder.search(this, start, goal);
|
||||
}
|
||||
|
||||
public getNeighbors(node: Vector2): Vector2[]{
|
||||
this._neighbors.length = 0;
|
||||
|
||||
this._dirs.forEach(dir => {
|
||||
let next = new Vector2(node.x + dir.x, node.y + dir.y);
|
||||
if (this.isNodeInBounds(next) && this.isNodePassable(next))
|
||||
this._neighbors.push(next);
|
||||
});
|
||||
|
||||
return this._neighbors;
|
||||
}
|
||||
|
||||
public cost(from: Vector2, to: Vector2): number{
|
||||
return this.weightedNodes.find(t => JSON.stringify(t) == JSON.stringify(to)) ? this.weightedNodeWeight : this.defaultWeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +1,85 @@
|
||||
class WeightedNode<T> extends PriorityQueueNode {
|
||||
public data: T;
|
||||
module es {
|
||||
export class WeightedNode<T> extends PriorityQueueNode {
|
||||
public data: T;
|
||||
|
||||
constructor(data: T){
|
||||
super();
|
||||
this.data = data;
|
||||
constructor(data: T){
|
||||
super();
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WeightedPathfinder {
|
||||
public static search<T>(graph: IWeightedGraph<T>, start: T, goal: T){
|
||||
let foundPath = false;
|
||||
export class WeightedPathfinder {
|
||||
public static search<T>(graph: IWeightedGraph<T>, start: T, goal: T){
|
||||
let foundPath = false;
|
||||
|
||||
let cameFrom = new Map<T, T>();
|
||||
cameFrom.set(start, start);
|
||||
let cameFrom = new Map<T, T>();
|
||||
cameFrom.set(start, start);
|
||||
|
||||
let costSoFar = new Map<T, number>();
|
||||
let frontier = new PriorityQueue<WeightedNode<T>>(1000);
|
||||
frontier.enqueue(new WeightedNode<T>(start), 0);
|
||||
let costSoFar = new Map<T, number>();
|
||||
let frontier = new PriorityQueue<WeightedNode<T>>(1000);
|
||||
frontier.enqueue(new WeightedNode<T>(start), 0);
|
||||
|
||||
costSoFar.set(start, 0);
|
||||
costSoFar.set(start, 0);
|
||||
|
||||
while (frontier.count > 0){
|
||||
let current = frontier.dequeue();
|
||||
|
||||
if (JSON.stringify(current.data) == JSON.stringify(goal)){
|
||||
foundPath = true;
|
||||
break;
|
||||
while (frontier.count > 0){
|
||||
let current = frontier.dequeue();
|
||||
|
||||
if (JSON.stringify(current.data) == JSON.stringify(goal)){
|
||||
foundPath = true;
|
||||
break;
|
||||
}
|
||||
|
||||
graph.getNeighbors(current.data).forEach(next => {
|
||||
let newCost = costSoFar.get(current.data) + graph.cost(current.data, next);
|
||||
if (!this.hasKey(costSoFar, next) || newCost < costSoFar.get(next)){
|
||||
costSoFar.set(next, newCost);
|
||||
let priprity = newCost;
|
||||
frontier.enqueue(new WeightedNode<T>(next), priprity);
|
||||
cameFrom.set(next, current.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
graph.getNeighbors(current.data).forEach(next => {
|
||||
let newCost = costSoFar.get(current.data) + graph.cost(current.data, next);
|
||||
if (!this.hasKey(costSoFar, next) || newCost < costSoFar.get(next)){
|
||||
costSoFar.set(next, newCost);
|
||||
let priprity = newCost;
|
||||
frontier.enqueue(new WeightedNode<T>(next), priprity);
|
||||
cameFrom.set(next, current.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return foundPath ? this.recontructPath(cameFrom, start, goal) : null;
|
||||
}
|
||||
|
||||
private static hasKey<T>(map: Map<T, number>, compareKey: T){
|
||||
let iterator = map.keys();
|
||||
let r: IteratorResult<T>;
|
||||
while (r = iterator.next() , !r.done) {
|
||||
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||
return true;
|
||||
return foundPath ? this.recontructPath(cameFrom, start, goal) : null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
private static hasKey<T>(map: Map<T, number>, compareKey: T){
|
||||
let iterator = map.keys();
|
||||
let r: IteratorResult<T>;
|
||||
while (r = iterator.next() , !r.done) {
|
||||
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||
return true;
|
||||
}
|
||||
|
||||
private static getKey<T>(map: Map<T, T>, compareKey: T){
|
||||
let iterator = map.keys();
|
||||
let valueIterator = map.values();
|
||||
let r: IteratorResult<T>;
|
||||
let v: IteratorResult<T>;
|
||||
while (r = iterator.next(), v = valueIterator.next(), !r.done) {
|
||||
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||
return v.value;
|
||||
return false;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
private static getKey<T>(map: Map<T, T>, compareKey: T){
|
||||
let iterator = map.keys();
|
||||
let valueIterator = map.values();
|
||||
let r: IteratorResult<T>;
|
||||
let v: IteratorResult<T>;
|
||||
while (r = iterator.next(), v = valueIterator.next(), !r.done) {
|
||||
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||
return v.value;
|
||||
}
|
||||
|
||||
public static recontructPath<T>(cameFrom: Map<T, T>, start: T, goal: T): T[]{
|
||||
let path = [];
|
||||
let current = goal;
|
||||
path.push(goal);
|
||||
|
||||
while (current != start){
|
||||
current = this.getKey(cameFrom, current);
|
||||
path.push(current);
|
||||
return null;
|
||||
}
|
||||
|
||||
path.reverse();
|
||||
public static recontructPath<T>(cameFrom: Map<T, T>, start: T, goal: T): T[]{
|
||||
let path = [];
|
||||
let current = goal;
|
||||
path.push(goal);
|
||||
|
||||
return path;
|
||||
while (current != start){
|
||||
current = this.getKey(cameFrom, current);
|
||||
path.push(current);
|
||||
}
|
||||
|
||||
path.reverse();
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
class Debug {
|
||||
private static _debugDrawItems: DebugDrawItem[] = [];
|
||||
module es {
|
||||
export class Debug {
|
||||
private static _debugDrawItems: DebugDrawItem[] = [];
|
||||
|
||||
public static drawHollowRect(rectanle: Rectangle, color: number, duration = 0){
|
||||
this._debugDrawItems.push(new DebugDrawItem(rectanle, color, duration));
|
||||
}
|
||||
public static drawHollowRect(rectanle: Rectangle, color: number, duration = 0){
|
||||
this._debugDrawItems.push(new DebugDrawItem(rectanle, color, duration));
|
||||
}
|
||||
|
||||
public static render(){
|
||||
if (this._debugDrawItems.length > 0){
|
||||
let debugShape = new egret.Shape();
|
||||
if (SceneManager.scene){
|
||||
SceneManager.scene.addChild(debugShape);
|
||||
}
|
||||
public static render(){
|
||||
if (this._debugDrawItems.length > 0){
|
||||
let debugShape = new egret.Shape();
|
||||
if (SceneManager.scene){
|
||||
SceneManager.scene.addChild(debugShape);
|
||||
}
|
||||
|
||||
for (let i = this._debugDrawItems.length - 1; i >= 0; i --){
|
||||
let item = this._debugDrawItems[i];
|
||||
if (item.draw(debugShape))
|
||||
this._debugDrawItems.removeAt(i);
|
||||
for (let i = this._debugDrawItems.length - 1; i >= 0; i --){
|
||||
let item = this._debugDrawItems[i];
|
||||
if (item.draw(debugShape))
|
||||
this._debugDrawItems.removeAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
class DebugDefaults {
|
||||
public static verletParticle = 0xDC345E;
|
||||
public static verletConstraintEdge = 0x433E36;
|
||||
}
|
||||
module es {
|
||||
export class DebugDefaults {
|
||||
public static verletParticle = 0xDC345E;
|
||||
public static verletConstraintEdge = 0x433E36;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,49 @@
|
||||
enum DebugDrawType {
|
||||
line,
|
||||
hollowRectangle,
|
||||
pixel,
|
||||
text
|
||||
}
|
||||
|
||||
class DebugDrawItem {
|
||||
public rectangle: Rectangle;
|
||||
public color: number;
|
||||
public duration: number;
|
||||
public drawType: DebugDrawType;
|
||||
public text: string;
|
||||
public start: Vector2;
|
||||
public end: Vector2;
|
||||
public x: number;
|
||||
public y: number;
|
||||
public size: number;
|
||||
|
||||
constructor(rectangle: Rectangle, color: number, duration: number){
|
||||
this.rectangle = rectangle;
|
||||
this.color = color;
|
||||
this.duration = duration;
|
||||
this.drawType = DebugDrawType.hollowRectangle;
|
||||
module es {
|
||||
export enum DebugDrawType {
|
||||
line,
|
||||
hollowRectangle,
|
||||
pixel,
|
||||
text
|
||||
}
|
||||
|
||||
public draw(shape: egret.Shape): boolean{
|
||||
switch (this.drawType){
|
||||
case DebugDrawType.line:
|
||||
DrawUtils.drawLine(shape, this.start, this.end, this.color);
|
||||
break;
|
||||
case DebugDrawType.hollowRectangle:
|
||||
DrawUtils.drawHollowRect(shape, this.rectangle, this.color);
|
||||
break;
|
||||
case DebugDrawType.pixel:
|
||||
DrawUtils.drawPixel(shape, new Vector2(this.x, this.y), this.color, this.size);
|
||||
break;
|
||||
case DebugDrawType.text:
|
||||
break;
|
||||
export class DebugDrawItem {
|
||||
public rectangle: Rectangle;
|
||||
public color: number;
|
||||
public duration: number;
|
||||
public drawType: DebugDrawType;
|
||||
public text: string;
|
||||
public start: Vector2;
|
||||
public end: Vector2;
|
||||
public x: number;
|
||||
public y: number;
|
||||
public size: number;
|
||||
|
||||
constructor(rectangle: Rectangle, color: number, duration: number){
|
||||
this.rectangle = rectangle;
|
||||
this.color = color;
|
||||
this.duration = duration;
|
||||
this.drawType = DebugDrawType.hollowRectangle;
|
||||
}
|
||||
|
||||
this.duration -= Time.deltaTime;
|
||||
return this.duration < 0;
|
||||
public draw(shape: egret.Shape): boolean{
|
||||
switch (this.drawType){
|
||||
case DebugDrawType.line:
|
||||
DrawUtils.drawLine(shape, this.start, this.end, this.color);
|
||||
break;
|
||||
case DebugDrawType.hollowRectangle:
|
||||
DrawUtils.drawHollowRect(shape, this.rectangle, this.color);
|
||||
break;
|
||||
case DebugDrawType.pixel:
|
||||
DrawUtils.drawPixel(shape, new Vector2(this.x, this.y), this.color, this.size);
|
||||
break;
|
||||
case DebugDrawType.text:
|
||||
break;
|
||||
}
|
||||
|
||||
this.duration -= Time.deltaTime;
|
||||
return this.duration < 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
enum CoreEvents{
|
||||
/** 当场景发生变化时触发 */
|
||||
SceneChanged,
|
||||
}
|
||||
module es {
|
||||
export enum CoreEvents{
|
||||
/** 当场景发生变化时触发 */
|
||||
SceneChanged,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ module es {
|
||||
*/
|
||||
public static createWithDefaultRenderer(){
|
||||
let scene = new Scene();
|
||||
scene.addRenderer(new DefaultRenderer());
|
||||
scene.addRenderer(new DefaultRenderer());
|
||||
return scene;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,144 +1,146 @@
|
||||
/** 运行时的场景管理。 */
|
||||
class SceneManager {
|
||||
private static _scene: Scene;
|
||||
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;
|
||||
private static timerRuler: TimeRuler;
|
||||
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);
|
||||
SceneManager.timerRuler = new TimeRuler();
|
||||
}
|
||||
|
||||
public static get scene() {
|
||||
return this._scene;
|
||||
}
|
||||
public static set scene(value: Scene) {
|
||||
if (!value)
|
||||
throw new Error("场景不能为空");
|
||||
|
||||
if (this._scene == null) {
|
||||
this._scene = value;
|
||||
this._scene.begin();
|
||||
SceneManager.Instance.onSceneChanged();
|
||||
} else {
|
||||
this._nextScene = value;
|
||||
module es {
|
||||
/** 运行时的场景管理。 */
|
||||
export class SceneManager {
|
||||
private static _scene: Scene;
|
||||
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;
|
||||
private static timerRuler: TimeRuler;
|
||||
public static get Instance(){
|
||||
return this._instnace;
|
||||
}
|
||||
|
||||
this.registerActiveSceneChanged(this._scene, this._nextScene);
|
||||
}
|
||||
constructor(stage: egret.Stage) {
|
||||
stage.addEventListener(egret.Event.ENTER_FRAME, SceneManager.update, this);
|
||||
|
||||
public static initialize(stage: egret.Stage) {
|
||||
Input.initialize(stage);
|
||||
}
|
||||
SceneManager._instnace = this;
|
||||
SceneManager.emitter = new Emitter<CoreEvents>();
|
||||
SceneManager.content = new ContentManager();
|
||||
|
||||
public static update() {
|
||||
SceneManager.startDebugUpdate();
|
||||
Time.update(egret.getTimer());
|
||||
SceneManager.stage = stage;
|
||||
SceneManager.initialize(stage);
|
||||
SceneManager.timerRuler = new TimeRuler();
|
||||
}
|
||||
|
||||
if (SceneManager._scene) {
|
||||
for (let i = GlobalManager.globalManagers.length - 1; i >= 0; i--) {
|
||||
if (GlobalManager.globalManagers[i].enabled)
|
||||
GlobalManager.globalManagers[i].update();
|
||||
public static get scene() {
|
||||
return this._scene;
|
||||
}
|
||||
public static set scene(value: Scene) {
|
||||
if (!value)
|
||||
throw new Error("场景不能为空");
|
||||
|
||||
if (this._scene == null) {
|
||||
this._scene = value;
|
||||
this._scene.begin();
|
||||
SceneManager.Instance.onSceneChanged();
|
||||
} else {
|
||||
this._nextScene = value;
|
||||
}
|
||||
|
||||
if (!SceneManager.sceneTransition ||
|
||||
(SceneManager.sceneTransition && (!SceneManager.sceneTransition.loadsNewScene || SceneManager.sceneTransition.isNewSceneLoaded))) {
|
||||
this.registerActiveSceneChanged(this._scene, this._nextScene);
|
||||
}
|
||||
|
||||
public static initialize(stage: egret.Stage) {
|
||||
Input.initialize(stage);
|
||||
}
|
||||
|
||||
public static update() {
|
||||
SceneManager.startDebugUpdate();
|
||||
Time.update(egret.getTimer());
|
||||
|
||||
if (SceneManager._scene) {
|
||||
for (let i = GlobalManager.globalManagers.length - 1; i >= 0; i--) {
|
||||
if (GlobalManager.globalManagers[i].enabled)
|
||||
GlobalManager.globalManagers[i].update();
|
||||
}
|
||||
|
||||
if (!SceneManager.sceneTransition ||
|
||||
(SceneManager.sceneTransition && (!SceneManager.sceneTransition.loadsNewScene || SceneManager.sceneTransition.isNewSceneLoaded))) {
|
||||
SceneManager._scene.update();
|
||||
}
|
||||
|
||||
if (SceneManager._nextScene) {
|
||||
SceneManager._scene.end();
|
||||
|
||||
SceneManager._scene = SceneManager._nextScene;
|
||||
SceneManager._nextScene = null;
|
||||
SceneManager._instnace.onSceneChanged();
|
||||
|
||||
SceneManager._scene.begin();
|
||||
}
|
||||
}
|
||||
|
||||
if (SceneManager._nextScene) {
|
||||
SceneManager._scene.end();
|
||||
|
||||
SceneManager._scene = SceneManager._nextScene;
|
||||
SceneManager._nextScene = null;
|
||||
SceneManager._instnace.onSceneChanged();
|
||||
|
||||
SceneManager._scene.begin();
|
||||
}
|
||||
SceneManager.endDebugUpdate();
|
||||
SceneManager.render();
|
||||
}
|
||||
|
||||
SceneManager.endDebugUpdate();
|
||||
SceneManager.render();
|
||||
}
|
||||
public static render() {
|
||||
if (this.sceneTransition){
|
||||
this.sceneTransition.preRender();
|
||||
|
||||
public static render() {
|
||||
if (this.sceneTransition){
|
||||
this.sceneTransition.preRender();
|
||||
|
||||
if (this._scene && !this.sceneTransition.hasPreviousSceneRender){
|
||||
this._scene.render();
|
||||
this._scene.postRender();
|
||||
this.sceneTransition.onBeginTransition();
|
||||
} else if (this.sceneTransition) {
|
||||
if (this._scene && this.sceneTransition.isNewSceneLoaded) {
|
||||
if (this._scene && !this.sceneTransition.hasPreviousSceneRender){
|
||||
this._scene.render();
|
||||
this._scene.postRender();
|
||||
this.sceneTransition.onBeginTransition();
|
||||
} else if (this.sceneTransition) {
|
||||
if (this._scene && this.sceneTransition.isNewSceneLoaded) {
|
||||
this._scene.render();
|
||||
this._scene.postRender();
|
||||
}
|
||||
|
||||
this.sceneTransition.render();
|
||||
}
|
||||
|
||||
this.sceneTransition.render();
|
||||
} else if (this._scene) {
|
||||
this._scene.render();
|
||||
|
||||
Debug.render();
|
||||
|
||||
this._scene.postRender();
|
||||
}
|
||||
} else if (this._scene) {
|
||||
this._scene.render();
|
||||
|
||||
Debug.render();
|
||||
|
||||
this._scene.postRender();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 临时运行SceneTransition,允许一个场景过渡到另一个平滑的自定义效果。
|
||||
* @param sceneTransition
|
||||
*/
|
||||
public static startSceneTransition<T extends SceneTransition>(sceneTransition: T): T {
|
||||
if (this.sceneTransition) {
|
||||
console.warn("在前一个场景完成之前,不能开始一个新的场景转换。");
|
||||
return;
|
||||
}
|
||||
|
||||
this.sceneTransition = sceneTransition;
|
||||
return sceneTransition;
|
||||
}
|
||||
/**
|
||||
* 临时运行SceneTransition,允许一个场景过渡到另一个平滑的自定义效果。
|
||||
* @param sceneTransition
|
||||
*/
|
||||
public static startSceneTransition<T extends SceneTransition>(sceneTransition: T): T {
|
||||
if (this.sceneTransition) {
|
||||
console.warn("在前一个场景完成之前,不能开始一个新的场景转换。");
|
||||
return;
|
||||
}
|
||||
|
||||
public static registerActiveSceneChanged(current: Scene, next: Scene){
|
||||
if (this.activeSceneChanged)
|
||||
this.activeSceneChanged(current, next);
|
||||
}
|
||||
this.sceneTransition = sceneTransition;
|
||||
return sceneTransition;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在一个场景结束后,下一个场景开始之前调用
|
||||
*/
|
||||
public onSceneChanged(){
|
||||
SceneManager.emitter.emit(CoreEvents.SceneChanged);
|
||||
Time.sceneChanged();
|
||||
}
|
||||
public static registerActiveSceneChanged(current: Scene, next: Scene){
|
||||
if (this.activeSceneChanged)
|
||||
this.activeSceneChanged(current, next);
|
||||
}
|
||||
|
||||
private static startDebugUpdate(){
|
||||
TimeRuler.Instance.startFrame();
|
||||
TimeRuler.Instance.beginMark("update", 0x00FF00);
|
||||
}
|
||||
/**
|
||||
* 在一个场景结束后,下一个场景开始之前调用
|
||||
*/
|
||||
public onSceneChanged(){
|
||||
SceneManager.emitter.emit(CoreEvents.SceneChanged);
|
||||
Time.sceneChanged();
|
||||
}
|
||||
|
||||
private static endDebugUpdate(){
|
||||
TimeRuler.Instance.endMark("update");
|
||||
private static startDebugUpdate(){
|
||||
TimeRuler.Instance.startFrame();
|
||||
TimeRuler.Instance.beginMark("update", 0x00FF00);
|
||||
}
|
||||
|
||||
private static endDebugUpdate(){
|
||||
TimeRuler.Instance.endMark("update");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,34 @@
|
||||
///<reference path="./EntitySystem.ts" />
|
||||
/**
|
||||
* 基本实体处理系统。将其用作处理具有特定组件的许多实体的基础
|
||||
*/
|
||||
abstract class EntityProcessingSystem extends EntitySystem {
|
||||
constructor(matcher: Matcher) {
|
||||
super(matcher);
|
||||
}
|
||||
|
||||
|
||||
module es {
|
||||
/**
|
||||
* 处理特定的实体
|
||||
* @param entity
|
||||
* 基本实体处理系统。将其用作处理具有特定组件的许多实体的基础
|
||||
*/
|
||||
public abstract processEntity(entity: Entity);
|
||||
export abstract class EntityProcessingSystem extends EntitySystem {
|
||||
constructor(matcher: Matcher) {
|
||||
super(matcher);
|
||||
}
|
||||
|
||||
public lateProcessEntity(entity: Entity) {
|
||||
|
||||
/**
|
||||
* 处理特定的实体
|
||||
* @param entity
|
||||
*/
|
||||
public abstract processEntity(entity: Entity);
|
||||
|
||||
public lateProcessEntity(entity: Entity) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历这个系统的所有实体并逐个处理它们
|
||||
* @param entities
|
||||
*/
|
||||
protected process(entities: Entity[]) {
|
||||
entities.forEach(entity => this.processEntity(entity));
|
||||
}
|
||||
|
||||
protected lateProcess(entities: Entity[]) {
|
||||
entities.forEach(entity => this.lateProcessEntity(entity));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历这个系统的所有实体并逐个处理它们
|
||||
* @param entities
|
||||
*/
|
||||
protected process(entities: Entity[]) {
|
||||
entities.forEach(entity => this.processEntity(entity));
|
||||
}
|
||||
|
||||
protected lateProcess(entities: Entity[]) {
|
||||
entities.forEach(entity => this.lateProcessEntity(entity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,79 +1,81 @@
|
||||
class EntitySystem {
|
||||
private _scene: Scene;
|
||||
private _entities: Entity[] = [];
|
||||
private _matcher: Matcher;
|
||||
module es {
|
||||
export class EntitySystem {
|
||||
private _scene: Scene;
|
||||
private _entities: Entity[] = [];
|
||||
private _matcher: Matcher;
|
||||
|
||||
public get matcher(){
|
||||
return this._matcher;
|
||||
public get matcher(){
|
||||
return this._matcher;
|
||||
}
|
||||
|
||||
public get scene(){
|
||||
return this._scene;
|
||||
}
|
||||
|
||||
public set scene(value: Scene){
|
||||
this._scene = value;
|
||||
this._entities = [];
|
||||
}
|
||||
|
||||
constructor(matcher?: Matcher){
|
||||
this._matcher = matcher ? matcher : Matcher.empty();
|
||||
}
|
||||
|
||||
public initialize(){
|
||||
|
||||
}
|
||||
|
||||
public onChanged(entity: Entity){
|
||||
let contains = this._entities.contains(entity);
|
||||
let interest = this._matcher.IsIntersted(entity);
|
||||
|
||||
if (interest && !contains)
|
||||
this.add(entity);
|
||||
else if(!interest && contains)
|
||||
this.remove(entity);
|
||||
}
|
||||
|
||||
public add(entity: Entity){
|
||||
this._entities.push(entity);
|
||||
this.onAdded(entity);
|
||||
}
|
||||
|
||||
public onAdded(entity: Entity){
|
||||
}
|
||||
|
||||
public remove(entity: Entity){
|
||||
this._entities.remove(entity);
|
||||
this.onRemoved(entity);
|
||||
}
|
||||
|
||||
public onRemoved(entity: Entity){
|
||||
|
||||
}
|
||||
|
||||
public update(){
|
||||
this.begin();
|
||||
this.process(this._entities);
|
||||
}
|
||||
|
||||
public lateUpdate(){
|
||||
this.lateProcess(this._entities);
|
||||
this.end();
|
||||
}
|
||||
|
||||
protected begin(){
|
||||
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]){
|
||||
|
||||
}
|
||||
|
||||
protected lateProcess(entities: Entity[]){
|
||||
|
||||
}
|
||||
|
||||
protected end(){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public get scene(){
|
||||
return this._scene;
|
||||
}
|
||||
|
||||
public set scene(value: Scene){
|
||||
this._scene = value;
|
||||
this._entities = [];
|
||||
}
|
||||
|
||||
constructor(matcher?: Matcher){
|
||||
this._matcher = matcher ? matcher : Matcher.empty();
|
||||
}
|
||||
|
||||
public initialize(){
|
||||
|
||||
}
|
||||
|
||||
public onChanged(entity: Entity){
|
||||
let contains = this._entities.contains(entity);
|
||||
let interest = this._matcher.IsIntersted(entity);
|
||||
|
||||
if (interest && !contains)
|
||||
this.add(entity);
|
||||
else if(!interest && contains)
|
||||
this.remove(entity);
|
||||
}
|
||||
|
||||
public add(entity: Entity){
|
||||
this._entities.push(entity);
|
||||
this.onAdded(entity);
|
||||
}
|
||||
|
||||
public onAdded(entity: Entity){
|
||||
}
|
||||
|
||||
public remove(entity: Entity){
|
||||
this._entities.remove(entity);
|
||||
this.onRemoved(entity);
|
||||
}
|
||||
|
||||
public onRemoved(entity: Entity){
|
||||
|
||||
}
|
||||
|
||||
public update(){
|
||||
this.begin();
|
||||
this.process(this._entities);
|
||||
}
|
||||
|
||||
public lateUpdate(){
|
||||
this.lateProcess(this._entities);
|
||||
this.end();
|
||||
}
|
||||
|
||||
protected begin(){
|
||||
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]){
|
||||
|
||||
}
|
||||
|
||||
protected lateProcess(entities: Entity[]){
|
||||
|
||||
}
|
||||
|
||||
protected end(){
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
abstract class PassiveSystem extends EntitySystem {
|
||||
public onChanged(entity: Entity){
|
||||
module es {
|
||||
export abstract class PassiveSystem extends EntitySystem {
|
||||
public onChanged(entity: Entity){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]){
|
||||
// 我们用我们自己的不考虑实体的基本实体系统来代替
|
||||
this.begin();
|
||||
this.end();
|
||||
protected process(entities: Entity[]){
|
||||
// 我们用我们自己的不考虑实体的基本实体系统来代替
|
||||
this.begin();
|
||||
this.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
/** 用于协调其他系统的通用系统基类 */
|
||||
abstract class ProcessingSystem extends EntitySystem {
|
||||
public onChanged(entity: Entity){
|
||||
module es {
|
||||
export abstract class ProcessingSystem extends EntitySystem {
|
||||
public onChanged(entity: Entity){
|
||||
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]){
|
||||
this.begin();
|
||||
this.processSystem();
|
||||
this.end();
|
||||
}
|
||||
|
||||
/** 处理我们的系统 每帧调用 */
|
||||
public abstract processSystem();
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]){
|
||||
this.begin();
|
||||
this.processSystem();
|
||||
this.end();
|
||||
}
|
||||
|
||||
/** 处理我们的系统 每帧调用 */
|
||||
public abstract processSystem();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ module es {
|
||||
this.updateLists();
|
||||
|
||||
for (let i = 0; i < this._entities.length; i ++){
|
||||
this._entities[i]._isDestoryed = true;
|
||||
this._entities[i]._isDestroyed = true;
|
||||
this._entities[i].onRemovedFromScene();
|
||||
this._entities[i].scene = null;
|
||||
}
|
||||
|
||||
@@ -1,62 +1,64 @@
|
||||
class Matcher{
|
||||
protected allSet = new BitSet();
|
||||
protected exclusionSet = new BitSet();
|
||||
protected oneSet = new BitSet();
|
||||
module es {
|
||||
export class Matcher{
|
||||
protected allSet = new BitSet();
|
||||
protected exclusionSet = new BitSet();
|
||||
protected oneSet = new BitSet();
|
||||
|
||||
public static empty(){
|
||||
return new Matcher();
|
||||
}
|
||||
|
||||
public getAllSet(){
|
||||
return this.allSet;
|
||||
}
|
||||
|
||||
public getExclusionSet(){
|
||||
return this.exclusionSet;
|
||||
}
|
||||
|
||||
public getOneSet(){
|
||||
return this.oneSet;
|
||||
}
|
||||
|
||||
public IsIntersted(e: Entity){
|
||||
if (!this.allSet.isEmpty()){
|
||||
for (let i = this.allSet.nextSetBit(0); i >= 0; i = this.allSet.nextSetBit(i + 1)){
|
||||
if (!e.componentBits.get(i))
|
||||
return false;
|
||||
}
|
||||
public static empty(){
|
||||
return new Matcher();
|
||||
}
|
||||
|
||||
if (!this.exclusionSet.isEmpty() && this.exclusionSet.intersects(e.componentBits))
|
||||
return false;
|
||||
public getAllSet(){
|
||||
return this.allSet;
|
||||
}
|
||||
|
||||
if (!this.oneSet.isEmpty() && !this.oneSet.intersects(e.componentBits))
|
||||
return false;
|
||||
public getExclusionSet(){
|
||||
return this.exclusionSet;
|
||||
}
|
||||
|
||||
return true;
|
||||
public getOneSet(){
|
||||
return this.oneSet;
|
||||
}
|
||||
|
||||
public IsIntersted(e: Entity){
|
||||
if (!this.allSet.isEmpty()){
|
||||
for (let i = this.allSet.nextSetBit(0); i >= 0; i = this.allSet.nextSetBit(i + 1)){
|
||||
if (!e.componentBits.get(i))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.exclusionSet.isEmpty() && this.exclusionSet.intersects(e.componentBits))
|
||||
return false;
|
||||
|
||||
if (!this.oneSet.isEmpty() && !this.oneSet.intersects(e.componentBits))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public all(...types: any[]): Matcher{
|
||||
types.forEach(type => {
|
||||
this.allSet.set(ComponentTypeManager.getIndexFor(type));
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public exclude(...types: any[]){
|
||||
types.forEach(type => {
|
||||
this.exclusionSet.set(ComponentTypeManager.getIndexFor(type));
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public one(...types: any[]){
|
||||
types.forEach(type => {
|
||||
this.oneSet.set(ComponentTypeManager.getIndexFor(type));
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public all(...types: any[]): Matcher{
|
||||
types.forEach(type => {
|
||||
this.allSet.set(ComponentTypeManager.getIndexFor(type));
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public exclude(...types: any[]){
|
||||
types.forEach(type => {
|
||||
this.exclusionSet.set(ComponentTypeManager.getIndexFor(type));
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public one(...types: any[]){
|
||||
types.forEach(type => {
|
||||
this.oneSet.set(ComponentTypeManager.getIndexFor(type));
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,154 +1,156 @@
|
||||
/**
|
||||
* 纹理帮助类
|
||||
*/
|
||||
class TextureUtils {
|
||||
public static sharedCanvas: HTMLCanvasElement;
|
||||
public static sharedContext: CanvasRenderingContext2D;
|
||||
module es {
|
||||
/**
|
||||
* 纹理帮助类
|
||||
*/
|
||||
export class TextureUtils {
|
||||
public static sharedCanvas: HTMLCanvasElement;
|
||||
public static sharedContext: CanvasRenderingContext2D;
|
||||
|
||||
public static convertImageToCanvas(texture: egret.Texture, rect?: egret.Rectangle): HTMLCanvasElement{
|
||||
if (!this.sharedCanvas){
|
||||
this.sharedCanvas = egret.sys.createCanvas();
|
||||
this.sharedContext = this.sharedCanvas.getContext("2d");
|
||||
}
|
||||
public static convertImageToCanvas(texture: egret.Texture, rect?: egret.Rectangle): HTMLCanvasElement{
|
||||
if (!this.sharedCanvas){
|
||||
this.sharedCanvas = egret.sys.createCanvas();
|
||||
this.sharedContext = this.sharedCanvas.getContext("2d");
|
||||
}
|
||||
|
||||
let w = texture.$getTextureWidth();
|
||||
let h = texture.$getTextureHeight();
|
||||
if (!rect){
|
||||
rect = egret.$TempRectangle;
|
||||
rect.x = 0;
|
||||
rect.y = 0;
|
||||
rect.width = w;
|
||||
rect.height = h;
|
||||
}
|
||||
|
||||
rect.x = Math.min(rect.x, w - 1);
|
||||
rect.y = Math.min(rect.y, h - 1);
|
||||
rect.width = Math.min(rect.width, w - rect.x);
|
||||
rect.height = Math.min(rect.height, h - rect.y);
|
||||
let w = texture.$getTextureWidth();
|
||||
let h = texture.$getTextureHeight();
|
||||
if (!rect){
|
||||
rect = egret.$TempRectangle;
|
||||
rect.x = 0;
|
||||
rect.y = 0;
|
||||
rect.width = w;
|
||||
rect.height = h;
|
||||
}
|
||||
|
||||
let iWidth = Math.floor(rect.width);
|
||||
let iHeight = Math.floor(rect.height);
|
||||
let surface = this.sharedCanvas;
|
||||
surface["style"]["width"] = iWidth + "px";
|
||||
surface["style"]["height"] = iHeight + "px";
|
||||
this.sharedCanvas.width = iWidth;
|
||||
this.sharedCanvas.height = iHeight;
|
||||
rect.x = Math.min(rect.x, w - 1);
|
||||
rect.y = Math.min(rect.y, h - 1);
|
||||
rect.width = Math.min(rect.width, w - rect.x);
|
||||
rect.height = Math.min(rect.height, h - rect.y);
|
||||
|
||||
if (egret.Capabilities.renderMode == "webgl") {
|
||||
let renderTexture: egret.RenderTexture;
|
||||
//webgl下非RenderTexture纹理先画到RenderTexture
|
||||
if (!(<egret.RenderTexture>texture).$renderBuffer) {
|
||||
if (egret.sys.systemRenderer["renderClear"]) {
|
||||
egret.sys.systemRenderer["renderClear"]();
|
||||
let iWidth = Math.floor(rect.width);
|
||||
let iHeight = Math.floor(rect.height);
|
||||
let surface = this.sharedCanvas;
|
||||
surface["style"]["width"] = iWidth + "px";
|
||||
surface["style"]["height"] = iHeight + "px";
|
||||
this.sharedCanvas.width = iWidth;
|
||||
this.sharedCanvas.height = iHeight;
|
||||
|
||||
if (egret.Capabilities.renderMode == "webgl") {
|
||||
let renderTexture: egret.RenderTexture;
|
||||
//webgl下非RenderTexture纹理先画到RenderTexture
|
||||
if (!(<egret.RenderTexture>texture).$renderBuffer) {
|
||||
if (egret.sys.systemRenderer["renderClear"]) {
|
||||
egret.sys.systemRenderer["renderClear"]();
|
||||
}
|
||||
renderTexture = new egret.RenderTexture();
|
||||
renderTexture.drawToTexture(new egret.Bitmap(texture));
|
||||
}
|
||||
renderTexture = new egret.RenderTexture();
|
||||
renderTexture.drawToTexture(new egret.Bitmap(texture));
|
||||
else {
|
||||
renderTexture = <egret.RenderTexture>texture;
|
||||
}
|
||||
//从RenderTexture中读取像素数据,填入canvas
|
||||
let pixels = renderTexture.$renderBuffer.getPixels(rect.x, rect.y, iWidth, iHeight);
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
for (let i = 0; i < pixels.length; i += 4) {
|
||||
this.sharedContext.fillStyle =
|
||||
'rgba(' + pixels[i]
|
||||
+ ',' + pixels[i + 1]
|
||||
+ ',' + pixels[i + 2]
|
||||
+ ',' + (pixels[i + 3] / 255) + ')';
|
||||
this.sharedContext.fillRect(x, y, 1, 1);
|
||||
x++;
|
||||
if (x == iWidth) {
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(<egret.RenderTexture>texture).$renderBuffer) {
|
||||
renderTexture.dispose();
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
else {
|
||||
renderTexture = <egret.RenderTexture>texture;
|
||||
let bitmapData = texture;
|
||||
let offsetX: number = Math.round(bitmapData.$offsetX);
|
||||
let offsetY: number = Math.round(bitmapData.$offsetY);
|
||||
let bitmapWidth: number = bitmapData.$bitmapWidth;
|
||||
let bitmapHeight: number = bitmapData.$bitmapHeight;
|
||||
let $TextureScaleFactor = SceneManager.stage.textureScaleFactor;
|
||||
this.sharedContext.drawImage(bitmapData.$bitmapData.source, bitmapData.$bitmapX + rect.x / $TextureScaleFactor, bitmapData.$bitmapY + rect.y / $TextureScaleFactor,
|
||||
bitmapWidth * rect.width / w, bitmapHeight * rect.height / h, offsetX, offsetY, rect.width, rect.height);
|
||||
return surface;
|
||||
}
|
||||
//从RenderTexture中读取像素数据,填入canvas
|
||||
let pixels = renderTexture.$renderBuffer.getPixels(rect.x, rect.y, iWidth, iHeight);
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
for (let i = 0; i < pixels.length; i += 4) {
|
||||
this.sharedContext.fillStyle =
|
||||
'rgba(' + pixels[i]
|
||||
+ ',' + pixels[i + 1]
|
||||
+ ',' + pixels[i + 2]
|
||||
+ ',' + (pixels[i + 3] / 255) + ')';
|
||||
this.sharedContext.fillRect(x, y, 1, 1);
|
||||
x++;
|
||||
if (x == iWidth) {
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(<egret.RenderTexture>texture).$renderBuffer) {
|
||||
renderTexture.dispose();
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
else {
|
||||
let bitmapData = texture;
|
||||
let offsetX: number = Math.round(bitmapData.$offsetX);
|
||||
let offsetY: number = Math.round(bitmapData.$offsetY);
|
||||
let bitmapWidth: number = bitmapData.$bitmapWidth;
|
||||
let bitmapHeight: number = bitmapData.$bitmapHeight;
|
||||
let $TextureScaleFactor = SceneManager.stage.textureScaleFactor;
|
||||
this.sharedContext.drawImage(bitmapData.$bitmapData.source, bitmapData.$bitmapX + rect.x / $TextureScaleFactor, bitmapData.$bitmapY + rect.y / $TextureScaleFactor,
|
||||
bitmapWidth * rect.width / w, bitmapHeight * rect.height / h, offsetX, offsetY, rect.width, rect.height);
|
||||
return surface;
|
||||
}
|
||||
}
|
||||
|
||||
public static toDataURL(type: string, texture: egret.Texture, rect?: egret.Rectangle, encoderOptions?): string {
|
||||
try {
|
||||
public static toDataURL(type: string, texture: egret.Texture, rect?: egret.Rectangle, encoderOptions?): string {
|
||||
try {
|
||||
let surface = this.convertImageToCanvas(texture, rect);
|
||||
let result = surface.toDataURL(type, encoderOptions);
|
||||
return result;
|
||||
}
|
||||
catch (e) {
|
||||
egret.$error(1033);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 有些杀毒软件认为 saveToFile 可能是一个病毒文件
|
||||
* @param type
|
||||
* @param texture
|
||||
* @param filePath
|
||||
* @param rect
|
||||
* @param encoderOptions
|
||||
*/
|
||||
public static eliFoTevas(type: string, texture: egret.Texture, filePath: string, rect?: egret.Rectangle, encoderOptions?): void {
|
||||
let surface = this.convertImageToCanvas(texture, rect);
|
||||
let result = surface.toDataURL(type, encoderOptions);
|
||||
let result = (surface as any).toTempFilePathSync({
|
||||
fileType: type.indexOf("png") >= 0 ? "png" : "jpg"
|
||||
});
|
||||
|
||||
wx.getFileSystemManager().saveFile({
|
||||
tempFilePath: result,
|
||||
filePath: `${wx.env.USER_DATA_PATH}/${filePath}`,
|
||||
success: function (res) {
|
||||
//todo
|
||||
}
|
||||
})
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (e) {
|
||||
egret.$error(1033);
|
||||
|
||||
public static getPixel32(texture: egret.Texture, x: number, y: number): number[] {
|
||||
egret.$warn(1041, "getPixel32", "getPixels");
|
||||
return texture.getPixels(x, y);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 有些杀毒软件认为 saveToFile 可能是一个病毒文件
|
||||
* @param type
|
||||
* @param texture
|
||||
* @param filePath
|
||||
* @param rect
|
||||
* @param encoderOptions
|
||||
*/
|
||||
public static eliFoTevas(type: string, texture: egret.Texture, filePath: string, rect?: egret.Rectangle, encoderOptions?): void {
|
||||
let surface = this.convertImageToCanvas(texture, rect);
|
||||
let result = (surface as any).toTempFilePathSync({
|
||||
fileType: type.indexOf("png") >= 0 ? "png" : "jpg"
|
||||
});
|
||||
|
||||
wx.getFileSystemManager().saveFile({
|
||||
tempFilePath: result,
|
||||
filePath: `${wx.env.USER_DATA_PATH}/${filePath}`,
|
||||
success: function (res) {
|
||||
//todo
|
||||
public static getPixels(texture: egret.Texture, x: number, y: number, width: number = 1, height: number = 1): number[] {
|
||||
//webgl环境下不需要转换成canvas获取像素信息
|
||||
if (egret.Capabilities.renderMode == "webgl") {
|
||||
let renderTexture: egret.RenderTexture;
|
||||
//webgl下非RenderTexture纹理先画到RenderTexture
|
||||
if (!(<egret.RenderTexture>texture).$renderBuffer) {
|
||||
renderTexture = new egret.RenderTexture();
|
||||
renderTexture.drawToTexture(new egret.Bitmap(texture));
|
||||
}
|
||||
else {
|
||||
renderTexture = <egret.RenderTexture>texture;
|
||||
}
|
||||
//从RenderTexture中读取像素数据
|
||||
let pixels = renderTexture.$renderBuffer.getPixels(x, y, width, height);
|
||||
return pixels;
|
||||
}
|
||||
})
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static getPixel32(texture: egret.Texture, x: number, y: number): number[] {
|
||||
egret.$warn(1041, "getPixel32", "getPixels");
|
||||
return texture.getPixels(x, y);
|
||||
}
|
||||
|
||||
public static getPixels(texture: egret.Texture, x: number, y: number, width: number = 1, height: number = 1): number[] {
|
||||
//webgl环境下不需要转换成canvas获取像素信息
|
||||
if (egret.Capabilities.renderMode == "webgl") {
|
||||
let renderTexture: egret.RenderTexture;
|
||||
//webgl下非RenderTexture纹理先画到RenderTexture
|
||||
if (!(<egret.RenderTexture>texture).$renderBuffer) {
|
||||
renderTexture = new egret.RenderTexture();
|
||||
renderTexture.drawToTexture(new egret.Bitmap(texture));
|
||||
try {
|
||||
let surface = this.convertImageToCanvas(texture);
|
||||
let result = this.sharedContext.getImageData(x, y, width, height).data;
|
||||
return <number[]><any>result;
|
||||
}
|
||||
else {
|
||||
renderTexture = <egret.RenderTexture>texture;
|
||||
catch (e) {
|
||||
egret.$error(1039);
|
||||
}
|
||||
//从RenderTexture中读取像素数据
|
||||
let pixels = renderTexture.$renderBuffer.getPixels(x, y, width, height);
|
||||
return pixels;
|
||||
}
|
||||
try {
|
||||
let surface = this.convertImageToCanvas(texture);
|
||||
let result = this.sharedContext.getImageData(x, y, width, height).data;
|
||||
return <number[]><any>result;
|
||||
}
|
||||
catch (e) {
|
||||
egret.$error(1039);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,40 @@
|
||||
/** 提供帧定时信息 */
|
||||
class Time {
|
||||
/** deltaTime的未缩放版本。不受时间尺度的影响 */
|
||||
public static unscaledDeltaTime;
|
||||
/** 前一帧到当前帧的时间增量,按时间刻度进行缩放 */
|
||||
public static deltaTime: number = 0;
|
||||
/** 时间刻度缩放 */
|
||||
public static timeScale = 1;
|
||||
/** 已传递的帧总数 */
|
||||
public static frameCount = 0;
|
||||
|
||||
private static _lastTime = 0;
|
||||
/** 自场景加载以来的总时间 */
|
||||
public static _timeSinceSceneLoad;
|
||||
module es {
|
||||
/** 提供帧定时信息 */
|
||||
export class Time {
|
||||
/** deltaTime的未缩放版本。不受时间尺度的影响 */
|
||||
public static unscaledDeltaTime;
|
||||
/** 前一帧到当前帧的时间增量,按时间刻度进行缩放 */
|
||||
public static deltaTime: number = 0;
|
||||
/** 时间刻度缩放 */
|
||||
public static timeScale = 1;
|
||||
/** 已传递的帧总数 */
|
||||
public static frameCount = 0;
|
||||
|
||||
public static update(currentTime: number){
|
||||
let dt = (currentTime - this._lastTime) / 1000;
|
||||
this.deltaTime = dt * this.timeScale;
|
||||
this.unscaledDeltaTime = dt;
|
||||
this._timeSinceSceneLoad += dt;
|
||||
this.frameCount ++;
|
||||
private static _lastTime = 0;
|
||||
/** 自场景加载以来的总时间 */
|
||||
public static _timeSinceSceneLoad;
|
||||
|
||||
this._lastTime = currentTime;
|
||||
public static update(currentTime: number){
|
||||
let dt = (currentTime - this._lastTime) / 1000;
|
||||
this.deltaTime = dt * this.timeScale;
|
||||
this.unscaledDeltaTime = dt;
|
||||
this._timeSinceSceneLoad += dt;
|
||||
this.frameCount ++;
|
||||
|
||||
this._lastTime = currentTime;
|
||||
}
|
||||
|
||||
public static sceneChanged(){
|
||||
this._timeSinceSceneLoad = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 允许在间隔检查。只应该使用高于delta的间隔值,否则它将始终返回true。
|
||||
* @param interval
|
||||
*/
|
||||
public static checkEvery(interval: number){
|
||||
// 我们减去了delta,因为timeSinceSceneLoad已经包含了这个update ticks delta
|
||||
return (this._timeSinceSceneLoad / interval) > ((this._timeSinceSceneLoad - this.deltaTime) / interval);
|
||||
}
|
||||
}
|
||||
|
||||
public static sceneChanged(){
|
||||
this._timeSinceSceneLoad = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 允许在间隔检查。只应该使用高于delta的间隔值,否则它将始终返回true。
|
||||
* @param interval
|
||||
*/
|
||||
public static checkEvery(interval: number){
|
||||
// 我们减去了delta,因为timeSinceSceneLoad已经包含了这个update ticks delta
|
||||
return (this._timeSinceSceneLoad / interval) > ((this._timeSinceSceneLoad - this.deltaTime) / interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +1,74 @@
|
||||
class GaussianBlurEffect extends egret.CustomFilter {
|
||||
// private static blur_frag = "precision mediump float;\n" +
|
||||
// "uniform vec2 blur;\n" +
|
||||
// "uniform sampler2D uSampler;\n" +
|
||||
// "varying vec2 vTextureCoord;\n" +
|
||||
// "uniform vec2 uTextureSize;\n" +
|
||||
// "void main()\n" +
|
||||
// "{\n " +
|
||||
// "const int sampleRadius = 5;\n" +
|
||||
// "const int samples = sampleRadius * 2 + 1;\n" +
|
||||
// "vec2 blurUv = blur / uTextureSize;\n" +
|
||||
// "vec4 color = vec4(0, 0, 0, 0);\n" +
|
||||
// "vec2 uv = vec2(0.0, 0.0);\n" +
|
||||
// "blurUv /= float(sampleRadius);\n" +
|
||||
|
||||
// "for (int i = -sampleRadius; i <= sampleRadius; i++) {\n" +
|
||||
// "uv.x = vTextureCoord.x + float(i) * blurUv.x;\n" +
|
||||
// "uv.y = vTextureCoord.y + float(i) * blurUv.y;\n" +
|
||||
// "color += texture2D(uSampler, uv);\n" +
|
||||
// "}\n" +
|
||||
|
||||
// "color /= float(samples);\n" +
|
||||
// "gl_FragColor = color;\n" +
|
||||
// "}";
|
||||
module es {
|
||||
export class GaussianBlurEffect extends egret.CustomFilter {
|
||||
// private static blur_frag = "precision mediump float;\n" +
|
||||
// "uniform vec2 blur;\n" +
|
||||
// "uniform sampler2D uSampler;\n" +
|
||||
// "varying vec2 vTextureCoord;\n" +
|
||||
// "uniform vec2 uTextureSize;\n" +
|
||||
// "void main()\n" +
|
||||
// "{\n " +
|
||||
// "const int sampleRadius = 5;\n" +
|
||||
// "const int samples = sampleRadius * 2 + 1;\n" +
|
||||
// "vec2 blurUv = blur / uTextureSize;\n" +
|
||||
// "vec4 color = vec4(0, 0, 0, 0);\n" +
|
||||
// "vec2 uv = vec2(0.0, 0.0);\n" +
|
||||
// "blurUv /= float(sampleRadius);\n" +
|
||||
|
||||
private static blur_frag = "precision mediump float;\n" +
|
||||
"uniform sampler2D uSampler;\n" +
|
||||
"uniform float screenWidth;\n" +
|
||||
"uniform float screenHeight;\n" +
|
||||
// "for (int i = -sampleRadius; i <= sampleRadius; i++) {\n" +
|
||||
// "uv.x = vTextureCoord.x + float(i) * blurUv.x;\n" +
|
||||
// "uv.y = vTextureCoord.y + float(i) * blurUv.y;\n" +
|
||||
// "color += texture2D(uSampler, uv);\n" +
|
||||
// "}\n" +
|
||||
|
||||
"float normpdf(in float x, in float sigma)\n" +
|
||||
"{\n" +
|
||||
"return 0.39894*exp(-0.5*x*x/(sigma*sigma))/sigma;\n" +
|
||||
"}\n" +
|
||||
// "color /= float(samples);\n" +
|
||||
// "gl_FragColor = color;\n" +
|
||||
// "}";
|
||||
|
||||
"void main()\n" +
|
||||
"{\n" +
|
||||
"vec3 c = texture2D(uSampler, gl_FragCoord.xy / vec2(screenWidth, screenHeight).xy).rgb;\n" +
|
||||
private static blur_frag = "precision mediump float;\n" +
|
||||
"uniform sampler2D uSampler;\n" +
|
||||
"uniform float screenWidth;\n" +
|
||||
"uniform float screenHeight;\n" +
|
||||
|
||||
"const int mSize = 11;\n" +
|
||||
"const int kSize = (mSize - 1)/2;\n" +
|
||||
"float kernel[mSize];\n" +
|
||||
"vec3 final_colour = vec3(0.0);\n" +
|
||||
"float normpdf(in float x, in float sigma)\n" +
|
||||
"{\n" +
|
||||
"return 0.39894*exp(-0.5*x*x/(sigma*sigma))/sigma;\n" +
|
||||
"}\n" +
|
||||
|
||||
"float sigma = 7.0;\n" +
|
||||
"float z = 0.0;\n" +
|
||||
"for (int j = 0; j <= kSize; ++j)\n" +
|
||||
"{\n" +
|
||||
"kernel[kSize+j] = kernel[kSize-j] = normpdf(float(j),sigma);\n" +
|
||||
"}\n" +
|
||||
|
||||
"for (int j = 0; j < mSize; ++j)\n" +
|
||||
"{\n" +
|
||||
"z += kernel[j];\n" +
|
||||
"}\n" +
|
||||
"void main()\n" +
|
||||
"{\n" +
|
||||
"vec3 c = texture2D(uSampler, gl_FragCoord.xy / vec2(screenWidth, screenHeight).xy).rgb;\n" +
|
||||
|
||||
"for (int i = -kSize; i <= kSize; ++i)\n" +
|
||||
"{\n" +
|
||||
"for (int j = -kSize; j <= kSize; ++j)\n" +
|
||||
"{\n" +
|
||||
"final_colour += kernel[kSize+j]*kernel[kSize+i]*texture2D(uSampler, (gl_FragCoord.xy+vec2(float(i),float(j))) / vec2(screenWidth, screenHeight).xy).rgb;\n" +
|
||||
"}\n}\n" +
|
||||
"gl_FragColor = vec4(final_colour/(z*z), 1.0);\n" +
|
||||
"}";
|
||||
"const int mSize = 11;\n" +
|
||||
"const int kSize = (mSize - 1)/2;\n" +
|
||||
"float kernel[mSize];\n" +
|
||||
"vec3 final_colour = vec3(0.0);\n" +
|
||||
|
||||
constructor(){
|
||||
super(PostProcessor.default_vert, GaussianBlurEffect.blur_frag,{
|
||||
screenWidth: SceneManager.stage.stageWidth,
|
||||
screenHeight: SceneManager.stage.stageHeight
|
||||
});
|
||||
"float sigma = 7.0;\n" +
|
||||
"float z = 0.0;\n" +
|
||||
"for (int j = 0; j <= kSize; ++j)\n" +
|
||||
"{\n" +
|
||||
"kernel[kSize+j] = kernel[kSize-j] = normpdf(float(j),sigma);\n" +
|
||||
"}\n" +
|
||||
|
||||
"for (int j = 0; j < mSize; ++j)\n" +
|
||||
"{\n" +
|
||||
"z += kernel[j];\n" +
|
||||
"}\n" +
|
||||
|
||||
"for (int i = -kSize; i <= kSize; ++i)\n" +
|
||||
"{\n" +
|
||||
"for (int j = -kSize; j <= kSize; ++j)\n" +
|
||||
"{\n" +
|
||||
"final_colour += kernel[kSize+j]*kernel[kSize+i]*texture2D(uSampler, (gl_FragCoord.xy+vec2(float(i),float(j))) / vec2(screenWidth, screenHeight).xy).rgb;\n" +
|
||||
"}\n}\n" +
|
||||
"gl_FragColor = vec4(final_colour/(z*z), 1.0);\n" +
|
||||
"}";
|
||||
|
||||
constructor(){
|
||||
super(PostProcessor.default_vert, GaussianBlurEffect.blur_frag,{
|
||||
screenWidth: SceneManager.stage.stageWidth,
|
||||
screenHeight: SceneManager.stage.stageHeight
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
class PolygonLightEffect extends egret.CustomFilter {
|
||||
private static vertSrc = "attribute vec2 aVertexPosition;\n" +
|
||||
"attribute vec2 aTextureCoord;\n" +
|
||||
module es {
|
||||
export class PolygonLightEffect extends egret.CustomFilter {
|
||||
private static vertSrc = "attribute vec2 aVertexPosition;\n" +
|
||||
"attribute vec2 aTextureCoord;\n" +
|
||||
|
||||
"uniform vec2 projectionVector;\n" +
|
||||
"uniform vec2 projectionVector;\n" +
|
||||
|
||||
"varying vec2 vTextureCoord;\n" +
|
||||
"varying vec2 vTextureCoord;\n" +
|
||||
|
||||
"const vec2 center = vec2(-1.0, 1.0);\n" +
|
||||
"const vec2 center = vec2(-1.0, 1.0);\n" +
|
||||
|
||||
"void main(void) {\n" +
|
||||
" gl_Position = vec4( (aVertexPosition / projectionVector) + center , 0.0, 1.0);\n" +
|
||||
" vTextureCoord = aTextureCoord;\n" +
|
||||
"}";
|
||||
private static fragmentSrc = "precision lowp float;\n" +
|
||||
"varying vec2 vTextureCoord;\n" +
|
||||
"uniform sampler2D uSampler;\n" +
|
||||
"void main(void) {\n" +
|
||||
" gl_Position = vec4( (aVertexPosition / projectionVector) + center , 0.0, 1.0);\n" +
|
||||
" vTextureCoord = aTextureCoord;\n" +
|
||||
"}";
|
||||
private static fragmentSrc = "precision lowp float;\n" +
|
||||
"varying vec2 vTextureCoord;\n" +
|
||||
"uniform sampler2D uSampler;\n" +
|
||||
|
||||
"#define SAMPLE_COUNT 15\n" +
|
||||
"#define SAMPLE_COUNT 15\n" +
|
||||
|
||||
"uniform vec2 _sampleOffsets[SAMPLE_COUNT];\n" +
|
||||
"uniform float _sampleWeights[SAMPLE_COUNT];\n" +
|
||||
"uniform vec2 _sampleOffsets[SAMPLE_COUNT];\n" +
|
||||
"uniform float _sampleWeights[SAMPLE_COUNT];\n" +
|
||||
|
||||
"void main(void) {\n" +
|
||||
"vec4 c = vec4(0, 0, 0, 0);\n" +
|
||||
"for( int i = 0; i < SAMPLE_COUNT; i++ )\n" +
|
||||
" c += texture2D( uSampler, vTextureCoord + _sampleOffsets[i] ) * _sampleWeights[i];\n" +
|
||||
"gl_FragColor = c;\n" +
|
||||
"}";
|
||||
"void main(void) {\n" +
|
||||
"vec4 c = vec4(0, 0, 0, 0);\n" +
|
||||
"for( int i = 0; i < SAMPLE_COUNT; i++ )\n" +
|
||||
" c += texture2D( uSampler, vTextureCoord + _sampleOffsets[i] ) * _sampleWeights[i];\n" +
|
||||
"gl_FragColor = c;\n" +
|
||||
"}";
|
||||
|
||||
constructor(){
|
||||
super(PolygonLightEffect.vertSrc, PolygonLightEffect.fragmentSrc);
|
||||
constructor(){
|
||||
super(PolygonLightEffect.vertSrc, PolygonLightEffect.fragmentSrc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
class GraphicsCapabilities extends egret.Capabilities {
|
||||
module es {
|
||||
export class GraphicsCapabilities extends egret.Capabilities {
|
||||
|
||||
public initialize(device: GraphicsDevice){
|
||||
this.platformInitialize(device);
|
||||
}
|
||||
|
||||
private platformInitialize(device: GraphicsDevice){
|
||||
let capabilities = this;
|
||||
capabilities["isMobile"] = true;
|
||||
|
||||
let systemInfo = wx.getSystemInfoSync();
|
||||
let systemStr = systemInfo.system.toLowerCase();
|
||||
if (systemStr.indexOf("ios") > -1){
|
||||
capabilities["os"] = "iOS";
|
||||
} else if(systemStr.indexOf("android") > -1){
|
||||
capabilities["os"] = "Android";
|
||||
public initialize(device: GraphicsDevice){
|
||||
this.platformInitialize(device);
|
||||
}
|
||||
|
||||
let language = systemInfo.language;
|
||||
if (language.indexOf('zh') > -1){
|
||||
language = "zh-CN";
|
||||
} else {
|
||||
language = "en-US";
|
||||
private platformInitialize(device: GraphicsDevice){
|
||||
let capabilities = this;
|
||||
capabilities["isMobile"] = true;
|
||||
|
||||
let systemInfo = wx.getSystemInfoSync();
|
||||
let systemStr = systemInfo.system.toLowerCase();
|
||||
if (systemStr.indexOf("ios") > -1){
|
||||
capabilities["os"] = "iOS";
|
||||
} else if(systemStr.indexOf("android") > -1){
|
||||
capabilities["os"] = "Android";
|
||||
}
|
||||
|
||||
let language = systemInfo.language;
|
||||
if (language.indexOf('zh') > -1){
|
||||
language = "zh-CN";
|
||||
} else {
|
||||
language = "en-US";
|
||||
}
|
||||
capabilities["language"] = language;
|
||||
}
|
||||
capabilities["language"] = language;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
class GraphicsDevice {
|
||||
private viewport: Viewport;
|
||||
module es {
|
||||
export class GraphicsDevice {
|
||||
private viewport: Viewport;
|
||||
|
||||
public graphicsCapabilities: GraphicsCapabilities;
|
||||
public graphicsCapabilities: GraphicsCapabilities;
|
||||
|
||||
constructor(){
|
||||
this.graphicsCapabilities = new GraphicsCapabilities();
|
||||
this.graphicsCapabilities.initialize(this);
|
||||
constructor(){
|
||||
this.graphicsCapabilities = new GraphicsCapabilities();
|
||||
this.graphicsCapabilities.initialize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +1,60 @@
|
||||
class PostProcessor {
|
||||
public enabled: boolean;
|
||||
public effect: egret.Filter;
|
||||
public scene: Scene;
|
||||
public shape: egret.Shape;
|
||||
module es {
|
||||
export class PostProcessor {
|
||||
public enabled: boolean;
|
||||
public effect: egret.Filter;
|
||||
public scene: Scene;
|
||||
public shape: egret.Shape;
|
||||
|
||||
public static default_vert = "attribute vec2 aVertexPosition;\n" +
|
||||
"attribute vec2 aTextureCoord;\n" +
|
||||
"attribute vec2 aColor;\n" +
|
||||
|
||||
"uniform vec2 projectionVector;\n" +
|
||||
//"uniform vec2 offsetVector;\n" +
|
||||
public static default_vert = "attribute vec2 aVertexPosition;\n" +
|
||||
"attribute vec2 aTextureCoord;\n" +
|
||||
"attribute vec2 aColor;\n" +
|
||||
|
||||
"varying vec2 vTextureCoord;\n" +
|
||||
"varying vec4 vColor;\n" +
|
||||
"uniform vec2 projectionVector;\n" +
|
||||
//"uniform vec2 offsetVector;\n" +
|
||||
|
||||
"const vec2 center = vec2(-1.0, 1.0);\n" +
|
||||
"varying vec2 vTextureCoord;\n" +
|
||||
"varying vec4 vColor;\n" +
|
||||
|
||||
"void main(void) {\n" +
|
||||
"gl_Position = vec4( (aVertexPosition / projectionVector) + center , 0.0, 1.0);\n" +
|
||||
"vTextureCoord = aTextureCoord;\n" +
|
||||
"vColor = vec4(aColor.x, aColor.x, aColor.x, aColor.x);\n" +
|
||||
"}";
|
||||
"const vec2 center = vec2(-1.0, 1.0);\n" +
|
||||
|
||||
constructor(effect: egret.Filter = null){
|
||||
this.enabled = true;
|
||||
this.effect = effect;
|
||||
}
|
||||
"void main(void) {\n" +
|
||||
"gl_Position = vec4( (aVertexPosition / projectionVector) + center , 0.0, 1.0);\n" +
|
||||
"vTextureCoord = aTextureCoord;\n" +
|
||||
"vColor = vec4(aColor.x, aColor.x, aColor.x, aColor.x);\n" +
|
||||
"}";
|
||||
|
||||
public onAddedToScene(scene: Scene){
|
||||
this.scene = scene;
|
||||
this.shape = new egret.Shape();
|
||||
this.shape.graphics.beginFill(0xFFFFFF, 1);
|
||||
this.shape.graphics.drawRect(0, 0, SceneManager.stage.stageWidth, SceneManager.stage.stageHeight);
|
||||
this.shape.graphics.endFill();
|
||||
scene.addChild(this.shape);
|
||||
}
|
||||
|
||||
public process(){
|
||||
this.drawFullscreenQuad();
|
||||
}
|
||||
|
||||
public onSceneBackBufferSizeChanged(newWidth: number, newHeight: number){}
|
||||
|
||||
protected drawFullscreenQuad(){
|
||||
this.scene.filters = [this.effect];
|
||||
// this.shape.filters = [this.effect];
|
||||
}
|
||||
|
||||
public unload(){
|
||||
if (this.effect){
|
||||
this.effect = null;
|
||||
constructor(effect: egret.Filter = null){
|
||||
this.enabled = true;
|
||||
this.effect = effect;
|
||||
}
|
||||
|
||||
this.scene.removeChild(this.shape);
|
||||
this.scene = null;
|
||||
public onAddedToScene(scene: Scene){
|
||||
this.scene = scene;
|
||||
this.shape = new egret.Shape();
|
||||
this.shape.graphics.beginFill(0xFFFFFF, 1);
|
||||
this.shape.graphics.drawRect(0, 0, SceneManager.stage.stageWidth, SceneManager.stage.stageHeight);
|
||||
this.shape.graphics.endFill();
|
||||
scene.addChild(this.shape);
|
||||
}
|
||||
|
||||
public process(){
|
||||
this.drawFullscreenQuad();
|
||||
}
|
||||
|
||||
public onSceneBackBufferSizeChanged(newWidth: number, newHeight: number){}
|
||||
|
||||
protected drawFullscreenQuad(){
|
||||
this.scene.filters = [this.effect];
|
||||
// this.shape.filters = [this.effect];
|
||||
}
|
||||
|
||||
public unload(){
|
||||
if (this.effect){
|
||||
this.effect = null;
|
||||
}
|
||||
|
||||
this.scene.removeChild(this.shape);
|
||||
this.scene = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
class GaussianBlurPostProcessor extends PostProcessor {
|
||||
public onAddedToScene(scene: Scene){
|
||||
super.onAddedToScene(scene);
|
||||
this.effect = new GaussianBlurEffect();
|
||||
module es {
|
||||
export class GaussianBlurPostProcessor extends PostProcessor {
|
||||
public onAddedToScene(scene: Scene){
|
||||
super.onAddedToScene(scene);
|
||||
this.effect = new GaussianBlurEffect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
///<reference path="./Renderer.ts" />
|
||||
class DefaultRenderer extends Renderer {
|
||||
public render(scene: Scene) {
|
||||
let cam = this.camera ? this.camera : scene.camera;
|
||||
this.beginRender(cam);
|
||||
module es {
|
||||
export class DefaultRenderer extends Renderer {
|
||||
public render(scene: Scene) {
|
||||
let cam = this.camera ? this.camera : scene.camera;
|
||||
this.beginRender(cam);
|
||||
|
||||
for (let i = 0; i < scene.renderableComponents.count; i++){
|
||||
let renderable = scene.renderableComponents.buffer[i];
|
||||
if (renderable.enabled && renderable.isVisibleFromCamera(cam))
|
||||
this.renderAfterStateCheck(renderable, cam);
|
||||
for (let i = 0; i < scene.renderableComponents.count; i++){
|
||||
let renderable = scene.renderableComponents.buffer[i];
|
||||
if (renderable.enabled && renderable.isVisibleFromCamera(cam))
|
||||
this.renderAfterStateCheck(renderable, cam);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,48 @@
|
||||
class PolyLight extends RenderableComponent {
|
||||
public power: number;
|
||||
protected _radius: number;
|
||||
private _lightEffect;
|
||||
private _indices: number[] = [];
|
||||
module es {
|
||||
export class PolyLight extends RenderableComponent {
|
||||
public power: number;
|
||||
protected _radius: number;
|
||||
private _lightEffect;
|
||||
private _indices: number[] = [];
|
||||
|
||||
public get radius(){
|
||||
return this._radius;
|
||||
}
|
||||
public set radius(value: number){
|
||||
this.setRadius(value);
|
||||
}
|
||||
public get radius(){
|
||||
return this._radius;
|
||||
}
|
||||
public set radius(value: number){
|
||||
this.setRadius(value);
|
||||
}
|
||||
|
||||
constructor(radius: number, color: number, power: number){
|
||||
super();
|
||||
constructor(radius: number, color: number, power: number){
|
||||
super();
|
||||
|
||||
this.radius = radius;
|
||||
this.power = power;
|
||||
this.color = color;
|
||||
this.computeTriangleIndices();
|
||||
}
|
||||
this.radius = radius;
|
||||
this.power = power;
|
||||
this.color = color;
|
||||
this.computeTriangleIndices();
|
||||
}
|
||||
|
||||
private computeTriangleIndices(totalTris: number = 20){
|
||||
this._indices.length = 0;
|
||||
private computeTriangleIndices(totalTris: number = 20){
|
||||
this._indices.length = 0;
|
||||
|
||||
for (let i = 0; i < totalTris; i += 2){
|
||||
this._indices.push(0);
|
||||
this._indices.push(i + 2);
|
||||
this._indices.push(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public setRadius(radius: number){
|
||||
if (radius != this._radius){
|
||||
this._radius = radius;
|
||||
this._areBoundsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
public render(camera: Camera) {
|
||||
}
|
||||
|
||||
public reset(){
|
||||
|
||||
for (let i = 0; i < totalTris; i += 2){
|
||||
this._indices.push(0);
|
||||
this._indices.push(i + 2);
|
||||
this._indices.push(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public setRadius(radius: number){
|
||||
if (radius != this._radius){
|
||||
this._radius = radius;
|
||||
this._areBoundsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
public render(camera: Camera) {
|
||||
}
|
||||
|
||||
public reset(){
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,40 @@
|
||||
/**
|
||||
* 渲染器被添加到场景中并处理所有对RenderableComponent的实际调用
|
||||
*/
|
||||
abstract class Renderer {
|
||||
/**
|
||||
* 渲染器用于渲染的摄像机(实际上是用于剔除的变换矩阵和边界)
|
||||
* 不是必须的
|
||||
* Renderer子类可以选择调用beginRender时使用的摄像头
|
||||
*/
|
||||
public camera: Camera;
|
||||
|
||||
module es {
|
||||
/**
|
||||
* 当渲染器被添加到场景时调用
|
||||
* @param scene
|
||||
* 渲染器被添加到场景中并处理所有对RenderableComponent的实际调用
|
||||
*/
|
||||
public onAddedToScene(scene: Scene){}
|
||||
export abstract class Renderer {
|
||||
/**
|
||||
* 渲染器用于渲染的摄像机(实际上是用于剔除的变换矩阵和边界)
|
||||
* 不是必须的
|
||||
* Renderer子类可以选择调用beginRender时使用的摄像头
|
||||
*/
|
||||
public camera: Camera;
|
||||
|
||||
protected beginRender(cam: Camera){
|
||||
|
||||
/**
|
||||
* 当渲染器被添加到场景时调用
|
||||
* @param scene
|
||||
*/
|
||||
public onAddedToScene(scene: Scene){}
|
||||
|
||||
protected beginRender(cam: Camera){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param scene
|
||||
*/
|
||||
public abstract render(scene: Scene);
|
||||
|
||||
public unload(){ }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param renderable
|
||||
* @param cam
|
||||
*/
|
||||
protected renderAfterStateCheck(renderable: IRenderable, cam: Camera){
|
||||
renderable.render(cam);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param scene
|
||||
*/
|
||||
public abstract render(scene: Scene);
|
||||
|
||||
public unload(){ }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param renderable
|
||||
* @param cam
|
||||
*/
|
||||
protected renderAfterStateCheck(renderable: IRenderable, cam: Camera){
|
||||
renderable.render(cam);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/**
|
||||
* 渲染器使用自己的不移动的摄像机进行渲染。
|
||||
*/
|
||||
class ScreenSpaceRenderer extends Renderer {
|
||||
public render(scene: Scene) {
|
||||
module es {
|
||||
/**
|
||||
* 渲染器使用自己的不移动的摄像机进行渲染。
|
||||
*/
|
||||
export class ScreenSpaceRenderer extends Renderer {
|
||||
public render(scene: Scene) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,40 @@
|
||||
///<reference path="./SceneTransition.ts"/>
|
||||
class FadeTransition extends SceneTransition {
|
||||
public fadeToColor: number = 0x000000;
|
||||
public fadeOutDuration = 0.4;
|
||||
public fadeEaseType: Function = egret.Ease.quadInOut;
|
||||
public delayBeforeFadeInDuration = 0.1;
|
||||
private _mask: egret.Shape;
|
||||
private _alpha: number = 0;
|
||||
module es {
|
||||
export class FadeTransition extends SceneTransition {
|
||||
public fadeToColor: number = 0x000000;
|
||||
public fadeOutDuration = 0.4;
|
||||
public fadeEaseType: Function = egret.Ease.quadInOut;
|
||||
public delayBeforeFadeInDuration = 0.1;
|
||||
private _mask: egret.Shape;
|
||||
private _alpha: number = 0;
|
||||
|
||||
constructor(sceneLoadAction: Function) {
|
||||
super(sceneLoadAction);
|
||||
this._mask = new egret.Shape();
|
||||
}
|
||||
constructor(sceneLoadAction: Function) {
|
||||
super(sceneLoadAction);
|
||||
this._mask = new egret.Shape();
|
||||
}
|
||||
|
||||
public async onBeginTransition() {
|
||||
this._mask.graphics.beginFill(this.fadeToColor, 1);
|
||||
this._mask.graphics.drawRect(0, 0, SceneManager.stage.stageWidth, SceneManager.stage.stageHeight);
|
||||
this._mask.graphics.endFill();
|
||||
SceneManager.stage.addChild(this._mask);
|
||||
public async onBeginTransition() {
|
||||
this._mask.graphics.beginFill(this.fadeToColor, 1);
|
||||
this._mask.graphics.drawRect(0, 0, SceneManager.stage.stageWidth, SceneManager.stage.stageHeight);
|
||||
this._mask.graphics.endFill();
|
||||
SceneManager.stage.addChild(this._mask);
|
||||
|
||||
egret.Tween.get(this).to({ _alpha: 1}, this.fadeOutDuration * 1000, this.fadeEaseType)
|
||||
.call(async () => {
|
||||
await this.loadNextScene();
|
||||
}).wait(this.delayBeforeFadeInDuration).call(() => {
|
||||
egret.Tween.get(this).to({ _alpha: 1}, this.fadeOutDuration * 1000, this.fadeEaseType)
|
||||
.call(async () => {
|
||||
await this.loadNextScene();
|
||||
}).wait(this.delayBeforeFadeInDuration).call(() => {
|
||||
egret.Tween.get(this).to({ _alpha: 0 }, this.fadeOutDuration * 1000, this.fadeEaseType).call(() => {
|
||||
this.transitionComplete();
|
||||
SceneManager.stage.removeChild(this._mask);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public render(){
|
||||
this._mask.graphics.clear();
|
||||
this._mask.graphics.beginFill(this.fadeToColor, this._alpha);
|
||||
this._mask.graphics.drawRect(0, 0, SceneManager.stage.stageWidth, SceneManager.stage.stageHeight);
|
||||
this._mask.graphics.endFill();
|
||||
public render(){
|
||||
this._mask.graphics.clear();
|
||||
this._mask.graphics.beginFill(this.fadeToColor, this._alpha);
|
||||
this._mask.graphics.drawRect(0, 0, SceneManager.stage.stageWidth, SceneManager.stage.stageHeight);
|
||||
this._mask.graphics.endFill();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,75 +1,77 @@
|
||||
/**
|
||||
* SceneTransition用于从一个场景过渡到另一个场景或在一个有效果的场景中过渡
|
||||
*/
|
||||
abstract class SceneTransition {
|
||||
private _hasPreviousSceneRender: boolean;
|
||||
/** 是否加载新场景的标志 */
|
||||
public loadsNewScene: boolean;
|
||||
/**
|
||||
* 将此用于两个部分的转换。例如,淡出会先淡出到黑色,然后当isNewSceneLoaded为true,它会淡出。
|
||||
* 对于场景过渡,isNewSceneLoaded应该在中点设置为true,这就标识一个新的场景被加载了。
|
||||
module es {
|
||||
/**
|
||||
* SceneTransition用于从一个场景过渡到另一个场景或在一个有效果的场景中过渡
|
||||
*/
|
||||
public isNewSceneLoaded: boolean;
|
||||
/** 返回新加载场景的函数 */
|
||||
protected sceneLoadAction: Function;
|
||||
/** 在loadNextScene执行时调用。这在进行场景间过渡时很有用,这样你就知道什么时候可以更多地使用相机或者重置任何实体 */
|
||||
public onScreenObscured: Function;
|
||||
/** 当转换完成执行时调用,以便可以调用其他工作,比如启动另一个转换。 */
|
||||
public onTransitionCompleted: Function;
|
||||
export abstract class SceneTransition {
|
||||
private _hasPreviousSceneRender: boolean;
|
||||
/** 是否加载新场景的标志 */
|
||||
public loadsNewScene: boolean;
|
||||
/**
|
||||
* 将此用于两个部分的转换。例如,淡出会先淡出到黑色,然后当isNewSceneLoaded为true,它会淡出。
|
||||
* 对于场景过渡,isNewSceneLoaded应该在中点设置为true,这就标识一个新的场景被加载了。
|
||||
*/
|
||||
public isNewSceneLoaded: boolean;
|
||||
/** 返回新加载场景的函数 */
|
||||
protected sceneLoadAction: Function;
|
||||
/** 在loadNextScene执行时调用。这在进行场景间过渡时很有用,这样你就知道什么时候可以更多地使用相机或者重置任何实体 */
|
||||
public onScreenObscured: Function;
|
||||
/** 当转换完成执行时调用,以便可以调用其他工作,比如启动另一个转换。 */
|
||||
public onTransitionCompleted: Function;
|
||||
|
||||
public get hasPreviousSceneRender(){
|
||||
if (!this._hasPreviousSceneRender){
|
||||
this._hasPreviousSceneRender = true;
|
||||
return false;
|
||||
public get hasPreviousSceneRender(){
|
||||
if (!this._hasPreviousSceneRender){
|
||||
this._hasPreviousSceneRender = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constructor(sceneLoadAction: Function) {
|
||||
this.sceneLoadAction = sceneLoadAction;
|
||||
this.loadsNewScene = sceneLoadAction != null;
|
||||
}
|
||||
|
||||
public preRender() { }
|
||||
|
||||
public render() {
|
||||
|
||||
}
|
||||
|
||||
public async onBeginTransition() {
|
||||
await this.loadNextScene();
|
||||
this.transitionComplete();
|
||||
}
|
||||
|
||||
protected transitionComplete() {
|
||||
SceneManager.sceneTransition = null;
|
||||
|
||||
if (this.onTransitionCompleted) {
|
||||
this.onTransitionCompleted();
|
||||
constructor(sceneLoadAction: Function) {
|
||||
this.sceneLoadAction = sceneLoadAction;
|
||||
this.loadsNewScene = sceneLoadAction != null;
|
||||
}
|
||||
}
|
||||
|
||||
protected async loadNextScene() {
|
||||
if (this.onScreenObscured)
|
||||
this.onScreenObscured();
|
||||
public preRender() { }
|
||||
|
||||
if (!this.loadsNewScene) {
|
||||
public render() {
|
||||
|
||||
}
|
||||
|
||||
public async onBeginTransition() {
|
||||
await this.loadNextScene();
|
||||
this.transitionComplete();
|
||||
}
|
||||
|
||||
protected transitionComplete() {
|
||||
SceneManager.sceneTransition = null;
|
||||
|
||||
if (this.onTransitionCompleted) {
|
||||
this.onTransitionCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
protected async loadNextScene() {
|
||||
if (this.onScreenObscured)
|
||||
this.onScreenObscured();
|
||||
|
||||
if (!this.loadsNewScene) {
|
||||
this.isNewSceneLoaded = true;
|
||||
}
|
||||
|
||||
SceneManager.scene = await this.sceneLoadAction();
|
||||
this.isNewSceneLoaded = true;
|
||||
}
|
||||
|
||||
SceneManager.scene = await this.sceneLoadAction();
|
||||
this.isNewSceneLoaded = true;
|
||||
}
|
||||
public tickEffectProgressProperty(filter: egret.CustomFilter, duration: number, easeType: Function, reverseDirection = false): Promise<boolean>{
|
||||
return new Promise((resolve)=>{
|
||||
let start = reverseDirection ? 1 : 0;
|
||||
let end = reverseDirection ? 0 : 1;
|
||||
|
||||
public tickEffectProgressProperty(filter: egret.CustomFilter, duration: number, easeType: Function, reverseDirection = false): Promise<boolean>{
|
||||
return new Promise((resolve)=>{
|
||||
let start = reverseDirection ? 1 : 0;
|
||||
let end = reverseDirection ? 0 : 1;
|
||||
|
||||
egret.Tween.get(filter.uniforms).set({_progress: start}).to({_progress: end}, duration * 1000, easeType).call(()=>{
|
||||
resolve();
|
||||
egret.Tween.get(filter.uniforms).set({_progress: start}).to({_progress: end}, duration * 1000, easeType).call(()=>{
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +1,67 @@
|
||||
class WindTransition extends SceneTransition {
|
||||
private _mask: egret.Shape;
|
||||
private _windEffect: egret.CustomFilter;
|
||||
module es {
|
||||
export class WindTransition extends SceneTransition {
|
||||
private _mask: egret.Shape;
|
||||
private _windEffect: egret.CustomFilter;
|
||||
|
||||
public duration = 1;
|
||||
public set windSegments(value: number) {
|
||||
this._windEffect.uniforms._windSegments = value;
|
||||
public duration = 1;
|
||||
public set windSegments(value: number) {
|
||||
this._windEffect.uniforms._windSegments = value;
|
||||
}
|
||||
public set size(value: number) {
|
||||
this._windEffect.uniforms._size = value;
|
||||
}
|
||||
public easeType = egret.Ease.quadOut;
|
||||
constructor(sceneLoadAction: Function) {
|
||||
super(sceneLoadAction);
|
||||
|
||||
let vertexSrc = "attribute vec2 aVertexPosition;\n" +
|
||||
"attribute vec2 aTextureCoord;\n" +
|
||||
|
||||
"uniform vec2 projectionVector;\n" +
|
||||
|
||||
"varying vec2 vTextureCoord;\n" +
|
||||
|
||||
"const vec2 center = vec2(-1.0, 1.0);\n" +
|
||||
|
||||
"void main(void) {\n" +
|
||||
" gl_Position = vec4( (aVertexPosition / projectionVector) + center , 0.0, 1.0);\n" +
|
||||
" vTextureCoord = aTextureCoord;\n" +
|
||||
"}";
|
||||
let fragmentSrc = "precision lowp float;\n" +
|
||||
"varying vec2 vTextureCoord;\n" +
|
||||
"uniform sampler2D uSampler;\n" +
|
||||
"uniform float _progress;\n" +
|
||||
"uniform float _size;\n" +
|
||||
"uniform float _windSegments;\n" +
|
||||
|
||||
"void main(void) {\n" +
|
||||
"vec2 co = floor(vec2(0.0, vTextureCoord.y * _windSegments));\n" +
|
||||
"float x = sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453;\n" +
|
||||
"float r = x - floor(x);\n" +
|
||||
"float m = smoothstep(0.0, -_size, vTextureCoord.x * (1.0 - _size) + _size * r - (_progress * (1.0 + _size)));\n" +
|
||||
"vec4 fg = texture2D(uSampler, vTextureCoord);\n" +
|
||||
"gl_FragColor = mix(fg, vec4(0, 0, 0, 0), m);\n" +
|
||||
"}";
|
||||
|
||||
this._windEffect = new egret.CustomFilter(vertexSrc, fragmentSrc, {
|
||||
_progress: 0,
|
||||
_size: 0.3,
|
||||
_windSegments: 100
|
||||
});
|
||||
|
||||
this._mask = new egret.Shape();
|
||||
this._mask.graphics.beginFill(0xFFFFFF, 1);
|
||||
this._mask.graphics.drawRect(0, 0, SceneManager.stage.stageWidth, SceneManager.stage.stageHeight);
|
||||
this._mask.graphics.endFill();
|
||||
this._mask.filters = [this._windEffect];
|
||||
SceneManager.stage.addChild(this._mask);
|
||||
}
|
||||
|
||||
public async onBeginTransition() {
|
||||
this.loadNextScene();
|
||||
await this.tickEffectProgressProperty(this._windEffect, this.duration, this.easeType);
|
||||
this.transitionComplete();
|
||||
SceneManager.stage.removeChild(this._mask);
|
||||
}
|
||||
}
|
||||
public set size(value: number) {
|
||||
this._windEffect.uniforms._size = value;
|
||||
}
|
||||
public easeType = egret.Ease.quadOut;
|
||||
constructor(sceneLoadAction: Function) {
|
||||
super(sceneLoadAction);
|
||||
|
||||
let vertexSrc = "attribute vec2 aVertexPosition;\n" +
|
||||
"attribute vec2 aTextureCoord;\n" +
|
||||
|
||||
"uniform vec2 projectionVector;\n" +
|
||||
|
||||
"varying vec2 vTextureCoord;\n" +
|
||||
|
||||
"const vec2 center = vec2(-1.0, 1.0);\n" +
|
||||
|
||||
"void main(void) {\n" +
|
||||
" gl_Position = vec4( (aVertexPosition / projectionVector) + center , 0.0, 1.0);\n" +
|
||||
" vTextureCoord = aTextureCoord;\n" +
|
||||
"}";
|
||||
let fragmentSrc = "precision lowp float;\n" +
|
||||
"varying vec2 vTextureCoord;\n" +
|
||||
"uniform sampler2D uSampler;\n" +
|
||||
"uniform float _progress;\n" +
|
||||
"uniform float _size;\n" +
|
||||
"uniform float _windSegments;\n" +
|
||||
|
||||
"void main(void) {\n" +
|
||||
"vec2 co = floor(vec2(0.0, vTextureCoord.y * _windSegments));\n" +
|
||||
"float x = sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453;\n" +
|
||||
"float r = x - floor(x);\n" +
|
||||
"float m = smoothstep(0.0, -_size, vTextureCoord.x * (1.0 - _size) + _size * r - (_progress * (1.0 + _size)));\n" +
|
||||
"vec4 fg = texture2D(uSampler, vTextureCoord);\n" +
|
||||
"gl_FragColor = mix(fg, vec4(0, 0, 0, 0), m);\n" +
|
||||
"}";
|
||||
|
||||
this._windEffect = new egret.CustomFilter(vertexSrc, fragmentSrc, {
|
||||
_progress: 0,
|
||||
_size: 0.3,
|
||||
_windSegments: 100
|
||||
});
|
||||
|
||||
this._mask = new egret.Shape();
|
||||
this._mask.graphics.beginFill(0xFFFFFF, 1);
|
||||
this._mask.graphics.drawRect(0, 0, SceneManager.stage.stageWidth, SceneManager.stage.stageHeight);
|
||||
this._mask.graphics.endFill();
|
||||
this._mask.filters = [this._windEffect];
|
||||
SceneManager.stage.addChild(this._mask);
|
||||
}
|
||||
|
||||
public async onBeginTransition() {
|
||||
this.loadNextScene();
|
||||
await this.tickEffectProgressProperty(this._windEffect, this.duration, this.easeType);
|
||||
this.transitionComplete();
|
||||
SceneManager.stage.removeChild(this._mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
class Viewport {
|
||||
private _x: number;
|
||||
private _y: number;
|
||||
private _width: number;
|
||||
private _height: number;
|
||||
private _minDepth: number;
|
||||
private _maxDepth: number;
|
||||
|
||||
public get aspectRatio(){
|
||||
if ((this._height != 0) && (this._width != 0))
|
||||
return (this._width / this._height);
|
||||
return 0;
|
||||
}
|
||||
module es {
|
||||
export class Viewport {
|
||||
private _x: number;
|
||||
private _y: number;
|
||||
private _width: number;
|
||||
private _height: number;
|
||||
private _minDepth: number;
|
||||
private _maxDepth: number;
|
||||
|
||||
public get bounds(){
|
||||
return new Rectangle(this._x, this._y, this._width, this._height);
|
||||
}
|
||||
public set bounds(value: Rectangle){
|
||||
this._x = value.x;
|
||||
this._y = value.y;
|
||||
this._width = value.width;
|
||||
this._height = value.height;
|
||||
}
|
||||
public get aspectRatio(){
|
||||
if ((this._height != 0) && (this._width != 0))
|
||||
return (this._width / this._height);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public get bounds(){
|
||||
return new Rectangle(this._x, this._y, this._width, this._height);
|
||||
}
|
||||
public set bounds(value: Rectangle){
|
||||
this._x = value.x;
|
||||
this._y = value.y;
|
||||
this._width = value.width;
|
||||
this._height = value.height;
|
||||
}
|
||||
|
||||
constructor(x: number, y: number, width: number, height: number){
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._minDepth = 0;
|
||||
this._maxDepth = 1;
|
||||
}
|
||||
|
||||
constructor(x: number, y: number, width: number, height: number){
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._minDepth = 0;
|
||||
this._maxDepth = 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,121 +1,123 @@
|
||||
/** 贝塞尔帮助类 */
|
||||
class Bezier {
|
||||
/**
|
||||
* 二次贝塞尔曲线
|
||||
* @param p0
|
||||
* @param p1
|
||||
* @param p2
|
||||
* @param t
|
||||
*/
|
||||
public static getPoint(p0: Vector2, p1: Vector2, p2: Vector2, t: number): Vector2 {
|
||||
t = MathHelper.clamp01(t);
|
||||
let oneMinusT = 1 - t;
|
||||
return Vector2.add(Vector2.add(Vector2.multiply(new Vector2(oneMinusT * oneMinusT), p0),
|
||||
Vector2.multiply(new Vector2(2 * oneMinusT * t), p1)), Vector2.multiply(new Vector2(t * t), p2));
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到二次贝塞尔函数的一阶导数
|
||||
* @param p0
|
||||
* @param p1
|
||||
* @param p2
|
||||
* @param t
|
||||
*/
|
||||
public static getFirstDerivative(p0: Vector2, p1: Vector2, p2: Vector2, t: number) {
|
||||
return Vector2.add(Vector2.multiply(new Vector2(2 * (1 - t)), Vector2.subtract(p1, p0)),
|
||||
Vector2.multiply(new Vector2(2 * t), Vector2.subtract(p2, p1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到一个三次贝塞尔函数的一阶导数
|
||||
* @param start
|
||||
* @param firstControlPoint
|
||||
* @param secondControlPoint
|
||||
* @param end
|
||||
* @param t
|
||||
*/
|
||||
public static getFirstDerivativeThree(start: Vector2, firstControlPoint: Vector2, secondControlPoint: Vector2,
|
||||
end: Vector2, t: number) {
|
||||
t = MathHelper.clamp01(t);
|
||||
let oneMunusT = 1 - t;
|
||||
return Vector2.add(Vector2.add(Vector2.multiply(new Vector2(3 * oneMunusT * oneMunusT), Vector2.subtract(firstControlPoint, start)),
|
||||
Vector2.multiply(new Vector2(6 * oneMunusT * t), Vector2.subtract(secondControlPoint, firstControlPoint))),
|
||||
Vector2.multiply(new Vector2(3 * t * t), Vector2.subtract(end, secondControlPoint)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算一个三次贝塞尔
|
||||
* @param start
|
||||
* @param firstControlPoint
|
||||
* @param secondControlPoint
|
||||
* @param end
|
||||
* @param t
|
||||
*/
|
||||
public static getPointThree(start: Vector2, firstControlPoint: Vector2, secondControlPoint: Vector2,
|
||||
end: Vector2, t: number) {
|
||||
t = MathHelper.clamp01(t);
|
||||
let oneMunusT = 1 - t;
|
||||
return Vector2.add(Vector2.add(Vector2.add(Vector2.multiply(new Vector2(oneMunusT * oneMunusT * oneMunusT), start),
|
||||
Vector2.multiply(new Vector2(3 * oneMunusT * oneMunusT * t), firstControlPoint)),
|
||||
Vector2.multiply(new Vector2(3 * oneMunusT * t * t), secondControlPoint)),
|
||||
Vector2.multiply(new Vector2(t * t * t), end));
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归地细分bezier曲线,直到满足距离校正
|
||||
* 在这种算法中,平面切片的点要比曲面切片少。返回完成后应返回到ListPool的合并列表。
|
||||
* @param start
|
||||
* @param firstCtrlPoint
|
||||
* @param secondCtrlPoint
|
||||
* @param end
|
||||
* @param distanceTolerance
|
||||
*/
|
||||
public static getOptimizedDrawingPoints(start: Vector2, firstCtrlPoint: Vector2, secondCtrlPoint: Vector2,
|
||||
end: Vector2, distanceTolerance: number = 1) {
|
||||
let points = ListPool.obtain<Vector2>();
|
||||
points.push(start);
|
||||
this.recursiveGetOptimizedDrawingPoints(start, firstCtrlPoint, secondCtrlPoint, end, points, distanceTolerance);
|
||||
points.push(end);
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归地细分bezier曲线,直到满足距离校正。在这种算法中,平面切片的点要比曲面切片少。
|
||||
* @param start
|
||||
* @param firstCtrlPoint
|
||||
* @param secondCtrlPoint
|
||||
* @param end
|
||||
* @param points
|
||||
* @param distanceTolerance
|
||||
*/
|
||||
private static recursiveGetOptimizedDrawingPoints(start: Vector2, firstCtrlPoint: Vector2, secondCtrlPoint: Vector2,
|
||||
end: Vector2, points: Vector2[], distanceTolerance: number) {
|
||||
// 计算线段的所有中点
|
||||
let pt12 = Vector2.divide(Vector2.add(start, firstCtrlPoint), new Vector2(2));
|
||||
let pt23 = Vector2.divide(Vector2.add(firstCtrlPoint, secondCtrlPoint), new Vector2(2));
|
||||
let pt34 = Vector2.divide(Vector2.add(secondCtrlPoint, end), new Vector2(2));
|
||||
|
||||
// 计算新半直线的中点
|
||||
let pt123 = Vector2.divide(Vector2.add(pt12, pt23), new Vector2(2));
|
||||
let pt234 = Vector2.divide(Vector2.add(pt23, pt34), new Vector2(2));
|
||||
|
||||
// 最后再细分最后两个中点。如果我们满足我们的距离公差,这将是我们使用的最后一点。
|
||||
let pt1234 = Vector2.divide(Vector2.add(pt123, pt234), new Vector2(2));
|
||||
|
||||
// 试着用一条直线来近似整个三次曲线
|
||||
let deltaLine = Vector2.subtract(end, start);
|
||||
|
||||
let d2 = Math.abs(((firstCtrlPoint.x, end.x) * deltaLine.y - (firstCtrlPoint.y - end.y) * deltaLine.x));
|
||||
let d3 = Math.abs(((secondCtrlPoint.x - end.x) * deltaLine.y - (secondCtrlPoint.y - end.y) * deltaLine.x));
|
||||
|
||||
if ((d2 + d3) * (d2 + d3) < distanceTolerance * (deltaLine.x * deltaLine.x + deltaLine.y * deltaLine.y)) {
|
||||
points.push(pt1234);
|
||||
return;
|
||||
module es {
|
||||
/** 贝塞尔帮助类 */
|
||||
export class Bezier {
|
||||
/**
|
||||
* 二次贝塞尔曲线
|
||||
* @param p0
|
||||
* @param p1
|
||||
* @param p2
|
||||
* @param t
|
||||
*/
|
||||
public static getPoint(p0: Vector2, p1: Vector2, p2: Vector2, t: number): Vector2 {
|
||||
t = MathHelper.clamp01(t);
|
||||
let oneMinusT = 1 - t;
|
||||
return Vector2.add(Vector2.add(Vector2.multiply(new Vector2(oneMinusT * oneMinusT), p0),
|
||||
Vector2.multiply(new Vector2(2 * oneMinusT * t), p1)), Vector2.multiply(new Vector2(t * t), p2));
|
||||
}
|
||||
|
||||
// 继续细分
|
||||
this.recursiveGetOptimizedDrawingPoints(start, pt12, pt123, pt1234, points, distanceTolerance);
|
||||
this.recursiveGetOptimizedDrawingPoints(pt1234, pt234, pt34, end, points, distanceTolerance);
|
||||
/**
|
||||
* 得到二次贝塞尔函数的一阶导数
|
||||
* @param p0
|
||||
* @param p1
|
||||
* @param p2
|
||||
* @param t
|
||||
*/
|
||||
public static getFirstDerivative(p0: Vector2, p1: Vector2, p2: Vector2, t: number) {
|
||||
return Vector2.add(Vector2.multiply(new Vector2(2 * (1 - t)), Vector2.subtract(p1, p0)),
|
||||
Vector2.multiply(new Vector2(2 * t), Vector2.subtract(p2, p1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到一个三次贝塞尔函数的一阶导数
|
||||
* @param start
|
||||
* @param firstControlPoint
|
||||
* @param secondControlPoint
|
||||
* @param end
|
||||
* @param t
|
||||
*/
|
||||
public static getFirstDerivativeThree(start: Vector2, firstControlPoint: Vector2, secondControlPoint: Vector2,
|
||||
end: Vector2, t: number) {
|
||||
t = MathHelper.clamp01(t);
|
||||
let oneMunusT = 1 - t;
|
||||
return Vector2.add(Vector2.add(Vector2.multiply(new Vector2(3 * oneMunusT * oneMunusT), Vector2.subtract(firstControlPoint, start)),
|
||||
Vector2.multiply(new Vector2(6 * oneMunusT * t), Vector2.subtract(secondControlPoint, firstControlPoint))),
|
||||
Vector2.multiply(new Vector2(3 * t * t), Vector2.subtract(end, secondControlPoint)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算一个三次贝塞尔
|
||||
* @param start
|
||||
* @param firstControlPoint
|
||||
* @param secondControlPoint
|
||||
* @param end
|
||||
* @param t
|
||||
*/
|
||||
public static getPointThree(start: Vector2, firstControlPoint: Vector2, secondControlPoint: Vector2,
|
||||
end: Vector2, t: number) {
|
||||
t = MathHelper.clamp01(t);
|
||||
let oneMunusT = 1 - t;
|
||||
return Vector2.add(Vector2.add(Vector2.add(Vector2.multiply(new Vector2(oneMunusT * oneMunusT * oneMunusT), start),
|
||||
Vector2.multiply(new Vector2(3 * oneMunusT * oneMunusT * t), firstControlPoint)),
|
||||
Vector2.multiply(new Vector2(3 * oneMunusT * t * t), secondControlPoint)),
|
||||
Vector2.multiply(new Vector2(t * t * t), end));
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归地细分bezier曲线,直到满足距离校正
|
||||
* 在这种算法中,平面切片的点要比曲面切片少。返回完成后应返回到ListPool的合并列表。
|
||||
* @param start
|
||||
* @param firstCtrlPoint
|
||||
* @param secondCtrlPoint
|
||||
* @param end
|
||||
* @param distanceTolerance
|
||||
*/
|
||||
public static getOptimizedDrawingPoints(start: Vector2, firstCtrlPoint: Vector2, secondCtrlPoint: Vector2,
|
||||
end: Vector2, distanceTolerance: number = 1) {
|
||||
let points = ListPool.obtain<Vector2>();
|
||||
points.push(start);
|
||||
this.recursiveGetOptimizedDrawingPoints(start, firstCtrlPoint, secondCtrlPoint, end, points, distanceTolerance);
|
||||
points.push(end);
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归地细分bezier曲线,直到满足距离校正。在这种算法中,平面切片的点要比曲面切片少。
|
||||
* @param start
|
||||
* @param firstCtrlPoint
|
||||
* @param secondCtrlPoint
|
||||
* @param end
|
||||
* @param points
|
||||
* @param distanceTolerance
|
||||
*/
|
||||
private static recursiveGetOptimizedDrawingPoints(start: Vector2, firstCtrlPoint: Vector2, secondCtrlPoint: Vector2,
|
||||
end: Vector2, points: Vector2[], distanceTolerance: number) {
|
||||
// 计算线段的所有中点
|
||||
let pt12 = Vector2.divide(Vector2.add(start, firstCtrlPoint), new Vector2(2));
|
||||
let pt23 = Vector2.divide(Vector2.add(firstCtrlPoint, secondCtrlPoint), new Vector2(2));
|
||||
let pt34 = Vector2.divide(Vector2.add(secondCtrlPoint, end), new Vector2(2));
|
||||
|
||||
// 计算新半直线的中点
|
||||
let pt123 = Vector2.divide(Vector2.add(pt12, pt23), new Vector2(2));
|
||||
let pt234 = Vector2.divide(Vector2.add(pt23, pt34), new Vector2(2));
|
||||
|
||||
// 最后再细分最后两个中点。如果我们满足我们的距离公差,这将是我们使用的最后一点。
|
||||
let pt1234 = Vector2.divide(Vector2.add(pt123, pt234), new Vector2(2));
|
||||
|
||||
// 试着用一条直线来近似整个三次曲线
|
||||
let deltaLine = Vector2.subtract(end, start);
|
||||
|
||||
let d2 = Math.abs(((firstCtrlPoint.x, end.x) * deltaLine.y - (firstCtrlPoint.y - end.y) * deltaLine.x));
|
||||
let d3 = Math.abs(((secondCtrlPoint.x - end.x) * deltaLine.y - (secondCtrlPoint.y - end.y) * deltaLine.x));
|
||||
|
||||
if ((d2 + d3) * (d2 + d3) < distanceTolerance * (deltaLine.x * deltaLine.x + deltaLine.y * deltaLine.y)) {
|
||||
points.push(pt1234);
|
||||
return;
|
||||
}
|
||||
|
||||
// 继续细分
|
||||
this.recursiveGetOptimizedDrawingPoints(start, pt12, pt123, pt1234, points, distanceTolerance);
|
||||
this.recursiveGetOptimizedDrawingPoints(pt1234, pt234, pt34, end, points, distanceTolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +1,64 @@
|
||||
/**
|
||||
* 帮助处理位掩码的实用程序类
|
||||
* 除了isFlagSet之外,所有方法都期望flag参数是一个非移位的标志
|
||||
* 允许您使用普通的(0、1、2、3等)来设置/取消您的标记
|
||||
*/
|
||||
class Flags {
|
||||
module es {
|
||||
/**
|
||||
* 检查位标志是否已在数值中设置
|
||||
* 检查期望标志是否已经移位
|
||||
* @param self
|
||||
* @param flag
|
||||
* 帮助处理位掩码的实用程序类
|
||||
* 除了isFlagSet之外,所有方法都期望flag参数是一个非移位的标志
|
||||
* 允许您使用普通的(0、1、2、3等)来设置/取消您的标记
|
||||
*/
|
||||
public static isFlagSet(self: number, flag: number): boolean{
|
||||
return (self & flag) != 0;
|
||||
}
|
||||
export class Flags {
|
||||
/**
|
||||
* 检查位标志是否已在数值中设置
|
||||
* 检查期望标志是否已经移位
|
||||
* @param self
|
||||
* @param flag
|
||||
*/
|
||||
public static isFlagSet(self: number, flag: number): boolean{
|
||||
return (self & flag) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查位标志是否在数值中设置
|
||||
* @param self
|
||||
* @param flag
|
||||
*/
|
||||
public static isUnshiftedFlagSet(self: number, flag: number): boolean{
|
||||
flag = 1 << flag;
|
||||
return (self & flag) != 0;
|
||||
}
|
||||
/**
|
||||
* 检查位标志是否在数值中设置
|
||||
* @param self
|
||||
* @param flag
|
||||
*/
|
||||
public static isUnshiftedFlagSet(self: number, flag: number): boolean{
|
||||
flag = 1 << flag;
|
||||
return (self & flag) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置数值标志位,移除所有已经设置的标志
|
||||
* @param self
|
||||
* @param flag
|
||||
*/
|
||||
public static setFlagExclusive(self: number, flag: number){
|
||||
return 1 << flag;
|
||||
}
|
||||
/**
|
||||
* 设置数值标志位,移除所有已经设置的标志
|
||||
* @param self
|
||||
* @param flag
|
||||
*/
|
||||
public static setFlagExclusive(self: number, flag: number){
|
||||
return 1 << flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置标志位
|
||||
* @param self
|
||||
* @param flag
|
||||
*/
|
||||
public static setFlag(self: number, flag: number){
|
||||
return (self | 1 << flag);
|
||||
}
|
||||
/**
|
||||
* 设置标志位
|
||||
* @param self
|
||||
* @param flag
|
||||
*/
|
||||
public static setFlag(self: number, flag: number){
|
||||
return (self | 1 << flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消标志位
|
||||
* @param self
|
||||
* @param flag
|
||||
*/
|
||||
public static unsetFlag(self: number, flag: number){
|
||||
flag = 1 << flag;
|
||||
return (self & (~flag));
|
||||
}
|
||||
/**
|
||||
* 取消标志位
|
||||
* @param self
|
||||
* @param flag
|
||||
*/
|
||||
public static unsetFlag(self: number, flag: number){
|
||||
flag = 1 << flag;
|
||||
return (self & (~flag));
|
||||
}
|
||||
|
||||
/**
|
||||
* 反转数值集合位
|
||||
* @param self
|
||||
*/
|
||||
public static invertFlags(self: number){
|
||||
return ~self;
|
||||
/**
|
||||
* 反转数值集合位
|
||||
* @param self
|
||||
*/
|
||||
public static invertFlags(self: number){
|
||||
return ~self;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,78 +1,80 @@
|
||||
class MathHelper {
|
||||
public static readonly Epsilon: number = 0.00001;
|
||||
public static readonly Rad2Deg = 57.29578;
|
||||
public static readonly Deg2Rad = 0.0174532924;
|
||||
module es {
|
||||
export class MathHelper {
|
||||
public static readonly Epsilon: number = 0.00001;
|
||||
public static readonly Rad2Deg = 57.29578;
|
||||
public static readonly Deg2Rad = 0.0174532924;
|
||||
|
||||
/**
|
||||
* 将弧度转换成角度。
|
||||
* @param radians 用弧度表示的角
|
||||
*/
|
||||
public static toDegrees(radians: number){
|
||||
return radians * 57.295779513082320876798154814105;
|
||||
/**
|
||||
* 将弧度转换成角度。
|
||||
* @param radians 用弧度表示的角
|
||||
*/
|
||||
public static toDegrees(radians: number){
|
||||
return radians * 57.295779513082320876798154814105;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将角度转换为弧度
|
||||
* @param degrees
|
||||
*/
|
||||
public static toRadians(degrees: number){
|
||||
return degrees * 0.017453292519943295769236907684886;
|
||||
}
|
||||
|
||||
/**
|
||||
* mapps值(在leftMin - leftMax范围内)到rightMin - rightMax范围内的值
|
||||
* @param value
|
||||
* @param leftMin
|
||||
* @param leftMax
|
||||
* @param rightMin
|
||||
* @param rightMax
|
||||
*/
|
||||
public static map(value: number, leftMin: number, leftMax: number, rightMin: number, rightMax: number){
|
||||
return rightMin + (value - leftMin) * (rightMax - rightMin) / (leftMax - leftMin);
|
||||
}
|
||||
|
||||
public static lerp(value1: number, value2: number, amount: number){
|
||||
return value1 + (value2 - value1) * amount;
|
||||
}
|
||||
|
||||
public static clamp(value: number, min: number, max: number){
|
||||
if (value < min)
|
||||
return min;
|
||||
|
||||
if (value > max)
|
||||
return max;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static pointOnCirlce(circleCenter: Vector2, radius: number, angleInDegrees: number){
|
||||
let radians = MathHelper.toRadians(angleInDegrees);
|
||||
return new Vector2(Math.cos(radians) * radians + circleCenter.x, Math.sin(radians) * radians + circleCenter.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果值为偶数,返回true
|
||||
* @param value
|
||||
*/
|
||||
public static isEven(value: number){
|
||||
return value % 2 == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数值限定在0-1之间
|
||||
* @param value
|
||||
*/
|
||||
public static clamp01(value: number){
|
||||
if (value < 0)
|
||||
return 0;
|
||||
|
||||
if (value > 1)
|
||||
return 1;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static angleBetweenVectors(from: Vector2, to: Vector2){
|
||||
return Math.atan2(to.y - from.y, to.x - from.x);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将角度转换为弧度
|
||||
* @param degrees
|
||||
*/
|
||||
public static toRadians(degrees: number){
|
||||
return degrees * 0.017453292519943295769236907684886;
|
||||
}
|
||||
|
||||
/**
|
||||
* mapps值(在leftMin - leftMax范围内)到rightMin - rightMax范围内的值
|
||||
* @param value
|
||||
* @param leftMin
|
||||
* @param leftMax
|
||||
* @param rightMin
|
||||
* @param rightMax
|
||||
*/
|
||||
public static map(value: number, leftMin: number, leftMax: number, rightMin: number, rightMax: number){
|
||||
return rightMin + (value - leftMin) * (rightMax - rightMin) / (leftMax - leftMin);
|
||||
}
|
||||
|
||||
public static lerp(value1: number, value2: number, amount: number){
|
||||
return value1 + (value2 - value1) * amount;
|
||||
}
|
||||
|
||||
public static clamp(value: number, min: number, max: number){
|
||||
if (value < min)
|
||||
return min;
|
||||
|
||||
if (value > max)
|
||||
return max;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static pointOnCirlce(circleCenter: Vector2, radius: number, angleInDegrees: number){
|
||||
let radians = MathHelper.toRadians(angleInDegrees);
|
||||
return new Vector2(Math.cos(radians) * radians + circleCenter.x, Math.sin(radians) * radians + circleCenter.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果值为偶数,返回true
|
||||
* @param value
|
||||
*/
|
||||
public static isEven(value: number){
|
||||
return value % 2 == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数值限定在0-1之间
|
||||
* @param value
|
||||
*/
|
||||
public static clamp01(value: number){
|
||||
if (value < 0)
|
||||
return 0;
|
||||
|
||||
if (value > 1)
|
||||
return 1;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static angleBetweenVectors(from: Vector2, to: Vector2){
|
||||
return Math.atan2(to.y - from.y, to.x - from.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,217 +1,219 @@
|
||||
/**
|
||||
* 表示右手3 * 3的浮点矩阵,可以存储平移、缩放和旋转信息。
|
||||
*/
|
||||
class Matrix2D {
|
||||
public m11: number = 0;
|
||||
public m12: number = 0;
|
||||
|
||||
public m21: number = 0;
|
||||
public m22: number = 0;
|
||||
|
||||
public m31: number = 0;
|
||||
public m32: number = 0;
|
||||
|
||||
private static _identity: Matrix2D = new Matrix2D(1, 0, 0, 1, 0, 0);
|
||||
|
||||
module es {
|
||||
/**
|
||||
* 单位矩阵
|
||||
* 表示右手3 * 3的浮点矩阵,可以存储平移、缩放和旋转信息。
|
||||
*/
|
||||
public static get identity(){
|
||||
return Matrix2D._identity;
|
||||
export class Matrix2D {
|
||||
public m11: number = 0;
|
||||
public m12: number = 0;
|
||||
|
||||
public m21: number = 0;
|
||||
public m22: number = 0;
|
||||
|
||||
public m31: number = 0;
|
||||
public m32: number = 0;
|
||||
|
||||
private static _identity: Matrix2D = new Matrix2D(1, 0, 0, 1, 0, 0);
|
||||
|
||||
/**
|
||||
* 单位矩阵
|
||||
*/
|
||||
public static get identity(){
|
||||
return Matrix2D._identity;
|
||||
}
|
||||
|
||||
constructor(m11?: number, m12?: number, m21?: number, m22?: number, m31?: number, m32?: number){
|
||||
this.m11 = m11 ? m11 : 1;
|
||||
this.m12 = m12 ? m12 : 0;
|
||||
|
||||
this.m21 = m21 ? m21 : 0;
|
||||
this.m22 = m22 ? m22 : 1;
|
||||
|
||||
this.m31 = m31 ? m31 : 0;
|
||||
this.m32 = m32 ? m32 : 0;
|
||||
}
|
||||
|
||||
/** 存储在这个矩阵中的位置 */
|
||||
public get translation(){
|
||||
return new Vector2(this.m31, this.m32);
|
||||
}
|
||||
|
||||
public set translation(value: Vector2){
|
||||
this.m31 = value.x;
|
||||
this.m32 = value.y;
|
||||
}
|
||||
|
||||
/** 以弧度表示的旋转存储在这个矩阵中 */
|
||||
public get rotation(){
|
||||
return Math.atan2(this.m21, this.m11);
|
||||
}
|
||||
|
||||
public set rotation(value: number){
|
||||
let val1 = Math.cos(value);
|
||||
let val2 = Math.sin(value);
|
||||
|
||||
this.m11 = val1;
|
||||
this.m12 = val2;
|
||||
this.m21 = -val2;
|
||||
this.m22 = val1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以度为单位的旋转存储在这个矩阵中
|
||||
*/
|
||||
public get rotationDegrees(){
|
||||
return MathHelper.toDegrees(this.rotation);
|
||||
}
|
||||
|
||||
public set rotationDegrees(value: number){
|
||||
this.rotation = MathHelper.toRadians(value);
|
||||
}
|
||||
|
||||
public get scale(){
|
||||
return new Vector2(this.m11, this.m22);
|
||||
}
|
||||
|
||||
public set scale(value: Vector2){
|
||||
this.m11 = value.x;
|
||||
this.m12 = value.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的matrix, 它包含两个矩阵的和。
|
||||
* @param matrix1
|
||||
* @param matrix2
|
||||
*/
|
||||
public static add(matrix1: Matrix2D, matrix2: Matrix2D){
|
||||
matrix1.m11 += matrix2.m11;
|
||||
matrix1.m12 += matrix2.m12;
|
||||
|
||||
matrix1.m21 += matrix2.m21;
|
||||
matrix1.m22 += matrix2.m22;
|
||||
|
||||
matrix1.m31 += matrix2.m31;
|
||||
matrix1.m32 += matrix2.m32;
|
||||
|
||||
return matrix1;
|
||||
}
|
||||
|
||||
public static divide(matrix1: Matrix2D, matrix2: Matrix2D){
|
||||
matrix1.m11 /= matrix2.m11;
|
||||
matrix1.m12 /= matrix2.m12;
|
||||
|
||||
matrix1.m21 /= matrix2.m21;
|
||||
matrix1.m22 /= matrix2.m22;
|
||||
|
||||
matrix1.m31 /= matrix2.m31;
|
||||
matrix1.m32 /= matrix2.m32;
|
||||
|
||||
return matrix1;
|
||||
}
|
||||
|
||||
public static multiply(matrix1: Matrix2D, matrix2: Matrix2D){
|
||||
let result = new Matrix2D();
|
||||
|
||||
let m11 = ( matrix1.m11 * matrix2.m11 ) + ( matrix1.m12 * matrix2.m21 );
|
||||
let m12 = ( matrix1.m11 * matrix2.m12 ) + ( matrix1.m12 * matrix2.m22 );
|
||||
|
||||
let m21 = ( matrix1.m21 * matrix2.m11 ) + ( matrix1.m22 * matrix2.m21 );
|
||||
let m22 = ( matrix1.m21 * matrix2.m12 ) + ( matrix1.m22 * matrix2.m22 );
|
||||
|
||||
let m31 = ( matrix1.m31 * matrix2.m11 ) + ( matrix1.m32 * matrix2.m21 ) + matrix2.m31;
|
||||
let m32 = ( matrix1.m31 * matrix2.m12 ) + ( matrix1.m32 * matrix2.m22 ) + matrix2.m32;
|
||||
|
||||
result.m11 = m11;
|
||||
result.m12 = m12;
|
||||
|
||||
result.m21 = m21;
|
||||
result.m22 = m22;
|
||||
|
||||
result.m31 = m31;
|
||||
result.m32 = m32;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static multiplyTranslation(matrix: Matrix2D, x: number, y: number){
|
||||
let trans = Matrix2D.createTranslation(x, y);
|
||||
return Matrix2D.multiply(matrix, trans);
|
||||
}
|
||||
|
||||
public determinant(){
|
||||
return this.m11 * this.m22 - this.m12 * this.m21;
|
||||
}
|
||||
|
||||
public static invert(matrix: Matrix2D, result: Matrix2D = new Matrix2D()){
|
||||
let det = 1 / matrix.determinant();
|
||||
|
||||
result.m11 = matrix.m22 * det;
|
||||
result.m12 = -matrix.m12 * det;
|
||||
|
||||
result.m21 = -matrix.m21 * det;
|
||||
result.m22 = matrix.m11 * det;
|
||||
|
||||
result.m31 = (matrix.m32 * matrix.m21 - matrix.m31 * matrix.m22) * det;
|
||||
result.m32 = -(matrix.m32 * matrix.m11 - matrix.m31 * matrix.m12) * det;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的tranlation
|
||||
* @param xPosition
|
||||
* @param yPosition
|
||||
*/
|
||||
public static createTranslation(xPosition: number, yPosition: number){
|
||||
let result = new Matrix2D();
|
||||
|
||||
result.m11 = 1;
|
||||
result.m12 = 0;
|
||||
|
||||
result.m21 = 0;
|
||||
result.m22 = 1;
|
||||
|
||||
result.m31 = xPosition;
|
||||
result.m32 = yPosition;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据position 创建一个translation
|
||||
* @param position
|
||||
*/
|
||||
public static createTranslationVector(position: Vector2){
|
||||
return this.createTranslation(position.x, position.y);
|
||||
}
|
||||
|
||||
public static createRotation(radians: number, result?: Matrix2D){
|
||||
result = new Matrix2D();
|
||||
|
||||
let val1 = Math.cos(radians);
|
||||
let val2 = Math.sin(radians);
|
||||
|
||||
result.m11 = val1;
|
||||
result.m12 = val2;
|
||||
result.m21 = -val2;
|
||||
result.m22 = val1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static createScale(xScale: number, yScale: number, result: Matrix2D = new Matrix2D()){
|
||||
result.m11 = xScale;
|
||||
result.m12 = 0;
|
||||
|
||||
result.m21 = 0;
|
||||
result.m22 = yScale;
|
||||
|
||||
result.m31 = 0;
|
||||
result.m32 = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public toEgretMatrix(): egret.Matrix{
|
||||
let matrix = new egret.Matrix(this.m11, this.m12, this.m21, this.m22, this.m31, this.m32);
|
||||
return matrix;
|
||||
}
|
||||
}
|
||||
|
||||
constructor(m11?: number, m12?: number, m21?: number, m22?: number, m31?: number, m32?: number){
|
||||
this.m11 = m11 ? m11 : 1;
|
||||
this.m12 = m12 ? m12 : 0;
|
||||
|
||||
this.m21 = m21 ? m21 : 0;
|
||||
this.m22 = m22 ? m22 : 1;
|
||||
|
||||
this.m31 = m31 ? m31 : 0;
|
||||
this.m32 = m32 ? m32 : 0;
|
||||
}
|
||||
|
||||
/** 存储在这个矩阵中的位置 */
|
||||
public get translation(){
|
||||
return new Vector2(this.m31, this.m32);
|
||||
}
|
||||
|
||||
public set translation(value: Vector2){
|
||||
this.m31 = value.x;
|
||||
this.m32 = value.y;
|
||||
}
|
||||
|
||||
/** 以弧度表示的旋转存储在这个矩阵中 */
|
||||
public get rotation(){
|
||||
return Math.atan2(this.m21, this.m11);
|
||||
}
|
||||
|
||||
public set rotation(value: number){
|
||||
let val1 = Math.cos(value);
|
||||
let val2 = Math.sin(value);
|
||||
|
||||
this.m11 = val1;
|
||||
this.m12 = val2;
|
||||
this.m21 = -val2;
|
||||
this.m22 = val1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以度为单位的旋转存储在这个矩阵中
|
||||
*/
|
||||
public get rotationDegrees(){
|
||||
return MathHelper.toDegrees(this.rotation);
|
||||
}
|
||||
|
||||
public set rotationDegrees(value: number){
|
||||
this.rotation = MathHelper.toRadians(value);
|
||||
}
|
||||
|
||||
public get scale(){
|
||||
return new Vector2(this.m11, this.m22);
|
||||
}
|
||||
|
||||
public set scale(value: Vector2){
|
||||
this.m11 = value.x;
|
||||
this.m12 = value.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的matrix, 它包含两个矩阵的和。
|
||||
* @param matrix1
|
||||
* @param matrix2
|
||||
*/
|
||||
public static add(matrix1: Matrix2D, matrix2: Matrix2D){
|
||||
matrix1.m11 += matrix2.m11;
|
||||
matrix1.m12 += matrix2.m12;
|
||||
|
||||
matrix1.m21 += matrix2.m21;
|
||||
matrix1.m22 += matrix2.m22;
|
||||
|
||||
matrix1.m31 += matrix2.m31;
|
||||
matrix1.m32 += matrix2.m32;
|
||||
|
||||
return matrix1;
|
||||
}
|
||||
|
||||
public static divide(matrix1: Matrix2D, matrix2: Matrix2D){
|
||||
matrix1.m11 /= matrix2.m11;
|
||||
matrix1.m12 /= matrix2.m12;
|
||||
|
||||
matrix1.m21 /= matrix2.m21;
|
||||
matrix1.m22 /= matrix2.m22;
|
||||
|
||||
matrix1.m31 /= matrix2.m31;
|
||||
matrix1.m32 /= matrix2.m32;
|
||||
|
||||
return matrix1;
|
||||
}
|
||||
|
||||
public static multiply(matrix1: Matrix2D, matrix2: Matrix2D){
|
||||
let result = new Matrix2D();
|
||||
|
||||
let m11 = ( matrix1.m11 * matrix2.m11 ) + ( matrix1.m12 * matrix2.m21 );
|
||||
let m12 = ( matrix1.m11 * matrix2.m12 ) + ( matrix1.m12 * matrix2.m22 );
|
||||
|
||||
let m21 = ( matrix1.m21 * matrix2.m11 ) + ( matrix1.m22 * matrix2.m21 );
|
||||
let m22 = ( matrix1.m21 * matrix2.m12 ) + ( matrix1.m22 * matrix2.m22 );
|
||||
|
||||
let m31 = ( matrix1.m31 * matrix2.m11 ) + ( matrix1.m32 * matrix2.m21 ) + matrix2.m31;
|
||||
let m32 = ( matrix1.m31 * matrix2.m12 ) + ( matrix1.m32 * matrix2.m22 ) + matrix2.m32;
|
||||
|
||||
result.m11 = m11;
|
||||
result.m12 = m12;
|
||||
|
||||
result.m21 = m21;
|
||||
result.m22 = m22;
|
||||
|
||||
result.m31 = m31;
|
||||
result.m32 = m32;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static multiplyTranslation(matrix: Matrix2D, x: number, y: number){
|
||||
let trans = Matrix2D.createTranslation(x, y);
|
||||
return Matrix2D.multiply(matrix, trans);
|
||||
}
|
||||
|
||||
public determinant(){
|
||||
return this.m11 * this.m22 - this.m12 * this.m21;
|
||||
}
|
||||
|
||||
public static invert(matrix: Matrix2D, result: Matrix2D = new Matrix2D()){
|
||||
let det = 1 / matrix.determinant();
|
||||
|
||||
result.m11 = matrix.m22 * det;
|
||||
result.m12 = -matrix.m12 * det;
|
||||
|
||||
result.m21 = -matrix.m21 * det;
|
||||
result.m22 = matrix.m11 * det;
|
||||
|
||||
result.m31 = (matrix.m32 * matrix.m21 - matrix.m31 * matrix.m22) * det;
|
||||
result.m32 = -(matrix.m32 * matrix.m11 - matrix.m31 * matrix.m12) * det;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的tranlation
|
||||
* @param xPosition
|
||||
* @param yPosition
|
||||
*/
|
||||
public static createTranslation(xPosition: number, yPosition: number){
|
||||
let result = new Matrix2D();
|
||||
|
||||
result.m11 = 1;
|
||||
result.m12 = 0;
|
||||
|
||||
result.m21 = 0;
|
||||
result.m22 = 1;
|
||||
|
||||
result.m31 = xPosition;
|
||||
result.m32 = yPosition;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据position 创建一个translation
|
||||
* @param position
|
||||
*/
|
||||
public static createTranslationVector(position: Vector2){
|
||||
return this.createTranslation(position.x, position.y);
|
||||
}
|
||||
|
||||
public static createRotation(radians: number, result?: Matrix2D){
|
||||
result = new Matrix2D();
|
||||
|
||||
let val1 = Math.cos(radians);
|
||||
let val2 = Math.sin(radians);
|
||||
|
||||
result.m11 = val1;
|
||||
result.m12 = val2;
|
||||
result.m21 = -val2;
|
||||
result.m22 = val1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static createScale(xScale: number, yScale: number, result: Matrix2D = new Matrix2D()){
|
||||
result.m11 = xScale;
|
||||
result.m12 = 0;
|
||||
|
||||
result.m21 = 0;
|
||||
result.m22 = yScale;
|
||||
|
||||
result.m31 = 0;
|
||||
result.m32 = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public toEgretMatrix(): egret.Matrix{
|
||||
let matrix = new egret.Matrix(this.m11, this.m12, this.m21, this.m22, this.m31, this.m32);
|
||||
return matrix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,183 +1,185 @@
|
||||
class Rectangle extends egret.Rectangle {
|
||||
/**
|
||||
* 获取矩形的最大点,即右下角
|
||||
*/
|
||||
public get max() {
|
||||
return new Vector2(this.right, this.bottom);
|
||||
}
|
||||
module es {
|
||||
export class Rectangle extends egret.Rectangle {
|
||||
/**
|
||||
* 获取矩形的最大点,即右下角
|
||||
*/
|
||||
public get max() {
|
||||
return new Vector2(this.right, this.bottom);
|
||||
}
|
||||
|
||||
/** 中心点坐标 */
|
||||
public get center() {
|
||||
return new Vector2(this.x + (this.width / 2), this.y + (this.height / 2));
|
||||
}
|
||||
/** 中心点坐标 */
|
||||
public get center() {
|
||||
return new Vector2(this.x + (this.width / 2), this.y + (this.height / 2));
|
||||
}
|
||||
|
||||
/** 左上角的坐标 */
|
||||
public get location() {
|
||||
return new Vector2(this.x, this.y);
|
||||
}
|
||||
/** 左上角的坐标 */
|
||||
public set location(value: Vector2) {
|
||||
this.x = value.x;
|
||||
this.y = value.y;
|
||||
}
|
||||
/** 左上角的坐标 */
|
||||
public get location() {
|
||||
return new Vector2(this.x, this.y);
|
||||
}
|
||||
/** 左上角的坐标 */
|
||||
public set location(value: Vector2) {
|
||||
this.x = value.x;
|
||||
this.y = value.y;
|
||||
}
|
||||
|
||||
public get size() {
|
||||
return new Vector2(this.width, this.height);
|
||||
}
|
||||
public get size() {
|
||||
return new Vector2(this.width, this.height);
|
||||
}
|
||||
|
||||
public set size(value: Vector2) {
|
||||
this.width = value.x;
|
||||
this.height = value.y;
|
||||
}
|
||||
public set size(value: Vector2) {
|
||||
this.width = value.x;
|
||||
this.height = value.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否与另一个矩形相交
|
||||
* @param value
|
||||
*/
|
||||
public intersects(value: egret.Rectangle) {
|
||||
return value.left < this.right &&
|
||||
this.left < value.right &&
|
||||
value.top < this.bottom &&
|
||||
this.top < value.bottom;
|
||||
}
|
||||
/**
|
||||
* 是否与另一个矩形相交
|
||||
* @param value
|
||||
*/
|
||||
public intersects(value: egret.Rectangle) {
|
||||
return value.left < this.right &&
|
||||
this.left < value.right &&
|
||||
value.top < this.bottom &&
|
||||
this.top < value.bottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所提供的矩形是否在此矩形的边界内
|
||||
* @param value
|
||||
*/
|
||||
public containsRect(value: Rectangle) {
|
||||
return ((((this.x <= value.x) && (value.x < (this.x + this.width))) &&
|
||||
(this.y <= value.y)) &&
|
||||
(value.y < (this.y + this.height)));
|
||||
}
|
||||
/**
|
||||
* 获取所提供的矩形是否在此矩形的边界内
|
||||
* @param value
|
||||
*/
|
||||
public containsRect(value: Rectangle) {
|
||||
return ((((this.x <= value.x) && (value.x < (this.x + this.width))) &&
|
||||
(this.y <= value.y)) &&
|
||||
(value.y < (this.y + this.height)));
|
||||
}
|
||||
|
||||
public getHalfSize() {
|
||||
return new Vector2(this.width * 0.5, this.height * 0.5);
|
||||
}
|
||||
public getHalfSize() {
|
||||
return new Vector2(this.width * 0.5, this.height * 0.5);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个矩形的最小/最大点(左上角,右下角的点)
|
||||
* @param minX
|
||||
* @param minY
|
||||
* @param maxX
|
||||
* @param maxY
|
||||
*/
|
||||
public static fromMinMax(minX: number, minY: number, maxX: number, maxY: number) {
|
||||
return new Rectangle(minX, minY, maxX - minX, maxY - minY);
|
||||
}
|
||||
/**
|
||||
* 创建一个矩形的最小/最大点(左上角,右下角的点)
|
||||
* @param minX
|
||||
* @param minY
|
||||
* @param maxX
|
||||
* @param maxY
|
||||
*/
|
||||
public static fromMinMax(minX: number, minY: number, maxX: number, maxY: number) {
|
||||
return new Rectangle(minX, minY, maxX - minX, maxY - minY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取矩形边界上与给定点最近的点
|
||||
* @param point
|
||||
*/
|
||||
public getClosestPointOnRectangleBorderToPoint(point: Vector2): { res: Vector2, edgeNormal: Vector2 } {
|
||||
let edgeNormal = Vector2.zero;
|
||||
/**
|
||||
* 获取矩形边界上与给定点最近的点
|
||||
* @param point
|
||||
*/
|
||||
public getClosestPointOnRectangleBorderToPoint(point: Vector2): { res: Vector2, edgeNormal: Vector2 } {
|
||||
let edgeNormal = Vector2.zero;
|
||||
|
||||
// 对于每个轴,如果点在盒子外面
|
||||
let res = new Vector2();
|
||||
res.x = MathHelper.clamp(point.x, this.left, this.right);
|
||||
res.y = MathHelper.clamp(point.y, this.top, this.bottom);
|
||||
// 对于每个轴,如果点在盒子外面
|
||||
let res = new Vector2();
|
||||
res.x = MathHelper.clamp(point.x, this.left, this.right);
|
||||
res.y = MathHelper.clamp(point.y, this.top, this.bottom);
|
||||
|
||||
// 如果点在矩形内,我们需要推res到边界,因为它将在矩形内
|
||||
if (this.contains(res.x, res.y)) {
|
||||
let dl = res.x - this.left;
|
||||
let dr = this.right - res.x;
|
||||
let dt = res.y - this.top;
|
||||
let db = this.bottom - res.y;
|
||||
// 如果点在矩形内,我们需要推res到边界,因为它将在矩形内
|
||||
if (this.contains(res.x, res.y)) {
|
||||
let dl = res.x - this.left;
|
||||
let dr = this.right - res.x;
|
||||
let dt = res.y - this.top;
|
||||
let db = this.bottom - res.y;
|
||||
|
||||
let min = Math.min(dl, dr, dt, db);
|
||||
if (min == dt) {
|
||||
res.y = this.top;
|
||||
edgeNormal.y = -1;
|
||||
} else if (min == db) {
|
||||
res.y = this.bottom;
|
||||
edgeNormal.y = 1;
|
||||
} else if (min == dl) {
|
||||
res.x = this.left;
|
||||
edgeNormal.x = -1;
|
||||
let min = Math.min(dl, dr, dt, db);
|
||||
if (min == dt) {
|
||||
res.y = this.top;
|
||||
edgeNormal.y = -1;
|
||||
} else if (min == db) {
|
||||
res.y = this.bottom;
|
||||
edgeNormal.y = 1;
|
||||
} else if (min == dl) {
|
||||
res.x = this.left;
|
||||
edgeNormal.x = -1;
|
||||
} else {
|
||||
res.x = this.right;
|
||||
edgeNormal.x = 1;
|
||||
}
|
||||
} else {
|
||||
res.x = this.right;
|
||||
edgeNormal.x = 1;
|
||||
if (res.x == this.left) edgeNormal.x = -1;
|
||||
if (res.x == this.right) edgeNormal.x = 1;
|
||||
if (res.y == this.top) edgeNormal.y = -1;
|
||||
if (res.y == this.bottom) edgeNormal.y = 1;
|
||||
}
|
||||
} else {
|
||||
if (res.x == this.left) edgeNormal.x = -1;
|
||||
if (res.x == this.right) edgeNormal.x = 1;
|
||||
if (res.y == this.top) edgeNormal.y = -1;
|
||||
if (res.y == this.bottom) edgeNormal.y = 1;
|
||||
|
||||
return { res: res, edgeNormal: edgeNormal };
|
||||
}
|
||||
|
||||
return { res: res, edgeNormal: edgeNormal };
|
||||
}
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public getClosestPointOnBoundsToOrigin() {
|
||||
let max = this.max;
|
||||
let minDist = Math.abs(this.location.x);
|
||||
let boundsPoint = new Vector2(this.location.x, 0);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public getClosestPointOnBoundsToOrigin() {
|
||||
let max = this.max;
|
||||
let minDist = Math.abs(this.location.x);
|
||||
let boundsPoint = new Vector2(this.location.x, 0);
|
||||
if (Math.abs(max.x) < minDist) {
|
||||
minDist = Math.abs(max.x);
|
||||
boundsPoint.x = max.x;
|
||||
boundsPoint.y = 0;
|
||||
}
|
||||
|
||||
if (Math.abs(max.x) < minDist) {
|
||||
minDist = Math.abs(max.x);
|
||||
boundsPoint.x = max.x;
|
||||
boundsPoint.y = 0;
|
||||
if (Math.abs(max.y) < minDist) {
|
||||
minDist = Math.abs(max.y);
|
||||
boundsPoint.x = 0;
|
||||
boundsPoint.y = max.y;
|
||||
}
|
||||
|
||||
if (Math.abs(this.location.y) < minDist) {
|
||||
minDist = Math.abs(this.location.y);
|
||||
boundsPoint.x = 0;
|
||||
boundsPoint.y = this.location.y;
|
||||
}
|
||||
|
||||
return boundsPoint;
|
||||
}
|
||||
|
||||
if (Math.abs(max.y) < minDist) {
|
||||
minDist = Math.abs(max.y);
|
||||
boundsPoint.x = 0;
|
||||
boundsPoint.y = max.y;
|
||||
public calculateBounds(parentPosition: Vector2, position: Vector2, origin: Vector2, scale: Vector2, rotation: number, width: number, height: number){
|
||||
this.x = parentPosition.x + position.x - origin.x * scale.x;
|
||||
this.y = parentPosition.y + position.y - origin.y * scale.y;
|
||||
this.width = width * scale.x;
|
||||
this.height = height * scale.y;
|
||||
}
|
||||
|
||||
if (Math.abs(this.location.y) < minDist) {
|
||||
minDist = Math.abs(this.location.y);
|
||||
boundsPoint.x = 0;
|
||||
boundsPoint.y = this.location.y;
|
||||
/**
|
||||
* 将egret矩形转化为Rectangle
|
||||
* @param rect
|
||||
*/
|
||||
public setEgretRect(rect: egret.Rectangle): Rectangle{
|
||||
this.x = rect.x;
|
||||
this.y = rect.y;
|
||||
this.width = rect.width;
|
||||
this.height = rect.height;
|
||||
return this;
|
||||
}
|
||||
|
||||
return boundsPoint;
|
||||
}
|
||||
/**
|
||||
* 给定多边形的点,计算边界
|
||||
* @param points
|
||||
*/
|
||||
public static rectEncompassingPoints(points: Vector2[]) {
|
||||
// 我们需要求出x/y的最小值/最大值
|
||||
let minX = Number.POSITIVE_INFINITY;
|
||||
let minY = Number.POSITIVE_INFINITY;
|
||||
let maxX = Number.NEGATIVE_INFINITY;
|
||||
let maxY = Number.NEGATIVE_INFINITY;
|
||||
|
||||
public calculateBounds(parentPosition: Vector2, position: Vector2, origin: Vector2, scale: Vector2, rotation: number, width: number, height: number){
|
||||
this.x = parentPosition.x + position.x - origin.x * scale.x;
|
||||
this.y = parentPosition.y + position.y - origin.y * scale.y;
|
||||
this.width = width * scale.x;
|
||||
this.height = height * scale.y;
|
||||
}
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
let pt = points[i];
|
||||
|
||||
/**
|
||||
* 将egret矩形转化为Rectangle
|
||||
* @param rect
|
||||
*/
|
||||
public setEgretRect(rect: egret.Rectangle): Rectangle{
|
||||
this.x = rect.x;
|
||||
this.y = rect.y;
|
||||
this.width = rect.width;
|
||||
this.height = rect.height;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定多边形的点,计算边界
|
||||
* @param points
|
||||
*/
|
||||
public static rectEncompassingPoints(points: Vector2[]) {
|
||||
// 我们需要求出x/y的最小值/最大值
|
||||
let minX = Number.POSITIVE_INFINITY;
|
||||
let minY = Number.POSITIVE_INFINITY;
|
||||
let maxX = Number.NEGATIVE_INFINITY;
|
||||
let maxY = Number.NEGATIVE_INFINITY;
|
||||
if (pt.x < minX) minX = pt.x;
|
||||
if (pt.x > maxX) maxX = pt.x;
|
||||
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
let pt = points[i];
|
||||
if (pt.y < minY) minY = pt.y;
|
||||
if (pt.y > maxY) maxY = pt.y;
|
||||
}
|
||||
|
||||
if (pt.x < minX) minX = pt.x;
|
||||
if (pt.x > maxX) maxX = pt.x;
|
||||
|
||||
if (pt.y < minY) minY = pt.y;
|
||||
if (pt.y > maxY) maxY = pt.y;
|
||||
return this.fromMinMax(minX, minY, maxX, maxY);
|
||||
}
|
||||
|
||||
return this.fromMinMax(minX, minY, maxX, maxY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,183 +1,185 @@
|
||||
/** 2d 向量 */
|
||||
class Vector2 {
|
||||
public x: number = 0;
|
||||
public y: number = 0;
|
||||
module es {
|
||||
/** 2d 向量 */
|
||||
export class Vector2 {
|
||||
public x: number = 0;
|
||||
public y: number = 0;
|
||||
|
||||
private static readonly unitYVector = new Vector2(0, 1);
|
||||
private static readonly unitXVector = new Vector2(1, 0);
|
||||
private static readonly unitVector2 = new Vector2(1, 1);
|
||||
private static readonly zeroVector2 = new Vector2(0, 0);
|
||||
public static get zero(){
|
||||
return Vector2.zeroVector2;
|
||||
private static readonly unitYVector = new Vector2(0, 1);
|
||||
private static readonly unitXVector = new Vector2(1, 0);
|
||||
private static readonly unitVector2 = new Vector2(1, 1);
|
||||
private static readonly zeroVector2 = new Vector2(0, 0);
|
||||
public static get zero(){
|
||||
return Vector2.zeroVector2;
|
||||
}
|
||||
|
||||
public static get one(){
|
||||
return Vector2.unitVector2;
|
||||
}
|
||||
|
||||
public static get unitX(){
|
||||
return Vector2.unitXVector;
|
||||
}
|
||||
|
||||
public static get unitY(){
|
||||
return Vector2.unitYVector;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从两个值构造一个带有X和Y的二维向量。
|
||||
* @param x 二维空间中的x坐标
|
||||
* @param y 二维空间的y坐标
|
||||
*/
|
||||
constructor(x? : number, y?: number){
|
||||
this.x = x ? x : 0;
|
||||
this.y = y ? y : this.x;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value1
|
||||
* @param value2
|
||||
*/
|
||||
public static add(value1: Vector2, value2: Vector2){
|
||||
let result: Vector2 = new Vector2(0, 0);
|
||||
result.x = value1.x + value2.x;
|
||||
result.y = value1.y + value2.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value1
|
||||
* @param value2
|
||||
*/
|
||||
public static divide(value1: Vector2, value2: Vector2){
|
||||
let result: Vector2 = new Vector2(0, 0);
|
||||
result.x = value1.x / value2.x;
|
||||
result.y = value1.y / value2.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value1
|
||||
* @param value2
|
||||
*/
|
||||
public static multiply(value1: Vector2, value2: Vector2){
|
||||
let result: Vector2 = new Vector2(0, 0);
|
||||
result.x = value1.x * value2.x;
|
||||
result.y = value1.y * value2.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value1
|
||||
* @param value2
|
||||
*/
|
||||
public static subtract(value1: Vector2, value2: Vector2){
|
||||
let result: Vector2 = new Vector2(0, 0);
|
||||
result.x = value1.x - value2.x;
|
||||
result.y = value1.y - value2.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 变成一个方向相同的单位向量 */
|
||||
public normalize(){
|
||||
let val = 1 / Math.sqrt((this.x * this.x) + (this.y * this.y));
|
||||
this.x *= val;
|
||||
this.y *= val;
|
||||
}
|
||||
|
||||
/** 返回它的长度 */
|
||||
public length(){
|
||||
return Math.sqrt((this.x * this.x) + (this.y * this.y));
|
||||
}
|
||||
|
||||
/** 对x和y值四舍五入 */
|
||||
public round(): Vector2{
|
||||
return new Vector2(Math.round(this.x), Math.round(this.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的Vector2
|
||||
* 它包含来自另一个向量的标准化值。
|
||||
* @param value
|
||||
*/
|
||||
public static normalize(value: Vector2){
|
||||
let val = 1 / Math.sqrt((value.x * value.x) + (value.y * value.y));
|
||||
value.x *= val;
|
||||
value.y *= val;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回两个向量的点积
|
||||
* @param value1
|
||||
* @param value2
|
||||
*/
|
||||
public static dot(value1: Vector2, value2: Vector2): number{
|
||||
return (value1.x * value2.x) + (value1.y * value2.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回两个向量之间距离的平方
|
||||
* @param value1
|
||||
* @param value2
|
||||
*/
|
||||
public static distanceSquared(value1: Vector2, value2: Vector2){
|
||||
let v1 = value1.x - value2.x, v2 = value1.y - value2.y;
|
||||
return (v1 * v1) + (v2 * v2);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value1
|
||||
* @param min
|
||||
* @param max
|
||||
*/
|
||||
public static clamp(value1: Vector2, min: Vector2, max: Vector2){
|
||||
return new Vector2(MathHelper.clamp(value1.x, min.x, max.x),
|
||||
MathHelper.clamp(value1.y, min.y, max.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* 包含指定向量的线性插值
|
||||
* @param value1 第一个向量
|
||||
* @param value2 第二个向量
|
||||
* @param amount 权重值(0.0到1.0之间)
|
||||
*/
|
||||
public static lerp(value1: Vector2, value2: Vector2, amount: number){
|
||||
return new Vector2(MathHelper.lerp(value1.x, value2.x, amount), MathHelper.lerp(value1.y, value2.y, amount));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param position
|
||||
* @param matrix
|
||||
*/
|
||||
public static transform(position: Vector2, matrix: Matrix2D){
|
||||
return new Vector2((position.x * matrix.m11) + (position.y * matrix.m21), (position.x * matrix.m12) + (position.y * matrix.m22));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回两个向量之间的距离
|
||||
* @param value1
|
||||
* @param value2
|
||||
*/
|
||||
public static distance(value1: Vector2, value2: Vector2){
|
||||
let v1 = value1.x - value2.x, v2 = value1.y - value2.y;
|
||||
return Math.sqrt((v1 * v1) + (v2 * v2));
|
||||
}
|
||||
|
||||
/**
|
||||
* 矢量反演的结果
|
||||
* @param value
|
||||
*/
|
||||
public static negate(value: Vector2){
|
||||
let result: Vector2 = new Vector2();
|
||||
result.x = -value.x;
|
||||
result.y = -value.y;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static get one(){
|
||||
return Vector2.unitVector2;
|
||||
}
|
||||
|
||||
public static get unitX(){
|
||||
return Vector2.unitXVector;
|
||||
}
|
||||
|
||||
public static get unitY(){
|
||||
return Vector2.unitYVector;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从两个值构造一个带有X和Y的二维向量。
|
||||
* @param x 二维空间中的x坐标
|
||||
* @param y 二维空间的y坐标
|
||||
*/
|
||||
constructor(x? : number, y?: number){
|
||||
this.x = x ? x : 0;
|
||||
this.y = y ? y : this.x;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value1
|
||||
* @param value2
|
||||
*/
|
||||
public static add(value1: Vector2, value2: Vector2){
|
||||
let result: Vector2 = new Vector2(0, 0);
|
||||
result.x = value1.x + value2.x;
|
||||
result.y = value1.y + value2.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value1
|
||||
* @param value2
|
||||
*/
|
||||
public static divide(value1: Vector2, value2: Vector2){
|
||||
let result: Vector2 = new Vector2(0, 0);
|
||||
result.x = value1.x / value2.x;
|
||||
result.y = value1.y / value2.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value1
|
||||
* @param value2
|
||||
*/
|
||||
public static multiply(value1: Vector2, value2: Vector2){
|
||||
let result: Vector2 = new Vector2(0, 0);
|
||||
result.x = value1.x * value2.x;
|
||||
result.y = value1.y * value2.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value1
|
||||
* @param value2
|
||||
*/
|
||||
public static subtract(value1: Vector2, value2: Vector2){
|
||||
let result: Vector2 = new Vector2(0, 0);
|
||||
result.x = value1.x - value2.x;
|
||||
result.y = value1.y - value2.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 变成一个方向相同的单位向量 */
|
||||
public normalize(){
|
||||
let val = 1 / Math.sqrt((this.x * this.x) + (this.y * this.y));
|
||||
this.x *= val;
|
||||
this.y *= val;
|
||||
}
|
||||
|
||||
/** 返回它的长度 */
|
||||
public length(){
|
||||
return Math.sqrt((this.x * this.x) + (this.y * this.y));
|
||||
}
|
||||
|
||||
/** 对x和y值四舍五入 */
|
||||
public round(): Vector2{
|
||||
return new Vector2(Math.round(this.x), Math.round(this.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的Vector2
|
||||
* 它包含来自另一个向量的标准化值。
|
||||
* @param value
|
||||
*/
|
||||
public static normalize(value: Vector2){
|
||||
let val = 1 / Math.sqrt((value.x * value.x) + (value.y * value.y));
|
||||
value.x *= val;
|
||||
value.y *= val;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回两个向量的点积
|
||||
* @param value1
|
||||
* @param value2
|
||||
*/
|
||||
public static dot(value1: Vector2, value2: Vector2): number{
|
||||
return (value1.x * value2.x) + (value1.y * value2.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回两个向量之间距离的平方
|
||||
* @param value1
|
||||
* @param value2
|
||||
*/
|
||||
public static distanceSquared(value1: Vector2, value2: Vector2){
|
||||
let v1 = value1.x - value2.x, v2 = value1.y - value2.y;
|
||||
return (v1 * v1) + (v2 * v2);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value1
|
||||
* @param min
|
||||
* @param max
|
||||
*/
|
||||
public static clamp(value1: Vector2, min: Vector2, max: Vector2){
|
||||
return new Vector2(MathHelper.clamp(value1.x, min.x, max.x),
|
||||
MathHelper.clamp(value1.y, min.y, max.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* 包含指定向量的线性插值
|
||||
* @param value1 第一个向量
|
||||
* @param value2 第二个向量
|
||||
* @param amount 权重值(0.0到1.0之间)
|
||||
*/
|
||||
public static lerp(value1: Vector2, value2: Vector2, amount: number){
|
||||
return new Vector2(MathHelper.lerp(value1.x, value2.x, amount), MathHelper.lerp(value1.y, value2.y, amount));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param position
|
||||
* @param matrix
|
||||
*/
|
||||
public static transform(position: Vector2, matrix: Matrix2D){
|
||||
return new Vector2((position.x * matrix.m11) + (position.y * matrix.m21), (position.x * matrix.m12) + (position.y * matrix.m22));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回两个向量之间的距离
|
||||
* @param value1
|
||||
* @param value2
|
||||
*/
|
||||
public static distance(value1: Vector2, value2: Vector2){
|
||||
let v1 = value1.x - value2.x, v2 = value1.y - value2.y;
|
||||
return Math.sqrt((v1 * v1) + (v2 * v2));
|
||||
}
|
||||
|
||||
/**
|
||||
* 矢量反演的结果
|
||||
* @param value
|
||||
*/
|
||||
public static negate(value: Vector2){
|
||||
let result: Vector2 = new Vector2();
|
||||
result.x = -value.x;
|
||||
result.y = -value.y;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
class Vector3 {
|
||||
public x: number;
|
||||
public y: number;
|
||||
public z: number;
|
||||
module es {
|
||||
export class Vector3 {
|
||||
public x: number;
|
||||
public y: number;
|
||||
public z: number;
|
||||
|
||||
constructor(x: number, y: number, z: number){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
constructor(x: number, y: number, z: number){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,101 +1,105 @@
|
||||
/** 移动器使用的帮助器类,用于管理触发器碰撞器交互并调用itriggerlistener。 */
|
||||
class ColliderTriggerHelper {
|
||||
private _entity: Entity;
|
||||
/** 存储当前帧中发生的所有活动交集对 */
|
||||
private _activeTriggerIntersections: Pair<Collider>[] = [];
|
||||
/** 存储前一帧的交叉对,以便我们可以在移动该帧后检测出口 */
|
||||
private _previousTriggerIntersections: Pair<Collider>[] = [];
|
||||
private _tempTriggerList: ITriggerListener[] = [];
|
||||
|
||||
constructor(entity: Entity) {
|
||||
this._entity = entity;
|
||||
}
|
||||
|
||||
module es {
|
||||
/**
|
||||
* 实体被移动后,应该调用更新。它会处理碰撞器重叠的任何itriggerlistener。
|
||||
* 移动器使用的帮助器类,用于管理触发器碰撞器交互并调用itriggerlistener
|
||||
*/
|
||||
public update() {
|
||||
let colliders = this._entity.getComponents(Collider);
|
||||
for (let i = 0; i < colliders.length; i++) {
|
||||
let collider = colliders[i];
|
||||
export class ColliderTriggerHelper {
|
||||
private _entity: Entity;
|
||||
/** 存储当前帧中发生的所有活动交集对 */
|
||||
private _activeTriggerIntersections: Pair<Collider>[] = [];
|
||||
/** 存储前一帧的交叉对,以便我们可以在移动该帧后检测出口 */
|
||||
private _previousTriggerIntersections: Pair<Collider>[] = [];
|
||||
private _tempTriggerList: ITriggerListener[] = [];
|
||||
|
||||
let boxcastResult = Physics.boxcastBroadphase(collider.bounds, collider.collidesWithLayers);
|
||||
collider.bounds = boxcastResult.rect;
|
||||
let neighbors = boxcastResult.colliders;
|
||||
for (let j = 0; j < neighbors.length; j++) {
|
||||
let neighbor = neighbors[j];
|
||||
if (!collider.isTrigger && !neighbor.isTrigger)
|
||||
continue;
|
||||
constructor(entity: Entity) {
|
||||
this._entity = entity;
|
||||
}
|
||||
|
||||
if (collider.overlaps(neighbor)) {
|
||||
let pair = new Pair<Collider>(collider, neighbor);
|
||||
let shouldReportTriggerEvent = this._activeTriggerIntersections.findIndex(value => {
|
||||
return value.first == pair.first && value.second == pair.second;
|
||||
}) == -1 && this._previousTriggerIntersections.findIndex(value => {
|
||||
return value.first == pair.first && value.second == pair.second;
|
||||
}) == -1;
|
||||
/**
|
||||
* 实体被移动后,应该调用更新。它会处理碰撞器重叠的任何itriggerlistener。
|
||||
*/
|
||||
public update() {
|
||||
let colliders = this._entity.getComponents(Collider);
|
||||
for (let i = 0; i < colliders.length; i++) {
|
||||
let collider = colliders[i];
|
||||
|
||||
if (shouldReportTriggerEvent)
|
||||
this.notifyTriggerListeners(pair, true);
|
||||
let boxcastResult = Physics.boxcastBroadphase(collider.bounds, collider.collidesWithLayers);
|
||||
collider.bounds = boxcastResult.rect;
|
||||
let neighbors = boxcastResult.colliders;
|
||||
for (let j = 0; j < neighbors.length; j++) {
|
||||
let neighbor = neighbors[j];
|
||||
if (!collider.isTrigger && !neighbor.isTrigger)
|
||||
continue;
|
||||
|
||||
if (!this._activeTriggerIntersections.contains(pair))
|
||||
this._activeTriggerIntersections.push(pair);
|
||||
if (collider.overlaps(neighbor)) {
|
||||
let pair = new Pair<Collider>(collider, neighbor);
|
||||
let shouldReportTriggerEvent = this._activeTriggerIntersections.findIndex(value => {
|
||||
return value.first == pair.first && value.second == pair.second;
|
||||
}) == -1 && this._previousTriggerIntersections.findIndex(value => {
|
||||
return value.first == pair.first && value.second == pair.second;
|
||||
}) == -1;
|
||||
|
||||
if (shouldReportTriggerEvent)
|
||||
this.notifyTriggerListeners(pair, true);
|
||||
|
||||
if (!this._activeTriggerIntersections.contains(pair))
|
||||
this._activeTriggerIntersections.push(pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListPool.free(colliders);
|
||||
|
||||
this.checkForExitedColliders();
|
||||
}
|
||||
|
||||
ListPool.free(colliders);
|
||||
private checkForExitedColliders(){
|
||||
for (let i = 0; i < this._activeTriggerIntersections.length; i ++){
|
||||
let index = this._previousTriggerIntersections.findIndex(value => {
|
||||
if (value.first == this._activeTriggerIntersections[i].first && value.second == this._activeTriggerIntersections[i].second)
|
||||
return true;
|
||||
|
||||
this.checkForExitedColliders();
|
||||
}
|
||||
|
||||
private checkForExitedColliders(){
|
||||
for (let i = 0; i < this._activeTriggerIntersections.length; i ++){
|
||||
let index = this._previousTriggerIntersections.findIndex(value => {
|
||||
if (value.first == this._activeTriggerIntersections[i].first && value.second == this._activeTriggerIntersections[i].second)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
});
|
||||
if (index != -1)
|
||||
this._previousTriggerIntersections.removeAt(index);
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._previousTriggerIntersections.length; i ++){
|
||||
this.notifyTriggerListeners(this._previousTriggerIntersections[i], false)
|
||||
}
|
||||
this._previousTriggerIntersections.length = 0;
|
||||
for (let i = 0; i < this._activeTriggerIntersections.length; i ++){
|
||||
if (!this._previousTriggerIntersections.contains(this._activeTriggerIntersections[i])){
|
||||
this._previousTriggerIntersections.push(this._activeTriggerIntersections[i]);
|
||||
}
|
||||
}
|
||||
this._activeTriggerIntersections.length = 0;
|
||||
}
|
||||
|
||||
private notifyTriggerListeners(collisionPair: Pair<Collider>, isEntering: boolean) {
|
||||
collisionPair.first.entity.getComponents("ITriggerListener", this._tempTriggerList);
|
||||
for (let i = 0; i < this._tempTriggerList.length; i ++){
|
||||
if (isEntering){
|
||||
this._tempTriggerList[i].onTriggerEnter(collisionPair.second, collisionPair.first);
|
||||
} else {
|
||||
this._tempTriggerList[i].onTriggerExit(collisionPair.second, collisionPair.first);
|
||||
return false;
|
||||
});
|
||||
if (index != -1)
|
||||
this._previousTriggerIntersections.removeAt(index);
|
||||
}
|
||||
|
||||
this._tempTriggerList.length = 0;
|
||||
for (let i = 0; i < this._previousTriggerIntersections.length; i ++){
|
||||
this.notifyTriggerListeners(this._previousTriggerIntersections[i], false)
|
||||
}
|
||||
this._previousTriggerIntersections.length = 0;
|
||||
for (let i = 0; i < this._activeTriggerIntersections.length; i ++){
|
||||
if (!this._previousTriggerIntersections.contains(this._activeTriggerIntersections[i])){
|
||||
this._previousTriggerIntersections.push(this._activeTriggerIntersections[i]);
|
||||
}
|
||||
}
|
||||
this._activeTriggerIntersections.length = 0;
|
||||
}
|
||||
|
||||
if (collisionPair.second.entity){
|
||||
collisionPair.second.entity.getComponents("ITriggerListener", this._tempTriggerList);
|
||||
for (let i = 0; i < this._tempTriggerList.length; i ++){
|
||||
if (isEntering){
|
||||
this._tempTriggerList[i].onTriggerEnter(collisionPair.first, collisionPair.second);
|
||||
} else {
|
||||
this._tempTriggerList[i].onTriggerExit(collisionPair.first, collisionPair.second);
|
||||
}
|
||||
private notifyTriggerListeners(collisionPair: Pair<Collider>, isEntering: boolean) {
|
||||
collisionPair.first.entity.getComponents("ITriggerListener", this._tempTriggerList);
|
||||
for (let i = 0; i < this._tempTriggerList.length; i ++){
|
||||
if (isEntering){
|
||||
this._tempTriggerList[i].onTriggerEnter(collisionPair.second, collisionPair.first);
|
||||
} else {
|
||||
this._tempTriggerList[i].onTriggerExit(collisionPair.second, collisionPair.first);
|
||||
}
|
||||
|
||||
this._tempTriggerList.length = 0;
|
||||
|
||||
if (collisionPair.second.entity){
|
||||
collisionPair.second.entity.getComponents("ITriggerListener", this._tempTriggerList);
|
||||
for (let i = 0; i < this._tempTriggerList.length; i ++){
|
||||
if (isEntering){
|
||||
this._tempTriggerList[i].onTriggerEnter(collisionPair.first, collisionPair.second);
|
||||
} else {
|
||||
this._tempTriggerList[i].onTriggerExit(collisionPair.first, collisionPair.second);
|
||||
}
|
||||
}
|
||||
|
||||
this._tempTriggerList.length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,168 +1,170 @@
|
||||
enum PointSectors {
|
||||
center = 0,
|
||||
top = 1,
|
||||
bottom = 2,
|
||||
topLeft = 9,
|
||||
topRight = 5,
|
||||
left = 8,
|
||||
right = 4,
|
||||
bottomLeft = 10,
|
||||
bottomRight = 6
|
||||
}
|
||||
|
||||
class Collisions {
|
||||
public static isLineToLine(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2): boolean {
|
||||
let b = Vector2.subtract(a2, a1);
|
||||
let d = Vector2.subtract(b2, b1);
|
||||
let bDotDPerp = b.x * d.y - b.y * d.x;
|
||||
|
||||
// 如果b*d = 0,表示这两条直线平行,因此有无穷个交点
|
||||
if (bDotDPerp == 0)
|
||||
return false;
|
||||
|
||||
let c = Vector2.subtract(b1, a1);
|
||||
let t = (c.x * d.y - c.y * d.x) / bDotDPerp;
|
||||
if (t < 0 || t > 1)
|
||||
return false;
|
||||
|
||||
let u = (c.x * b.y - c.y * b.x) / bDotDPerp;
|
||||
if (u < 0 || u > 1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
module es {
|
||||
export enum PointSectors {
|
||||
center = 0,
|
||||
top = 1,
|
||||
bottom = 2,
|
||||
topLeft = 9,
|
||||
topRight = 5,
|
||||
left = 8,
|
||||
right = 4,
|
||||
bottomLeft = 10,
|
||||
bottomRight = 6
|
||||
}
|
||||
|
||||
public static lineToLineIntersection(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2): Vector2 {
|
||||
let intersection = new Vector2(0, 0);
|
||||
export class Collisions {
|
||||
public static isLineToLine(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2): boolean {
|
||||
let b = Vector2.subtract(a2, a1);
|
||||
let d = Vector2.subtract(b2, b1);
|
||||
let bDotDPerp = b.x * d.y - b.y * d.x;
|
||||
|
||||
let b = Vector2.subtract(a2, a1);
|
||||
let d = Vector2.subtract(b2, b1);
|
||||
let bDotDPerp = b.x * d.y - b.y * d.x;
|
||||
// 如果b*d = 0,表示这两条直线平行,因此有无穷个交点
|
||||
if (bDotDPerp == 0)
|
||||
return false;
|
||||
|
||||
// 如果b*d = 0,表示这两条直线平行,因此有无穷个交点
|
||||
if (bDotDPerp == 0)
|
||||
return intersection;
|
||||
let c = Vector2.subtract(b1, a1);
|
||||
let t = (c.x * d.y - c.y * d.x) / bDotDPerp;
|
||||
if (t < 0 || t > 1)
|
||||
return false;
|
||||
|
||||
let c = Vector2.subtract(b1, a1);
|
||||
let t = (c.x * d.y - c.y * d.x) / bDotDPerp;
|
||||
if (t < 0 || t > 1)
|
||||
return intersection;
|
||||
let u = (c.x * b.y - c.y * b.x) / bDotDPerp;
|
||||
if (u < 0 || u > 1)
|
||||
return false;
|
||||
|
||||
let u = (c.x * b.y - c.y * b.x) / bDotDPerp;
|
||||
if (u < 0 || u > 1)
|
||||
return intersection;
|
||||
|
||||
intersection = Vector2.add(a1, new Vector2(t * b.x, t * b.y));
|
||||
|
||||
return intersection;
|
||||
}
|
||||
|
||||
public static closestPointOnLine(lineA: Vector2, lineB: Vector2, closestTo: Vector2) {
|
||||
let v = Vector2.subtract(lineB, lineA);
|
||||
let w = Vector2.subtract(closestTo, lineA);
|
||||
let t = Vector2.dot(w, v) / Vector2.dot(v, v);
|
||||
t = MathHelper.clamp(t, 0, 1);
|
||||
|
||||
return Vector2.add(lineA, new Vector2(v.x * t, v.y * t));
|
||||
}
|
||||
|
||||
public static isCircleToCircle(circleCenter1: Vector2, circleRadius1: number, circleCenter2: Vector2, circleRadius2: number): boolean {
|
||||
return Vector2.distanceSquared(circleCenter1, circleCenter2) < (circleRadius1 + circleRadius2) * (circleRadius1 + circleRadius2);
|
||||
}
|
||||
|
||||
public static isCircleToLine(circleCenter: Vector2, radius: number, lineFrom: Vector2, lineTo: Vector2): boolean {
|
||||
return Vector2.distanceSquared(circleCenter, this.closestPointOnLine(lineFrom, lineTo, circleCenter)) < radius * radius;
|
||||
}
|
||||
|
||||
public static isCircleToPoint(circleCenter: Vector2, radius: number, point: Vector2): boolean {
|
||||
return Vector2.distanceSquared(circleCenter, point) < radius * radius;
|
||||
}
|
||||
|
||||
public static isRectToCircle(rect: egret.Rectangle, cPosition: Vector2, cRadius: number): boolean {
|
||||
let ew = rect.width * 0.5;
|
||||
let eh = rect.height * 0.5;
|
||||
let vx = Math.max(0, Math.max(cPosition.x - rect.x) - ew);
|
||||
let vy = Math.max(0, Math.max(cPosition.y - rect.y) - eh);
|
||||
|
||||
return vx * vx + vy * vy < cRadius * cRadius;
|
||||
}
|
||||
|
||||
public static isRectToLine(rect: Rectangle, lineFrom: Vector2, lineTo: Vector2){
|
||||
let fromSector = this.getSector(rect.x, rect.y, rect.width, rect.height, lineFrom);
|
||||
let toSector = this.getSector(rect.x, rect.y, rect.width, rect.height, lineTo);
|
||||
|
||||
if (fromSector == PointSectors.center || toSector == PointSectors.center){
|
||||
return true;
|
||||
} else if((fromSector & toSector) != 0){
|
||||
return false;
|
||||
} else{
|
||||
let both = fromSector | toSector;
|
||||
// 线对边进行检查
|
||||
let edgeFrom: Vector2;
|
||||
let edgeTo: Vector2;
|
||||
|
||||
if ((both & PointSectors.top) != 0){
|
||||
edgeFrom = new Vector2(rect.x, rect.y);
|
||||
edgeTo = new Vector2(rect.x + rect.width, rect.y);
|
||||
if (this.isLineToLine(edgeFrom, edgeTo, lineFrom, lineTo))
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((both & PointSectors.bottom) != 0){
|
||||
edgeFrom = new Vector2(rect.x, rect.y + rect.height);
|
||||
edgeTo = new Vector2(rect.x + rect.width, rect.y + rect.height);
|
||||
if (this.isLineToLine(edgeFrom, edgeTo, lineFrom, lineTo))
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((both & PointSectors.left) != 0){
|
||||
edgeFrom = new Vector2(rect.x, rect.y);
|
||||
edgeTo = new Vector2(rect.x, rect.y + rect.height);
|
||||
if (this.isLineToLine(edgeFrom, edgeTo, lineFrom, lineTo))
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((both & PointSectors.right) != 0){
|
||||
edgeFrom = new Vector2(rect.x + rect.width, rect.y);
|
||||
edgeTo = new Vector2(rect.x + rect.width, rect.y + rect.height);
|
||||
if (this.isLineToLine(edgeFrom, edgeTo, lineFrom, lineTo))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
public static lineToLineIntersection(a1: Vector2, a2: Vector2, b1: Vector2, b2: Vector2): Vector2 {
|
||||
let intersection = new Vector2(0, 0);
|
||||
|
||||
let b = Vector2.subtract(a2, a1);
|
||||
let d = Vector2.subtract(b2, b1);
|
||||
let bDotDPerp = b.x * d.y - b.y * d.x;
|
||||
|
||||
// 如果b*d = 0,表示这两条直线平行,因此有无穷个交点
|
||||
if (bDotDPerp == 0)
|
||||
return intersection;
|
||||
|
||||
let c = Vector2.subtract(b1, a1);
|
||||
let t = (c.x * d.y - c.y * d.x) / bDotDPerp;
|
||||
if (t < 0 || t > 1)
|
||||
return intersection;
|
||||
|
||||
let u = (c.x * b.y - c.y * b.x) / bDotDPerp;
|
||||
if (u < 0 || u > 1)
|
||||
return intersection;
|
||||
|
||||
intersection = Vector2.add(a1, new Vector2(t * b.x, t * b.y));
|
||||
|
||||
return intersection;
|
||||
}
|
||||
|
||||
public static closestPointOnLine(lineA: Vector2, lineB: Vector2, closestTo: Vector2) {
|
||||
let v = Vector2.subtract(lineB, lineA);
|
||||
let w = Vector2.subtract(closestTo, lineA);
|
||||
let t = Vector2.dot(w, v) / Vector2.dot(v, v);
|
||||
t = MathHelper.clamp(t, 0, 1);
|
||||
|
||||
return Vector2.add(lineA, new Vector2(v.x * t, v.y * t));
|
||||
}
|
||||
|
||||
public static isCircleToCircle(circleCenter1: Vector2, circleRadius1: number, circleCenter2: Vector2, circleRadius2: number): boolean {
|
||||
return Vector2.distanceSquared(circleCenter1, circleCenter2) < (circleRadius1 + circleRadius2) * (circleRadius1 + circleRadius2);
|
||||
}
|
||||
|
||||
public static isCircleToLine(circleCenter: Vector2, radius: number, lineFrom: Vector2, lineTo: Vector2): boolean {
|
||||
return Vector2.distanceSquared(circleCenter, this.closestPointOnLine(lineFrom, lineTo, circleCenter)) < radius * radius;
|
||||
}
|
||||
|
||||
public static isCircleToPoint(circleCenter: Vector2, radius: number, point: Vector2): boolean {
|
||||
return Vector2.distanceSquared(circleCenter, point) < radius * radius;
|
||||
}
|
||||
|
||||
public static isRectToCircle(rect: egret.Rectangle, cPosition: Vector2, cRadius: number): boolean {
|
||||
let ew = rect.width * 0.5;
|
||||
let eh = rect.height * 0.5;
|
||||
let vx = Math.max(0, Math.max(cPosition.x - rect.x) - ew);
|
||||
let vy = Math.max(0, Math.max(cPosition.y - rect.y) - eh);
|
||||
|
||||
return vx * vx + vy * vy < cRadius * cRadius;
|
||||
}
|
||||
|
||||
public static isRectToLine(rect: Rectangle, lineFrom: Vector2, lineTo: Vector2){
|
||||
let fromSector = this.getSector(rect.x, rect.y, rect.width, rect.height, lineFrom);
|
||||
let toSector = this.getSector(rect.x, rect.y, rect.width, rect.height, lineTo);
|
||||
|
||||
if (fromSector == PointSectors.center || toSector == PointSectors.center){
|
||||
return true;
|
||||
} else if((fromSector & toSector) != 0){
|
||||
return false;
|
||||
} else{
|
||||
let both = fromSector | toSector;
|
||||
// 线对边进行检查
|
||||
let edgeFrom: Vector2;
|
||||
let edgeTo: Vector2;
|
||||
|
||||
if ((both & PointSectors.top) != 0){
|
||||
edgeFrom = new Vector2(rect.x, rect.y);
|
||||
edgeTo = new Vector2(rect.x + rect.width, rect.y);
|
||||
if (this.isLineToLine(edgeFrom, edgeTo, lineFrom, lineTo))
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((both & PointSectors.bottom) != 0){
|
||||
edgeFrom = new Vector2(rect.x, rect.y + rect.height);
|
||||
edgeTo = new Vector2(rect.x + rect.width, rect.y + rect.height);
|
||||
if (this.isLineToLine(edgeFrom, edgeTo, lineFrom, lineTo))
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((both & PointSectors.left) != 0){
|
||||
edgeFrom = new Vector2(rect.x, rect.y);
|
||||
edgeTo = new Vector2(rect.x, rect.y + rect.height);
|
||||
if (this.isLineToLine(edgeFrom, edgeTo, lineFrom, lineTo))
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((both & PointSectors.right) != 0){
|
||||
edgeFrom = new Vector2(rect.x + rect.width, rect.y);
|
||||
edgeTo = new Vector2(rect.x + rect.width, rect.y + rect.height);
|
||||
if (this.isLineToLine(edgeFrom, edgeTo, lineFrom, lineTo))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static isRectToPoint(rX: number, rY: number, rW: number, rH: number, point: Vector2) {
|
||||
return point.x >= rX && point.y >= rY && point.x < rX + rW && point.y < rY + rH;
|
||||
}
|
||||
|
||||
/**
|
||||
* 位标志和帮助使用Cohen–Sutherland算法
|
||||
*
|
||||
* 位标志:
|
||||
* 1001 1000 1010
|
||||
* 0001 0000 0010
|
||||
* 0101 0100 0110
|
||||
* @param rX
|
||||
* @param rY
|
||||
* @param rW
|
||||
* @param rH
|
||||
* @param point
|
||||
*/
|
||||
public static getSector(rX: number, rY: number, rW: number, rH: number, point: Vector2): PointSectors {
|
||||
let sector = PointSectors.center;
|
||||
|
||||
if (point.x < rX)
|
||||
sector |= PointSectors.left;
|
||||
else if (point.x >= rX + rW)
|
||||
sector |= PointSectors.right;
|
||||
|
||||
if (point.y < rY)
|
||||
sector |= PointSectors.top;
|
||||
else if (point.y >= rY + rH)
|
||||
sector |= PointSectors.bottom;
|
||||
|
||||
return sector;
|
||||
}
|
||||
}
|
||||
|
||||
public static isRectToPoint(rX: number, rY: number, rW: number, rH: number, point: Vector2) {
|
||||
return point.x >= rX && point.y >= rY && point.x < rX + rW && point.y < rY + rH;
|
||||
}
|
||||
|
||||
/**
|
||||
* 位标志和帮助使用Cohen–Sutherland算法
|
||||
*
|
||||
* 位标志:
|
||||
* 1001 1000 1010
|
||||
* 0001 0000 0010
|
||||
* 0101 0100 0110
|
||||
* @param rX
|
||||
* @param rY
|
||||
* @param rW
|
||||
* @param rH
|
||||
* @param point
|
||||
*/
|
||||
public static getSector(rX: number, rY: number, rW: number, rH: number, point: Vector2): PointSectors {
|
||||
let sector = PointSectors.center;
|
||||
|
||||
if (point.x < rX)
|
||||
sector |= PointSectors.left;
|
||||
else if (point.x >= rX + rW)
|
||||
sector |= PointSectors.right;
|
||||
|
||||
if (point.y < rY)
|
||||
sector |= PointSectors.top;
|
||||
else if (point.y >= rY + rH)
|
||||
sector |= PointSectors.bottom;
|
||||
|
||||
return sector;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +1,66 @@
|
||||
class Physics {
|
||||
private static _spatialHash: SpatialHash;
|
||||
/** 调用reset并创建一个新的SpatialHash时使用的单元格大小 */
|
||||
public static spatialHashCellSize = 100;
|
||||
/** 接受layerMask的所有方法的默认值 */
|
||||
public static readonly allLayers: number = -1;
|
||||
module es {
|
||||
export class Physics {
|
||||
private static _spatialHash: SpatialHash;
|
||||
/** 调用reset并创建一个新的SpatialHash时使用的单元格大小 */
|
||||
public static spatialHashCellSize = 100;
|
||||
/** 接受layerMask的所有方法的默认值 */
|
||||
public static readonly allLayers: number = -1;
|
||||
|
||||
public static reset(){
|
||||
this._spatialHash = new SpatialHash(this.spatialHashCellSize);
|
||||
}
|
||||
public static reset(){
|
||||
this._spatialHash = new SpatialHash(this.spatialHashCellSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从SpatialHash中移除所有碰撞器
|
||||
*/
|
||||
public static clear(){
|
||||
this._spatialHash.clear();
|
||||
}
|
||||
/**
|
||||
* 从SpatialHash中移除所有碰撞器
|
||||
*/
|
||||
public static clear(){
|
||||
this._spatialHash.clear();
|
||||
}
|
||||
|
||||
public static overlapCircleAll(center: Vector2, randius: number, results: any[], layerMask = -1){
|
||||
return this._spatialHash.overlapCircle(center, randius, results, layerMask);
|
||||
}
|
||||
public static overlapCircleAll(center: Vector2, randius: number, results: any[], layerMask = -1){
|
||||
return this._spatialHash.overlapCircle(center, randius, results, layerMask);
|
||||
}
|
||||
|
||||
public static boxcastBroadphase(rect: Rectangle, layerMask: number = this.allLayers){
|
||||
let boxcastResult = this._spatialHash.aabbBroadphase(rect, null, layerMask);
|
||||
return {colliders: boxcastResult.tempHashSet, rect: boxcastResult.bounds};
|
||||
}
|
||||
public static boxcastBroadphase(rect: Rectangle, layerMask: number = this.allLayers){
|
||||
let boxcastResult = this._spatialHash.aabbBroadphase(rect, null, layerMask);
|
||||
return {colliders: boxcastResult.tempHashSet, rect: boxcastResult.bounds};
|
||||
}
|
||||
|
||||
public static boxcastBroadphaseExcludingSelf(collider: Collider, rect: Rectangle, layerMask = this.allLayers){
|
||||
return this._spatialHash.aabbBroadphase(rect, collider, layerMask);
|
||||
}
|
||||
public static boxcastBroadphaseExcludingSelf(collider: Collider, rect: Rectangle, layerMask = this.allLayers){
|
||||
return this._spatialHash.aabbBroadphase(rect, collider, layerMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对撞机添加到物理系统中
|
||||
* @param collider
|
||||
*/
|
||||
public static addCollider(collider: Collider){
|
||||
Physics._spatialHash.register(collider);
|
||||
}
|
||||
/**
|
||||
* 将对撞机添加到物理系统中
|
||||
* @param collider
|
||||
*/
|
||||
public static addCollider(collider: Collider){
|
||||
Physics._spatialHash.register(collider);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从物理系统中移除对撞机
|
||||
* @param collider
|
||||
*/
|
||||
public static removeCollider(collider: Collider){
|
||||
Physics._spatialHash.remove(collider);
|
||||
}
|
||||
/**
|
||||
* 从物理系统中移除对撞机
|
||||
* @param collider
|
||||
*/
|
||||
public static removeCollider(collider: Collider){
|
||||
Physics._spatialHash.remove(collider);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新物理系统中对撞机的位置。这实际上只是移除然后重新添加带有新边界的碰撞器
|
||||
* @param collider
|
||||
*/
|
||||
public static updateCollider(collider: Collider){
|
||||
this._spatialHash.remove(collider);
|
||||
this._spatialHash.register(collider);
|
||||
}
|
||||
/**
|
||||
* 更新物理系统中对撞机的位置。这实际上只是移除然后重新添加带有新边界的碰撞器
|
||||
* @param collider
|
||||
*/
|
||||
public static updateCollider(collider: Collider){
|
||||
this._spatialHash.remove(collider);
|
||||
this._spatialHash.register(collider);
|
||||
}
|
||||
|
||||
/**
|
||||
* debug绘制空间散列的内容
|
||||
* @param secondsToDisplay
|
||||
*/
|
||||
public static debugDraw(secondsToDisplay){
|
||||
this._spatialHash.debugDraw(secondsToDisplay, 2);
|
||||
/**
|
||||
* debug绘制空间散列的内容
|
||||
* @param secondsToDisplay
|
||||
*/
|
||||
public static debugDraw(secondsToDisplay){
|
||||
this._spatialHash.debugDraw(secondsToDisplay, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
class CollisionResult {
|
||||
public collider: Collider;
|
||||
public minimumTranslationVector: Vector2 = Vector2.zero;
|
||||
public normal: Vector2 = Vector2.zero;
|
||||
public point: Vector2 = Vector2.zero;
|
||||
module es {
|
||||
export class CollisionResult {
|
||||
public collider: Collider;
|
||||
public minimumTranslationVector: Vector2 = Vector2.zero;
|
||||
public normal: Vector2 = Vector2.zero;
|
||||
public point: Vector2 = Vector2.zero;
|
||||
|
||||
public invertResult(){
|
||||
this.minimumTranslationVector = Vector2.negate(this.minimumTranslationVector);
|
||||
this.normal = Vector2.negate(this.normal);
|
||||
public invertResult(){
|
||||
this.minimumTranslationVector = Vector2.negate(this.minimumTranslationVector);
|
||||
this.normal = Vector2.negate(this.normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,302 +1,304 @@
|
||||
class ShapeCollisions {
|
||||
/**
|
||||
* 检查两个多边形之间的碰撞
|
||||
* @param first
|
||||
* @param second
|
||||
*/
|
||||
public static polygonToPolygon(first: Polygon, second: Polygon) {
|
||||
let result = new CollisionResult();
|
||||
let isIntersecting = true;
|
||||
module es {
|
||||
export class ShapeCollisions {
|
||||
/**
|
||||
* 检查两个多边形之间的碰撞
|
||||
* @param first
|
||||
* @param second
|
||||
*/
|
||||
public static polygonToPolygon(first: Polygon, second: Polygon) {
|
||||
let result = new CollisionResult();
|
||||
let isIntersecting = true;
|
||||
|
||||
let firstEdges = first.edgeNormals;
|
||||
let secondEdges = second.edgeNormals;
|
||||
let minIntervalDistance = Number.POSITIVE_INFINITY;
|
||||
let translationAxis = new Vector2();
|
||||
let polygonOffset = Vector2.subtract(first.position, second.position);
|
||||
let axis: Vector2;
|
||||
let firstEdges = first.edgeNormals;
|
||||
let secondEdges = second.edgeNormals;
|
||||
let minIntervalDistance = Number.POSITIVE_INFINITY;
|
||||
let translationAxis = new Vector2();
|
||||
let polygonOffset = Vector2.subtract(first.position, second.position);
|
||||
let axis: Vector2;
|
||||
|
||||
// 循环穿过两个多边形的所有边
|
||||
for (let edgeIndex = 0; edgeIndex < firstEdges.length + secondEdges.length; edgeIndex++) {
|
||||
// 1. 找出当前多边形是否相交
|
||||
// 多边形的归一化轴垂直于缓存给我们的当前边
|
||||
if (edgeIndex < firstEdges.length) {
|
||||
axis = firstEdges[edgeIndex];
|
||||
} else {
|
||||
axis = secondEdges[edgeIndex - firstEdges.length];
|
||||
// 循环穿过两个多边形的所有边
|
||||
for (let edgeIndex = 0; edgeIndex < firstEdges.length + secondEdges.length; edgeIndex++) {
|
||||
// 1. 找出当前多边形是否相交
|
||||
// 多边形的归一化轴垂直于缓存给我们的当前边
|
||||
if (edgeIndex < firstEdges.length) {
|
||||
axis = firstEdges[edgeIndex];
|
||||
} else {
|
||||
axis = secondEdges[edgeIndex - firstEdges.length];
|
||||
}
|
||||
|
||||
// 求多边形在当前轴上的投影
|
||||
let minA = 0;
|
||||
let minB = 0;
|
||||
let maxA = 0;
|
||||
let maxB = 0;
|
||||
let intervalDist = 0;
|
||||
let ta = this.getInterval(axis, first, minA, maxA);
|
||||
minA = ta.min;
|
||||
minB = ta.max;
|
||||
let tb = this.getInterval(axis, second, minB, maxB);
|
||||
minB = tb.min;
|
||||
maxB = tb.max;
|
||||
|
||||
// 将区间设为第二个多边形的空间。由轴上投影的位置差偏移。
|
||||
let relativeIntervalOffset = Vector2.dot(polygonOffset, axis);
|
||||
minA += relativeIntervalOffset;
|
||||
maxA += relativeIntervalOffset;
|
||||
|
||||
// 检查多边形投影是否正在相交
|
||||
intervalDist = this.intervalDistance(minA, maxA, minB, maxB);
|
||||
if (intervalDist > 0)
|
||||
isIntersecting = false;
|
||||
|
||||
// 对于多对多数据类型转换,添加一个Vector2?参数称为deltaMovement。为了提高速度,我们这里不使用它
|
||||
// TODO: 现在找出多边形是否会相交。只要检查速度就行了
|
||||
|
||||
// 如果多边形不相交,也不会相交,退出循环
|
||||
if (!isIntersecting)
|
||||
return null;
|
||||
|
||||
// 检查当前间隔距离是否为最小值。如果是,则存储间隔距离和当前距离。这将用于计算最小平移向量
|
||||
intervalDist = Math.abs(intervalDist);
|
||||
if (intervalDist < minIntervalDistance) {
|
||||
minIntervalDistance = intervalDist;
|
||||
translationAxis = axis;
|
||||
|
||||
if (Vector2.dot(translationAxis, polygonOffset) < 0)
|
||||
translationAxis = new Vector2(-translationAxis);
|
||||
}
|
||||
}
|
||||
|
||||
// 求多边形在当前轴上的投影
|
||||
let minA = 0;
|
||||
let minB = 0;
|
||||
let maxA = 0;
|
||||
let maxB = 0;
|
||||
let intervalDist = 0;
|
||||
let ta = this.getInterval(axis, first, minA, maxA);
|
||||
minA = ta.min;
|
||||
minB = ta.max;
|
||||
let tb = this.getInterval(axis, second, minB, maxB);
|
||||
minB = tb.min;
|
||||
maxB = tb.max;
|
||||
|
||||
// 将区间设为第二个多边形的空间。由轴上投影的位置差偏移。
|
||||
let relativeIntervalOffset = Vector2.dot(polygonOffset, axis);
|
||||
minA += relativeIntervalOffset;
|
||||
maxA += relativeIntervalOffset;
|
||||
|
||||
// 检查多边形投影是否正在相交
|
||||
intervalDist = this.intervalDistance(minA, maxA, minB, maxB);
|
||||
if (intervalDist > 0)
|
||||
isIntersecting = false;
|
||||
|
||||
// 对于多对多数据类型转换,添加一个Vector2?参数称为deltaMovement。为了提高速度,我们这里不使用它
|
||||
// TODO: 现在找出多边形是否会相交。只要检查速度就行了
|
||||
|
||||
// 如果多边形不相交,也不会相交,退出循环
|
||||
if (!isIntersecting)
|
||||
return null;
|
||||
|
||||
// 检查当前间隔距离是否为最小值。如果是,则存储间隔距离和当前距离。这将用于计算最小平移向量
|
||||
intervalDist = Math.abs(intervalDist);
|
||||
if (intervalDist < minIntervalDistance) {
|
||||
minIntervalDistance = intervalDist;
|
||||
translationAxis = axis;
|
||||
|
||||
if (Vector2.dot(translationAxis, polygonOffset) < 0)
|
||||
translationAxis = new Vector2(-translationAxis);
|
||||
}
|
||||
}
|
||||
|
||||
// 利用最小平移向量对多边形进行推入。
|
||||
result.normal = translationAxis;
|
||||
result.minimumTranslationVector = Vector2.multiply(new Vector2(-translationAxis.x, -translationAxis.y), new Vector2(minIntervalDistance));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算[minA, maxA]和[minB, maxB]之间的距离。如果间隔重叠,距离是负的
|
||||
* @param minA
|
||||
* @param maxA
|
||||
* @param minB
|
||||
* @param maxB
|
||||
*/
|
||||
public static intervalDistance(minA: number, maxA: number, minB: number, maxB) {
|
||||
if (minA < minB)
|
||||
return minB - maxA;
|
||||
|
||||
return minA - minB;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算一个多边形在一个轴上的投影,并返回一个[min,max]区间
|
||||
* @param axis
|
||||
* @param polygon
|
||||
* @param min
|
||||
* @param max
|
||||
*/
|
||||
public static getInterval(axis: Vector2, polygon: Polygon, min: number, max: number) {
|
||||
let dot = Vector2.dot(polygon.points[0], axis);
|
||||
min = max = dot;
|
||||
|
||||
for (let i = 1; i < polygon.points.length; i++) {
|
||||
dot = Vector2.dot(polygon.points[i], axis);
|
||||
if (dot < min) {
|
||||
min = dot;
|
||||
} else if (dot > max) {
|
||||
max = dot;
|
||||
}
|
||||
}
|
||||
|
||||
return { min: min, max: max };
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param circle
|
||||
* @param polygon
|
||||
*/
|
||||
public static circleToPolygon(circle: Circle, polygon: Polygon) {
|
||||
let result = new CollisionResult();
|
||||
|
||||
let poly2Circle = Vector2.subtract(circle.position, polygon.position);
|
||||
|
||||
let gpp = Polygon.getClosestPointOnPolygonToPoint(polygon.points, poly2Circle);
|
||||
let closestPoint: Vector2 = gpp.closestPoint;
|
||||
let distanceSquared: number = gpp.distanceSquared;
|
||||
result.normal = gpp.edgeNormal;
|
||||
|
||||
let circleCenterInsidePoly = polygon.containsPoint(circle.position);
|
||||
if (distanceSquared > circle.radius * circle.radius && !circleCenterInsidePoly)
|
||||
return null;
|
||||
|
||||
let mtv: Vector2;
|
||||
if (circleCenterInsidePoly) {
|
||||
mtv = Vector2.multiply(result.normal, new Vector2(Math.sqrt(distanceSquared) - circle.radius));
|
||||
} else {
|
||||
if (distanceSquared == 0) {
|
||||
mtv = Vector2.multiply(result.normal, new Vector2(circle.radius));
|
||||
} else {
|
||||
let distance = Math.sqrt(distanceSquared);
|
||||
mtv = Vector2.multiply(new Vector2(-Vector2.subtract(poly2Circle, closestPoint)), new Vector2((circle.radius - distanceSquared) / distance));
|
||||
}
|
||||
}
|
||||
|
||||
result.minimumTranslationVector = mtv;
|
||||
result.point = Vector2.add(closestPoint, polygon.position);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 适用于圆心在方框内以及只与方框外圆心重叠的圆。
|
||||
* @param circle
|
||||
* @param box
|
||||
*/
|
||||
public static circleToBox(circle: Circle, box: Box): CollisionResult {
|
||||
let result = new CollisionResult();
|
||||
let closestPointOnBounds = box.bounds.getClosestPointOnRectangleBorderToPoint(circle.position).res;
|
||||
|
||||
if (box.containsPoint(circle.position)) {
|
||||
result.point = closestPointOnBounds;
|
||||
|
||||
let safePlace = Vector2.add(closestPointOnBounds, Vector2.subtract(result.normal, new Vector2(circle.radius)));
|
||||
result.minimumTranslationVector = Vector2.subtract(circle.position, safePlace);
|
||||
// 利用最小平移向量对多边形进行推入。
|
||||
result.normal = translationAxis;
|
||||
result.minimumTranslationVector = Vector2.multiply(new Vector2(-translationAxis.x, -translationAxis.y), new Vector2(minIntervalDistance));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
let sqrDistance = Vector2.distanceSquared(closestPointOnBounds, circle.position);
|
||||
if (sqrDistance == 0) {
|
||||
result.minimumTranslationVector = Vector2.multiply(result.normal, new Vector2(circle.radius));
|
||||
} else if (sqrDistance <= circle.radius * circle.radius) {
|
||||
result.normal = Vector2.subtract(circle.position, closestPointOnBounds);
|
||||
let depth = result.normal.length() - circle.radius;
|
||||
result.normal = Vector2Ext.normalize(result.normal);
|
||||
result.minimumTranslationVector = Vector2.multiply(new Vector2(depth), result.normal);
|
||||
/**
|
||||
* 计算[minA, maxA]和[minB, maxB]之间的距离。如果间隔重叠,距离是负的
|
||||
* @param minA
|
||||
* @param maxA
|
||||
* @param minB
|
||||
* @param maxB
|
||||
*/
|
||||
public static intervalDistance(minA: number, maxA: number, minB: number, maxB) {
|
||||
if (minA < minB)
|
||||
return minB - maxA;
|
||||
|
||||
return result;
|
||||
return minA - minB;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* 计算一个多边形在一个轴上的投影,并返回一个[min,max]区间
|
||||
* @param axis
|
||||
* @param polygon
|
||||
* @param min
|
||||
* @param max
|
||||
*/
|
||||
public static getInterval(axis: Vector2, polygon: Polygon, min: number, max: number) {
|
||||
let dot = Vector2.dot(polygon.points[0], axis);
|
||||
min = max = dot;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param point
|
||||
* @param circle
|
||||
*/
|
||||
public static pointToCircle(point: Vector2, circle: Circle) {
|
||||
let result = new CollisionResult();
|
||||
for (let i = 1; i < polygon.points.length; i++) {
|
||||
dot = Vector2.dot(polygon.points[i], axis);
|
||||
if (dot < min) {
|
||||
min = dot;
|
||||
} else if (dot > max) {
|
||||
max = dot;
|
||||
}
|
||||
}
|
||||
|
||||
let distanceSquared = Vector2.distanceSquared(point, circle.position);
|
||||
let sumOfRadii = 1 + circle.radius;
|
||||
let collided = distanceSquared < sumOfRadii * sumOfRadii;
|
||||
if (collided) {
|
||||
result.normal = Vector2.normalize(Vector2.subtract(point, circle.position));
|
||||
let depth = sumOfRadii - Math.sqrt(distanceSquared);
|
||||
result.minimumTranslationVector = Vector2.multiply(new Vector2(-depth, -depth), result.normal);
|
||||
result.point = Vector2.add(circle.position, Vector2.multiply(result.normal, new Vector2(circle.radius, circle.radius)));
|
||||
|
||||
return result;
|
||||
return { min: min, max: max };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param circle
|
||||
* @param polygon
|
||||
*/
|
||||
public static circleToPolygon(circle: Circle, polygon: Polygon) {
|
||||
let result = new CollisionResult();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param lineA
|
||||
* @param lineB
|
||||
* @param closestTo
|
||||
*/
|
||||
public static closestPointOnLine(lineA: Vector2, lineB: Vector2, closestTo: Vector2) {
|
||||
let v = Vector2.subtract(lineB, lineA);
|
||||
let w = Vector2.subtract(closestTo, lineA);
|
||||
let t = Vector2.dot(w, v) / Vector2.dot(v, v);
|
||||
t = MathHelper.clamp(t, 0, 1);
|
||||
let poly2Circle = Vector2.subtract(circle.position, polygon.position);
|
||||
|
||||
return Vector2.add(lineA, Vector2.multiply(v, new Vector2(t, t)));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param point
|
||||
* @param poly
|
||||
*/
|
||||
public static pointToPoly(point: Vector2, poly: Polygon) {
|
||||
let result = new CollisionResult();
|
||||
|
||||
if (poly.containsPoint(point)) {
|
||||
let distanceSquared: number;
|
||||
let gpp = Polygon.getClosestPointOnPolygonToPoint(poly.points, Vector2.subtract(point, poly.position));
|
||||
let closestPoint = gpp.closestPoint;
|
||||
distanceSquared = gpp.distanceSquared;
|
||||
let gpp = Polygon.getClosestPointOnPolygonToPoint(polygon.points, poly2Circle);
|
||||
let closestPoint: Vector2 = gpp.closestPoint;
|
||||
let distanceSquared: number = gpp.distanceSquared;
|
||||
result.normal = gpp.edgeNormal;
|
||||
|
||||
result.minimumTranslationVector = Vector2.multiply(result.normal, new Vector2(Math.sqrt(distanceSquared), Math.sqrt(distanceSquared)));
|
||||
result.point = Vector2.add(closestPoint, poly.position);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param first
|
||||
* @param second
|
||||
*/
|
||||
public static circleToCircle(first: Circle, second: Circle){
|
||||
let result = new CollisionResult();
|
||||
|
||||
let distanceSquared = Vector2.distanceSquared(first.position, second.position);
|
||||
let sumOfRadii = first.radius + second.radius;
|
||||
let collided = distanceSquared < sumOfRadii * sumOfRadii;
|
||||
if (collided){
|
||||
result.normal = Vector2.normalize(Vector2.subtract(first.position, second.position));
|
||||
let depth = sumOfRadii - Math.sqrt(distanceSquared);
|
||||
result.minimumTranslationVector = Vector2.multiply(new Vector2(-depth), result.normal);
|
||||
result.point = Vector2.add(second.position, Vector2.multiply(result.normal, new Vector2(second.radius)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param first
|
||||
* @param second
|
||||
*/
|
||||
public static boxToBox(first: Box, second: Box){
|
||||
let result = new CollisionResult();
|
||||
|
||||
let minkowskiDiff = this.minkowskiDifference(first, second);
|
||||
if (minkowskiDiff.contains(0, 0)){
|
||||
// 计算MTV。如果它是零,我们就可以称它为非碰撞
|
||||
result.minimumTranslationVector = minkowskiDiff.getClosestPointOnBoundsToOrigin();
|
||||
|
||||
if (result.minimumTranslationVector.x == 0 && result.minimumTranslationVector.y == 0)
|
||||
let circleCenterInsidePoly = polygon.containsPoint(circle.position);
|
||||
if (distanceSquared > circle.radius * circle.radius && !circleCenterInsidePoly)
|
||||
return null;
|
||||
|
||||
result.normal = new Vector2(-result.minimumTranslationVector.x, -result.minimumTranslationVector.y);
|
||||
result.normal.normalize();
|
||||
|
||||
let mtv: Vector2;
|
||||
if (circleCenterInsidePoly) {
|
||||
mtv = Vector2.multiply(result.normal, new Vector2(Math.sqrt(distanceSquared) - circle.radius));
|
||||
} else {
|
||||
if (distanceSquared == 0) {
|
||||
mtv = Vector2.multiply(result.normal, new Vector2(circle.radius));
|
||||
} else {
|
||||
let distance = Math.sqrt(distanceSquared);
|
||||
mtv = Vector2.multiply(new Vector2(-Vector2.subtract(poly2Circle, closestPoint)), new Vector2((circle.radius - distanceSquared) / distance));
|
||||
}
|
||||
}
|
||||
|
||||
result.minimumTranslationVector = mtv;
|
||||
result.point = Vector2.add(closestPoint, polygon.position);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* 适用于圆心在方框内以及只与方框外圆心重叠的圆。
|
||||
* @param circle
|
||||
* @param box
|
||||
*/
|
||||
public static circleToBox(circle: Circle, box: Box): CollisionResult {
|
||||
let result = new CollisionResult();
|
||||
let closestPointOnBounds = box.bounds.getClosestPointOnRectangleBorderToPoint(circle.position).res;
|
||||
|
||||
private static minkowskiDifference(first: Box, second: Box){
|
||||
// 我们需要第一个框的左上角
|
||||
// 碰撞器只会修改运动的位置所以我们需要用位置来计算出运动是什么。
|
||||
let positionOffset = Vector2.subtract(first.position, Vector2.add(first.bounds.location, Vector2.divide(first.bounds.size, new Vector2(2))));
|
||||
let topLeft = Vector2.subtract(Vector2.add(first.bounds.location, positionOffset), second.bounds.max);
|
||||
let fullSize = Vector2.add(first.bounds.size, second.bounds.size);
|
||||
if (box.containsPoint(circle.position)) {
|
||||
result.point = closestPointOnBounds;
|
||||
|
||||
return new Rectangle(topLeft.x, topLeft.y, fullSize.x, fullSize.y)
|
||||
let safePlace = Vector2.add(closestPointOnBounds, Vector2.subtract(result.normal, new Vector2(circle.radius)));
|
||||
result.minimumTranslationVector = Vector2.subtract(circle.position, safePlace);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
let sqrDistance = Vector2.distanceSquared(closestPointOnBounds, circle.position);
|
||||
if (sqrDistance == 0) {
|
||||
result.minimumTranslationVector = Vector2.multiply(result.normal, new Vector2(circle.radius));
|
||||
} else if (sqrDistance <= circle.radius * circle.radius) {
|
||||
result.normal = Vector2.subtract(circle.position, closestPointOnBounds);
|
||||
let depth = result.normal.length() - circle.radius;
|
||||
result.normal = Vector2Ext.normalize(result.normal);
|
||||
result.minimumTranslationVector = Vector2.multiply(new Vector2(depth), result.normal);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param point
|
||||
* @param circle
|
||||
*/
|
||||
public static pointToCircle(point: Vector2, circle: Circle) {
|
||||
let result = new CollisionResult();
|
||||
|
||||
let distanceSquared = Vector2.distanceSquared(point, circle.position);
|
||||
let sumOfRadii = 1 + circle.radius;
|
||||
let collided = distanceSquared < sumOfRadii * sumOfRadii;
|
||||
if (collided) {
|
||||
result.normal = Vector2.normalize(Vector2.subtract(point, circle.position));
|
||||
let depth = sumOfRadii - Math.sqrt(distanceSquared);
|
||||
result.minimumTranslationVector = Vector2.multiply(new Vector2(-depth, -depth), result.normal);
|
||||
result.point = Vector2.add(circle.position, Vector2.multiply(result.normal, new Vector2(circle.radius, circle.radius)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param lineA
|
||||
* @param lineB
|
||||
* @param closestTo
|
||||
*/
|
||||
public static closestPointOnLine(lineA: Vector2, lineB: Vector2, closestTo: Vector2) {
|
||||
let v = Vector2.subtract(lineB, lineA);
|
||||
let w = Vector2.subtract(closestTo, lineA);
|
||||
let t = Vector2.dot(w, v) / Vector2.dot(v, v);
|
||||
t = MathHelper.clamp(t, 0, 1);
|
||||
|
||||
return Vector2.add(lineA, Vector2.multiply(v, new Vector2(t, t)));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param point
|
||||
* @param poly
|
||||
*/
|
||||
public static pointToPoly(point: Vector2, poly: Polygon) {
|
||||
let result = new CollisionResult();
|
||||
|
||||
if (poly.containsPoint(point)) {
|
||||
let distanceSquared: number;
|
||||
let gpp = Polygon.getClosestPointOnPolygonToPoint(poly.points, Vector2.subtract(point, poly.position));
|
||||
let closestPoint = gpp.closestPoint;
|
||||
distanceSquared = gpp.distanceSquared;
|
||||
result.normal = gpp.edgeNormal;
|
||||
|
||||
result.minimumTranslationVector = Vector2.multiply(result.normal, new Vector2(Math.sqrt(distanceSquared), Math.sqrt(distanceSquared)));
|
||||
result.point = Vector2.add(closestPoint, poly.position);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param first
|
||||
* @param second
|
||||
*/
|
||||
public static circleToCircle(first: Circle, second: Circle){
|
||||
let result = new CollisionResult();
|
||||
|
||||
let distanceSquared = Vector2.distanceSquared(first.position, second.position);
|
||||
let sumOfRadii = first.radius + second.radius;
|
||||
let collided = distanceSquared < sumOfRadii * sumOfRadii;
|
||||
if (collided){
|
||||
result.normal = Vector2.normalize(Vector2.subtract(first.position, second.position));
|
||||
let depth = sumOfRadii - Math.sqrt(distanceSquared);
|
||||
result.minimumTranslationVector = Vector2.multiply(new Vector2(-depth), result.normal);
|
||||
result.point = Vector2.add(second.position, Vector2.multiply(result.normal, new Vector2(second.radius)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param first
|
||||
* @param second
|
||||
*/
|
||||
public static boxToBox(first: Box, second: Box){
|
||||
let result = new CollisionResult();
|
||||
|
||||
let minkowskiDiff = this.minkowskiDifference(first, second);
|
||||
if (minkowskiDiff.contains(0, 0)){
|
||||
// 计算MTV。如果它是零,我们就可以称它为非碰撞
|
||||
result.minimumTranslationVector = minkowskiDiff.getClosestPointOnBoundsToOrigin();
|
||||
|
||||
if (result.minimumTranslationVector.x == 0 && result.minimumTranslationVector.y == 0)
|
||||
return null;
|
||||
|
||||
result.normal = new Vector2(-result.minimumTranslationVector.x, -result.minimumTranslationVector.y);
|
||||
result.normal.normalize();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static minkowskiDifference(first: Box, second: Box){
|
||||
// 我们需要第一个框的左上角
|
||||
// 碰撞器只会修改运动的位置所以我们需要用位置来计算出运动是什么。
|
||||
let positionOffset = Vector2.subtract(first.position, Vector2.add(first.bounds.location, Vector2.divide(first.bounds.size, new Vector2(2))));
|
||||
let topLeft = Vector2.subtract(Vector2.add(first.bounds.location, positionOffset), second.bounds.max);
|
||||
let fullSize = Vector2.add(first.bounds.size, second.bounds.size);
|
||||
|
||||
return new Rectangle(topLeft.x, topLeft.y, fullSize.x, fullSize.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +1,71 @@
|
||||
/**
|
||||
* 支持标题安全区的布局类。
|
||||
*/
|
||||
class Layout {
|
||||
public clientArea: Rectangle;
|
||||
public safeArea: Rectangle;
|
||||
module es {
|
||||
/**
|
||||
* 支持标题安全区的布局类。
|
||||
*/
|
||||
export 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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
export 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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,349 +1,351 @@
|
||||
/**
|
||||
* 通过使用这个类,您可以直观地找到瓶颈和基本的CPU使用情况。
|
||||
*/
|
||||
class TimeRuler {
|
||||
/** 最大条数 8 */
|
||||
public static readonly maxBars = 8;
|
||||
/** */
|
||||
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>();
|
||||
module es {
|
||||
/**
|
||||
* 你想在游戏开始时调用StartFrame更新方法。
|
||||
* 当游戏在固定时间步进模式下运行缓慢时,更新会多次调用。
|
||||
* 在这种情况下,我们应该忽略StartFrame调用。
|
||||
* 为此,我们只需一直跟踪StartFrame调用的次数,直到Draw被调用。
|
||||
* 通过使用这个类,您可以直观地找到瓶颈和基本的CPU使用情况。
|
||||
*/
|
||||
private _updateCount: number;
|
||||
/** */
|
||||
public showLog = false;
|
||||
private _frameAdjust: number;
|
||||
export class TimeRuler {
|
||||
/** 最大条数 8 */
|
||||
public static readonly maxBars = 8;
|
||||
/** */
|
||||
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';
|
||||
|
||||
constructor() {
|
||||
TimeRuler.Instance = this;
|
||||
this._logs = new Array<FrameLog>(2);
|
||||
for (let i = 0; i < this._logs.length; ++i)
|
||||
this._logs[i] = new FrameLog();
|
||||
/** 每帧的日志 */
|
||||
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;
|
||||
|
||||
this.sampleFrames = this.targetSampleFrames = 1;
|
||||
this.width = SceneManager.stage.stageWidth * 0.8;
|
||||
this.onGraphicsDeviceReset();
|
||||
}
|
||||
constructor() {
|
||||
TimeRuler.Instance = this;
|
||||
this._logs = new Array<FrameLog>(2);
|
||||
for (let i = 0; i < this._logs.length; ++i)
|
||||
this._logs[i] = new FrameLog();
|
||||
|
||||
private onGraphicsDeviceReset() {
|
||||
let layout = new Layout();
|
||||
this._position = layout.place(new Vector2(this.width, TimeRuler.barHeight), 0, 0.01, Alignment.bottomCenter).location;
|
||||
}
|
||||
this.sampleFrames = this.targetSampleFrames = 1;
|
||||
this.width = SceneManager.stage.stageWidth * 0.8;
|
||||
this.onGraphicsDeviceReset();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public startFrame() {
|
||||
// 当这个方法被多次调用时,我们跳过重置帧。
|
||||
let lock = new LockUtils(this._frameKey);
|
||||
lock.lock().then(() => {
|
||||
this._updateCount = parseInt(egret.localStorage.getItem(this._frameKey), 10);
|
||||
if (isNaN(this._updateCount))
|
||||
this._updateCount = 0;
|
||||
let count = this._updateCount;
|
||||
count += 1;
|
||||
egret.localStorage.setItem(this._frameKey, count.toString());
|
||||
if (this.enabled && (1 < count && count < TimeRuler.maxSampleFrames))
|
||||
return;
|
||||
private onGraphicsDeviceReset() {
|
||||
let layout = new Layout();
|
||||
this._position = layout.place(new Vector2(this.width, TimeRuler.barHeight), 0, 0.01, Alignment.bottomCenter).location;
|
||||
}
|
||||
|
||||
// 更新当前帧日志。
|
||||
this._prevLog = this._logs[this.frameCount++ & 0x1];
|
||||
this._curLog = this._logs[this.frameCount & 0x1];
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public startFrame() {
|
||||
// 当这个方法被多次调用时,我们跳过重置帧。
|
||||
let lock = new LockUtils(this._frameKey);
|
||||
lock.lock().then(() => {
|
||||
this._updateCount = parseInt(egret.localStorage.getItem(this._frameKey), 10);
|
||||
if (isNaN(this._updateCount))
|
||||
this._updateCount = 0;
|
||||
let count = this._updateCount;
|
||||
count += 1;
|
||||
egret.localStorage.setItem(this._frameKey, count.toString());
|
||||
if (this.enabled && (1 < count && count < TimeRuler.maxSampleFrames))
|
||||
return;
|
||||
|
||||
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];
|
||||
// 更新当前帧日志。
|
||||
this._prevLog = this._logs[this.frameCount++ & 0x1];
|
||||
this._curLog = this._logs[this.frameCount & 0x1];
|
||||
|
||||
// 重新打开在前一帧中没有调用结束标记的标记。
|
||||
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;
|
||||
}
|
||||
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 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];
|
||||
// 重新打开在前一帧中没有调用结束标记的标记。
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
// 更新日志标记
|
||||
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];
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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 (isNaN(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 (isNaN(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;
|
||||
}
|
||||
this.stopwacth.reset();
|
||||
this.stopwacth.start();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public render(position: Vector2 = this._position, width: number = this.width){
|
||||
egret.localStorage.setItem(this._frameKey, "0");
|
||||
/**
|
||||
* 开始测量时间。
|
||||
* @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");
|
||||
|
||||
if (!this.showLog)
|
||||
return;
|
||||
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");
|
||||
}
|
||||
|
||||
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);
|
||||
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 (isNaN(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 (isNaN(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;
|
||||
}
|
||||
})
|
||||
|
||||
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;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (Math.max(this._frameAdjust) > TimeRuler.autoAdjustDelay){
|
||||
this.sampleFrames = Math.min(TimeRuler.maxSampleFrames, this.sampleFrames);
|
||||
this.sampleFrames = Math.max(this.targetSampleFrames, (maxTime / frameSpan) + 1);
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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;
|
||||
|
||||
this._frameAdjust = 0;
|
||||
markerInfo.logs[i].min = 0;
|
||||
markerInfo.logs[i].max = 0;
|
||||
markerInfo.logs[i].avg = 0;
|
||||
|
||||
markerInfo.logs[i].samples = 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let msToPs = width / sampleSpan;
|
||||
let startY = position.y - (height - TimeRuler.barHeight);
|
||||
let y = startY;
|
||||
public render(position: Vector2 = this._position, width: number = this.width){
|
||||
egret.localStorage.setItem(this._frameKey, "0");
|
||||
|
||||
// TODO: draw
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志信息
|
||||
*/
|
||||
export class FrameLog {
|
||||
public bars: MarkerCollection[];
|
||||
|
||||
constructor() {
|
||||
this.bars = new Array<MarkerCollection>(TimeRuler.maxBars);
|
||||
this.bars.fill(new MarkerCollection(), 0, TimeRuler.maxBars);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记的集合
|
||||
*/
|
||||
export class MarkerCollection {
|
||||
public markers: Marker[] = new Array<Marker>(TimeRuler.maxSamples);
|
||||
public markCount: number = 0;
|
||||
public markerNests: number[] = new Array<number>(TimeRuler.maxNestCall);
|
||||
public nestCount: number = 0;
|
||||
|
||||
constructor(){
|
||||
this.markers.fill(new Marker(), 0, TimeRuler.maxSamples);
|
||||
this.markerNests.fill(0, 0, TimeRuler.maxNestCall);
|
||||
}
|
||||
}
|
||||
|
||||
export class Marker {
|
||||
public markerId: number = 0;
|
||||
public beginTime: number = 0;
|
||||
public endTime: number = 0;
|
||||
public color: number = 0x000000;
|
||||
}
|
||||
|
||||
export class MarkerInfo {
|
||||
public name: string;
|
||||
public logs: MarkerLog[] = new Array<MarkerLog>(TimeRuler.maxBars);
|
||||
|
||||
constructor(name) {
|
||||
this.name = name;
|
||||
this.logs.fill(new MarkerLog(), 0, TimeRuler.maxBars);
|
||||
}
|
||||
}
|
||||
|
||||
export class MarkerLog {
|
||||
public snapMin: number = 0;
|
||||
public snapMax: number = 0;
|
||||
public snapAvg: number = 0;
|
||||
public min: number = 0;
|
||||
public max: number = 0;
|
||||
public avg: number = 0;
|
||||
public samples: number = 0;
|
||||
public color: number = 0x000000;
|
||||
public initialized: boolean = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志信息
|
||||
*/
|
||||
class FrameLog {
|
||||
public bars: MarkerCollection[];
|
||||
|
||||
constructor() {
|
||||
this.bars = new Array<MarkerCollection>(TimeRuler.maxBars);
|
||||
this.bars.fill(new MarkerCollection(), 0, TimeRuler.maxBars);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记的集合
|
||||
*/
|
||||
class MarkerCollection {
|
||||
public markers: Marker[] = new Array<Marker>(TimeRuler.maxSamples);
|
||||
public markCount: number = 0;
|
||||
public markerNests: number[] = new Array<number>(TimeRuler.maxNestCall);
|
||||
public nestCount: number = 0;
|
||||
|
||||
constructor(){
|
||||
this.markers.fill(new Marker(), 0, TimeRuler.maxSamples);
|
||||
this.markerNests.fill(0, 0, TimeRuler.maxNestCall);
|
||||
}
|
||||
}
|
||||
|
||||
class Marker {
|
||||
public markerId: number = 0;
|
||||
public beginTime: number = 0;
|
||||
public endTime: number = 0;
|
||||
public color: number = 0x000000;
|
||||
}
|
||||
|
||||
class MarkerInfo {
|
||||
public name: string;
|
||||
public logs: MarkerLog[] = new Array<MarkerLog>(TimeRuler.maxBars);
|
||||
|
||||
constructor(name) {
|
||||
this.name = name;
|
||||
this.logs.fill(new MarkerLog(), 0, TimeRuler.maxBars);
|
||||
}
|
||||
}
|
||||
|
||||
class MarkerLog {
|
||||
public snapMin: number = 0;
|
||||
public snapMax: number = 0;
|
||||
public snapAvg: number = 0;
|
||||
public min: number = 0;
|
||||
public max: number = 0;
|
||||
public avg: number = 0;
|
||||
public samples: number = 0;
|
||||
public color: number = 0x000000;
|
||||
public initialized: boolean = false;
|
||||
}
|
||||
@@ -1,41 +1,43 @@
|
||||
class ContentManager {
|
||||
protected loadedAssets: Map<string, any> = new Map<string, any>();
|
||||
module es {
|
||||
export class ContentManager {
|
||||
protected loadedAssets: Map<string, any> = new Map<string, any>();
|
||||
|
||||
/** 异步加载资源 */
|
||||
public loadRes(name: string, local: boolean = true): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let res = this.loadedAssets.get(name);
|
||||
if (res) {
|
||||
resolve(res);
|
||||
return;
|
||||
}
|
||||
/** 异步加载资源 */
|
||||
public loadRes(name: string, local: boolean = true): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let res = this.loadedAssets.get(name);
|
||||
if (res) {
|
||||
resolve(res);
|
||||
return;
|
||||
}
|
||||
|
||||
if (local) {
|
||||
RES.getResAsync(name).then((data) => {
|
||||
this.loadedAssets.set(name, data);
|
||||
resolve(data);
|
||||
}).catch((err) => {
|
||||
console.error("资源加载错误:", name, err);
|
||||
reject(err);
|
||||
});
|
||||
} else {
|
||||
RES.getResByUrl(name).then((data) => {
|
||||
this.loadedAssets.set(name, data);
|
||||
resolve(data);
|
||||
}).catch((err) => {
|
||||
console.error("资源加载错误:", name, err);
|
||||
reject(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
if (local) {
|
||||
RES.getResAsync(name).then((data) => {
|
||||
this.loadedAssets.set(name, data);
|
||||
resolve(data);
|
||||
}).catch((err) => {
|
||||
console.error("资源加载错误:", name, err);
|
||||
reject(err);
|
||||
});
|
||||
} else {
|
||||
RES.getResByUrl(name).then((data) => {
|
||||
this.loadedAssets.set(name, data);
|
||||
resolve(data);
|
||||
}).catch((err) => {
|
||||
console.error("资源加载错误:", name, err);
|
||||
reject(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this.loadedAssets.forEach(value => {
|
||||
let assetsToRemove = value;
|
||||
assetsToRemove.dispose();
|
||||
});
|
||||
|
||||
this.loadedAssets.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this.loadedAssets.forEach(value => {
|
||||
let assetsToRemove = value;
|
||||
assetsToRemove.dispose();
|
||||
});
|
||||
|
||||
this.loadedAssets.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +1,61 @@
|
||||
/** 各种辅助方法来辅助绘图 */
|
||||
class DrawUtils {
|
||||
public static drawLine(shape: egret.Shape, start: Vector2, end: Vector2, color: number, thickness: number = 1){
|
||||
this.drawLineAngle(shape, start, MathHelper.angleBetweenVectors(start, end), Vector2.distance(start, end), color, thickness);
|
||||
}
|
||||
|
||||
public static drawLineAngle(shape: egret.Shape, start: Vector2, radians: number, length: number, color: number, thickness = 1){
|
||||
shape.graphics.beginFill(color);
|
||||
shape.graphics.drawRect(start.x, start.y, 1, 1);
|
||||
shape.graphics.endFill();
|
||||
|
||||
shape.scaleX = length;
|
||||
shape.scaleY = thickness;
|
||||
shape.$anchorOffsetX = 0;
|
||||
shape.$anchorOffsetY = 0;
|
||||
shape.rotation = radians;
|
||||
}
|
||||
|
||||
public static drawHollowRect(shape: egret.Shape, rect: Rectangle, color: number, thickness = 1){
|
||||
this.drawHollowRectR(shape, rect.x, rect.y, rect.width, rect.height, color, thickness);
|
||||
}
|
||||
|
||||
public static drawHollowRectR(shape: egret.Shape, x: number, y: number, width: number, height: number, color: number, thickness = 1){
|
||||
let tl = new Vector2(x, y).round();
|
||||
let tr = new Vector2(x + width, y).round();
|
||||
let br = new Vector2(x + width, y + height).round();
|
||||
let bl = new Vector2(x, y + height).round();
|
||||
|
||||
this.drawLine(shape, tl, tr, color, thickness);
|
||||
this.drawLine(shape, tr, br, color, thickness);
|
||||
this.drawLine(shape, br, bl, color, thickness);
|
||||
this.drawLine(shape, bl, tl, color, thickness);
|
||||
}
|
||||
|
||||
public static drawPixel(shape: egret.Shape, position: Vector2, color: number, size: number = 1){
|
||||
let destRect = new Rectangle(position.x, position.y, size, size);
|
||||
if (size != 1){
|
||||
destRect.x -= size * 0.5;
|
||||
destRect.y -= size * 0.5;
|
||||
module es {
|
||||
/** 各种辅助方法来辅助绘图 */
|
||||
export class DrawUtils {
|
||||
public static drawLine(shape: egret.Shape, start: Vector2, end: Vector2, color: number, thickness: number = 1){
|
||||
this.drawLineAngle(shape, start, MathHelper.angleBetweenVectors(start, end), Vector2.distance(start, end), color, thickness);
|
||||
}
|
||||
|
||||
shape.graphics.beginFill(color);
|
||||
shape.graphics.drawRect(destRect.x, destRect.y, destRect.width, destRect.height);
|
||||
shape.graphics.endFill();
|
||||
}
|
||||
public static drawLineAngle(shape: egret.Shape, start: Vector2, radians: number, length: number, color: number, thickness = 1){
|
||||
shape.graphics.beginFill(color);
|
||||
shape.graphics.drawRect(start.x, start.y, 1, 1);
|
||||
shape.graphics.endFill();
|
||||
|
||||
public static getColorMatrix(color: number): egret.ColorMatrixFilter{
|
||||
let colorMatrix = [
|
||||
1, 0, 0, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 0, 1, 0
|
||||
];
|
||||
colorMatrix[0] = Math.floor(color / 256 / 256) / 255;
|
||||
colorMatrix[6] = Math.floor(color / 256 % 256) / 255;
|
||||
colorMatrix[12] = color % 256 / 255;
|
||||
return new egret.ColorMatrixFilter(colorMatrix);
|
||||
shape.scaleX = length;
|
||||
shape.scaleY = thickness;
|
||||
shape.$anchorOffsetX = 0;
|
||||
shape.$anchorOffsetY = 0;
|
||||
shape.rotation = radians;
|
||||
}
|
||||
|
||||
public static drawHollowRect(shape: egret.Shape, rect: Rectangle, color: number, thickness = 1){
|
||||
this.drawHollowRectR(shape, rect.x, rect.y, rect.width, rect.height, color, thickness);
|
||||
}
|
||||
|
||||
public static drawHollowRectR(shape: egret.Shape, x: number, y: number, width: number, height: number, color: number, thickness = 1){
|
||||
let tl = new Vector2(x, y).round();
|
||||
let tr = new Vector2(x + width, y).round();
|
||||
let br = new Vector2(x + width, y + height).round();
|
||||
let bl = new Vector2(x, y + height).round();
|
||||
|
||||
this.drawLine(shape, tl, tr, color, thickness);
|
||||
this.drawLine(shape, tr, br, color, thickness);
|
||||
this.drawLine(shape, br, bl, color, thickness);
|
||||
this.drawLine(shape, bl, tl, color, thickness);
|
||||
}
|
||||
|
||||
public static drawPixel(shape: egret.Shape, position: Vector2, color: number, size: number = 1){
|
||||
let destRect = new Rectangle(position.x, position.y, size, size);
|
||||
if (size != 1){
|
||||
destRect.x -= size * 0.5;
|
||||
destRect.y -= size * 0.5;
|
||||
}
|
||||
|
||||
shape.graphics.beginFill(color);
|
||||
shape.graphics.drawRect(destRect.x, destRect.y, destRect.width, destRect.height);
|
||||
shape.graphics.endFill();
|
||||
}
|
||||
|
||||
public static getColorMatrix(color: number): egret.ColorMatrixFilter{
|
||||
let colorMatrix = [
|
||||
1, 0, 0, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 0, 1, 0
|
||||
];
|
||||
colorMatrix[0] = Math.floor(color / 256 / 256) / 255;
|
||||
colorMatrix[6] = Math.floor(color / 256 % 256) / 255;
|
||||
colorMatrix[12] = color % 256 / 255;
|
||||
return new egret.ColorMatrixFilter(colorMatrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,69 @@
|
||||
/**
|
||||
* 用于事件管理
|
||||
*/
|
||||
class Emitter<T> {
|
||||
private _messageTable: Map<T, FuncPack[]>;
|
||||
|
||||
constructor(){
|
||||
this._messageTable = new Map<T, FuncPack[]>();
|
||||
}
|
||||
|
||||
module es {
|
||||
/**
|
||||
* 开始监听项
|
||||
* @param eventType 监听类型
|
||||
* @param handler 监听函数
|
||||
* @param context 监听上下文
|
||||
* 用于包装事件的一个小类
|
||||
*/
|
||||
public addObserver(eventType: T, handler: Function, context: any){
|
||||
let list: FuncPack[] = this._messageTable.get(eventType);
|
||||
if (!list){
|
||||
list = [];
|
||||
this._messageTable.set(eventType, list);
|
||||
export class FuncPack {
|
||||
/** 函数 */
|
||||
public func: Function;
|
||||
/** 上下文 */
|
||||
public context: any;
|
||||
|
||||
constructor(func: Function, context: any){
|
||||
this.func = func;
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 用于事件管理
|
||||
*/
|
||||
export class Emitter<T> {
|
||||
private _messageTable: Map<T, FuncPack[]>;
|
||||
|
||||
constructor(){
|
||||
this._messageTable = new Map<T, FuncPack[]>();
|
||||
}
|
||||
|
||||
if (list.contains(handler))
|
||||
console.warn("您试图添加相同的观察者两次");
|
||||
list.push(new FuncPack(handler, context));
|
||||
}
|
||||
/**
|
||||
* 开始监听项
|
||||
* @param eventType 监听类型
|
||||
* @param handler 监听函数
|
||||
* @param context 监听上下文
|
||||
*/
|
||||
public addObserver(eventType: T, handler: Function, context: any){
|
||||
let list: FuncPack[] = this._messageTable.get(eventType);
|
||||
if (!list){
|
||||
list = [];
|
||||
this._messageTable.set(eventType, list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除监听项
|
||||
* @param eventType 事件类型
|
||||
* @param handler 事件函数
|
||||
*/
|
||||
public removeObserver(eventType: T, handler: Function){
|
||||
let messageData = this._messageTable.get(eventType);
|
||||
let index = messageData.findIndex(data => data.func == handler);
|
||||
if (index != -1)
|
||||
messageData.removeAt(index);
|
||||
}
|
||||
if (list.contains(handler))
|
||||
console.warn("您试图添加相同的观察者两次");
|
||||
list.push(new FuncPack(handler, context));
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发该事件
|
||||
* @param eventType 事件类型
|
||||
* @param data 事件数据
|
||||
*/
|
||||
public emit(eventType: T, data?: any){
|
||||
let list: FuncPack[] = this._messageTable.get(eventType);
|
||||
if (list){
|
||||
for (let i = list.length - 1; i >= 0; i --)
|
||||
list[i].func.call(list[i].context, data);
|
||||
/**
|
||||
* 移除监听项
|
||||
* @param eventType 事件类型
|
||||
* @param handler 事件函数
|
||||
*/
|
||||
public removeObserver(eventType: T, handler: Function){
|
||||
let messageData = this._messageTable.get(eventType);
|
||||
let index = messageData.findIndex(data => data.func == handler);
|
||||
if (index != -1)
|
||||
messageData.removeAt(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发该事件
|
||||
* @param eventType 事件类型
|
||||
* @param data 事件数据
|
||||
*/
|
||||
public emit(eventType: T, data?: any){
|
||||
let list: FuncPack[] = this._messageTable.get(eventType);
|
||||
if (list){
|
||||
for (let i = list.length - 1; i >= 0; i --)
|
||||
list[i].func.call(list[i].context, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于包装事件的一个小类
|
||||
*/
|
||||
class FuncPack {
|
||||
/** 函数 */
|
||||
public func: Function;
|
||||
/** 上下文 */
|
||||
public context: any;
|
||||
|
||||
constructor(func: Function, context: any){
|
||||
this.func = func;
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,48 @@
|
||||
class GlobalManager {
|
||||
public static globalManagers: GlobalManager[] = [];
|
||||
private _enabled: boolean;
|
||||
module es {
|
||||
export class GlobalManager {
|
||||
public static globalManagers: GlobalManager[] = [];
|
||||
private _enabled: boolean;
|
||||
|
||||
public get enabled(){
|
||||
return this._enabled;
|
||||
}
|
||||
public set enabled(value: boolean){
|
||||
this.setEnabled(value);
|
||||
}
|
||||
public setEnabled(isEnabled: boolean){
|
||||
if (this._enabled != isEnabled){
|
||||
this._enabled = isEnabled;
|
||||
if (this._enabled){
|
||||
this.onEnabled();
|
||||
} else {
|
||||
this.onDisabled();
|
||||
public get enabled(){
|
||||
return this._enabled;
|
||||
}
|
||||
public set enabled(value: boolean){
|
||||
this.setEnabled(value);
|
||||
}
|
||||
public setEnabled(isEnabled: boolean){
|
||||
if (this._enabled != isEnabled){
|
||||
this._enabled = isEnabled;
|
||||
if (this._enabled){
|
||||
this.onEnabled();
|
||||
} else {
|
||||
this.onDisabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public onEnabled(){}
|
||||
public onEnabled(){}
|
||||
|
||||
public onDisabled(){}
|
||||
public onDisabled(){}
|
||||
|
||||
public update(){}
|
||||
public update(){}
|
||||
|
||||
public static registerGlobalManager(manager: GlobalManager){
|
||||
this.globalManagers.push(manager);
|
||||
manager.enabled = true;
|
||||
}
|
||||
|
||||
public static unregisterGlobalManager(manager: GlobalManager){
|
||||
this.globalManagers.remove(manager);
|
||||
manager.enabled = false;
|
||||
}
|
||||
|
||||
public static getGlobalManager<T extends GlobalManager>(type){
|
||||
for (let i = 0; i < this.globalManagers.length; i ++){
|
||||
if (this.globalManagers[i] instanceof type)
|
||||
return this.globalManagers[i] as T;
|
||||
public static registerGlobalManager(manager: GlobalManager){
|
||||
this.globalManagers.push(manager);
|
||||
manager.enabled = true;
|
||||
}
|
||||
|
||||
return null;
|
||||
public static unregisterGlobalManager(manager: GlobalManager){
|
||||
this.globalManagers.remove(manager);
|
||||
manager.enabled = false;
|
||||
}
|
||||
|
||||
public static getGlobalManager<T extends GlobalManager>(type){
|
||||
for (let i = 0; i < this.globalManagers.length; i ++){
|
||||
if (this.globalManagers[i] instanceof type)
|
||||
return this.globalManagers[i] as T;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,147 +1,149 @@
|
||||
class TouchState {
|
||||
public x = 0;
|
||||
public y = 0;
|
||||
public touchPoint: number = -1;
|
||||
public touchDown: boolean = false;
|
||||
public get position(){
|
||||
return new Vector2(this.x, this.y);
|
||||
}
|
||||
|
||||
public reset(){
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.touchDown = false;
|
||||
this.touchPoint = -1;
|
||||
}
|
||||
}
|
||||
|
||||
class Input {
|
||||
private static _init: boolean = false;
|
||||
private static _stage: egret.Stage;
|
||||
private static _previousTouchState: TouchState = new TouchState();
|
||||
private static _gameTouchs: TouchState[] = [];
|
||||
private static _resolutionOffset: Vector2 = new Vector2();
|
||||
private static _resolutionScale: Vector2 = Vector2.one;
|
||||
private static _touchIndex: number = 0;
|
||||
private static _totalTouchCount: number = 0;
|
||||
/** 返回第一个触摸点的坐标 */
|
||||
public static get touchPosition(){
|
||||
if (!this._gameTouchs[0])
|
||||
return Vector2.zero;
|
||||
return this._gameTouchs[0].position;
|
||||
}
|
||||
/** 获取最大触摸数 */
|
||||
public static get maxSupportedTouch(){
|
||||
return this._stage.maxTouches;
|
||||
}
|
||||
/**
|
||||
* 设置最大触摸数
|
||||
*/
|
||||
public static set maxSupportedTouch(value: number){
|
||||
this._stage.maxTouches = value;
|
||||
this.initTouchCache();
|
||||
}
|
||||
/** 获取缩放值 默认为1 */
|
||||
public static get resolutionScale(){
|
||||
return this._resolutionScale;
|
||||
}
|
||||
/** 当前触摸点数量 */
|
||||
public static get totalTouchCount(){
|
||||
return this._totalTouchCount;
|
||||
}
|
||||
/**
|
||||
* 触摸列表 存放最大个数量触摸点信息
|
||||
* 可通过判断touchPoint是否为-1 来确定是否为有触摸
|
||||
* 通过判断touchDown 判断触摸点是否有按下
|
||||
*/
|
||||
public static get gameTouchs(){
|
||||
return this._gameTouchs;
|
||||
}
|
||||
|
||||
/** 获取第一个触摸点距离上次距离的增量 */
|
||||
public static get touchPositionDelta(){
|
||||
let delta = Vector2.subtract(this.touchPosition, this._previousTouchState.position);
|
||||
if (delta.length() > 0){
|
||||
this.setpreviousTouchState(this._gameTouchs[0]);
|
||||
module es {
|
||||
export class TouchState {
|
||||
public x = 0;
|
||||
public y = 0;
|
||||
public touchPoint: number = -1;
|
||||
public touchDown: boolean = false;
|
||||
public get position(){
|
||||
return new Vector2(this.x, this.y);
|
||||
}
|
||||
return delta;
|
||||
}
|
||||
|
||||
public static initialize(stage: egret.Stage){
|
||||
if (this._init)
|
||||
return;
|
||||
|
||||
this._init = true;
|
||||
this._stage = stage;
|
||||
this._stage.addEventListener(egret.TouchEvent.TOUCH_BEGIN, this.touchBegin, this);
|
||||
this._stage.addEventListener(egret.TouchEvent.TOUCH_MOVE, this.touchMove, this);
|
||||
this._stage.addEventListener(egret.TouchEvent.TOUCH_END, this.touchEnd, this);
|
||||
this._stage.addEventListener(egret.TouchEvent.TOUCH_CANCEL, this.touchEnd, this);
|
||||
this._stage.addEventListener(egret.TouchEvent.TOUCH_RELEASE_OUTSIDE, this.touchEnd, this);
|
||||
|
||||
this.initTouchCache();
|
||||
}
|
||||
|
||||
private static initTouchCache(){
|
||||
this._totalTouchCount = 0;
|
||||
this._touchIndex = 0;
|
||||
this._gameTouchs.length = 0;
|
||||
for (let i = 0; i < this.maxSupportedTouch; i ++){
|
||||
this._gameTouchs.push(new TouchState());
|
||||
public reset(){
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.touchDown = false;
|
||||
this.touchPoint = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static touchBegin(evt: egret.TouchEvent){
|
||||
if (this._touchIndex < this.maxSupportedTouch){
|
||||
this._gameTouchs[this._touchIndex].touchPoint = evt.touchPointID;
|
||||
this._gameTouchs[this._touchIndex].touchDown = evt.touchDown;
|
||||
this._gameTouchs[this._touchIndex].x = evt.stageX;
|
||||
this._gameTouchs[this._touchIndex].y = evt.stageY;
|
||||
if (this._touchIndex == 0){
|
||||
export class Input {
|
||||
private static _init: boolean = false;
|
||||
private static _stage: egret.Stage;
|
||||
private static _previousTouchState: TouchState = new TouchState();
|
||||
private static _gameTouchs: TouchState[] = [];
|
||||
private static _resolutionOffset: Vector2 = new Vector2();
|
||||
private static _resolutionScale: Vector2 = Vector2.one;
|
||||
private static _touchIndex: number = 0;
|
||||
private static _totalTouchCount: number = 0;
|
||||
/** 返回第一个触摸点的坐标 */
|
||||
public static get touchPosition(){
|
||||
if (!this._gameTouchs[0])
|
||||
return Vector2.zero;
|
||||
return this._gameTouchs[0].position;
|
||||
}
|
||||
/** 获取最大触摸数 */
|
||||
public static get maxSupportedTouch(){
|
||||
return this._stage.maxTouches;
|
||||
}
|
||||
/**
|
||||
* 设置最大触摸数
|
||||
*/
|
||||
public static set maxSupportedTouch(value: number){
|
||||
this._stage.maxTouches = value;
|
||||
this.initTouchCache();
|
||||
}
|
||||
/** 获取缩放值 默认为1 */
|
||||
public static get resolutionScale(){
|
||||
return this._resolutionScale;
|
||||
}
|
||||
/** 当前触摸点数量 */
|
||||
public static get totalTouchCount(){
|
||||
return this._totalTouchCount;
|
||||
}
|
||||
/**
|
||||
* 触摸列表 存放最大个数量触摸点信息
|
||||
* 可通过判断touchPoint是否为-1 来确定是否为有触摸
|
||||
* 通过判断touchDown 判断触摸点是否有按下
|
||||
*/
|
||||
public static get gameTouchs(){
|
||||
return this._gameTouchs;
|
||||
}
|
||||
|
||||
/** 获取第一个触摸点距离上次距离的增量 */
|
||||
public static get touchPositionDelta(){
|
||||
let delta = Vector2.subtract(this.touchPosition, this._previousTouchState.position);
|
||||
if (delta.length() > 0){
|
||||
this.setpreviousTouchState(this._gameTouchs[0]);
|
||||
}
|
||||
this._touchIndex ++;
|
||||
this._totalTouchCount ++;
|
||||
}
|
||||
}
|
||||
|
||||
private static touchMove(evt: egret.TouchEvent){
|
||||
if (evt.touchPointID == this._gameTouchs[0].touchPoint){
|
||||
this.setpreviousTouchState(this._gameTouchs[0]);
|
||||
return delta;
|
||||
}
|
||||
|
||||
let touchIndex = this._gameTouchs.findIndex(touch => touch.touchPoint == evt.touchPointID);
|
||||
if (touchIndex != -1){
|
||||
let touchData = this._gameTouchs[touchIndex];
|
||||
touchData.x = evt.stageX;
|
||||
touchData.y = evt.stageY;
|
||||
}
|
||||
}
|
||||
public static initialize(stage: egret.Stage){
|
||||
if (this._init)
|
||||
return;
|
||||
|
||||
private static touchEnd(evt: egret.TouchEvent){
|
||||
let touchIndex = this._gameTouchs.findIndex(touch => touch.touchPoint == evt.touchPointID);
|
||||
if (touchIndex != -1){
|
||||
let touchData = this._gameTouchs[touchIndex];
|
||||
touchData.reset();
|
||||
if (touchIndex == 0)
|
||||
this._previousTouchState.reset();
|
||||
this._totalTouchCount --;
|
||||
if (this.totalTouchCount == 0){
|
||||
this._touchIndex = 0;
|
||||
this._init = true;
|
||||
this._stage = stage;
|
||||
this._stage.addEventListener(egret.TouchEvent.TOUCH_BEGIN, this.touchBegin, this);
|
||||
this._stage.addEventListener(egret.TouchEvent.TOUCH_MOVE, this.touchMove, this);
|
||||
this._stage.addEventListener(egret.TouchEvent.TOUCH_END, this.touchEnd, this);
|
||||
this._stage.addEventListener(egret.TouchEvent.TOUCH_CANCEL, this.touchEnd, this);
|
||||
this._stage.addEventListener(egret.TouchEvent.TOUCH_RELEASE_OUTSIDE, this.touchEnd, this);
|
||||
|
||||
this.initTouchCache();
|
||||
}
|
||||
|
||||
private static initTouchCache(){
|
||||
this._totalTouchCount = 0;
|
||||
this._touchIndex = 0;
|
||||
this._gameTouchs.length = 0;
|
||||
for (let i = 0; i < this.maxSupportedTouch; i ++){
|
||||
this._gameTouchs.push(new TouchState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static setpreviousTouchState(touchState: TouchState){
|
||||
this._previousTouchState = new TouchState();
|
||||
this._previousTouchState.x = touchState.position.x;
|
||||
this._previousTouchState.y = touchState.position.y;
|
||||
this._previousTouchState.touchPoint = touchState.touchPoint;
|
||||
this._previousTouchState.touchDown = touchState.touchDown;
|
||||
}
|
||||
private static touchBegin(evt: egret.TouchEvent){
|
||||
if (this._touchIndex < this.maxSupportedTouch){
|
||||
this._gameTouchs[this._touchIndex].touchPoint = evt.touchPointID;
|
||||
this._gameTouchs[this._touchIndex].touchDown = evt.touchDown;
|
||||
this._gameTouchs[this._touchIndex].x = evt.stageX;
|
||||
this._gameTouchs[this._touchIndex].y = evt.stageY;
|
||||
if (this._touchIndex == 0){
|
||||
this.setpreviousTouchState(this._gameTouchs[0]);
|
||||
}
|
||||
this._touchIndex ++;
|
||||
this._totalTouchCount ++;
|
||||
}
|
||||
}
|
||||
|
||||
public static scaledPosition(position: Vector2){
|
||||
let scaledPos = new Vector2(position.x - this._resolutionOffset.x, position.y - this._resolutionOffset.y);
|
||||
return Vector2.multiply(scaledPos, this.resolutionScale);
|
||||
private static touchMove(evt: egret.TouchEvent){
|
||||
if (evt.touchPointID == this._gameTouchs[0].touchPoint){
|
||||
this.setpreviousTouchState(this._gameTouchs[0]);
|
||||
}
|
||||
|
||||
let touchIndex = this._gameTouchs.findIndex(touch => touch.touchPoint == evt.touchPointID);
|
||||
if (touchIndex != -1){
|
||||
let touchData = this._gameTouchs[touchIndex];
|
||||
touchData.x = evt.stageX;
|
||||
touchData.y = evt.stageY;
|
||||
}
|
||||
}
|
||||
|
||||
private static touchEnd(evt: egret.TouchEvent){
|
||||
let touchIndex = this._gameTouchs.findIndex(touch => touch.touchPoint == evt.touchPointID);
|
||||
if (touchIndex != -1){
|
||||
let touchData = this._gameTouchs[touchIndex];
|
||||
touchData.reset();
|
||||
if (touchIndex == 0)
|
||||
this._previousTouchState.reset();
|
||||
this._totalTouchCount --;
|
||||
if (this.totalTouchCount == 0){
|
||||
this._touchIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static setpreviousTouchState(touchState: TouchState){
|
||||
this._previousTouchState = new TouchState();
|
||||
this._previousTouchState.x = touchState.position.x;
|
||||
this._previousTouchState.y = touchState.position.y;
|
||||
this._previousTouchState.touchPoint = touchState.touchPoint;
|
||||
this._previousTouchState.touchDown = touchState.touchDown;
|
||||
}
|
||||
|
||||
public static scaledPosition(position: Vector2){
|
||||
let scaledPos = new Vector2(position.x - this._resolutionOffset.x, position.y - this._resolutionOffset.y);
|
||||
return Vector2.multiply(scaledPos, this.resolutionScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +1,56 @@
|
||||
/**
|
||||
* 可以用于列表池的简单类
|
||||
*/
|
||||
class ListPool {
|
||||
private static readonly _objectQueue = [];
|
||||
|
||||
module es {
|
||||
/**
|
||||
* 预热缓存,使用最大的cacheCount对象填充缓存
|
||||
* @param cacheCount
|
||||
* 可以用于列表池的简单类
|
||||
*/
|
||||
public static warmCache(cacheCount: number){
|
||||
cacheCount -= this._objectQueue.length;
|
||||
if (cacheCount > 0){
|
||||
for (let i = 0; i < cacheCount; i ++){
|
||||
this._objectQueue.unshift([]);
|
||||
export class ListPool {
|
||||
private static readonly _objectQueue = [];
|
||||
|
||||
/**
|
||||
* 预热缓存,使用最大的cacheCount对象填充缓存
|
||||
* @param cacheCount
|
||||
*/
|
||||
public static warmCache(cacheCount: number){
|
||||
cacheCount -= this._objectQueue.length;
|
||||
if (cacheCount > 0){
|
||||
for (let i = 0; i < cacheCount; i ++){
|
||||
this._objectQueue.unshift([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将缓存修剪为cacheCount项目
|
||||
* @param cacheCount
|
||||
*/
|
||||
public static trimCache(cacheCount){
|
||||
while (cacheCount > this._objectQueue.length)
|
||||
this._objectQueue.shift();
|
||||
}
|
||||
/**
|
||||
* 将缓存修剪为cacheCount项目
|
||||
* @param cacheCount
|
||||
*/
|
||||
public static trimCache(cacheCount){
|
||||
while (cacheCount > this._objectQueue.length)
|
||||
this._objectQueue.shift();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
*/
|
||||
public static clearCache(){
|
||||
this._objectQueue.length = 0;
|
||||
}
|
||||
/**
|
||||
* 清除缓存
|
||||
*/
|
||||
public static clearCache(){
|
||||
this._objectQueue.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果可以的话,从堆栈中弹出一个项
|
||||
*/
|
||||
public static obtain<T>(): T[] {
|
||||
if (this._objectQueue.length > 0)
|
||||
return this._objectQueue.shift();
|
||||
/**
|
||||
* 如果可以的话,从堆栈中弹出一个项
|
||||
*/
|
||||
public static obtain<T>(): T[] {
|
||||
if (this._objectQueue.length > 0)
|
||||
return this._objectQueue.shift();
|
||||
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 将项推回堆栈
|
||||
* @param obj
|
||||
*/
|
||||
public static free<T>(obj: Array<T>){
|
||||
this._objectQueue.unshift(obj);
|
||||
obj.length = 0;
|
||||
/**
|
||||
* 将项推回堆栈
|
||||
* @param obj
|
||||
*/
|
||||
public static free<T>(obj: Array<T>){
|
||||
this._objectQueue.unshift(obj);
|
||||
obj.length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
/**
|
||||
* 用于管理一对对象的简单DTO
|
||||
*/
|
||||
class Pair<T> {
|
||||
public first: T;
|
||||
public second: T;
|
||||
module es {
|
||||
/**
|
||||
* 用于管理一对对象的简单DTO
|
||||
*/
|
||||
export class Pair<T> {
|
||||
public first: T;
|
||||
public second: T;
|
||||
|
||||
constructor(first: T, second: T){
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
constructor(first: T, second: T){
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
public clear(){
|
||||
this.first = this.second = null;
|
||||
}
|
||||
public clear(){
|
||||
this.first = this.second = null;
|
||||
}
|
||||
|
||||
public equals(other: Pair<T>){
|
||||
return this.first == other.first && this.second == other.second;
|
||||
public equals(other: Pair<T>){
|
||||
return this.first == other.first && this.second == other.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
class RectangleExt {
|
||||
/**
|
||||
* 计算两个矩形的并集。结果将是一个包含其他两个的矩形。
|
||||
* @param first
|
||||
* @param point
|
||||
*/
|
||||
public static union(first: Rectangle, point: Vector2){
|
||||
let rect = new Rectangle(point.x, point.y, 0, 0);
|
||||
// let rectResult = first.union(rect);
|
||||
let result = new Rectangle();
|
||||
result.x = Math.min(first.x, rect.x);
|
||||
result.y = Math.min(first.y, rect.y);
|
||||
result.width = Math.max(first.right, rect.right) - result.x;
|
||||
result.height = Math.max(first.bottom, result.bottom) - result.y;
|
||||
return result;
|
||||
module es {
|
||||
export class RectangleExt {
|
||||
/**
|
||||
* 计算两个矩形的并集。结果将是一个包含其他两个的矩形。
|
||||
* @param first
|
||||
* @param point
|
||||
*/
|
||||
public static union(first: Rectangle, point: Vector2){
|
||||
let rect = new Rectangle(point.x, point.y, 0, 0);
|
||||
// let rectResult = first.union(rect);
|
||||
let result = new Rectangle();
|
||||
result.x = Math.min(first.x, rect.x);
|
||||
result.y = Math.min(first.y, rect.y);
|
||||
result.width = Math.max(first.right, rect.right) - result.x;
|
||||
result.height = Math.max(first.bottom, result.bottom) - result.y;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,111 +1,113 @@
|
||||
/**
|
||||
* 三角剖分
|
||||
*/
|
||||
class Triangulator {
|
||||
/**
|
||||
* 最后一次三角调用中使用的点列表的三角形列表项的索引
|
||||
*/
|
||||
public triangleIndices: number[] = [];
|
||||
|
||||
private _triPrev: number[] = new Array<number>(12);
|
||||
private _triNext: number[] = new Array<number>(12);
|
||||
|
||||
module es {
|
||||
/**
|
||||
* 计算一个三角形列表,该列表完全覆盖给定点集所包含的区域。如果点不是CCW,则将arePointsCCW参数传递为false
|
||||
* @param points 定义封闭路径的点列表
|
||||
* @param arePointsCCW
|
||||
* 三角剖分
|
||||
*/
|
||||
public triangulate(points: Vector2[], arePointsCCW: boolean = true){
|
||||
let count = points.length;
|
||||
export class Triangulator {
|
||||
/**
|
||||
* 最后一次三角调用中使用的点列表的三角形列表项的索引
|
||||
*/
|
||||
public triangleIndices: number[] = [];
|
||||
|
||||
// 设置前一个链接和下一个链接
|
||||
this.initialize(count);
|
||||
private _triPrev: number[] = new Array<number>(12);
|
||||
private _triNext: number[] = new Array<number>(12);
|
||||
|
||||
// 非三角的多边形断路器
|
||||
let iterations = 0;
|
||||
/**
|
||||
* 计算一个三角形列表,该列表完全覆盖给定点集所包含的区域。如果点不是CCW,则将arePointsCCW参数传递为false
|
||||
* @param points 定义封闭路径的点列表
|
||||
* @param arePointsCCW
|
||||
*/
|
||||
public triangulate(points: Vector2[], arePointsCCW: boolean = true){
|
||||
let count = points.length;
|
||||
|
||||
// 从0开始
|
||||
let index = 0;
|
||||
// 设置前一个链接和下一个链接
|
||||
this.initialize(count);
|
||||
|
||||
// 继续移除所有的三角形,直到只剩下一个三角形
|
||||
while (count > 3 && iterations < 500){
|
||||
iterations ++;
|
||||
// 非三角的多边形断路器
|
||||
let iterations = 0;
|
||||
|
||||
let isEar = true;
|
||||
let a = points[this._triPrev[index]];
|
||||
let b = points[index];
|
||||
let c = points[this._triNext[index]];
|
||||
// 从0开始
|
||||
let index = 0;
|
||||
|
||||
if (Vector2Ext.isTriangleCCW(a, b, c)){
|
||||
let k = this._triNext[this._triNext[index]];
|
||||
do {
|
||||
if (Triangulator.testPointTriangle(points[k], a, b, c)){
|
||||
isEar = false;
|
||||
break;
|
||||
}
|
||||
// 继续移除所有的三角形,直到只剩下一个三角形
|
||||
while (count > 3 && iterations < 500){
|
||||
iterations ++;
|
||||
|
||||
k = this._triNext[k];
|
||||
} while (k != this._triPrev[index]);
|
||||
}else{
|
||||
isEar = false;
|
||||
let isEar = true;
|
||||
let a = points[this._triPrev[index]];
|
||||
let b = points[index];
|
||||
let c = points[this._triNext[index]];
|
||||
|
||||
if (Vector2Ext.isTriangleCCW(a, b, c)){
|
||||
let k = this._triNext[this._triNext[index]];
|
||||
do {
|
||||
if (Triangulator.testPointTriangle(points[k], a, b, c)){
|
||||
isEar = false;
|
||||
break;
|
||||
}
|
||||
|
||||
k = this._triNext[k];
|
||||
} while (k != this._triPrev[index]);
|
||||
}else{
|
||||
isEar = false;
|
||||
}
|
||||
|
||||
if (isEar){
|
||||
this.triangleIndices.push(this._triPrev[index]);
|
||||
this.triangleIndices.push(index);
|
||||
this.triangleIndices.push(this._triNext[index]);
|
||||
|
||||
// 删除vert通过重定向相邻vert的上一个和下一个链接,从而减少vertext计数
|
||||
this._triNext[this._triPrev[index]] = this._triNext[index];
|
||||
this._triPrev[this._triNext[index]] = this._triPrev[index];
|
||||
count --;
|
||||
|
||||
// 接下来访问前一个vert
|
||||
index = this._triPrev[index];
|
||||
}else{
|
||||
index = this._triNext[index];
|
||||
}
|
||||
}
|
||||
|
||||
if (isEar){
|
||||
this.triangleIndices.push(this._triPrev[index]);
|
||||
this.triangleIndices.push(index);
|
||||
this.triangleIndices.push(this._triNext[index]);
|
||||
this.triangleIndices.push(this._triPrev[index]);
|
||||
this.triangleIndices.push(index);
|
||||
this.triangleIndices.push(this._triNext[index]);
|
||||
|
||||
// 删除vert通过重定向相邻vert的上一个和下一个链接,从而减少vertext计数
|
||||
this._triNext[this._triPrev[index]] = this._triNext[index];
|
||||
this._triPrev[this._triNext[index]] = this._triPrev[index];
|
||||
count --;
|
||||
if (!arePointsCCW)
|
||||
this.triangleIndices.reverse();
|
||||
}
|
||||
|
||||
// 接下来访问前一个vert
|
||||
index = this._triPrev[index];
|
||||
}else{
|
||||
index = this._triNext[index];
|
||||
private initialize(count: number){
|
||||
this.triangleIndices.length = 0;
|
||||
|
||||
if (this._triNext.length < count){
|
||||
this._triNext.reverse();
|
||||
this._triNext = new Array<number>(Math.max(this._triNext.length * 2, count));
|
||||
}
|
||||
if (this._triPrev.length < count){
|
||||
this._triPrev.reverse();
|
||||
this._triPrev = new Array<number>(Math.max(this._triPrev.length * 2, count));
|
||||
}
|
||||
|
||||
for (let i = 0; i < count;i ++){
|
||||
this._triPrev[i] = i - 1;
|
||||
this._triNext[i] = i + 1;
|
||||
}
|
||||
|
||||
this._triPrev[0] = count - 1;
|
||||
this._triNext[count - 1] = 0;
|
||||
}
|
||||
|
||||
this.triangleIndices.push(this._triPrev[index]);
|
||||
this.triangleIndices.push(index);
|
||||
this.triangleIndices.push(this._triNext[index]);
|
||||
public static testPointTriangle(point: Vector2, a: Vector2, b: Vector2, c: Vector2): boolean{
|
||||
if (Vector2Ext.cross(Vector2.subtract(point, a), Vector2.subtract(b, a)) < 0)
|
||||
return false;
|
||||
|
||||
if (!arePointsCCW)
|
||||
this.triangleIndices.reverse();
|
||||
}
|
||||
if (Vector2Ext.cross(Vector2.subtract(point, b), Vector2.subtract(c, b)) < 0)
|
||||
return false;
|
||||
|
||||
private initialize(count: number){
|
||||
this.triangleIndices.length = 0;
|
||||
if (Vector2Ext.cross(Vector2.subtract(point, c), Vector2.subtract(a, c)) < 0)
|
||||
return false;
|
||||
|
||||
if (this._triNext.length < count){
|
||||
this._triNext.reverse();
|
||||
this._triNext = new Array<number>(Math.max(this._triNext.length * 2, count));
|
||||
}
|
||||
if (this._triPrev.length < count){
|
||||
this._triPrev.reverse();
|
||||
this._triPrev = new Array<number>(Math.max(this._triPrev.length * 2, count));
|
||||
return true;
|
||||
}
|
||||
|
||||
for (let i = 0; i < count;i ++){
|
||||
this._triPrev[i] = i - 1;
|
||||
this._triNext[i] = i + 1;
|
||||
}
|
||||
|
||||
this._triPrev[0] = count - 1;
|
||||
this._triNext[count - 1] = 0;
|
||||
}
|
||||
|
||||
public static testPointTriangle(point: Vector2, a: Vector2, b: Vector2, c: Vector2): boolean{
|
||||
if (Vector2Ext.cross(Vector2.subtract(point, a), Vector2.subtract(b, a)) < 0)
|
||||
return false;
|
||||
|
||||
if (Vector2Ext.cross(Vector2.subtract(point, b), Vector2.subtract(c, b)) < 0)
|
||||
return false;
|
||||
|
||||
if (Vector2Ext.cross(Vector2.subtract(point, c), Vector2.subtract(a, c)) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +1,60 @@
|
||||
class Vector2Ext {
|
||||
/**
|
||||
* 检查三角形是CCW还是CW
|
||||
* @param a
|
||||
* @param center
|
||||
* @param c
|
||||
*/
|
||||
public static isTriangleCCW(a: Vector2, center: Vector2, c: Vector2) {
|
||||
return this.cross(Vector2.subtract(center, a), Vector2.subtract(c, center)) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算二维伪叉乘点(Perp(u), v)
|
||||
* @param u
|
||||
* @param v
|
||||
*/
|
||||
public static cross(u: Vector2, v: Vector2) {
|
||||
return u.y * v.x - u.x * v.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回与传入向量垂直的向量
|
||||
* @param first
|
||||
* @param second
|
||||
*/
|
||||
public static perpendicular(first: Vector2, second: Vector2) {
|
||||
return new Vector2(-1 * (second.y - first.y), second.x - first.x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vector2的临时解决方案
|
||||
* 标准化把向量弄乱了
|
||||
* @param vec
|
||||
*/
|
||||
public static normalize(vec: Vector2) {
|
||||
let magnitude = Math.sqrt((vec.x * vec.x) + (vec.y * vec.y));
|
||||
if (magnitude > MathHelper.Epsilon) {
|
||||
vec = Vector2.divide(vec, new Vector2(magnitude));
|
||||
} else {
|
||||
vec.x = vec.y = 0;
|
||||
module es {
|
||||
export class Vector2Ext {
|
||||
/**
|
||||
* 检查三角形是CCW还是CW
|
||||
* @param a
|
||||
* @param center
|
||||
* @param c
|
||||
*/
|
||||
public static isTriangleCCW(a: Vector2, center: Vector2, c: Vector2) {
|
||||
return this.cross(Vector2.subtract(center, a), Vector2.subtract(c, center)) < 0;
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
/**
|
||||
* 计算二维伪叉乘点(Perp(u), v)
|
||||
* @param u
|
||||
* @param v
|
||||
*/
|
||||
public static cross(u: Vector2, v: Vector2) {
|
||||
return u.y * v.x - u.x * v.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过指定的矩阵对Vector2的数组中的向量应用变换,并将结果放置在另一个数组中。
|
||||
* @param sourceArray
|
||||
* @param sourceIndex
|
||||
* @param matrix
|
||||
* @param destinationArray
|
||||
* @param destinationIndex
|
||||
* @param length
|
||||
*/
|
||||
public static transformA(sourceArray: Vector2[], sourceIndex: number, matrix: Matrix2D,
|
||||
destinationArray: Vector2[], destinationIndex: number, length: number) {
|
||||
/**
|
||||
* 返回与传入向量垂直的向量
|
||||
* @param first
|
||||
* @param second
|
||||
*/
|
||||
public static perpendicular(first: Vector2, second: Vector2) {
|
||||
return new Vector2(-1 * (second.y - first.y), second.x - first.x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vector2的临时解决方案
|
||||
* 标准化把向量弄乱了
|
||||
* @param vec
|
||||
*/
|
||||
public static normalize(vec: Vector2) {
|
||||
let magnitude = Math.sqrt((vec.x * vec.x) + (vec.y * vec.y));
|
||||
if (magnitude > MathHelper.Epsilon) {
|
||||
vec = Vector2.divide(vec, new Vector2(magnitude));
|
||||
} else {
|
||||
vec.x = vec.y = 0;
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过指定的矩阵对Vector2的数组中的向量应用变换,并将结果放置在另一个数组中。
|
||||
* @param sourceArray
|
||||
* @param sourceIndex
|
||||
* @param matrix
|
||||
* @param destinationArray
|
||||
* @param destinationIndex
|
||||
* @param length
|
||||
*/
|
||||
public static transformA(sourceArray: Vector2[], sourceIndex: number, matrix: Matrix2D,
|
||||
destinationArray: Vector2[], destinationIndex: number, length: number) {
|
||||
for (let i = 0; i < length; i ++){
|
||||
let position = sourceArray[sourceIndex + i];
|
||||
let destination = destinationArray[destinationIndex + i];
|
||||
@@ -61,25 +62,26 @@ class Vector2Ext {
|
||||
destination.y = (position.x * matrix.m12) + (position.y * matrix.m22) + matrix.m32;
|
||||
destinationArray[destinationIndex + i] = destination;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static transformR(position: Vector2, matrix: Matrix2D){
|
||||
let x = (position.x * matrix.m11) + (position.y * matrix.m21) + matrix.m31;
|
||||
let y = (position.x * matrix.m12) + (position.y * matrix.m22) + matrix.m32;
|
||||
return new Vector2(x, y);
|
||||
}
|
||||
public static transformR(position: Vector2, matrix: Matrix2D){
|
||||
let x = (position.x * matrix.m11) + (position.y * matrix.m21) + matrix.m31;
|
||||
let y = (position.x * matrix.m12) + (position.y * matrix.m22) + matrix.m32;
|
||||
return new Vector2(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过指定的矩阵对Vector2的数组中的所有向量应用变换,并将结果放到另一个数组中。
|
||||
* @param sourceArray
|
||||
* @param matrix
|
||||
* @param destinationArray
|
||||
*/
|
||||
public static transform(sourceArray: Vector2[], matrix: Matrix2D, destinationArray: Vector2[]) {
|
||||
this.transformA(sourceArray, 0, matrix, destinationArray, 0, sourceArray.length);
|
||||
}
|
||||
/**
|
||||
* 通过指定的矩阵对Vector2的数组中的所有向量应用变换,并将结果放到另一个数组中。
|
||||
* @param sourceArray
|
||||
* @param matrix
|
||||
* @param destinationArray
|
||||
*/
|
||||
public static transform(sourceArray: Vector2[], matrix: Matrix2D, destinationArray: Vector2[]) {
|
||||
this.transformA(sourceArray, 0, matrix, destinationArray, 0, sourceArray.length);
|
||||
}
|
||||
|
||||
public static round(vec: Vector2){
|
||||
return new Vector2(Math.round(vec.x), Math.round(vec.y));
|
||||
public static round(vec: Vector2){
|
||||
return new Vector2(Math.round(vec.x), Math.round(vec.y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user