1.新增es.TimeUtils

2.优化并给部分类添加注释
3.移除fasterDictionary
This commit is contained in:
yhh
2023-03-13 23:32:24 +08:00
parent 323fb6a5fe
commit caa3ffc8f5
14 changed files with 2240 additions and 1796 deletions

View File

@@ -216,10 +216,35 @@ declare module es {
onDisabled(): void;
setEnabled(isEnabled: boolean): this;
setUpdateOrder(updateOrder: number): this;
/**
* 添加组件
* @param component 要添加的组件实例
* @returns 返回添加的组件实例
*/
addComponent<T extends Component>(component: T): T;
/**
* 获取组件
* @param type 组件类型
* @returns 返回获取到的组件实例
*/
getComponent<T extends Component>(type: new (...args: any[]) => T): T;
/**
* 获取一组指定类型的组件
* @param typeName 组件类型名
* @param componentList 可选参数,存储组件实例的数组
* @returns 返回指定类型的组件实例数组
*/
getComponents(typeName: any, componentList?: any[]): any[];
/**
* 判断实体是否包含指定类型的组件
* @param type 组件类型
* @returns 如果实体包含指定类型的组件,返回 true否则返回 false。
*/
hasComponent(type: new (...args: any[]) => Component): boolean;
/**
* 删除组件
* @param component 可选参数,要删除的组件实例。如果未指定该参数,则删除当前实例上的组件。
*/
removeComponent(component?: Component): void;
}
}
@@ -1610,7 +1635,7 @@ declare module es {
}
declare module es {
/**
* 追踪实体的子集,但不实现任何排序或迭代
* 实体系统的基类,用于处理一组实体
*/
abstract class EntitySystem {
private _entities;
@@ -1634,7 +1659,7 @@ declare module es {
readonly matcher: Matcher;
/**
* 设置更新时序
* @param order
* @param order 更新时序
*/
setUpdateOrder(order: number): void;
initialize(): void;
@@ -1804,11 +1829,7 @@ declare module es {
}
declare module es {
/**
* 每x个ticks处理一个实体的子集
*
* 典型的用法是每隔一定的时间间隔重新生成弹药或生命值
* 而无需在每个游戏循环中都进行
* 而是每100毫秒一次或每秒
* 定时遍历处理实体的系统,用于按指定的时间间隔遍历并处理感兴趣的实体。
*/
abstract class IntervalIteratingSystem extends IntervalSystem {
constructor(matcher: Matcher, interval: number);
@@ -1817,6 +1838,10 @@ declare module es {
* @param entity
*/
abstract processEntity(entity: Entity): any;
/**
* 遍历处理实体。
* @param entities 本系统感兴趣的实体列表
*/
protected process(entities: Entity[]): void;
}
}
@@ -1861,83 +1886,142 @@ declare module es {
}
}
declare module es {
/**
* 位操作类,用于操作一个位数组。
*/
class Bits {
private _bit;
/**
* 设置指定位置的位值。
* @param index 位置索引
* @param value 位值0 或 1
*/
set(index: number, value: number): void;
/**
* 获取指定位置的位值。
* @param index 位置索引
* @returns 位值0 或 1
*/
get(index: number): number;
}
}
declare module es {
class ComponentList {
/**
* 组件列表的全局updateOrder排序
* 比较IUpdatable对象的更新顺序。
*/
static compareUpdatableOrder: IUpdatableComparer;
_entity: Entity;
/**
* 添加到实体的组件列表
* 实体的组件列表
*/
_components: Component[];
/**
* 所有需要更新的组件列表
* 更新的组件列表
*/
_updatableComponents: IUpdatable[];
/**
* 添加到此框架的组件列表。用来对组件进行分组,这样我们就可以同时进行加工
* 等待添加到实体的组件列表。
*/
_componentsToAdd: {
[index: number]: Component;
};
/**
* 标记要删除此框架的组件列表。用来对组件进行分组,这样我们就可以同时进行加工
* 等待从实体中移除的组件列表。
*/
_componentsToRemove: {
[index: number]: Component;
};
/**
* 等待添加到实体的组件列表(作为数组)。
*/
_componentsToAddList: Component[];
/**
* 等待从实体中移除的组件列表(作为数组)。
*/
_componentsToRemoveList: Component[];
/**
* 临时的组件缓冲列表。
*/
_tempBufferList: Component[];
/**
* 用于确定是否需要对该框架中的组件进行排序的标志
* 指示组件列表是否已排序的标志
*/
_isComponentListUnsorted: boolean;
/**
* 按组件类型组织的组件列表字典。
*/
private componentsByType;
/**
* 按组件类型组织的等待添加到实体的组件列表字典。
*/
private componentsToAddByType;
constructor(entity: Entity);
readonly count: number;
readonly buffer: Component[];
markEntityListUnsorted(): void;
/**
* 将组件添加到实体的组件列表中,并添加到组件类型字典中。
* @param component 要添加的组件。
*/
add(component: Component): void;
/**
* 从实体的组件列表中移除组件,并从相应的组件类型字典中移除组件。
* @param component 要从实体中移除的组件。
*/
remove(component: Component): void;
/**
* 立即从组件列表中删除所有组件
*/
removeAllComponents(): void;
/**
* 从实体的所有组件上注销并从相关数据结构中删除它们。
*/
deregisterAllComponents(): void;
/**
* 注册实体的所有组件,并将它们添加到相应的数据结构中。
*/
registerAllComponents(): void;
/**
* 从实体的位掩码中减去组件类型的索引。
* @param component 要从实体中删除的组件。
*/
private decreaseBits;
/**
* 在实体的位掩码中添加组件类型的索引。
* @param component 要添加到实体的组件。
*/
private addBits;
/**
* 处理任何需要删除或添加的组件
* 更新实体的组件列表和相关数据结构。
* 如果有组件要添加或删除,它将相应地更新组件列表和其他数据结构。
*/
updateLists(): void;
handleRemove(component: Component): void;
private removeComponentsByType;
private addComponentsByType;
/**
* 从待添加组件列表中移除指定类型的组件。
* @param component 要移除的组件
*/
private removeComponentsToAddByType;
/**
* 向待添加组件列表中添加指定类型的组件。
* @param component 要添加的组件
*/
private addComponentsToAddByType;
/**
* 获取类型T的第一个组件并返回它
* 可以选择跳过检查未初始化的组件(尚未调用onAddedToEntity方法的组件)
* 如果没有找到组件则返回null。
* @param type
* @param onlyReturnInitializedComponents
* 获取指定类型的第一个组件实例。
* @param type 组件类型
* @param onlyReturnInitializedComponents 是否仅返回已初始化的组件
* @returns 指定类型的第一个组件实例,如果不存在则返回 null
*/
getComponent<T extends Component>(type: any, onlyReturnInitializedComponents: boolean): T;
getComponent<T extends Component>(type: new (...args: any[]) => T, onlyReturnInitializedComponents: boolean): T;
/**
* 获取T类型的所有组件,但不使用列表分配
* @param typeName
* @param components
* 获取指定类型的所有组件实例。
* @param typeName 组件类型名称
* @param components 存储组件实例的数组
* @returns 存储了指定类型的所有组件实例的数组
*/
getComponents(typeName: any, components?: any[]): any[];
update(): void;
@@ -1998,33 +2082,45 @@ declare module es {
}
declare module es {
class EntityList {
/**
* 场景引用
*/
scene: Scene;
/**
* 场景中添加的实体列表
* 实体列表
*/
_entities: Entity[];
/**
* 本帧添加的实体列表。用于对实体进行分组,以便我们可以同时处理它们
* 添加的实体字典
*/
_entitiesToAdded: {
[index: number]: Entity;
};
/**
* 本帧被标记为删除的实体列表。用于对实体进行分组,以便我们可以同时处理它们
* 待移除的实体字典
*/
_entitiesToRemove: {
[index: number]: Entity;
};
/**
* 待添加的实体列表
*/
_entitiesToAddedList: Entity[];
/**
* 待移除的实体列表
*/
_entitiesToRemoveList: Entity[];
/**
* 标志,用于确定我们是否需要在这一帧中对实体进行排序
* 实体列表是否已排序
*/
_isEntityListUnsorted: boolean;
/**
* 通过标签跟踪实体,便于检索
* 实体字典,以实体标签为键
*/
_entityDict: Map<number, Set<Entity>>;
/**
* 未排序的标签集合
*/
_unsortedTags: Set<number>;
constructor(scene: Scene);
readonly count: number;
@@ -2037,23 +2133,45 @@ declare module es {
*/
add(entity: Entity): void;
/**
* 从列表中删除一个实体。所有的生命周期方法将在下一帧中被调用
* @param entity
* 从场景中移除实体。
* @param entity 要从场景中移除的实体。
*/
remove(entity: Entity): void;
/**
* 从实体列表中删除所有实体
* 从场景中移除所有实体
*/
removeAllEntities(): void;
/**
* 检查实体目前是否由这个EntityList管理
* @param entity
* 检查实体是否已经被添加到场景中。
* @param entity 要检查的实体
* @returns 如果实体已经被添加到场景中则返回true否则返回false
*/
contains(entity: Entity): boolean;
/**
* 获取具有指定标签的实体列表。
* 如果列表不存在,则创建一个新列表并返回。
* @param tag 实体标签
* @returns 具有指定标签的实体列表
*/
getTagList(tag: number): Set<Entity>;
/**
* 添加实体到标签列表中。
* @param entity 实体
*/
addToTagList(entity: Entity): void;
/**
* 从标签列表中移除实体。
* @param entity 实体
*/
removeFromTagList(entity: Entity): void;
/**
* 更新场景中所有启用的实体的Update方法
* 如果实体的UpdateInterval为1或Time.frameCount模除UpdateInterval为0则每帧调用Update
*/
update(): void;
/**
* 更新场景中实体的列表。
*/
updateLists(): void;
/**
* 返回第一个找到的名字为name的实体。如果没有找到则返回null
@@ -2061,15 +2179,15 @@ declare module es {
*/
findEntity(name: string): Entity;
/**
*
* @param id
* @returns
* 通过实体ID在场景中查找对应实体
* @param id 实体ID
* @returns 返回找到的实体,如果没有找到则返回 null
*/
findEntityById(id: number): Entity;
/**
* 返回带有标签的所有实体列表。如果没有实体有标签,则返回一个空列表。
* 返回的List可以通过ListPool.free放回池中
* @param tag
* 获取标签对应的实体列表
* @param tag 实体的标签
* @returns 返回所有拥有该标签的实体列表
*/
entitiesWithTag(tag: number): Entity[];
/**
@@ -2079,20 +2197,21 @@ declare module es {
*/
entityWithTag(tag: number): Entity;
/**
* 返回在场景中找到的第一个T类型的组件。
* @param type
* 在场景中查找具有给定类型的组件。
* @param type 要查找的组件类型。
* @returns 如果找到则返回该组件否则返回null。
*/
findComponentOfType<T extends Component>(type: any): T;
findComponentOfType<T extends Component>(type: new (...args: any[]) => T): T | null;
/**
* 返回在场景中找到的所有T类型的组件。
* 返回的List可以通过ListPool.free放回池中
* @param type
* 在场景中查找具有给定类型的所有组件。
* @param type 要查找的组件类型
* @returns 具有给定类型的所有组件的列表。
*/
findComponentsOfType<T extends Component>(type: any): T[];
findComponentsOfType<T extends Component>(type: new (...args: any[]) => T): T[];
/**
* 返回场景中包含特定组件的实体列表
* @param types
* @returns
* 返回拥有指定类型组件的所有实体
* @param types 要查询的组件类型列表
* @returns 返回拥有指定类型组件的所有实体
*/
findEntitiesOfComponent(...types: any[]): Entity[];
}
@@ -2101,53 +2220,100 @@ declare module es {
class EntityProcessorList {
private _processors;
private _orderDirty;
/** 获取系统列表 */
/** 获取处理器列表 */
readonly processors: EntitySystem[];
/** 系统数量 */
/** 获取处理器数量 */
readonly count: number;
/**
* 添加处理器
* @param processor 要添加的处理器
*/
add(processor: EntitySystem): void;
/**
* 移除处理器
* @param processor 要移除的处理器
*/
remove(processor: EntitySystem): void;
/**
* 在实体上添加组件时被调用
* @param entity 添加组件的实体
*/
onComponentAdded(entity: Entity): void;
/**
* 在实体上移除组件时被调用
* @param entity 移除组件的实体
*/
onComponentRemoved(entity: Entity): void;
/**
* 在场景中添加实体时被调用
* @param entity 添加的实体
*/
onEntityAdded(entity: Entity): void;
/**
* 在场景中移除实体时被调用
* @param entity 移除的实体
*/
onEntityRemoved(entity: Entity): void;
/** 在处理器列表上开始循环 */
begin(): void;
/** 更新处理器列表 */
update(): void;
lateUpdate(): void;
/** 在处理器列表上完成循环 */
end(): void;
/** 设置处理器排序标志 */
setDirty(): void;
/** 清除处理器排序标志 */
clearDirty(): void;
/**
* 获取指定类型的处理器
* @param type 指定类型的构造函数
* @returns 指定类型的处理器
*/
getProcessor<T extends EntitySystem>(type: new (...args: any[]) => T): T;
/**
* 通知处理器实体已更改
* @param entity 发生更改的实体
*/
protected notifyEntityChanged(entity: Entity): void;
/**
* 从处理器列表中移除实体
* @param entity 要移除的实体
*/
protected removeFromProcessors(entity: Entity): void;
/** 在处理器列表上进行后期更新 */
lateUpdate(): void;
}
}
declare module es {
class HashHelpers {
static readonly hashCollisionThreshold: number;
static readonly hashPrime: number;
/**
* 用来作为哈希表大小的质数表。
* 一个典型的调整大小的算法会在这个数组中选取比之前容量大两倍的最小质数。
* 假设我们的Hashtable当前的容量为x并且添加了足够多的元素因此需要进行大小调整。
* 调整大小首先计算2x然后在表中找到第一个大于2x的质数即如果质数的顺序是p_1p_2...p_i...则找到p_n使p_n-1 < 2x < p_n。
* 双倍对于保持哈希特操作的渐近复杂度是很重要的,比如添加。
* 拥有一个质数可以保证双倍哈希不会导致无限循环。 IE你的哈希函数将是h1(key)+i*h2(key)0 <= i < size.h2和size必须是相对质数。
*/
static readonly primes: number[];
/**
* 这是比Array.MaxArrayLength小的最大质数
*/
static readonly maxPrimeArrayLength: number;
/**
* 判断一个数是否为质数
* @param candidate 要判断的数
* @returns 是否为质数
*/
static isPrime(candidate: number): boolean;
/**
* 获取大于等于指定值的最小质数
* @param min 指定值
* @returns 大于等于指定值的最小质数
*/
static getPrime(min: number): number;
/**
*
* @param oldSize
* @returns 返回要增长的哈希特表的大小
* 扩展哈希表容量
* @param oldSize 原哈希表容量
* @returns 扩展后的哈希表容量
*/
static expandPrime(oldSize: number): number;
static getHashCode(str: any): number;
/**
* 计算字符串的哈希值
* @param str 要计算哈希值的字符串
* @returns 哈希值
*/
static getHashCode(str: string): number;
}
}
declare module es {
@@ -2281,57 +2447,74 @@ declare module es {
}
}
declare module es {
/** 提供帧定时信息 */
/**
* 时间管理器,用于管理游戏中的时间相关属性
*/
class Time {
/** 游戏运行的总时间 */
/** 游戏运行的总时间,单位为秒 */
static totalTime: number;
/** deltaTime的未缩放版本不受时间尺度的影响 */
/** deltaTime 的未缩放版本不受时间尺度的影响 */
static unscaledDeltaTime: number;
/** 前一帧到当前帧的时间增量,按时间刻度进行缩放 */
static deltaTime: number;
/** 时间刻度缩放 */
/** 时间刻度缩放,可以加快或减慢游戏时间 */
static timeScale: number;
/** DeltaTime可以为的最大值 */
/** DeltaTime 可以为的最大值,避免游戏出现卡顿情况 */
static maxDeltaTime: number;
/** 已传递的帧总数 */
static frameCount: number;
/** 自场景加载以来的总时间 */
/** 自场景加载以来的总时间,单位为秒 */
static timeSinceSceneLoad: number;
/** 上一次记录的时间,用于计算两次调用 update 之间的时间差 */
private static _lastTime;
/**
* 更新时间管理器
* @param currentTime 当前时间
* @param useEngineTime 是否使用引擎时间
*/
static update(currentTime: number, useEngineTime: boolean): void;
static sceneChanged(): void;
/**
* 允许在间隔检查。只应该使用高于delta的间隔值否则它将始终返回true。
* @param interval
* 检查指定时间间隔是否已过去
* @param interval 指定时间间隔
* @returns 是否已过去指定时间间隔
*/
static checkEvery(interval: number): boolean;
}
}
declare class TimeUtils {
declare module es {
class TimeUtils {
/**
* 计算月份ID
* @param d 指定计算日期
* @returns 月ID
* 获取日期对应的年份和月份的数字组合
* @param d 要获取月份的日期对象,不传则默认为当前时间
* @returns 返回数字组合的年份和月份
*/
static monthId(d?: Date): number;
/**
* 计算日期ID
* @param d 指定计算日期
* @returns 日期ID
* 获取日期的数字组合
* @param t - 可选参数,传入时间,若不传入则使用当前时间
* @returns 数字组合
*/
static dateId(t?: Date): number;
/**
* 计算周ID
* @param d 指定计算日期
* @returns 周ID
* 获取当前日期所在周的数字组合
* @param d - 可选参数,传入日期,若不传入则使用当前日期
* @param first - 是否将当前周视为本年度的第1周默认为true
* @returns 数字组合
*/
static weekId(d?: Date, first?: boolean): number;
/**
* 计算俩日期时间差如果a比b小返回负
* 计算两个日期之间相差的天
* @param a 第一个日期
* @param b 第二个日期
* @param fixOne 是否将相差天数四舍五入到整数
* @returns 两个日期之间相差的天数
*/
static diffDay(a: Date, b: Date, fixOne?: boolean): number;
/**
* 获取本周一 凌晨时间
* 获取指定日期所在周的第一天
* @param d 指定日期,默认值为今天
* @returns 指定日期所在周的第一天
*/
static getFirstDayOfWeek(d?: Date): Date;
/**
@@ -2343,42 +2526,39 @@ declare class TimeUtils {
*/
static getNextFirstOfDay(d?: Date): Date;
/**
* @returns 2018-12-12
* 格式化日期为 "YYYY-MM-DD" 的字符串形式
* @param date 要格式化的日期
* @returns 格式化后的日期字符串
*/
static formatDate(date: Date): string;
/**
* @returns 2018-12-12 12:12:12
* 将日期对象格式化为 "YYYY-MM-DD HH:mm:ss" 的字符串
* @param date 日期对象
* @returns 格式化后的字符串
*/
static formatDateTime(date: Date): string;
/**
* @returns s 2018-12-12 或者 2018-12-12 12:12:12
* 将字符串解析为Date对象
* @param s 要解析的日期字符串例如2022-01-01
* @returns 返回解析后的Date对象如果解析失败则返回当前时间的Date对象
*/
static parseDate(s: string): Date;
/**
* 秒数转换为时间形式。
* 秒数转换为时分秒的格式
* @param time 秒数
* @param partition 分隔符
* @param showHour 是否显示小时
* @return 返回一个以分隔符分割的时, 分, 秒
*
* 比如: time = 4351; secondToTime(time)返回字符串01:12:31;
* @param showHour 是否显示小时
* @returns 转换后的时间字符串
*/
static secondToTime(time?: number, partition?: string, showHour?: boolean): string;
/**
* 时间形式转换为毫秒数
* @param time 以指定分隔符分割的时间字符串
* @param partition 分隔符
* @return 毫秒数显示的字符串
* @throws Error Exception
*
* 用法1 trace(MillisecondTransform.timeToMillisecond("00:60:00"))
* 输出 3600000
*
*
* 用法2 trace(MillisecondTransform.timeToMillisecond("00.60.00","."))
* 输出 3600000
* 时间字符串转换为毫秒数
* @param time 时间字符串,如 "01:30:15" 表示 1小时30分钟15秒
* @param partition 分隔符,默认为 ":"
* @returns 转换后的毫秒数字符串
*/
static timeToMillisecond(time: string, partition?: string): string;
}
}
declare module es {
/**
@@ -5321,53 +5501,6 @@ declare module es {
addAll(items: ImmutableBag<E>): void;
}
}
declare module es {
/**
* 创建这个字典的原因只有一个:
* 我需要一个能让我直接以数组的形式对值进行迭代的字典,而不需要生成一个数组或使用迭代器。
* 对于这个目标是比标准字典快N倍。
* Faster dictionary在大部分操作上也比标准字典快但差别可以忽略不计。
* 唯一较慢的操作是在添加时调整内存大小,因为与标准数组相比,这个实现需要使用两个单独的数组。
*/
class FasterDictionary<TKey, TValue> {
_values: TValue[];
_valuesInfo: FastNode[];
_buckets: number[];
_freeValueCellIndex: number;
_collisions: number;
constructor(size?: number);
getValuesArray(count: {
value: number;
}): TValue[];
readonly valuesArray: TValue[];
readonly count: number;
add(key: TKey, value: TValue): void;
addValue(key: TKey, value: TValue, indexSet: {
value: number;
}): boolean;
remove(key: TKey): boolean;
trim(): void;
clear(): void;
fastClear(): void;
containsKey(key: TKey): boolean;
tryGetValue(key: TKey): TValue;
tryFindIndex(key: TKey, findIndex: {
value: number;
}): boolean;
getDirectValue(index: number): TValue;
getIndex(key: TKey): number;
static updateLinkedList(index: number, valuesInfo: FastNode[]): void;
static hash(key: any): number;
static reduce(x: number, n: number): number;
}
class FastNode {
readonly key: any;
readonly hashcode: number;
previous: number;
next: number;
constructor(key: any, hash: number, previousNode?: number);
}
}
declare module es {
interface ImmutableBag<E> {
get(index: number): E;

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -117,23 +117,48 @@ module es {
return this;
}
/**
* 添加组件
* @param component 要添加的组件实例
* @returns 返回添加的组件实例
*/
public addComponent<T extends Component>(component: T): T {
return this.entity.addComponent<T>(component);
}
public getComponent<T extends Component>(type: new (...args: any[])=>T): T {
/**
* 获取组件
* @param type 组件类型
* @returns 返回获取到的组件实例
*/
public getComponent<T extends Component>(type: new (...args: any[]) => T): T {
return this.entity.getComponent<T>(type);
}
public getComponents(typeName: any, componentList?: any[]) {
/**
* 获取一组指定类型的组件
* @param typeName 组件类型名
* @param componentList 可选参数,存储组件实例的数组
* @returns 返回指定类型的组件实例数组
*/
public getComponents(typeName: any, componentList?: any[]): any[] {
return this.entity.getComponents(typeName, componentList);
}
public hasComponent(type: new (...args: any[])=>Component) {
/**
* 判断实体是否包含指定类型的组件
* @param type 组件类型
* @returns 如果实体包含指定类型的组件,返回 true否则返回 false。
*/
public hasComponent(type: new (...args: any[]) => Component): boolean {
return this.entity.hasComponent(type);
}
public removeComponent(component?: Component) {
/**
* 删除组件
* @param component 可选参数,要删除的组件实例。如果未指定该参数,则删除当前实例上的组件。
*/
public removeComponent(component?: Component): void {
if (component) {
this.entity.removeComponent(component);
} else {

View File

@@ -1,7 +1,7 @@
///<reference path="../../Utils/Collections/HashMap.ts"/>
module es {
/**
* 追踪实体的子集,但不实现任何排序或迭代
* 实体系统的基类,用于处理一组实体
*/
export abstract class EntitySystem {
private _entities: Entity[] = [];
@@ -53,7 +53,7 @@ module es {
/**
* 设置更新时序
* @param order
* @param order 更新时序
*/
public setUpdateOrder(order: number) {
this._updateOrder = order;
@@ -80,14 +80,16 @@ module es {
this.onAdded(entity);
}
public onAdded(entity: Entity) { }
public onAdded(entity: Entity) {
}
public remove(entity: Entity) {
new es.List(this._entities).remove(entity);
this.onRemoved(entity);
}
public onRemoved(entity: Entity) { }
public onRemoved(entity: Entity) {
}
public update() {
if (this.checkProcessing()) {
@@ -114,9 +116,11 @@ module es {
this._startTime = Date.now();
}
protected process(entities: Entity[]) { }
protected process(entities: Entity[]) {
}
protected lateProcess(entities: Entity[]) { }
protected lateProcess(entities: Entity[]) {
}
/**
* 系统处理完毕后调用

View File

@@ -1,13 +1,10 @@
///<reference path="./IntervalSystem.ts"/>
module es {
/**
* 每x个ticks处理一个实体的子集
*
* 典型的用法是每隔一定的时间间隔重新生成弹药或生命值
* 而无需在每个游戏循环中都进行
* 而是每100毫秒一次或每秒
* 定时遍历处理实体的系统,用于按指定的时间间隔遍历并处理感兴趣的实体。
*/
export abstract class IntervalIteratingSystem extends IntervalSystem {
constructor(matcher: Matcher, interval: number) {
super(matcher, interval);
}
@@ -18,6 +15,10 @@ module es {
*/
public abstract processEntity(entity: Entity);
/**
* 遍历处理实体。
* @param entities 本系统感兴趣的实体列表
*/
protected process(entities: Entity[]) {
entities.forEach(entity => this.processEntity(entity));
}

View File

@@ -1,14 +1,28 @@
module es {
/**
* 位操作类,用于操作一个位数组。
*/
export class Bits {
private _bit: {[index: number]: number} = {};
private _bit: { [index: number]: number } = {};
/**
* 设置指定位置的位值。
* @param index 位置索引
* @param value 位值0 或 1
*/
public set(index: number, value: number) {
this._bit[index] = value;
}
/**
* 获取指定位置的位值。
* @param index 位置索引
* @returns 位值0 或 1
*/
public get(index: number): number {
let v = this._bit[index];
return v == null ? 0 : v;
}
}
}

View File

@@ -2,37 +2,61 @@
module es {
export class ComponentList {
/**
* 组件列表的全局updateOrder排序
* 比较IUpdatable对象的更新顺序。
*/
public static compareUpdatableOrder: IUpdatableComparer = new IUpdatableComparer();
public _entity: Entity;
/**
* 添加到实体的组件列表
* 实体的组件列表
*/
public _components: Component[] = [];
/**
* 所有需要更新的组件列表
* 更新的组件列表
*/
public _updatableComponents: IUpdatable[] = [];
/**
* 添加到此框架的组件列表。用来对组件进行分组,这样我们就可以同时进行加工
* 等待添加到实体的组件列表。
*/
public _componentsToAdd: { [index: number]: Component } = {};
/**
* 标记要删除此框架的组件列表。用来对组件进行分组,这样我们就可以同时进行加工
* 等待从实体中移除的组件列表。
*/
public _componentsToRemove: { [index: number]: Component } = {};
public _componentsToAddList: Component[] = [];
public _componentsToRemoveList: Component[] = [];
public _tempBufferList: Component[] = [];
/**
* 用于确定是否需要对该框架中的组件进行排序的标志
* 等待添加到实体的组件列表(作为数组)。
*/
public _componentsToAddList: Component[] = [];
/**
* 等待从实体中移除的组件列表(作为数组)。
*/
public _componentsToRemoveList: Component[] = [];
/**
* 临时的组件缓冲列表。
*/
public _tempBufferList: Component[] = [];
/**
* 指示组件列表是否已排序的标志。
*/
public _isComponentListUnsorted: boolean;
/**
* 按组件类型组织的组件列表字典。
*/
private componentsByType = new Map<new (...args: any[]) => Component, es.Component[]>();
/**
* 按组件类型组织的等待添加到实体的组件列表字典。
*/
private componentsToAddByType = new Map<new (...args: any[]) => Component, es.Component[]>();
constructor(entity: Entity) {
this._entity = entity;
}
@@ -49,32 +73,45 @@ module es {
this._isComponentListUnsorted = true;
}
/**
* 将组件添加到实体的组件列表中,并添加到组件类型字典中。
* @param component 要添加的组件。
*/
public add(component: Component) {
// 将组件添加到_componentsToAdd和_componentsToAddList中并添加到相应的组件类型字典中
this._componentsToAdd[component.id] = component;
this._componentsToAddList.push(component);
this.addComponentsToAddByType(component);
}
/**
* 从实体的组件列表中移除组件,并从相应的组件类型字典中移除组件。
* @param component 要从实体中移除的组件。
*/
public remove(component: Component) {
// 如果组件在_componentsToAdd中则将其从_componentsToAddList中移除并从相应的组件类型字典中移除组件
if (this._componentsToAdd[component.id]) {
let index = this._componentsToAddList.findIndex(c => c.id == component.id);
if (index != -1)
const index = this._componentsToAddList.findIndex((c) => c.id === component.id);
if (index !== -1) {
this._componentsToAddList.splice(index, 1);
}
delete this._componentsToAdd[component.id];
this.removeComponentsToAddByType(component);
return;
}
// 如果组件不在_componentsToAdd中则将其添加到_componentsToRemove和_componentsToRemoveList中
this._componentsToRemove[component.id] = component;
this._componentsToRemoveList.push(component);
}
/**
* 立即从组件列表中删除所有组件
*/
public removeAllComponents() {
if (this._components.length > 0) {
for (let i = 0, s = this._components.length; i < s; ++ i) {
for (let i = 0, s = this._components.length; i < s; ++i) {
this.handleRemove(this._components[i]);
}
}
@@ -89,193 +126,272 @@ module es {
this._componentsToRemoveList.length = 0;
}
/**
* 从实体的所有组件上注销并从相关数据结构中删除它们。
*/
public deregisterAllComponents() {
if (this._components.length > 0) {
for (let i = 0, s = this._components.length; i < s; ++ i) {
let component = this._components[i];
if (!component) continue;
for (const component of this._components) {
// 处理IUpdatable
if (isIUpdatable(component))
if (isIUpdatable(component)) {
// 创建一个新的List实例从_updatableComponents中移除组件以避免并发修改异常
new es.List(this._updatableComponents).remove(component);
}
// 从位掩码中减去组件类型的索引,通知实体处理器一个组件已被移除
this.decreaseBits(component);
this._entity.scene.entityProcessors.onComponentRemoved(this._entity);
}
}
}
/**
* 注册实体的所有组件,并将它们添加到相应的数据结构中。
*/
public registerAllComponents() {
if (this._components.length > 0) {
for (let i = 0, s = this._components.length; i < s; ++ i) {
let component = this._components[i];
if (isIUpdatable(component))
for (const component of this._components) {
if (isIUpdatable(component)) {
// 如果组件是可更新的则将其添加到_updatableComponents中
this._updatableComponents.push(component);
}
// 将组件类型的索引添加到实体的位掩码中,通知实体处理器一个组件已被添加
this.addBits(component);
this._entity.scene.entityProcessors.onComponentAdded(this._entity);
}
}
}
/**
* 从实体的位掩码中减去组件类型的索引。
* @param component 要从实体中删除的组件。
*/
private decreaseBits(component: Component) {
let bits = this._entity.componentBits;
let typeIndex = ComponentTypeManager.getIndexFor(TypeUtils.getType(component));
const bits = this._entity.componentBits;
// 获取组件类型的索引将其对应位掩码减1
const typeIndex = ComponentTypeManager.getIndexFor(TypeUtils.getType(component));
bits.set(typeIndex, bits.get(typeIndex) - 1);
}
/**
* 在实体的位掩码中添加组件类型的索引。
* @param component 要添加到实体的组件。
*/
private addBits(component: Component) {
let bits = this._entity.componentBits;
let typeIndex = ComponentTypeManager.getIndexFor(TypeUtils.getType(component));
const bits = this._entity.componentBits;
// 获取组件类型的索引将其对应位掩码加1
const typeIndex = ComponentTypeManager.getIndexFor(TypeUtils.getType(component));
bits.set(typeIndex, bits.get(typeIndex) + 1);
}
/**
* 处理任何需要删除或添加的组件
* 更新实体的组件列表和相关数据结构。
* 如果有组件要添加或删除,它将相应地更新组件列表和其他数据结构。
*/
public updateLists() {
// 处理要删除的组件
if (this._componentsToRemoveList.length > 0) {
for (let i = 0, l = this._componentsToRemoveList.length; i < l; ++ i) {
let component = this._componentsToRemoveList[i];
for (const component of this._componentsToRemoveList) {
// 从实体中删除组件,从组件列表和相关数据结构中删除组件
this.handleRemove(component);
let index = this._components.findIndex(c => c.id == component.id);
if (index != -1)
// 从_components数组中删除组件
const index = this._components.findIndex((c) => c.id === component.id);
if (index !== -1) {
this._components.splice(index, 1);
}
// 从组件类型字典中删除组件
this.removeComponentsByType(component);
}
// 清空_componentsToRemove和_componentsToRemoveList
this._componentsToRemove = {};
this._componentsToRemoveList.length = 0;
}
// 处理要添加的组件
if (this._componentsToAddList.length > 0) {
for (let i = 0, l = this._componentsToAddList.length; i < l; ++ i) {
let component = this._componentsToAddList[i];
if (isIUpdatable(component))
for (const component of this._componentsToAddList) {
// 如果组件可以更新,则添加到可更新组件列表中
if (isIUpdatable(component)) {
this._updatableComponents.push(component);
}
// 更新实体的组件位掩码,通知实体处理器一个组件已经添加
this.addBits(component);
this._entity.scene.entityProcessors.onComponentAdded(this._entity);
// 将组件添加到相应类型的fastList中将组件添加到_components数组中
this.addComponentsByType(component);
this._components.push(component);
// 将组件添加到_tempBufferList中稍后调用onAddedToEntity和onEnabled
this._tempBufferList.push(component);
}
// 在调用onAddedToEntity之前清除以防添加更多组件
// 清空_componentsToAdd、_componentsToAddList和componentsToAddByType设置_isComponentListUnsorted标志
this._componentsToAdd = {};
this._componentsToAddList.length = 0;
this.componentsToAddByType.clear();
this._isComponentListUnsorted = true;
}
// 调用新添加组件的onAddedToEntity和onEnabled方法
if (this._tempBufferList.length > 0) {
// 现在所有的组件都添加到了场景中我们再次循环并调用onAddedToEntity/onEnabled
for (let i = 0, l = this._tempBufferList.length; i < l; ++ i) {
let component = this._tempBufferList[i];
for (const component of this._tempBufferList) {
component.onAddedToEntity();
// enabled检查实体和组件
// 如果组件已启用则调用onEnabled方法
if (component.enabled) {
component.onEnabled();
}
}
// 清空_tempBufferList
this._tempBufferList.length = 0;
}
}
public handleRemove(component: Component) {
// 如果组件可以更新,从可更新组件列表中删除该组件
if (isIUpdatable(component) && this._updatableComponents.length > 0) {
let index = this._updatableComponents.findIndex((c) => (<any>c as Component).id == component.id);
if (index != -1)
const index = this._updatableComponents.findIndex((c) => (<any>c as Component).id === component.id);
if (index !== -1) {
this._updatableComponents.splice(index, 1);
}
}
// 更新实体的组件位掩码
this.decreaseBits(component);
// 通知实体处理器一个组件已被删除
this._entity.scene.entityProcessors.onComponentRemoved(this._entity);
// 调用组件的onRemovedFromEntity方法将其entity属性设置为null
component.onRemovedFromEntity();
component.entity = null;
}
private removeComponentsByType(component: Component) {
let fastList = this.componentsByType.get(TypeUtils.getType(component));
let fastIndex = fastList.findIndex(c => c.id == component.id);
if (fastIndex != -1) {
fastList.splice(fastIndex, 1);
// 获取存储指定类型组件的fastList数组
const fastList = this.componentsByType.get(TypeUtils.getType(component));
// 在fastList中查找要删除的组件
const index = fastList.findIndex((c) => c.id === component.id);
if (index !== -1) {
// 找到组件后使用splice方法将其从fastList中删除
fastList.splice(index, 1);
}
}
private addComponentsByType(component: Component) {
// 获取存储指定类型组件的fastList数组
let fastList = this.componentsByType.get(TypeUtils.getType(component));
if (!fastList) fastList = [];
// 如果fastList不存在则创建一个空数组
if (!fastList) {
fastList = [];
}
// 在fastList中添加组件
fastList.push(component);
// 更新componentsByType字典以便它包含fastList数组
this.componentsByType.set(TypeUtils.getType(component), fastList);
}
/**
* 从待添加组件列表中移除指定类型的组件。
* @param component 要移除的组件
*/
private removeComponentsToAddByType(component: Component) {
// 获取待添加组件列表中指定类型的组件列表
let fastList = this.componentsToAddByType.get(TypeUtils.getType(component));
// 在该列表中查找指定组件
let fastIndex = fastList.findIndex(c => c.id == component.id);
// 如果找到了指定组件,则从列表中移除它
if (fastIndex != -1) {
fastList.splice(fastIndex, 1);
}
}
/**
* 向待添加组件列表中添加指定类型的组件。
* @param component 要添加的组件
*/
private addComponentsToAddByType(component: Component) {
// 获取待添加组件列表中指定类型的组件列表
let fastList = this.componentsToAddByType.get(TypeUtils.getType(component));
// 如果指定类型的组件列表不存在,则创建一个新的列表
if (!fastList) fastList = [];
// 向指定类型的组件列表中添加组件
fastList.push(component);
// 更新待添加组件列表中指定类型的组件列表
this.componentsToAddByType.set(TypeUtils.getType(component), fastList);
}
/**
* 获取类型T的第一个组件并返回它
* 可以选择跳过检查未初始化的组件(尚未调用onAddedToEntity方法的组件)
* 如果没有找到组件则返回null。
* @param type
* @param onlyReturnInitializedComponents
* 获取指定类型的第一个组件实例。
* @param type 组件类型
* @param onlyReturnInitializedComponents 是否仅返回已初始化的组件
* @returns 指定类型的第一个组件实例,如果不存在则返回 null
*/
public getComponent<T extends Component>(type, onlyReturnInitializedComponents: boolean): T {
public getComponent<T extends Component>(
type: new (...args: any[]) => T,
onlyReturnInitializedComponents: boolean
): T {
// 获取指定类型的组件列表
let fastList = this.componentsByType.get(type);
if (fastList && fastList.length > 0)
return fastList[0] as T;
// 我们可以选择检查挂起的组件以防addComponent和getComponent在同一个框架中被调用
// 如果指定类型的组件列表存在并且不为空,则返回第一个组件实例
if (fastList && fastList.length > 0) return fastList[0] as T;
// 如果不仅返回已初始化的组件,则检查待添加组件列表中是否存在指定类型的组件
if (!onlyReturnInitializedComponents) {
let fastToAddList = this.componentsToAddByType.get(type);
if (fastToAddList && fastToAddList.length > 0)
return fastToAddList[0] as T;
}
// 如果指定类型的组件列表为空且待添加组件列表中也不存在该类型的组件,则返回 null
return null;
}
/**
* 获取T类型的所有组件,但不使用列表分配
* @param typeName
* @param components
* 获取指定类型的所有组件实例。
* @param typeName 组件类型名称
* @param components 存储组件实例的数组
* @returns 存储了指定类型的所有组件实例的数组
*/
public getComponents(typeName: any, components?: any[]) {
if (!components)
components = [];
// 如果没有传入组件实例数组,则创建一个新数组
if (!components) components = [];
// 获取指定类型的组件列表,并将其添加到组件实例数组中
let fastList = this.componentsByType.get(typeName);
if (fastList)
components = components.concat(fastList);
if (fastList) components = components.concat(fastList);
// 获取待添加组件列表中的指定类型的组件列表,并将其添加到组件实例数组中
let fastToAddList = this.componentsToAddByType.get(typeName);
if (fastToAddList)
components = components.concat(fastToAddList);
if (fastToAddList) components = components.concat(fastToAddList);
// 返回存储了指定类型的所有组件实例的数组
return components;
}
public update() {
this.updateLists();
if (this._updatableComponents.length > 0) {
for (let i = 0, s = this._updatableComponents.length; i < s; ++ i) {
for (let i = 0, s = this._updatableComponents.length; i < s; ++i) {
let updateComponent = this._updatableComponents[i];
if (updateComponent.enabled)
updateComponent.update();
@@ -284,8 +400,8 @@ module es {
}
public onEntityTransformChanged(comp: ComponentTransform) {
if (this._components.length > 0 ){
for (let i = 0, s = this._components.length; i < s; ++ i) {
if (this._components.length > 0) {
for (let i = 0, s = this._components.length; i < s; ++i) {
let component = this._components[i];
if (component.enabled)
component.onEntityTransformChanged(comp);
@@ -293,7 +409,7 @@ module es {
}
if (this._componentsToAddList.length > 0) {
for (let i = 0, s = this._componentsToAddList.length; i < s; ++ i) {
for (let i = 0, s = this._componentsToAddList.length; i < s; ++i) {
let component = this._componentsToAddList[i];
if (component.enabled)
component.onEntityTransformChanged(comp);
@@ -303,14 +419,14 @@ module es {
public onEntityEnabled() {
if (this._components.length > 0) {
for (let i = 0, s = this._components.length; i < s; i ++)
for (let i = 0, s = this._components.length; i < s; i++)
this._components[i].onEnabled();
}
}
public onEntityDisabled() {
if (this._components.length > 0) {
for (let i = 0, s = this._components.length; i < s; i ++)
for (let i = 0, s = this._components.length; i < s; i++)
this._components[i].onDisabled();
}
}

View File

@@ -1,28 +1,48 @@
module es {
export class EntityList {
public scene: Scene;
/**
* 场景中添加的实体列表
* 场景引用
*/
public scene: Scene;
/**
* 实体列表
*/
public _entities: Entity[] = [];
/**
* 本帧添加的实体列表。用于对实体进行分组,以便我们可以同时处理它们
* 添加的实体字典
*/
public _entitiesToAdded: {[index: number]: Entity} = {};
public _entitiesToAdded: { [index: number]: Entity } = {};
/**
* 本帧被标记为删除的实体列表。用于对实体进行分组,以便我们可以同时处理它们
* 待移除的实体字典
*/
public _entitiesToRemove: { [index: number]: Entity } = {};
/**
* 待添加的实体列表
*/
public _entitiesToRemove: {[index: number]: Entity} = {};
public _entitiesToAddedList: Entity[] = [];
public _entitiesToRemoveList: Entity[] = [];
/**
* 标志,用于确定我们是否需要在这一帧中对实体进行排序
* 待移除的实体列表
*/
public _entitiesToRemoveList: Entity[] = [];
/**
* 实体列表是否已排序
*/
public _isEntityListUnsorted: boolean;
/**
* 通过标签跟踪实体,便于检索
* 实体字典,以实体标签为键
*/
public _entityDict: Map<number, Set<Entity>> = new Map<number, Set<Entity>>();
/**
* 未排序的标签集合
*/
public _unsortedTags: Set<number> = new Set<number>();
constructor(scene: Scene) {
@@ -55,132 +75,186 @@ module es {
}
/**
* 从列表中删除一个实体。所有的生命周期方法将在下一帧中被调用
* @param entity
* 从场景中移除实体。
* @param entity 要从场景中移除的实体。
*/
public remove(entity: Entity) {
// 防止在同一帧中添加或删除实体
// 如果实体在添加列表中,则将其从添加列表中移除
if (this._entitiesToAdded[entity.id]) {
let index = this._entitiesToAddedList.findIndex(e => e.id == entity.id);
if (index != -1)
const index = this._entitiesToAddedList.findIndex((e) => e.id === entity.id);
if (index !== -1) {
this._entitiesToAddedList.splice(index, 1);
}
delete this._entitiesToAdded[entity.id];
return;
}
// 如果实体不在添加列表中,则将其添加到移除列表中并将其添加到移除字典中
this._entitiesToRemoveList.push(entity);
if (!this._entitiesToRemove[entity.id])
if (!this._entitiesToRemove[entity.id]) {
this._entitiesToRemove[entity.id] = entity;
}
}
/**
* 从实体列表中删除所有实体
* 从场景中移除所有实体
*/
public removeAllEntities() {
// 清除字典和列表,以及是否已排序的标志
this._unsortedTags.clear();
this._entitiesToAdded = {};
this._entitiesToAddedList.length = 0;
this._isEntityListUnsorted = false;
// 为什么我们要在这里更新列表?主要是为了处理在场景切换前被分离的实体
// 它们仍然会在_entitiesToRemove列表中这将由updateLists处理。
// 调用updateLists方法以处理要移除的实体
this.updateLists();
for (let i = 0; i < this._entities.length; i++) {
this._entities[i]._isDestroyed = true;
this._entities[i].onRemovedFromScene();
this._entities[i].scene = null;
// 标记并移除所有实体
for (const entity of this._entities) {
entity._isDestroyed = true;
entity.onRemovedFromScene();
entity.scene = null;
}
// 清空实体列表和实体字典
this._entities.length = 0;
this._entityDict.clear();
}
/**
* 检查实体目前是否由这个EntityList管理
* @param entity
* 检查实体是否已经被添加到场景中。
* @param entity 要检查的实体
* @returns 如果实体已经被添加到场景中则返回true否则返回false
*/
public contains(entity: Entity): boolean {
// 检查实体是否存在于_entitiesToAdded字典中
return !!this._entitiesToAdded[entity.id];
}
public getTagList(tag: number) {
/**
* 获取具有指定标签的实体列表。
* 如果列表不存在,则创建一个新列表并返回。
* @param tag 实体标签
* @returns 具有指定标签的实体列表
*/
public getTagList(tag: number): Set<Entity> {
// 尝试从_entityDict中获取具有指定标签的实体列表
let list = this._entityDict.get(tag);
// 如果列表不存在则创建一个新的Set实例并添加到_entityDict中
if (!list) {
list = new Set();
list = new Set<Entity>();
this._entityDict.set(tag, list);
}
return list;
}
/**
* 添加实体到标签列表中。
* @param entity 实体
*/
public addToTagList(entity: Entity) {
this.getTagList(entity.tag).add(entity);
// 获取标签列表
const list = this.getTagList(entity.tag);
// 将实体添加到标签列表中
list.add(entity);
// 添加未排序标志
this._unsortedTags.add(entity.tag);
}
/**
* 从标签列表中移除实体。
* @param entity 实体
*/
public removeFromTagList(entity: Entity) {
let list = this._entityDict.get(entity.tag);
if (list)
// 获取实体的标签列表
const list = this._entityDict.get(entity.tag);
// 如果标签列表存在,则从中移除实体
if (list) {
list.delete(entity);
}
}
/**
* 更新场景中所有启用的实体的Update方法
* 如果实体的UpdateInterval为1或Time.frameCount模除UpdateInterval为0则每帧调用Update
*/
public update() {
for (let i = 0, s = this._entities.length; i < s; ++ i) {
let entity = this._entities[i];
if (entity.enabled && (entity.updateInterval == 1 || Time.frameCount % entity.updateInterval == 0))
for (let i = 0; i < this._entities.length; i++) {
const entity = this._entities[i];
if (entity.enabled && (entity.updateInterval === 1 || Time.frameCount % entity.updateInterval === 0)) {
entity.update();
}
}
}
/**
* 更新场景中实体的列表。
*/
public updateLists() {
// 处理要移除的实体
if (this._entitiesToRemoveList.length > 0) {
for (let i = 0, s = this._entitiesToRemoveList.length; i < s; ++ i) {
let entity = this._entitiesToRemoveList[i];
for (const entity of this._entitiesToRemoveList) {
// 从标签列表中删除实体
this.removeFromTagList(entity);
// 处理常规实体列表
let index = this._entities.findIndex(e => e.id == entity.id);
if (index != -1)
// 从场景实体列表中删除实体
const index = this._entities.findIndex((e) => e.id === entity.id);
if (index !== -1) {
this._entities.splice(index, 1);
}
// 调用实体的onRemovedFromScene方法并将其scene属性设置为null
entity.onRemovedFromScene();
entity.scene = null;
// 通知场景实体处理器,一个实体已被移除
this.scene.entityProcessors.onEntityRemoved(entity);
}
// 清空要移除的实体列表和字典
this._entitiesToRemove = {};
this._entitiesToRemoveList.length = 0;
}
// 处理要添加的实体
if (this._entitiesToAddedList.length > 0) {
for (let i = 0, s = this._entitiesToAddedList.length; i < s; ++ i) {
let entity = this._entitiesToAddedList[i];
// 添加实体到场景实体列表和标签列表中
for (const entity of this._entitiesToAddedList) {
this._entities.push(entity);
entity.scene = this.scene;
this.addToTagList(entity);
}
// 通知场景实体处理器,有新的实体已添加
for (const entity of this._entitiesToAddedList) {
this.scene.entityProcessors.onEntityAdded(entity);
}
for (let i = 0, s = this._entitiesToAddedList.length; i < s; ++ i) {
let entity = this._entitiesToAddedList[i];
// 调用实体的onAddedToScene方法以允许它们执行任何场景相关的操作
for (const entity of this._entitiesToAddedList) {
entity.onAddedToScene();
}
// 清空要添加的实体列表和字典
this._entitiesToAdded = {};
this._entitiesToAddedList.length = 0;
}
}
/**
* 返回第一个找到的名字为name的实体。如果没有找到则返回null
* @param name
*/
public findEntity(name: string) {
if (this._entities.length > 0) {
for (let i = 0, s = this._entities.length; i < s; ++ i) {
for (let i = 0, s = this._entities.length; i < s; ++i) {
let entity = this._entities[i];
if (entity.name == name)
return entity;
@@ -188,7 +262,7 @@ module es {
}
if (this._entitiesToAddedList.length > 0) {
for (let i = 0, s = this._entitiesToAddedList.length; i < s; ++ i) {
for (let i = 0, s = this._entitiesToAddedList.length; i < s; ++i) {
let entity = this._entitiesToAddedList[i];
if (entity.name == name)
return entity;
@@ -199,37 +273,45 @@ module es {
}
/**
*
* @param id
* @returns
* 通过实体ID在场景中查找对应实体
* @param id 实体ID
* @returns 返回找到的实体,如果没有找到则返回 null
*/
public findEntityById(id: number) {
// 遍历场景中所有实体
if (this._entities.length > 0) {
for (let i = 0, s = this._entities.length; i < s; ++ i) {
for (let i = 0, s = this._entities.length; i < s; ++i) {
let entity = this._entities[i];
// 如果实体的ID匹配返回该实体
if (entity.id == id)
return entity;
}
}
// 在未添加的实体列表中查找
return this._entitiesToAdded[id];
}
/**
* 返回带有标签的所有实体列表。如果没有实体有标签,则返回一个空列表。
* 返回的List可以通过ListPool.free放回池中
* @param tag
* 获取标签对应的实体列表
* @param tag 实体的标签
* @returns 返回所有拥有该标签的实体列表
*/
public entitiesWithTag(tag: number) {
let list = this.getTagList(tag);
public entitiesWithTag(tag: number): Entity[] {
// 从字典中获取对应标签的实体列表
const list = this.getTagList(tag);
// 从对象池中获取 Entity 类型的数组
const returnList = ListPool.obtain<Entity>(Entity);
let returnList = ListPool.obtain<Entity>(Entity);
if (list.size > 0) {
for (let entity of list) {
// 将实体列表中的实体添加到返回列表中
for (const entity of list) {
returnList.push(entity);
}
}
// 返回已填充好实体的返回列表
return returnList;
}
@@ -251,98 +333,110 @@ module es {
}
/**
* 返回在场景中找到的第一个T类型的组件。
* @param type
* 在场景中查找具有给定类型的组件。
* @param type 要查找的组件类型。
* @returns 如果找到则返回该组件否则返回null。
*/
public findComponentOfType<T extends Component>(type): T {
if (this._entities.length > 0 ){
for (let i = 0, s = this._entities.length; i < s; i++) {
let entity = this._entities[i];
public findComponentOfType<T extends Component>(type: new (...args: any[]) => T): T | null {
// 遍历场景中的所有实体,查找具有给定类型的组件
for (const entity of this._entities) {
if (entity.enabled) {
let comp = entity.getComponent<T>(type);
if (comp)
const comp = entity.getComponent(type);
if (comp) {
return comp;
}
}
}
if (this._entitiesToAddedList.length > 0) {
for (let i = 0; i < this._entitiesToAddedList.length; i++) {
let entity = this._entitiesToAddedList[i];
// 遍历待添加的实体列表中的所有实体,查找具有给定类型的组件
for (const entity of this._entitiesToAddedList) {
if (entity.enabled) {
let comp = entity.getComponent<T>(type);
if (comp)
const comp = entity.getComponent(type);
if (comp) {
return comp;
}
}
}
// 如果找不到具有给定类型的组件则返回null
return null;
}
/**
* 返回在场景中找到的所有T类型的组件。
* 返回的List可以通过ListPool.free放回池中
* @param type
* 在场景中查找具有给定类型的所有组件。
* @param type 要查找的组件类型
* @returns 具有给定类型的所有组件的列表。
*/
public findComponentsOfType<T extends Component>(type): T[] {
let comps = ListPool.obtain<T>(type);
if (this._entities.length > 0) {
for (let i = 0, s = this._entities.length; i < s; i++) {
let entity = this._entities[i];
if (entity.enabled)
public findComponentsOfType<T extends Component>(type: new (...args: any[]) => T): T[] {
// 从池中获取一个可重用的组件列表
const comps = ListPool.obtain<T>(type);
// 遍历场景中的所有实体,查找具有给定类型的组件并添加到组件列表中
for (const entity of this._entities) {
if (entity.enabled) {
entity.getComponents(type, comps);
}
}
if (this._entitiesToAddedList.length > 0) {
for (let i = 0, s = this._entitiesToAddedList.length; i < s; i ++) {
let entity = this._entitiesToAddedList[i];
if (entity.enabled)
// 遍历待添加的实体列表中的所有实体,查找具有给定类型的组件并添加到组件列表中
for (const entity of this._entitiesToAddedList) {
if (entity.enabled) {
entity.getComponents(type, comps);
}
}
// 返回具有给定类型的所有组件的列表
return comps;
}
/**
* 返回场景中包含特定组件的实体列表
* @param types
* @returns
* 返回拥有指定类型组件的所有实体
* @param types 要查询的组件类型列表
* @returns 返回拥有指定类型组件的所有实体
*/
public findEntitiesOfComponent(...types: any[]): Entity[] {
let entities = [];
if (this._entities.length > 0) {
for (let i = 0, s = this._entities.length; i < s; i++) {
if (this._entities[i].enabled) {
const entities = [];
// 遍历所有已存在的实体
for (const entity of this._entities) {
// 只有启用的实体才会被考虑
if (entity.enabled) {
// 如果types数组为空直接将实体添加到结果数组中
if (types.length === 0) {
entities.push(entity);
continue;
}
// 对于每个指定的组件类型,检查实体是否具有该组件
let meet = true;
if (types.length > 0)
for (let t = 0, ts = types.length; t < ts; t ++) {
let type = types[t];
let hasComp = this._entities[i].hasComponent(type);
for (const type of types) {
const hasComp = entity.hasComponent(type);
if (!hasComp) {
meet = false;
break;
}
}
// 如果实体满足要求,将其添加到结果数组中
if (meet) {
entities.push(this._entities[i]);
}
entities.push(entity);
}
}
}
if (this._entitiesToAddedList.length > 0) {
for (let i = 0, s = this._entitiesToAddedList.length; i < s; i ++) {
let entity = this._entitiesToAddedList[i];
// 遍历所有等待添加的实体,和上面的操作类似
for (const entity of this._entitiesToAddedList) {
if (entity.enabled) {
if (types.length === 0) {
entities.push(entity);
continue;
}
let meet = true;
if (types.length > 0)
for (let t = 0, ts = types.length; t < ts; t ++) {
let type = types[t];
let hasComp = entity.hasComponent(type);
for (const type of types) {
const hasComp = entity.hasComponent(type);
if (!hasComp) {
meet = false;
break;
@@ -354,10 +448,9 @@ module es {
}
}
}
}
return entities;
}
}
}

View File

@@ -1,113 +1,181 @@
module es {
export class EntityProcessorList {
private _processors: EntitySystem[] = [];
private _orderDirty: boolean = false;
/** 获取系统列表 */
private _processors: EntitySystem[] = []; // 处理器列表
private _orderDirty: boolean = false; // 处理器排序标志
/** 获取处理器列表 */
public get processors() {
return this._processors;
}
/** 系统数量 */
/** 获取处理器数量 */
public get count() {
return this._processors.length;
}
public add(processor: EntitySystem) {
/**
* 添加处理器
* @param processor 要添加的处理器
*/
public add(processor: EntitySystem): void {
this._processors.push(processor);
}
public remove(processor: EntitySystem) {
/**
* 移除处理器
* @param processor 要移除的处理器
*/
public remove(processor: EntitySystem): void {
// 使用 es.List 类的 remove() 方法从处理器列表中移除指定处理器
new es.List(this._processors).remove(processor);
}
public onComponentAdded(entity: Entity) {
/**
* 在实体上添加组件时被调用
* @param entity 添加组件的实体
*/
public onComponentAdded(entity: Entity): void {
this.notifyEntityChanged(entity);
}
public onComponentRemoved(entity: Entity) {
/**
* 在实体上移除组件时被调用
* @param entity 移除组件的实体
*/
public onComponentRemoved(entity: Entity): void {
this.notifyEntityChanged(entity);
}
public onEntityAdded(entity: Entity) {
/**
* 在场景中添加实体时被调用
* @param entity 添加的实体
*/
public onEntityAdded(entity: Entity): void {
this.notifyEntityChanged(entity);
}
public onEntityRemoved(entity: Entity) {
/**
* 在场景中移除实体时被调用
* @param entity 移除的实体
*/
public onEntityRemoved(entity: Entity): void {
this.removeFromProcessors(entity);
}
public begin() {
/** 在处理器列表上开始循环 */
public begin(): void {
}
public update() {
if (this._processors.length == 0)
/** 更新处理器列表 */
public update(): void {
// 如果处理器列表为空,则直接返回
if (this._processors.length === 0) {
return;
}
// 如果需要重新排序处理器列表
if (this._orderDirty) {
// 进行排序
this._processors = this._processors.sort((a, b) => a.updateOrder - b.updateOrder);
for (let i = 0, s = this._processors.length; i < s; ++ i) {
// 对处理器列表进行排序
this._processors.sort((a, b) => a.updateOrder - b.updateOrder);
// 重新设置处理器的更新顺序
for (let i = 0, s = this._processors.length; i < s; ++i) {
const processor = this._processors[i];
processor.setUpdateOrder(i);
}
// 将标志设置为“未脏”
this.clearDirty();
}
for (let i = 0, s = this._processors.length; i < s; ++ i) {
this._processors[i].update();
// 调用每个处理器的 update() 方法
for (let i = 0, s = this._processors.length; i < s; ++i) {
const processor = this._processors[i];
processor.update();
}
}
public lateUpdate() {
if (this._processors.length == 0)
return;
for (let i = 0, s = this._processors.length; i < s; ++ i) {
this._processors[i].lateUpdate();
}
/** 在处理器列表上完成循环 */
public end(): void {
}
public end() {
}
public setDirty() {
/** 设置处理器排序标志 */
public setDirty(): void {
this._orderDirty = true;
}
public clearDirty() {
/** 清除处理器排序标志 */
public clearDirty(): void {
this._orderDirty = false;
}
/**
* 获取指定类型的处理器
* @param type 指定类型的构造函数
* @returns 指定类型的处理器
*/
public getProcessor<T extends EntitySystem>(type: new (...args: any[]) => T): T {
if (this._processors.length == 0)
// 如果处理器列表为空则返回null
if (this._processors.length === 0) {
return null;
}
for (let i = 0, s = this._processors.length; i < s; ++ i) {
let processor = this._processors[i];
if (processor instanceof type)
// 遍历处理器列表,查找指定类型的处理器
for (let i = 0, s = this._processors.length; i < s; ++i) {
const processor = this._processors[i];
// 如果当前处理器是指定类型的实例,则返回当前处理器
if (processor instanceof type) {
return processor as T;
}
}
// 如果没有找到指定类型的处理器则返回null
return null;
}
protected notifyEntityChanged(entity: Entity) {
if (this._processors.length == 0)
/**
* 通知处理器实体已更改
* @param entity 发生更改的实体
*/
protected notifyEntityChanged(entity: Entity): void {
if (this._processors.length === 0) {
return;
}
for (let i = 0, s = this._processors.length; i < s; ++ i) {
this._processors[i].onChanged(entity);
// 遍历处理器列表,调用每个处理器的 onChanged() 方法
for (let i = 0, s = this._processors.length; i < s; ++i) {
const processor = this._processors[i];
processor.onChanged(entity);
}
}
protected removeFromProcessors(entity: Entity) {
if (this._processors.length == 0)
/**
* 从处理器列表中移除实体
* @param entity 要移除的实体
*/
protected removeFromProcessors(entity: Entity): void {
if (this._processors.length === 0) {
return;
}
for (let i = 0, s = this._processors.length; i < s; ++ i) {
this._processors[i].remove(entity);
// 遍历处理器列表,调用每个处理器的 remove() 方法
for (let i = 0, s = this._processors.length; i < s; ++i) {
const processor = this._processors[i];
processor.remove(entity);
}
}
/** 在处理器列表上进行后期更新 */
public lateUpdate(): void {
if (this._processors.length === 0) {
return;
}
// 调用每个处理器的 lateUpdate() 方法
for (let i = 0, s = this._processors.length; i < s; ++i) {
const processor = this._processors[i];
processor.lateUpdate();
}
}
}

View File

@@ -1,89 +1,96 @@
module es {
export class HashHelpers {
// 哈希冲突阈值,超过此值将使用另一种哈希算法
public static readonly hashCollisionThreshold: number = 100;
// 哈希值用于计算哈希表索引的质数
public static readonly hashPrime: number = 101;
/**
* 用来作为哈希表大小的质数表。
* 一个典型的调整大小的算法会在这个数组中选取比之前容量大两倍的最小质数。
* 假设我们的Hashtable当前的容量为x并且添加了足够多的元素因此需要进行大小调整。
* 调整大小首先计算2x然后在表中找到第一个大于2x的质数即如果质数的顺序是p_1p_2...p_i...则找到p_n使p_n-1 < 2x < p_n。
* 双倍对于保持哈希特操作的渐近复杂度是很重要的,比如添加。
* 拥有一个质数可以保证双倍哈希不会导致无限循环。 IE你的哈希函数将是h1(key)+i*h2(key)0 <= i < size.h2和size必须是相对质数。
*/
public static readonly primes = [3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
// 一组预定义的质数,用于计算哈希表容量
public static readonly primes = [
3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,
187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369];
/**
* 这是比Array.MaxArrayLength小的最大质数
*/
1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369
];
// 可分配的最大数组长度,用于避免 OutOfMemoryException
public static readonly maxPrimeArrayLength = 0x7FEFFFFD;
/**
* 判断一个数是否为质数
* @param candidate 要判断的数
* @returns 是否为质数
*/
public static isPrime(candidate: number): boolean {
if ((candidate & 1) != 0){
if ((candidate & 1) !== 0) { // 位运算判断奇偶性
let limit = Math.sqrt(candidate);
for (let divisor = 3; divisor <= limit; divisor += 2){
if ((candidate & divisor) == 0)
for (let divisor = 3; divisor <= limit; divisor += 2) { // 奇数因子判断
if ((candidate % divisor) === 0) {
return false;
}
}
return true;
}
return (candidate == 2);
return (candidate === 2); // 2是质数
}
public static getPrime(min: number): number{
if (min < 0)
throw new Error("参数错误 min不能小于0");
/**
* 获取大于等于指定值的最小质数
* @param min 指定值
* @returns 大于等于指定值的最小质数
*/
public static getPrime(min: number): number {
if (min < 0) {
throw new Error("参数错误 min 不能小于0");
}
for (let i = 0; i < this.primes.length; i ++){
for (let i = 0; i < this.primes.length; i++) {
let prime = this.primes[i];
if (prime >= min) return prime;
if (prime >= min) {
return prime;
}
}
// 在我们预定义的表之外,计算的方式稍复杂。
for (let i = (min | 1); i < Number.MAX_VALUE; i += 2){
if (this.isPrime(i) && ((i - 1) % this.hashPrime != 0))
// 在预定义的质数列表之外,需要计算最小的质数
for (let i = (min | 1); i < Number.MAX_VALUE; i += 2) { // 从 min 向上计算奇数
if (this.isPrime(i) && ((i - 1) % this.hashPrime !== 0)) { // i是质数且不是hashPrime的倍数
return i;
}
}
return min;
}
/**
*
* @param oldSize
* @returns 返回要增长的哈希特表的大小
* 扩展哈希表容量
* @param oldSize 原哈希表容量
* @returns 扩展后的哈希表容量
*/
public static expandPrime(oldSize: number): number {
let newSize = 2 * oldSize;
// 在遇到容量溢出之前,允许哈希特表增长到最大可能的大小
// 请注意即使当_items.Length溢出时这项检查也会起作用
if (newSize > this.maxPrimeArrayLength && this.maxPrimeArrayLength > oldSize){
if (newSize > this.maxPrimeArrayLength && this.maxPrimeArrayLength > oldSize) {
return this.maxPrimeArrayLength;
}
return this.getPrime(newSize);
}
public static getHashCode(str){
let s;
if (typeof str == 'object'){
s = JSON.stringify(str);
} else {
s = str.toString();
}
/**
* 计算字符串的哈希值
* @param str 要计算哈希值的字符串
* @returns 哈希值
*/
public static getHashCode(str: string): number {
let hash = 0;
if (s.length == 0) return hash;
for (let i = 0; i < s.length; i ++){
let char = s.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
if (str.length === 0) {
return hash;
}
for (let i = 0; i < str.length; i++) {
let char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char; // 采用 FNV-1a 哈希算法
hash = hash & hash; // 将hash值转换为32位整数
}
return hash;
}
}

View File

@@ -1,59 +1,92 @@
module es {
/** 提供帧定时信息 */
/**
* 时间管理器,用于管理游戏中的时间相关属性
*/
export class Time {
/** 游戏运行的总时间 */
/** 游戏运行的总时间,单位为秒 */
public static totalTime: number = 0;
/** deltaTime的未缩放版本。不受时间尺度的影响 */
/** deltaTime 的未缩放版本,不受时间尺度的影响 */
public static unscaledDeltaTime: number = 0;
/** 前一帧到当前帧的时间增量,按时间刻度进行缩放 */
public static deltaTime: number = 0;
/** 时间刻度缩放 */
/** 时间刻度缩放,可以加快或减慢游戏时间 */
public static timeScale = 1;
/** DeltaTime可以为的最大值 */
/** DeltaTime 可以为的最大值,避免游戏出现卡顿情况 */
public static maxDeltaTime = Number.MAX_VALUE;
/** 已传递的帧总数 */
public static frameCount = 0;
/** 自场景加载以来的总时间 */
/** 自场景加载以来的总时间,单位为秒 */
public static timeSinceSceneLoad: number = 0;
/** 上一次记录的时间,用于计算两次调用 update 之间的时间差 */
private static _lastTime = -1;
/**
* 更新时间管理器
* @param currentTime 当前时间
* @param useEngineTime 是否使用引擎时间
*/
public static update(currentTime: number, useEngineTime: boolean) {
let dt = 0;
if (useEngineTime) {
dt = currentTime;
} else {
if (currentTime == -1) {
// 如果当前时间为 -1则表示使用系统时间
if (currentTime === -1) {
currentTime = Date.now();
}
if (this._lastTime == -1)
// 如果上一次记录的时间为 -1则表示当前为第一次调用 update
if (this._lastTime === -1) {
this._lastTime = currentTime;
}
// 计算两次调用 update 之间的时间差,并将其转换为秒
dt = (currentTime - this._lastTime) / 1000;
}
if (dt > this.maxDeltaTime)
// 如果计算得到的时间差超过了最大时间步长,则将其限制为最大时间步长
if (dt > this.maxDeltaTime) {
dt = this.maxDeltaTime;
}
// 更新时间管理器的各个属性
this.totalTime += dt;
this.deltaTime = dt * this.timeScale;
this.unscaledDeltaTime = dt;
this.timeSinceSceneLoad += dt;
this.frameCount++;
// 记录当前时间,以备下一次调用 update 使用
this._lastTime = currentTime;
}
public static sceneChanged() {
this.timeSinceSceneLoad = 0;
}
/**
* 允许在间隔检查。只应该使用高于delta的间隔值否则它将始终返回true。
* @param interval
* 检查指定时间间隔是否已过去
* @param interval 指定时间间隔
* @returns 是否已过去指定时间间隔
*/
public static checkEvery(interval: number) {
// 我们减去了delta因为timeSinceSceneLoad已经包含了这个update ticks delta
return MathHelper.toInt(this.timeSinceSceneLoad / interval) > MathHelper.toInt((this.timeSinceSceneLoad - this.deltaTime) / interval);
public static checkEvery(interval: number): boolean {
// 计算当前时刻所经过的完整时间间隔个数(向下取整)
const passedIntervals = Math.floor(this.timeSinceSceneLoad / interval);
// 计算上一帧到当前帧经过的时间所包含的时间间隔个数(向下取整)
const deltaIntervals = Math.floor(this.deltaTime / interval);
// 如果当前时刻所经过的时间间隔数比上一帧所经过的时间间隔数多,则说明时间间隔已过去
return passedIntervals > deltaIntervals;
}
}
}

View File

@@ -1,48 +1,64 @@
class TimeUtils {
module es {
export class TimeUtils {
/**
* 计算月份ID
* @param d 指定计算日期
* @returns 月ID
* 获取日期对应的年份和月份的数字组合
* @param d 要获取月份的日期对象,不传则默认为当前时间
* @returns 返回数字组合的年份和月份
*/
public static monthId(d: Date = null): number {
// 如果传入了时间,则使用传入的时间,否则使用当前时间
d = d ? d : new Date();
// 获取当前年份
let y = d.getFullYear();
// 获取当前月份,并将月份转化为两位数的字符串格式
let m = d.getMonth() + 1;
let g = m < 10 ? "0" : "";
// 返回年份和月份的数字组合
return parseInt(y + g + m);
}
/**
* 计算日期ID
* @param d 指定计算日期
* @returns 日期ID
* 获取日期的数字组合
* @param t - 可选参数,传入时间,若不传入则使用当前时间
* @returns 数字组合
*/
public static dateId(t: Date = null): number {
// 如果传入了时间,则使用传入的时间,否则使用当前时间
t = t ? t : new Date();
// 获取当前月份,并将月份转化为两位数的字符串格式
let m: number = t.getMonth() + 1;
let a = m < 10 ? "0" : "";
// 获取当前日期,并将日期转化为两位数的字符串格式
let d: number = t.getDate();
let b = d < 10 ? "0" : "";
// 返回年份、月份和日期的数字组合
return parseInt(t.getFullYear() + a + m + b + d);
}
/**
* 计算周ID
* @param d 指定计算日期
* @returns 周ID
* 获取当前日期所在周的数字组合
* @param d - 可选参数,传入日期,若不传入则使用当前日期
* @param first - 是否将当前周视为本年度的第1周默认为true
* @returns 数字组合
*/
public static weekId(d: Date = null, first: boolean = true): number {
d = d ? d : new Date();
let c: Date = new Date();
c.setTime(d.getTime());
const c: Date = new Date(d.getTime()); // 复制一个新的日期对象,以免改变原始日期对象
c.setDate(1);
c.setMonth(0);//当年第一天
c.setMonth(0); // 将日期设置为当年第一天
let year: number = c.getFullYear();
const year: number = c.getFullYear();
let firstDay: number = c.getDay();
if (firstDay == 0) {
firstDay = 7;
}
let max: boolean = false;
if (firstDay <= 4) {
max = firstDay > 1;
@@ -50,15 +66,19 @@ class TimeUtils {
} else {
c.setDate(c.getDate() + 7 - firstDay + 1);
}
let num: number = this.diffDay(d, c, false);
let num: number = this.diffDay(d, c, false); // 计算当前日期与本年度的第一个星期一之间的天数
if (num < 0) {
// 当前日期在本年度第一个星期一之前,则返回上一年度的最后一个星期
c.setDate(1);
c.setMonth(0);//当年第一天
c.setMonth(0);
c.setDate(c.getDate() - 1);
return this.weekId(c, false);
}
let week: number = num / 7;
let weekIdx: number = Math.floor(week) + 1;
// 计算当前日期在本年度中是第几个星期
const week: number = Math.floor(num / 7);
const weekIdx: number = Math.floor(week) + 1;
if (weekIdx == 53) {
c.setTime(d.getTime());
c.setDate(c.getDate() - 1);
@@ -67,32 +87,41 @@ class TimeUtils {
endDay = 7;
}
if (first && (!max || endDay < 4)) {
// 如果当前日期在本年度的最后一个星期并且当前年度的星期数不足53或当前日期在本年度第53周的星期4或更早则返回下一年度的第1周
c.setFullYear(c.getFullYear() + 1);
c.setDate(1);
c.setMonth(0);//当年第一天
c.setMonth(0);
return this.weekId(c, false);
}
}
let g: string = weekIdx > 9 ? "" : "0";
let s: string = year + "00" + g + weekIdx;//加上00防止和月份ID冲突
const g: string = weekIdx > 9 ? "" : "0";
const s: string = year + "00" + g + weekIdx; // 加上00防止和月份ID冲突
return parseInt(s);
}
/**
* 计算俩日期时间差如果a比b小返回负
* 计算两个日期之间相差的天
* @param a 第一个日期
* @param b 第二个日期
* @param fixOne 是否将相差天数四舍五入到整数
* @returns 两个日期之间相差的天数
*/
public static diffDay(a: Date, b: Date, fixOne: boolean = false): number {
let x = (a.getTime() - b.getTime()) / 86400000;
return fixOne ? Math.ceil(x) : Math.floor(x);
let x = (a.getTime() - b.getTime()) / 86400000; // 计算两个日期相差的毫秒数,然后除以一天的毫秒数,得到相差的天数
return fixOne ? Math.ceil(x) : Math.floor(x); // 如果 fixOne 参数为 true则将相差天数四舍五入到整数否则向下取整
}
/**
* 获取本周一 凌晨时间
* 获取指定日期所在周的第一天
* @param d 指定日期,默认值为今天
* @returns 指定日期所在周的第一天
*/
public static getFirstDayOfWeek(d?: Date): Date {
d = d ? d : new Date();
let day = d.getDay() || 7;
return new Date(d.getFullYear(), d.getMonth(), d.getDate() + 1 - day, 0, 0, 0, 0);
public static getFirstDayOfWeek(d: Date = new Date()): Date {
// 获取当前日期是星期几如果是0则设置为7
let dayOfWeek = d.getDay() || 7;
// 计算出指定日期所在周的第一天即将指定日期减去星期几再加1
// 这里用1减去dayOfWeek是为了保证星期一为一周的第一天
return new Date(d.getFullYear(), d.getMonth(), d.getDate() + 1 - dayOfWeek, 0, 0, 0, 0);
}
/**
@@ -112,7 +141,9 @@ class TimeUtils {
}
/**
* @returns 2018-12-12
* 格式化日期为 "YYYY-MM-DD" 的字符串形式
* @param date 要格式化的日期
* @returns 格式化后的日期字符串
*/
public static formatDate(date: Date): string {
let y = date.getFullYear();
@@ -123,8 +154,11 @@ class TimeUtils {
return y + '-' + m + '-' + d;
}
/**
* @returns 2018-12-12 12:12:12
* 将日期对象格式化为 "YYYY-MM-DD HH:mm:ss" 的字符串
* @param date 日期对象
* @returns 格式化后的字符串
*/
public static formatDateTime(date: Date): string {
let y = date.getFullYear();
@@ -141,11 +175,14 @@ class TimeUtils {
}
/**
* @returns s 2018-12-12 或者 2018-12-12 12:12:12
* 将字符串解析为Date对象
* @param s 要解析的日期字符串例如2022-01-01
* @returns 返回解析后的Date对象如果解析失败则返回当前时间的Date对象
*/
public static parseDate(s: string): Date {
let t = Date.parse(s);
if (!isNaN(t)) {
// 如果日期字符串中的分隔符为“-”,则需要先将其转换为“/”,否则解析会失败
return new Date(Date.parse(s.replace(/-/g, "/")));
} else {
return new Date();
@@ -153,13 +190,11 @@ class TimeUtils {
}
/**
* 秒数转换为时间形式。
* 秒数转换为时分秒的格式
* @param time 秒数
* @param partition 分隔符
* @param showHour 是否显示小时
* @return 返回一个以分隔符分割的时, 分, 秒
*
* 比如: time = 4351; secondToTime(time)返回字符串01:12:31;
* @param showHour 是否显示小时
* @returns 转换后的时间字符串
*/
public static secondToTime(time: number = 0, partition: string = ":", showHour: boolean = true): string {
let hours: number = Math.floor(time / 3600);
@@ -183,30 +218,25 @@ class TimeUtils {
return timeStr;
}
/**
* 时间形式转换为毫秒数
* @param time 以指定分隔符分割的时间字符串
* @param partition 分隔符
* @return 毫秒数显示的字符串
* @throws Error Exception
*
* 用法1 trace(MillisecondTransform.timeToMillisecond("00:60:00"))
* 输出 3600000
*
*
* 用法2 trace(MillisecondTransform.timeToMillisecond("00.60.00","."))
* 输出 3600000
* 时间字符串转换为毫秒数
* @param time 时间字符串,如 "01:30:15" 表示 1小时30分钟15秒
* @param partition 分隔符,默认为 ":"
* @returns 转换后的毫秒数字符串
*/
public static timeToMillisecond(time: string, partition: string = ":"): string {
let _ary: any[] = time.split(partition);
let timeNum: number = 0;
let len: number = _ary.length;
// 将时间转换成毫秒数
for (let i: number = 0; i < len; i++) {
let n: number = <number>_ary[i];
timeNum += n * Math.pow(60, (len - 1 - i));
}
timeNum *= 1000;
return timeNum.toString();
}
}
}

View File

@@ -1,317 +0,0 @@
module es {
/**
* 创建这个字典的原因只有一个:
* 我需要一个能让我直接以数组的形式对值进行迭代的字典,而不需要生成一个数组或使用迭代器。
* 对于这个目标是比标准字典快N倍。
* Faster dictionary在大部分操作上也比标准字典快但差别可以忽略不计。
* 唯一较慢的操作是在添加时调整内存大小,因为与标准数组相比,这个实现需要使用两个单独的数组。
*/
export class FasterDictionary<TKey, TValue> {
public _values: TValue[];
public _valuesInfo: FastNode[];
public _buckets: number[];
public _freeValueCellIndex: number = 0;
public _collisions: number = 0;
constructor(size: number = 1) {
this._valuesInfo = new Array(size);
this._values = new Array(size);
this._buckets = new Array(HashHelpers.getPrime(size));
}
public getValuesArray(count: {value: number}): TValue[] {
count.value = this._freeValueCellIndex;
return this._values;
}
public get valuesArray(): TValue[] {
return this._values;
}
public get count(): number {
return this._freeValueCellIndex;
}
public add(key: TKey, value: TValue) {
if (!this.addValue(key, value, {value: 0}))
throw new Error("key 已经存在")
}
public addValue(key: TKey, value: TValue, indexSet: {value: number}) {
let hash = HashHelpers.getHashCode(key);
let bucketIndex = FasterDictionary.reduce(hash, this._buckets.length);
if (this._freeValueCellIndex == this._values.length) {
let expandPrime = HashHelpers.expandPrime(this._freeValueCellIndex);
this._values.length = expandPrime;
this._valuesInfo.length = expandPrime;
}
// buckets值-1表示它是空的
let valueIndex = NumberExtension.toNumber(this._buckets[bucketIndex]) - 1;
if (valueIndex == -1) {
// 在最后一个位置创建信息节点,并填入相关信息
this._valuesInfo[this._freeValueCellIndex] = new FastNode(key, hash);
} else {
{
let currentValueIndex = valueIndex;
do {
// 必须检查键是否已经存在于字典中
if (this._valuesInfo[currentValueIndex].hashcode == hash &&
this._valuesInfo[currentValueIndex].key == key) {
// 键已经存在,只需将其值替换掉即可
this._values[currentValueIndex] = value;
indexSet.value = currentValueIndex;
return false;
}
currentValueIndex = this._valuesInfo[currentValueIndex].previous;
}
while (currentValueIndex != -1); // -1表示没有更多的值与相同的哈希值的键
}
this._collisions++;
// 创建一个新的节点,该节点之前的索引指向当前指向桶的节点
this._valuesInfo[this._freeValueCellIndex] = new FastNode(key, hash, valueIndex);
// 更新现有单元格的下一个单元格指向新的单元格,旧的单元格 -> 新的单元格 -> 旧的单元格 <- 下一个单元格
this._valuesInfo[valueIndex].next = this._freeValueCellIndex;
}
// 重要的是:新的节点总是被桶单元格指向的那个节点,所以我可以假设被桶指向的那个节点总是最后添加的值(next = -1)
// item与这个bucketIndex将指向最后创建的值
// TODO: 如果相反我假设原来的那个是bucket中的那个我就不需要在这里更新bucket了
this._buckets[bucketIndex] = (this._freeValueCellIndex + 1);
this._values[this._freeValueCellIndex] = value;
indexSet.value = this._freeValueCellIndex;
this._freeValueCellIndex++;
if (this._collisions > this._buckets.length) {
// 我们需要更多的空间和更少的碰撞
this._buckets = new Array(HashHelpers.expandPrime(this._collisions));
this._collisions = 0;
// 我们需要得到目前存储的所有值的哈希码,并将它们分布在新的桶长上
for (let newValueIndex = 0; newValueIndex < this._freeValueCellIndex; newValueIndex++) {
// 获取原始哈希码并根据新的长度找到新的bucketIndex
bucketIndex = FasterDictionary.reduce(this._valuesInfo[newValueIndex].hashcode, this._buckets.length);
// bucketsIndex可以是-1或下一个值。
// 如果是-1意味着没有碰撞。
// 如果有碰撞,我们创建一个新节点,它的上一个指向旧节点。
// 旧节点指向新节点新节点指向旧节点旧节点指向新节点现在bucket指向新节点这样我们就可以重建linkedlist.
// 获取当前值Index如果没有碰撞则为-1。
let existingValueIndex = NumberExtension.toNumber(this._buckets[bucketIndex]) - 1;
// 将bucket索引更新为共享bucketIndex的当前项目的索引最后找到的总是bucket中的那个
this._buckets[bucketIndex] = newValueIndex + 1;
if (existingValueIndex != -1) {
// 这个单元格已经指向了新的bucket list中的一个值这意味着有一个碰撞出了问题
this._collisions++;
// bucket将指向这个值所以新的值将使用以前的索引
this._valuesInfo[newValueIndex].previous = existingValueIndex;
this._valuesInfo[newValueIndex].next = -1;
// 并将之前的下一个索引更新为新的索引
this._valuesInfo[existingValueIndex].next = newValueIndex;
} else {
// 什么都没有被索引,桶是空的。我们需要更新之前的 next 和 previous 的值。
this._valuesInfo[newValueIndex].next = -1;
this._valuesInfo[newValueIndex].previous = -1;
}
}
}
return true;
}
public remove(key: TKey): boolean {
let hash = FasterDictionary.hash(key);
let bucketIndex = FasterDictionary.reduce(hash, this._buckets.length);
// 找桶
let indexToValueToRemove = NumberExtension.toNumber(this._buckets[bucketIndex]) - 1;
// 第一部分在bucket list中寻找实际的键如果找到了我就更新bucket list使它不再指向要删除的单元格。
while (indexToValueToRemove != -1) {
if (this._valuesInfo[indexToValueToRemove].hashcode == hash &&
this._valuesInfo[indexToValueToRemove].key == key) {
// 如果找到了密钥,并且桶直接指向了要删除的节点
if (this._buckets[bucketIndex] - 1 == indexToValueToRemove){
if (this._valuesInfo[indexToValueToRemove].next != -1)
throw new Error("如果 bucket 指向单元格,那么 next 必须不存在。");
// 如果前一个单元格存在,它的下一个指针必须被更新!
//<---迭代顺序
// B(ucket总是指向最后一个)
// ------- ------- -------
// 1 | | | | 2 | | | 3 | //bucket不能有下一个只能有上一个。
// ------- ------- -------
//--> 插入
let value = this._valuesInfo[indexToValueToRemove].previous;
this._buckets[bucketIndex] = value + 1;
}else{
if (this._valuesInfo[indexToValueToRemove].next == -1)
throw new Error("如果 bucket 指向另一个单元格,则 NEXT 必须存在");
}
FasterDictionary.updateLinkedList(indexToValueToRemove, this._valuesInfo);
break;
}
indexToValueToRemove = this._valuesInfo[indexToValueToRemove].previous;
}
if (indexToValueToRemove == -1)
return false; // 未找到
this._freeValueCellIndex --; // 少了一个需要反复计算的值
// 第二部分
// 这时节点指针和水桶会被更新但_values数组会被更新仍然有要删除的值
// 这个字典的目标是能够做到像数组一样对数值进行迭代,所以数值数组必须始终是最新的
// 如果要删除的单元格是列表中的最后一个,我们可以执行较少的操作(不需要交换),否则我们要将最后一个值的单元格移到要删除的值上。
if (indexToValueToRemove != this._freeValueCellIndex){
// 我们可以将两个数组的最后一个值移到要删除的数组中。
// 为了做到这一点,我们需要确保 bucket 指针已经更新了
// 首先我们在桶列表中找到指向要移动的单元格的指针的索引
let movingBucketIndex = FasterDictionary.reduce(this._valuesInfo[this._freeValueCellIndex].hashcode, this._buckets.length);
// 如果找到了键,并且桶直接指向要删除的节点,现在必须指向要移动的单元格。
if (this._buckets[movingBucketIndex] - 1 == this._freeValueCellIndex)
this._buckets[movingBucketIndex] = (indexToValueToRemove + 1);
// 否则意味着有多个键具有相同的哈希值(碰撞),所以我们需要更新链接列表和它的指针
let next = this._valuesInfo[this._freeValueCellIndex].next;
let previous = this._valuesInfo[this._freeValueCellIndex].previous;
// 现在它们指向最后一个值被移入的单元格
if (next != -1)
this._valuesInfo[next].previous = indexToValueToRemove;
if (previous != -1)
this._valuesInfo[previous].next = indexToValueToRemove;
// 最后,实际上是移动值
this._valuesInfo[indexToValueToRemove] = this._valuesInfo[this._freeValueCellIndex];
this._values[indexToValueToRemove] = this._values[this._freeValueCellIndex];
}
return true;
}
public trim(){
let expandPrime = HashHelpers.expandPrime(this._freeValueCellIndex);
if (expandPrime < this._valuesInfo.length){
this._values.length = expandPrime;
this._valuesInfo.length = expandPrime;
}
}
public clear(){
if (this._freeValueCellIndex == 0) return;
this._freeValueCellIndex = 0;
this._buckets.length = 0;
this._values.length = 0;
this._valuesInfo.length = 0;
}
public fastClear(){
if (this._freeValueCellIndex == 0) return;
this._freeValueCellIndex = 0;
this._buckets.length = 0;
this._valuesInfo.length = 0;
}
public containsKey(key: TKey){
if (this.tryFindIndex(key, {value: 0})){
return true;
}
return false;
}
public tryGetValue(key: TKey): TValue {
let findIndex = {value: 0};
if (this.tryFindIndex(key, findIndex)){
return this._values[findIndex.value];
}
return null;
}
public tryFindIndex(key: TKey, findIndex: {value: number}){
// 我把所有的索引都用偏移量+1来存储这样在bucket list中0就意味着实际上不存在
// 当读取时,偏移量必须再偏移-1才是真实的
// 这样我就避免了将数组初始化为-1
let hash = FasterDictionary.hash(key);
let bucketIndex = FasterDictionary.reduce(hash, this._buckets.length);
let valueIndex = NumberExtension.toNumber(this._buckets[bucketIndex]) - 1;
// 即使我们找到了一个现有的值,我们也需要确定它是我们所要求的值
while (valueIndex != -1){
if (this._valuesInfo[valueIndex].hashcode == hash && this._valuesInfo[valueIndex].key == key){
findIndex.value = valueIndex;
return true;
}
valueIndex = this._valuesInfo[valueIndex].previous;
}
findIndex.value = 0;
return false;
}
public getDirectValue(index: number): TValue {
return this._values[index];
}
public getIndex(key: TKey): number {
let findIndex = {value: 0};
if (this.tryFindIndex(key, findIndex))
return findIndex.value;
throw new Error("未找到key");
}
public static updateLinkedList(index: number, valuesInfo: FastNode[]){
let next = valuesInfo[index].next;
let previous = valuesInfo[index].previous;
if (next != -1)
valuesInfo[next].previous = previous;
if (previous != -1)
valuesInfo[previous].next = next;
}
public static hash(key) {
return HashHelpers.getHashCode(key);
}
public static reduce(x: number, n: number) {
if (x >= n)
return x % n;
return x;
}
}
export class FastNode {
readonly key;
readonly hashcode: number;
previous: number;
next: number;
constructor(key, hash: number, previousNode: number = -1) {
this.key = key;
this.hashcode = hash;
this.previous = previousNode;
this.next = -1;
}
}
}