@cc-component/cc-fly 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/.cc-ex-component.md +12 -0
  2. package/assets/fly/Interface.ts +32 -0
  3. package/assets/fly/Interface.ts.meta +9 -0
  4. package/assets/fly/base/BuffUI.ts +68 -0
  5. package/assets/fly/base/BuffUI.ts.meta +9 -0
  6. package/assets/fly/base/FlyArrowLauncher.ts +166 -0
  7. package/assets/fly/base/FlyArrowLauncher.ts.meta +9 -0
  8. package/assets/fly/base/FlyBaseBullet.ts +9 -0
  9. package/assets/fly/base/FlyBaseBullet.ts.meta +9 -0
  10. package/assets/fly/base/FlyBaseHit.ts +10 -0
  11. package/assets/fly/base/FlyBaseHit.ts.meta +9 -0
  12. package/assets/fly/base/FlyBaseHp.ts +96 -0
  13. package/assets/fly/base/FlyBaseHp.ts.meta +9 -0
  14. package/assets/fly/base/FlyBasePlayer.ts +359 -0
  15. package/assets/fly/base/FlyBasePlayer.ts.meta +9 -0
  16. package/assets/fly/base/FlyDamageLabel.ts +198 -0
  17. package/assets/fly/base/FlyDamageLabel.ts.meta +9 -0
  18. package/assets/fly/base/FlyDamageLabelManager.ts +111 -0
  19. package/assets/fly/base/FlyDamageLabelManager.ts.meta +9 -0
  20. package/assets/fly/base/FlyModule.ts +145 -0
  21. package/assets/fly/base/FlyModule.ts.meta +9 -0
  22. package/assets/fly/base/FlyNodePoolUtil.ts +56 -0
  23. package/assets/fly/base/FlyNodePoolUtil.ts.meta +9 -0
  24. package/assets/fly/base/FlyPlayerBuffSystem.ts +673 -0
  25. package/assets/fly/base/FlyPlayerBuffSystem.ts.meta +9 -0
  26. package/assets/fly/base/FlyPooledParticleEffect.ts +58 -0
  27. package/assets/fly/base/FlyPooledParticleEffect.ts.meta +9 -0
  28. package/assets/fly/base/FlySkiiManager.ts +19 -0
  29. package/assets/fly/base/FlySkiiManager.ts.meta +9 -0
  30. package/assets/fly/base.meta +9 -0
  31. package/assets/fly.meta +9 -0
  32. package/assets/pathfinding.meta +9 -0
  33. package/assets.meta +9 -0
  34. package/index.ts +19 -0
  35. package/index.ts.meta +9 -0
  36. package/package.json +18 -5
  37. package/package.json.meta +11 -0
  38. package/README.md +0 -3
@@ -0,0 +1,12 @@
1
+ # 系统组件添加常用扩展方法
2
+
3
+ ## 前置条件
4
+ [游戏引擎](https://www.cocos.com/en/creator-download)
5
+ [开发框架](https://github.com/a1076559139/cocos-creator-frame-3d)
6
+
7
+ ## 使用案例
8
+ ```TS
9
+ import {} from 'db://pkg/@gamex/cc-ex-component';
10
+
11
+ ...
12
+ ```
@@ -0,0 +1,32 @@
1
+ import { Vec3 } from "cc";
2
+
3
+ import { Component } from "cc";
4
+ import { Node } from "cc";
5
+ import { FlyBasePlayer } from "./base/FlyBasePlayer";
6
+ import { PlayerBuffType } from "./base/FlyPlayerBuffSystem";
7
+
8
+ declare module "cc" {
9
+
10
+ }
11
+ declare global {
12
+ namespace FlyModule {
13
+ /**开启日志打印 */
14
+ //export function Debug(enabled: boolean);
15
+ // 调用 A* 路径寻找,从 (1,1) 到 (5,5),attackDis: 攻击距离
16
+ /**
17
+ * 开始抛物线缓动
18
+ * @param item 运动节点
19
+ * @param target_postion 目标位置
20
+ * @param update 更新 运动中回调
21
+ * @param offset_ratio 最高点 的偏移量
22
+ * @param time 动画时间
23
+ * @param is_random 是否随机上下方向
24
+ * @param start_position 起始位置--比如 父节点的位置和 抛物线的起点位置 不同,,用父节点的起始位置就传入
25
+ */
26
+ export function RunParabola(item: Node, target_postion: Vec3, update: (ratio: number) => void, param?: { offset_ratio?: number, is_random: boolean, time?: number, speed?: number, scale_offset_with_distance?: boolean, offset_reference_distance?: number, start_position?: Vec3, is_end_clone?: boolean });
27
+
28
+ export function OnSkillAnim<T extends FlyBasePlayer>(call: (buffType: PlayerBuffType | any, attacker: T, primaryTarget: T, candidateTargets: readonly T[], onHit: (target: T) => void) => void): void
29
+ }
30
+ }
31
+ // // 必须在 game.init() 之前设置!
32
+ //macro.ENABLE_TRANSPARENT_CANVAS = true;
@@ -0,0 +1,9 @@
1
+ {
2
+ "ver": "4.0.24",
3
+ "importer": "typescript",
4
+ "imported": true,
5
+ "uuid": "b295f1b0-6f95-4fef-af46-2cdb71f3cd40",
6
+ "files": [],
7
+ "subMetas": {},
8
+ "userData": {}
9
+ }
@@ -0,0 +1,68 @@
1
+ import { _decorator, Component, instantiate, Label, Layout, Node } from 'cc';
2
+ import { PlayerBuffDescription } from './FlyPlayerBuffSystem';
3
+ import { FlyBasePlayer } from './FlyBasePlayer';
4
+ const { ccclass, property } = _decorator;
5
+
6
+ @ccclass('FlyBuffUI')
7
+ export class FlyBuffUI extends Component {
8
+ @property({ type: FlyBasePlayer, displayName: '显示 Buff 的角色' })
9
+ private FlyBasePlayer: FlyBasePlayer | null = null;
10
+
11
+ @property({ type: Label })
12
+ private templateLabel: Label | null = null;
13
+
14
+ private layout: Layout | null = null;
15
+ private displayedLabels: Node[] = [];
16
+ private lastDescription = '';
17
+
18
+ protected onLoad(): void {
19
+ this.layout = this.node.getComponent(Layout);
20
+ if (this.templateLabel) {
21
+ this.templateLabel.node.active = false;
22
+ }
23
+ }
24
+
25
+ protected update(): void {
26
+ if (!this.FlyBasePlayer?.buffSystem)
27
+ return;
28
+
29
+ const descriptions = this.FlyBasePlayer?.buffSystem?.getBuffDescriptions() ?? [];
30
+ const currentDescription = JSON.stringify(descriptions);
31
+ if (currentDescription === this.lastDescription) return;
32
+
33
+ this.lastDescription = currentDescription;
34
+ this.refreshLabels(descriptions);
35
+ }
36
+
37
+ private refreshLabels(descriptions: PlayerBuffDescription[]): void {
38
+ if (!this.templateLabel) return;
39
+
40
+ for (const labelNode of this.displayedLabels) {
41
+ if (labelNode.isValid) labelNode.destroy();
42
+ }
43
+ this.displayedLabels = [];
44
+
45
+ for (const buff of descriptions) {
46
+ const labelNode = instantiate(this.templateLabel.node);
47
+ labelNode.parent = this.node;
48
+ labelNode.active = true;
49
+ const label = labelNode.getComponent(Label);
50
+ if (!label) {
51
+ labelNode.destroy();
52
+ continue;
53
+ }
54
+ label.string = this.formatBuffDescription(buff);
55
+ this.displayedLabels.push(labelNode);
56
+ }
57
+
58
+ this.layout?.updateLayout();
59
+ }
60
+
61
+ private formatBuffDescription(buff: PlayerBuffDescription): string {
62
+ const details: string[] = [buff.description];
63
+ if (buff.duration !== undefined) {
64
+ details.push(`持续${buff.duration}秒`);
65
+ }
66
+ return details.join(',');
67
+ }
68
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "ver": "4.0.24",
3
+ "importer": "typescript",
4
+ "imported": true,
5
+ "uuid": "0ffef054-401a-41c6-afdb-d2577b984187",
6
+ "files": [],
7
+ "subMetas": {},
8
+ "userData": {}
9
+ }
@@ -0,0 +1,166 @@
1
+ import { _decorator, Component, Enum, Node, Prefab, Vec3 } from 'cc';
2
+ import { FlyBaseHit } from './FlyBaseHit';
3
+ import { FlyBaseBullet } from './FlyBaseBullet';
4
+ import { FlyNodePoolUtil } from './FlyNodePoolUtil';
5
+
6
+ export interface FlyArrowLauncherConfig {
7
+ arrowTemplateType?: FlyArrowTemplateType;
8
+ arrow?: Node | null;
9
+ hit?: Node | null;
10
+ arrowPrefab?: Prefab | null;
11
+ hitPrefab?: Prefab | null;
12
+ poolParent?: Node | null;
13
+ isRandom?: boolean;
14
+ offsetRatio?: number;
15
+ scaleOffsetWithDistance?: boolean;
16
+ offsetReferenceDistance?: number;
17
+ speed?: number;
18
+ time?: number;
19
+ arrowReturnDelay?: number;
20
+ }
21
+
22
+ export enum FlyArrowTemplateType {
23
+ Node,
24
+ Prefab,
25
+ }
26
+
27
+ export type FlyArrowHitCallback = (hitPosition: Vec3, arrow: Node) => void;
28
+
29
+ const { ccclass, property } = _decorator;
30
+
31
+ @ccclass('FlyArrowLauncher')
32
+ export class FlyArrowLauncher extends Component {
33
+ private static nextPoolId = 0;
34
+ private readonly poolId = FlyArrowLauncher.nextPoolId++;
35
+
36
+ @property({ type: Enum(FlyArrowTemplateType), displayName: '资源类型' })
37
+ arrowTemplateType = FlyArrowTemplateType.Node;
38
+
39
+ @property({ type: Node, displayName: '箭矢节点', tooltip: '箭矢对象池使用的模板节点,需挂载 FlyBaseBullet。', visible(this: FlyArrowLauncher) { return this.arrowTemplateType === FlyArrowTemplateType.Node; } })
40
+ arrow: Node = null;
41
+
42
+ @property({ type: Node, displayName: '命中效果节点', tooltip: '命中粒子对象池使用的模板节点,需挂载 FlyBaseHit。', visible(this: FlyArrowLauncher) { return this.arrowTemplateType === FlyArrowTemplateType.Node; } })
43
+ hit: Node = null;
44
+
45
+ @property({ type: Prefab, displayName: '箭矢 Prefab', tooltip: '作为箭矢对象池模板。', visible(this: FlyArrowLauncher) { return this.arrowTemplateType === FlyArrowTemplateType.Prefab; } })
46
+ arrowPrefab: Prefab = null;
47
+
48
+ @property({ type: Prefab, displayName: '命中效果 Prefab', tooltip: '作为命中效果对象池模板。', visible(this: FlyArrowLauncher) { return this.arrowTemplateType === FlyArrowTemplateType.Prefab; } })
49
+ hitPrefab: Prefab = null;
50
+
51
+ @property({ type: Node, displayName: '箭矢和命中父节点', tooltip: '箭矢和命中效果共用此父节点;未设置时使用发射器所在节点。' })
52
+ poolParent: Node = null;
53
+
54
+ @property({ displayName: '随机抛物线方向', tooltip: '启用后随机选择抛物线弯曲方向。' })
55
+ isRandom = true;
56
+
57
+ @property({ displayName: '抛物线偏移比例', tooltip: '控制抛物线最高点相对起终点距离的偏移比例。' })
58
+ offsetRatio = 0.5;
59
+
60
+ @property({ displayName: '弧线随距离缩放', tooltip: '开启后距离越远弧线幅度越大,距离越近幅度越小;关闭时使用参考距离对应的固定幅度。' })
61
+ scaleOffsetWithDistance = true;
62
+
63
+ @property({ displayName: '弧线参考距离', tooltip: '反向距离缩放的基准距离;默认 500 单位时弧线高度与原效果一致。' })
64
+ offsetReferenceDistance = 500;
65
+
66
+ @property({ displayName: '发射速度', tooltip: '大于 0 时按距离和速度计算时长;否则使用动画时间。' })
67
+ speed = 500;
68
+
69
+ @property({ displayName: '动画时间', tooltip: '发射速度小于或等于 0 时使用的动画时长,单位为秒。' })
70
+ time = 0.5;
71
+
72
+ @property({ displayName: '箭矢回收延迟', tooltip: '箭矢到达终点后等待多久再返回对象池,单位为秒。' })
73
+ arrowReturnDelay = 0.2;
74
+
75
+ private poolsRegistered = false;
76
+ private get arrowPoolKey(): string { return `FlyArrowLauncher:${this.poolId}:arrow`; }
77
+ private get hitPoolKey(): string { return `FlyArrowLauncher:${this.poolId}:hit`; }
78
+ private get arrowTemplate(): Node | Prefab | null {
79
+ return this.arrowTemplateType === FlyArrowTemplateType.Prefab ? this.arrowPrefab : this.arrow;
80
+ }
81
+ private get hitTemplate(): Node | Prefab | null {
82
+ return this.arrowTemplateType === FlyArrowTemplateType.Prefab ? this.hitPrefab : this.hit;
83
+ }
84
+
85
+ protected onLoad(): void {
86
+ this.registerPools();
87
+ }
88
+
89
+ onDestroy(): void {
90
+ FlyNodePoolUtil.clear(this.arrowPoolKey);
91
+ FlyNodePoolUtil.clear(this.hitPoolKey);
92
+ }
93
+
94
+ setConfig(config: FlyArrowLauncherConfig): void {
95
+ const templatesChanged =
96
+ (config.arrowTemplateType !== undefined && config.arrowTemplateType !== this.arrowTemplateType) ||
97
+ (config.arrow !== undefined && config.arrow !== this.arrow) ||
98
+ (config.hit !== undefined && config.hit !== this.hit) ||
99
+ (config.arrowPrefab !== undefined && config.arrowPrefab !== this.arrowPrefab) ||
100
+ (config.hitPrefab !== undefined && config.hitPrefab !== this.hitPrefab) ||
101
+ (config.poolParent !== undefined && config.poolParent !== this.poolParent);
102
+
103
+ if (templatesChanged && this.poolsRegistered) {
104
+ FlyNodePoolUtil.clear(this.arrowPoolKey);
105
+ FlyNodePoolUtil.clear(this.hitPoolKey);
106
+ this.poolsRegistered = false;
107
+ }
108
+
109
+ if (config.arrowTemplateType !== undefined) this.arrowTemplateType = config.arrowTemplateType;
110
+ if (config.arrow !== undefined) this.arrow = config.arrow;
111
+ if (config.hit !== undefined) this.hit = config.hit;
112
+ if (config.arrowPrefab !== undefined) this.arrowPrefab = config.arrowPrefab;
113
+ if (config.hitPrefab !== undefined) this.hitPrefab = config.hitPrefab;
114
+ if (config.poolParent !== undefined) this.poolParent = config.poolParent;
115
+ if (config.isRandom !== undefined) this.isRandom = config.isRandom;
116
+ if (config.offsetRatio !== undefined) this.offsetRatio = config.offsetRatio;
117
+ if (config.scaleOffsetWithDistance !== undefined) this.scaleOffsetWithDistance = config.scaleOffsetWithDistance;
118
+ if (config.offsetReferenceDistance !== undefined) this.offsetReferenceDistance = config.offsetReferenceDistance;
119
+ if (config.speed !== undefined) this.speed = config.speed;
120
+ if (config.time !== undefined) this.time = config.time;
121
+ if (config.arrowReturnDelay !== undefined) this.arrowReturnDelay = config.arrowReturnDelay;
122
+
123
+ if (templatesChanged) this.registerPools();
124
+ }
125
+
126
+ fire(start: Vec3, end: Vec3, onHit?: FlyArrowHitCallback): void {
127
+ if (!this.poolsRegistered || !this.arrowTemplate || !this.hitTemplate) return;
128
+
129
+ const arrow = FlyNodePoolUtil.get(this.arrowPoolKey);
130
+ arrow.setWorldPosition(start);
131
+ arrow.getComponent(FlyBaseBullet).play();
132
+
133
+ let completed = false;
134
+ FlyModule.RunParabola(arrow, end, (ratio) => {
135
+ if (ratio < 1 || completed || !arrow.isValid) return;
136
+ completed = true;
137
+ const hit = FlyNodePoolUtil.get(this.hitPoolKey);
138
+ hit.setWorldPosition(arrow.worldPosition);
139
+ hit.getComponent(FlyBaseHit).play();
140
+ onHit?.(hit.worldPosition, arrow);
141
+ this.scheduleOnce(() => {
142
+ if (arrow.isValid) FlyNodePoolUtil.put(arrow);
143
+ }, this.arrowReturnDelay);
144
+ }, {
145
+ is_random: this.isRandom,
146
+ offset_ratio: this.offsetRatio,
147
+ scale_offset_with_distance: this.scaleOffsetWithDistance,
148
+ offset_reference_distance: this.offsetReferenceDistance,
149
+ speed: this.speed,
150
+ time: this.time,
151
+ start_position: start,
152
+ });
153
+ }
154
+
155
+ private registerPools(): void {
156
+ const arrowTemplate = this.arrowTemplate;
157
+ const hitTemplate = this.hitTemplate;
158
+ if (!arrowTemplate || !hitTemplate) return;
159
+
160
+ const poolParent = this.poolParent ?? this.node;
161
+ FlyNodePoolUtil.register(this.arrowPoolKey, arrowTemplate, poolParent);
162
+ FlyNodePoolUtil.register(this.hitPoolKey, hitTemplate, poolParent);
163
+ if (this.arrow) this.arrow.active = false;
164
+ this.poolsRegistered = true;
165
+ }
166
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "ver": "4.0.24",
3
+ "importer": "typescript",
4
+ "imported": true,
5
+ "uuid": "3a9d2f4c-7b81-4e06-9c15-2f638a41d7b0",
6
+ "files": [],
7
+ "subMetas": {},
8
+ "userData": {}
9
+ }
@@ -0,0 +1,9 @@
1
+ import { _decorator } from 'cc';
2
+ import { FlyPooledParticleEffect } from './FlyPooledParticleEffect';
3
+
4
+ const { ccclass } = _decorator;
5
+
6
+ @ccclass('FlyBaseBullet')
7
+ export class FlyBaseBullet extends FlyPooledParticleEffect { }
8
+
9
+
@@ -0,0 +1,9 @@
1
+ {
2
+ "ver": "4.0.24",
3
+ "importer": "typescript",
4
+ "imported": true,
5
+ "uuid": "c41cb785-3286-4af2-a72f-459e1f762240",
6
+ "files": [],
7
+ "subMetas": {},
8
+ "userData": {}
9
+ }
@@ -0,0 +1,10 @@
1
+ import { _decorator } from 'cc';
2
+ import { FlyPooledParticleEffect } from './FlyPooledParticleEffect';
3
+
4
+ const { ccclass } = _decorator;
5
+
6
+ @ccclass('FlyBaseHit')
7
+ export class FlyBaseHit extends FlyPooledParticleEffect {
8
+ }
9
+
10
+
@@ -0,0 +1,9 @@
1
+ {
2
+ "ver": "4.0.24",
3
+ "importer": "typescript",
4
+ "imported": true,
5
+ "uuid": "eb746171-d178-4a6b-951c-a91b8e71d419",
6
+ "files": [],
7
+ "subMetas": {},
8
+ "userData": {}
9
+ }
@@ -0,0 +1,96 @@
1
+ import { Label } from 'cc';
2
+ import { _decorator, Component, ProgressBar, tween, Tween } from 'cc';
3
+ const { ccclass, property } = _decorator;
4
+
5
+ export interface FlyBaseHpConfig {
6
+ maxHp?: number;
7
+ currentHp?: number;
8
+ animationDuration?: number;
9
+ }
10
+
11
+ @ccclass('FlyBaseHp')
12
+ export class FlyBaseHp extends Component {
13
+ @property({ type: ProgressBar, displayName: '血条进度条' })
14
+ progressBar: ProgressBar | null = null;
15
+
16
+ @property({ type: Label, displayName: '血量文本' })
17
+ textLabel: Label | null = null;
18
+
19
+ @property({ displayName: '最大生命值', min: 0 })
20
+ maxHp = 100;
21
+
22
+ @property({ displayName: '当前生命值', min: 0 })
23
+ currentHp = 100000;
24
+
25
+ @property({ displayName: '血条动画时长', min: 0 })
26
+ animationDuration = 0.2;
27
+
28
+ // 初始化时限制当前血量并同步进度条。
29
+ protected onLoad(): void {
30
+ this.setHp(this.currentHp, false);
31
+ }
32
+
33
+ // 从代码批量设置血量组件配置并立即同步进度条。
34
+ public configure(config: FlyBaseHpConfig): void {
35
+ if (config.maxHp !== undefined && Number.isFinite(config.maxHp)) {
36
+ this.maxHp = Math.max(1, config.maxHp);
37
+ }
38
+ if (config.animationDuration !== undefined && Number.isFinite(config.animationDuration)) {
39
+ this.animationDuration = Math.max(0, config.animationDuration);
40
+ }
41
+ const currentHp = config.currentHp !== undefined && Number.isFinite(config.currentHp)
42
+ ? config.currentHp
43
+ : this.currentHp;
44
+ this.setHp(currentHp, false);
45
+ }
46
+
47
+ // 当前生命值归零时视为死亡。
48
+ public get isDead(): boolean {
49
+ return this.currentHp <= 0;
50
+ }
51
+
52
+ // 扣除指定生命值,血量不会低于零。
53
+ public takeDamage(amount: number): void {
54
+ if (!Number.isFinite(amount) || amount <= 0) return;
55
+ this.setHp(this.currentHp - amount);
56
+ }
57
+
58
+ // 恢复指定生命值,血量不会超过最大值。
59
+ public heal(amount: number, animate = true): void {
60
+ if (!Number.isFinite(amount) || amount <= 0) return;
61
+ this.setHp(this.currentHp + amount, animate);
62
+ }
63
+
64
+ // 设置并限制生命值,同时按 0 到 1 的比例更新进度条。
65
+ public setHp(value: number, animate = true): void {
66
+ const safeMaxHp = Math.max(0, this.maxHp);
67
+ this.currentHp = Math.min(safeMaxHp, Math.max(0, value));
68
+ if (this.textLabel) {
69
+ this.textLabel.string = `${Math.ceil(this.currentHp)}/${Math.ceil(safeMaxHp)}`;
70
+ }
71
+ if (this.progressBar) {
72
+ const progress = safeMaxHp > 0 ? this.currentHp / safeMaxHp : 0;
73
+ if (animate && this.animationDuration > 0) {
74
+ this.playProgressAnimation(progress);
75
+ } else {
76
+ Tween.stopAllByTarget(this.progressBar);
77
+ this.progressBar.progress = progress;
78
+ }
79
+ }
80
+ }
81
+
82
+ protected playProgressAnimation(targetProgress: number): void {
83
+ if (!this.progressBar) return;
84
+
85
+ Tween.stopAllByTarget(this.progressBar);
86
+ tween(this.progressBar)
87
+ .to(this.animationDuration, { progress: targetProgress }, { easing: 'sineOut' })
88
+ .start();
89
+ }
90
+
91
+ // 将生命值恢复到最大值。
92
+ public resetHp(): void {
93
+ this.setHp(this.maxHp);
94
+ }
95
+ }
96
+
@@ -0,0 +1,9 @@
1
+ {
2
+ "ver": "4.0.24",
3
+ "importer": "typescript",
4
+ "imported": true,
5
+ "uuid": "bb9456c6-0528-4e71-9d8c-a6d41f4d25ee",
6
+ "files": [],
7
+ "subMetas": {},
8
+ "userData": {}
9
+ }