扩展Array、Diector、Node对象功能

This commit is contained in:
donggang
2024-03-04 14:46:13 +08:00
parent 35c44340c2
commit 35a593e635
10 changed files with 874 additions and 25 deletions

View File

@@ -0,0 +1,9 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "1e41578f-5401-45ed-be1f-ad846351d895",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,373 @@
declare global {
interface Array<T> {
remove(filter: (v: T, i: number, arr: Array<T>) => boolean): Array<T>;
remove(filter: T): Array<T>;
removeOne(filter: (v: T, i: number, arr: Array<T>) => boolean): Array<T>;
removeOne(filter: T): Array<T>;
random(): T;
first(): T;
last(): T;
max(): T;
max<P>(mapper: (v: T, i: number, arr: this) => P): P | null;
min(): T;
min<P>(mapper: (v: T, i: number, arr: this) => P): P | null;
distinct(): Array<T>;
filterIndex(filter: (v: T, i: number, arr: this) => boolean): Array<number>;
count(filter: (v: T, i: number, arr: this) => boolean): number;
sum(mapper?: (v: T, i: number, arr: this) => number): number;
average(mapper?: (v: T, i: number, arr: this) => number): number;
// 移除指定位置的元素 返回移除的元素
fastRemoveAt(index: number): T;
fastRemove(value: T): boolean;
/**
* 同find但返回整个Array<T>中最后一个匹配元素
*/
findLast(predicate: (value: T, index: number, obj: Array<T>) => boolean): T | undefined;
/**
* 同find但返回整个Array<T>中最后一个匹配元素的index
*/
findLastIndex(predicate: (value: T, index: number, obj: Array<T>) => boolean): number;
//排序升序 返回新的数组
orderBy(...mappers: ((v: T) => any)[]): Array<T>;
// 排序 降序
orderByDesc(...mappers: ((v: T) => any)[]): Array<T>;
/**
* 二分查找 前提是数组一定是有序的
* @param value 要查找的值
* @param keyMapper 要查找的值的mapper方法默认为查找数组元素本身
* @return 查找到的index查不到返回-1
*/
binarySearch(value: number | string, keyMapper?: (v: T) => (number | string)): number;
/**
* 二分插入 前提是数组一定是有序的
* @param item 要插入的值
* @param keyMapper 二分查找时要查找的值的mapper方法默认为查找数组元素本身
* @param unique 是否去重如果为true则如果数组内已经有值时不插入返回已有值的number
* @return 返回插入的index位置
*/
binaryInsert(item: T, unique?: boolean): number;
binaryInsert(item: T, keyMapper: (v: T) => (number | string), unique?: boolean): number;
/**
* 二分去重 前提是数组一定是有序的
* @param keyMapper 二分查找时要查找的值的mapper方法默认为查找数组元素本身
*/
binaryDistinct(keyMapper?: (v: T) => (number | string)): Array<T>;
groupBy(grouper: (v: T) => any): (T[] & { key: any })[];
}
}
// @ts-ignore
!Array.prototype.__cc_extended && Object.defineProperties(Array.prototype, {
remove: {
value: function (filter) {
if (typeof (filter) == 'function') {
for (let i = this.length - 1; i > -1; --i) {
filter(this[i], i, this) && this.splice(i, 1);
}
}
else {
for (let i = this.length - 1; i > -1; --i) {
this[i] === filter && this.splice(i, 1);
}
}
return this;
}
},
removeOne: {
value: function (filter) {
if (typeof (filter) == 'function') {
for (let i = 0; i < this.length; ++i) {
if (filter(this[i], i, this)) {
this.splice(i, 1);
return this;
}
}
}
else {
for (let i = 0; i < this.length; ++i) {
if (this[i] === filter) {
this.splice(i, 1);
return this;
}
}
}
return this;
}
},
random: {
value: function () {
let element = this[Math.floor(Math.random() * this.length)];
return element;
}
},
fastRemoveAt: {
value: function (index) {
const length = this.length;
if (index < 0 || index >= length) {
return null;
}
let res = this[index];
this[index] = this[length - 1];
this.length = length - 1;
return res;
}
},
fastRemove: {
value: function (value) {
const index = this.indexOf(value);
if (index >= 0) {
this[index] = this[this.length - 1];
--this.length;
return true;
}
return false;
}
},
first: {
value: function () {
return this.length ? this[0] : null;
}
},
last: {
value: function () {
return this.length ? this[this.length - 1] : null;
}
},
max: {
value: function (mapper) {
if (!this.length) {
return null;
}
function _max(a, b) {
return a > b ? a : b;
}
if (typeof (mapper) == 'function') {
let max = mapper(this[0], 0, this);
for (let i = 1; i < this.length; ++i) {
let temp = mapper(this[i], i, this);
max = temp > max ? temp : max;
}
return max;
}
else {
return this.reduce(function (prev, cur) { return _max(prev, cur); });
}
}
},
min: {
value: function (mapper) {
if (!this.length) {
return null;
}
function _min(a, b) {
return a < b ? a : b;
}
if (typeof (mapper) == 'function') {
let min = mapper(this[0], 0, this);
for (let i = 1; i < this.length; ++i) {
let temp = mapper(this[i], i, this);
min = temp < min ? temp : min;
}
return min;
}
else {
return this.reduce(function (prev, cur) { return _min(prev, cur); });
}
}
},
distinct: {
value: function () {
return this.filter(function (v, i, arr) { return arr.indexOf(v) === i; });
}
},
filterIndex: {
value: function (filter) {
let output = [];
for (let i = 0; i < this.length; ++i) {
if (filter(this[i], i, this)) {
output.push(i);
}
}
return output;
}
},
count: {
value: function (filter) {
let result = 0;
for (let i = 0; i < this.length; ++i) {
if (filter(this[i], i, this)) {
++result;
}
}
return result;
}
},
sum: {
value: function (mapper) {
let result = 0;
for (let i = 0; i < this.length; ++i) {
result += mapper ? mapper(this[i], i, this) : this[i];
}
return result;
}
},
average: {
value: function (mapper) {
return this.sum(mapper) / this.length;
}
},
orderBy: {
value: function () {
let mappers = [];
for (let _i = 0; _i < arguments.length; _i++) {
mappers[_i] = arguments[_i];
}
return this.slice().sort(function (a, b) {
for (let i = 0; i < mappers.length; ++i) {
let va = mappers[i](a);
let vb = mappers[i](b);
if (va > vb) {
return 1;
}
else if (va < vb) {
return -1;
}
}
return 0;
});
}
},
orderByDesc: {
value: function () {
let mappers = [];
for (let _i = 0; _i < arguments.length; _i++) {
mappers[_i] = arguments[_i];
}
return this.slice().sort(function (a, b) {
for (let i = 0; i < mappers.length; ++i) {
let va = mappers[i](a);
let vb = mappers[i](b);
if (va > vb) {
return -1;
}
else if (va < vb) {
return 1;
}
}
return 0;
});
}
},
binarySearch: {
value: function (value, keyMapper) {
let low = 0, high = this.length - 1;
while (low <= high) {
let mid = ((high + low) / 2) | 0;
let midValue = keyMapper ? keyMapper(this[mid]) : this[mid];
if (value === midValue) {
return mid;
}
else if (value > midValue) {
low = mid + 1;
}
else if (value < midValue) {
high = mid - 1;
}
}
return -1;
}
},
binaryInsert: {
value: function (item, keyMapper, unique) {
if (typeof (keyMapper) == 'boolean') {
unique = keyMapper;
keyMapper = undefined;
}
let low = 0, high = this.length - 1;
let mid = NaN;
let itemValue = keyMapper ? keyMapper(item) : item;
while (low <= high) {
mid = ((high + low) / 2) | 0;
let midValue = keyMapper ? keyMapper(this[mid]) : this[mid];
if (itemValue === midValue) {
if (unique) {
return mid;
}
else {
break;
}
}
else if (itemValue > midValue) {
low = mid + 1;
}
else if (itemValue < midValue) {
high = mid - 1;
}
}
let index = low > mid ? mid + 1 : mid;
this.splice(index, 0, item);
return index;
}
},
binaryDistinct: {
value: function (keyMapper) {
return this.filter(function (v, i, arr) { return arr.binarySearch(v, keyMapper) === i; });
}
},
findLast: {
value: function (predicate) {
for (let i = this.length - 1; i > -1; --i) {
if (predicate(this[i], i, this)) {
return this[i];
}
}
return undefined;
}
},
findLastIndex: {
value: function (predicate) {
for (let i = this.length - 1; i > -1; --i) {
if (predicate(this[i], i, this)) {
return i;
}
}
return -1;
}
},
groupBy: {
value: function (grouper) {
let group = this.reduce(function (prev, next) {
let groupKey = grouper(next);
if (!prev[groupKey]) {
prev[groupKey] = [];
}
prev[groupKey].push(next);
return prev;
}, {});
return Object.keys(group).map(function (key) {
let arr = group[key];
arr.key = key;
return arr;
});
}
},
__cc_extended: {
value: true
}
});
export { };

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.23",
"importer": "typescript",
"imported": true,
"uuid": "2696f555-d14d-4172-8b70-b6e496eea7bd",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,29 @@
import { Director, director, js } from "cc";
import { EDITOR } from "cc/env";
/** 全局游戏时间缩放 */
if (!EDITOR) {
//@ts-ignore
if (!Director.prototype["__$cc-director-speed-extension$__"]) {
//@ts-ignore
Director.prototype["__$cc-director-speed-extension$__"] = true;
let oldTick = director.tick.bind(director);
director.tick = function (dt) {
dt *= director.globalGameTimeScale;
oldTick(dt);
};
js.mixin(Director.prototype, {
globalGameTimeScale: 1,
});
}
}
declare module "cc" {
interface Director {
globalGameTimeScale: number;
}
}
// director.globalGameTimeScale = 0.5;

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.23",
"importer": "typescript",
"imported": true,
"uuid": "37f480c3-2d1d-445c-a873-9feee92e5581",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,185 @@
import { EventTouch, Node, Vec2, js, v3 } from "cc";
import { DEV, EDITOR } from "cc/env";
/** 节点拖拽功能 */
if (!EDITOR) {
//@ts-ignore
if (!Node.prototype["__$cc-node-drag-extension$__"]) {
//@ts-ignore
Node.prototype["__$cc-node-drag-extension$__"] = true;
let _DragEvent = {
DRAG_START: "drag_start",
DRAG_MOVE: "drag_move",
DRAG_END: "drag_end"
}
js.mixin(Node, {
DragEvent: _DragEvent
});
//---------------- Node 添加 拖拽属性 ----------------
js.mixin(Node.prototype, {
_draggable: false,
_dragging: false,
_dragTesting: false,
_dragStartPoint: null,
initDrag: function () {
if (this._draggable) {
this.on(Node.EventType.TOUCH_START, this.onTouchBegin_0, this);
this.on(Node.EventType.TOUCH_MOVE, this.onTouchMove_0, this);
this.on(Node.EventType.TOUCH_END, this.onTouchEnd_0, this);
this.on(Node.EventType.TOUCH_CANCEL, this.onTouchCancel_0, this);
}
else {
this.off(Node.EventType.TOUCH_START, this.onTouchBegin_0, this);
this.off(Node.EventType.TOUCH_MOVE, this.onTouchMove_0, this);
this.off(Node.EventType.TOUCH_END, this.onTouchEnd_0, this);
this.off(Node.EventType.TOUCH_CANCEL, this.onTouchCancel_0, this);
}
},
onTouchBegin_0: function (event: EventTouch) {
if (this._dragStartPoint == null) {
this._dragStartPoint = new Vec2();
}
DEV && console.log(`cc-node-drag-extension-> onTouchBegin_0 ${this.name}`);
// event.preventSwallow = true;
let pos = event.getUILocation();
this._dragStartPoint.set(pos);
this._dragTesting = true;
},
onTouchMove_0: function (event: EventTouch) {
if (!this._dragging && this._draggable && this._dragTesting) {
let sensitivity = 10;
let pos = event.getUILocation();
if (Math.abs(this._dragStartPoint.x - pos.x) < sensitivity
&& Math.abs(this._dragStartPoint.y - pos.y) < sensitivity) {
return;
}
// event.preventSwallow = true;
this._dragging = true;
this._dragTesting = false;
this.emit(Node.DragEvent.DRAG_START, event);
}
if (this._dragging) {
let delta = event.getUIDelta();
// /** 这里除以 世界缩放,在有缩放的时候拖拽不至于很怪 */
// this.position = this.position.add(v3(delta.x / this.worldScale.x, delta.y / this.worldScale.y, 0));
this.position = this.position.add(v3(delta.x, delta.y, 0));
this.emit(Node.DragEvent.DRAG_MOVE, event);
}
},
onTouchEnd_0: function (event: EventTouch) {
if (this._dragging) {
this._dragging = false;
this.emit(Node.DragEvent.DRAG_END, event);
}
DEV && console.log(`cc-node-drag-extension-> onTouchEnd_0 ${this.name}, _dragging: ${this._dragging}`);
},
onTouchCancel_0: function (event: EventTouch) {
if (this._dragging) {
this._dragging = false;
this.emit(Node.DragEvent.DRAG_END, event);
}
DEV && console.log(`cc-node-drag-extension-> onTouchCancel_0 ${this.name}, _dragging: ${this._dragging}`);
},
startDrag: function () {
//此节点是否在场景中激活
if (!this.activeInHierarchy) {
return;
}
this.dragBegin();
},
dragBegin: function () {
this._dragging = true;
this._dragTesting = true;
this.on(Node.EventType.TOUCH_MOVE, this.onTouchMove_0, this);
this.on(Node.EventType.TOUCH_END, this.onTouchEnd_0, this);
this.on(Node.EventType.TOUCH_CANCEL, this.onTouchCancel_0, this);
},
dragEnd: function () {
if (this._dragging) {
this._dragTesting = false;
this._dragging = false;
}
},
// 停止拖拽
stopDrag: function () {
this.dragEnd();
},
// 移除 touch 事件
removeDragEvent: function () {
this.off(Node.EventType.TOUCH_START, this.onTouchBegin_0, this);
this.off(Node.EventType.TOUCH_MOVE, this.onTouchMove_0, this);
this.off(Node.EventType.TOUCH_END, this.onTouchEnd_0, this);
this.off(Node.EventType.TOUCH_CANCEL, this.onTouchCancel_0, this);
}
});
// 如果 node 设置 node.draggable = true, 则启用 拖拽
Object.defineProperty(Node.prototype, "draggable", {
get: function () {
return this._draggable;
},
set: function (value) {
if (this._draggable != value) {
this._draggable = value;
this.initDrag();
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(Node.prototype, "dragTesting", {
get: function () {
return this._dragTesting;
},
set: function (value) {
if (this._dragTesting != value) {
this._dragTesting = value;
}
},
enumerable: true,
configurable: true
});
//---------------- Node 添加 拖拽属性 ---------------- end
}
}
declare module "cc" {
// 这里使用 interface 进行扩展,如果使用 class 则会与现有的 d.ts 有冲突
export interface Node {
/** 是否启动拖拽 - true为启动 */
draggable: boolean;
/** 开始拖拽 */
startDrag(): void;
/** 停止拖拽 */
stopDrag(): void;
/** 移除拖拽事件 */
removeDragEvent(): void;
}
export namespace Node {
/** 支持的拖拽事件 */
export class DragEvent {
/** 拖拽开始事件 */
static DRAG_START: string;
/** 拖拽移动事件 */
static DRAG_MOVE: string;
/** 拖拽结束事件 */
static DRAG_END: string;
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.23",
"importer": "typescript",
"imported": true,
"uuid": "1a072fee-76ef-4cb5-b75b-c8be7107c891",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,239 @@
import { Color, Node, Size, UIOpacity, UIRenderer, UITransform, v3 } from "cc";
import { EDITOR } from "cc/env";
// ========= 扩展 cc 提示声明 =========
/** 扩展节点属性 */
declare module "cc" {
interface Node {
/** 获取、设置节点的本地X坐标 */
x: number;
/** 获取、设置节点的本地Y坐标 */
y: number;
/** 获取、设置节点的本地Z坐标 */
z: number;
/** 获取、设置节点宽 */
w: number;
/** 获取、设置节点高 */
h: number;
/** 获取、设置节点尺寸 */
size: Size;
/** 获取、设置X轴锚点 */
anchor_x: number;
/** 获取、设置Y轴锚点 */
anchor_y: number;
/** 获取、设置节点透明度 */
opacity: number;
/** 获取、设置节点颜色 */
color: Color;
/** 获取、设置X轴缩放 */
scale_x: number;
/** 获取、设置Y轴缩放 */
scale_y: number;
/** 获取、设置Z轴缩放 */
scale_z: number;
}
}
if (!EDITOR) {
//@ts-ignore
if (!Node.prototype["$__definedProperties__"]) {
//@ts-ignore
Node.prototype["$__definedProperties__"] = true;
/** 获取、设置节点的 X 坐标 */
Object.defineProperty(Node.prototype, "x", {
get: function () {
let self: Node = this;
return self.position.x;
},
set: function (value: number) {
let self: Node = this;
self.setPosition(value, self.position.y);
}
});
/** 获取、设置节点的 Y 坐标 */
Object.defineProperty(Node.prototype, "y", {
get: function () {
let self: Node = this;
return self.position.y;
},
set: function (value: number) {
let self: Node = this;
self.setPosition(self.position.x, value);
}
});
/** 获取、设置节点的 Z 坐标 */
Object.defineProperty(Node.prototype, "z", {
get: function () {
let self: Node = this;
return self.position.z;
},
set: function (value: number) {
let self: Node = this;
self.setPosition(self.position.x, self.position.y, value);
}
});
/** 获取、设置节点的宽度 */
Object.defineProperty(Node.prototype, "w", {
configurable: true,
get: function () {
let self: Node = this;
return self.getComponent(UITransform)?.width ?? 0;
},
set: function (value: number) {
let self: Node = this;
(self.getComponent(UITransform) || self.addComponent(UITransform)).width = value;
}
});
/** 获取、设置节点的高度 */
Object.defineProperty(Node.prototype, "h", {
configurable: true,
get: function () {
let self: Node = this;
return self.getComponent(UITransform)?.height ?? 0;
},
set: function (value: number) {
let self: Node = this;
(self.getComponent(UITransform) || self.addComponent(UITransform)).height = value;
}
});
/** 获取、设置节点的尺寸 */
Object.defineProperty(Node.prototype, "size", {
get: function () {
let self: Node = this;
let uiTransform = self.getComponent(UITransform)!;
return new Size(uiTransform.width, uiTransform.height);
},
set: function (value: Size) {
let self: Node = this;
let uiTransform = self.getComponent(UITransform) || self.addComponent(UITransform);
uiTransform.width = value.width;
uiTransform.height = value.height;
}
});
/** 获取、设置节点的水平锚点 */
Object.defineProperty(Node.prototype, "anchor_x", {
get: function () {
let self: Node = this;
return self.getComponent(UITransform)?.anchorX ?? 0.5;
},
set: function (value: number) {
let self: Node = this;
(self.getComponent(UITransform) || self.addComponent(UITransform)).anchorX = value;
}
});
/** 获取、设置节点的垂直锚点 */
Object.defineProperty(Node.prototype, "anchor_y", {
get: function () {
let self: Node = this;
return self.getComponent(UITransform)?.anchorY ?? 0.5;
},
set: function (value: number) {
let self: Node = this;
(self.getComponent(UITransform) || self.addComponent(UITransform)).anchorY = value;
}
});
/** 获取、设置节点的透明度 */
Object.defineProperty(Node.prototype, "opacity", {
get: function () {
let self: Node = this;
let op = self.getComponent(UIOpacity);
if (op != null) {
return op.opacity;
}
let render = self.getComponent(UIRenderer);
if (render) {
return render.color.a;
}
return 255;
},
set: function (value: number) {
let self: Node = this;
let op = self.getComponent(UIOpacity);
if (op != null) {
op.opacity = value;
return;
}
let render = self.getComponent(UIRenderer);
if (render) {
// 直接通过 color.a 设置透明度会有bug没能直接生效需要激活节点才生效
// (render.color.a as any) = value;
// 创建一个颜色缓存对象,避免每次都创建新对象
if (!this.$__color__) {
this.$__color__ = new Color(render.color.r, render.color.g, render.color.b, value);
}
else {
this.$__color__.a = value;
}
render.color = this.$__color__; // 设置 color 对象则可以立刻生效
}
else {
self.addComponent(UIOpacity).opacity = value;
}
}
});
/** 获取、设置节点的颜色 */
Object.defineProperty(Node.prototype, "color", {
get: function () {
let self: Node = this;
return self.getComponent(UIRenderer)?.color;
},
set: function (value: Color) {
let self: Node = this;
let render = self.getComponent(UIRenderer);
render && (render.color = value);
}
});
/** 获取、设置节点的 X 缩放系数 */
Object.defineProperty(Node.prototype, "scale_x", {
get: function () {
let self: Node = this;
return self.scale.x;
},
set: function (value: number) {
let self: Node = this;
self.scale = v3(value, self.scale.y, self.scale.z);
}
});
/** 获取、设置节点的 Y 缩放系数 */
Object.defineProperty(Node.prototype, "scale_y", {
get: function () {
let self: Node = this;
return self.scale.y;
},
set: function (value: number) {
let self: Node = this;
self.scale = v3(self.scale.x, value, self.scale.z);
}
});
/** 获取、设置节点的 Z 缩放系数 */
Object.defineProperty(Node.prototype, "scale_z", {
get: function () {
let self: Node = this;
return self.scale.z;
},
set: function (value: number) {
let self: Node = this;
self.scale = v3(self.scale.x, self.scale.y, value);
}
});
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.23",
"importer": "typescript",
"imported": true,
"uuid": "493a6bed-02c3-428b-bfdd-adb28720068b",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -4,9 +4,7 @@
* @LastEditors: dgflash
* @LastEditTime: 2022-09-02 12:09:55
*/
import { game, Game, Node } from 'cc';
var timeScale = 1;
import { Node, director } from 'cc';
/** 游戏世界管理 */
export class GameManager {
@@ -15,30 +13,10 @@ export class GameManager {
constructor(root: Node) {
this.root = root;
this.gameTimeScaleExtend();
}
/** 设置游戏动画速度(不支持 Native 模式) */
/** 设置游戏动画速度 */
setTimeScale(scale: number) {
timeScale = scale;
}
/** 只支持web整体加速 */
private gameTimeScaleExtend() {
//@ts-ignore
game._calculateDT = function (now: number) {
if (!now) now = performance.now();
//@ts-ignore
this._deltaTime = now > this._startTime ? (now - this._startTime) / 1000 : 0;
//@ts-ignore
if (this._deltaTime > Game.DEBUG_DT_THRESHOLD) {
//@ts-ignore
this._deltaTime = this.frameTime / 1000;
}
//@ts-ignore
this._startTime = now;
//@ts-ignore
return this._deltaTime * timeScale;
};
director.globalGameTimeScale = scale;
}
}