first commit

This commit is contained in:
zzc
1970-01-01 08:45:37 +08:00
commit e9c9afb3b9
53 changed files with 5254 additions and 0 deletions

View File

@@ -0,0 +1,133 @@
// assets/scripts/BallPhysics.ts
import {
_decorator,
Component,
Node,
RigidBody,
Vec3,
math,
SphereCollider,
Contact2DType,
MeshCollider,
ICollisionEvent,
ITriggerEvent,
} from "cc";
const { ccclass, property } = _decorator;
@ccclass("BallPhysics")
export class BallPhysics extends Component {
@property({ tooltip: "马格努斯系数(越大弧线越明显)" }) magnusK = 0.1;
@property({ tooltip: "空气阻力" }) dragK = 0.001;
@property({ tooltip: "风力m/s^2" }) windStrength = 0;
@property({ tooltip: "风向" }) windDir: Vec3 = new Vec3(1, 0, 0);
@property({ tooltip: "自动重置高度阈值(低于视为无效球)" }) resetY = -2;
@property(Node) spawnPoint!: Node;
@property(Node) ball!: Node;
private rb!: RigidBody;
private tmpF = new Vec3();
private v = new Vec3();
private w = new Vec3();
private wind = new Vec3();
private pos = new Vec3();
// ⚽ 保存 spinKickInput 设置进来)
private spin = new Vec3();
onLoad() {
this.rb = this.ball.getComponent(RigidBody)!;
const col = this.ball.getComponent(MeshCollider);
if (col) {
console.log(22223333, col);
col.on("onTriggerEnter", this.onTriggerEnter, this);
col.on("onTriggerStay", this.onTriggerStay, this);
col.on("onCollisionEnter", this.onCollisionEnter, this);
}
this.wind.set(this.windDir).normalize();
}
private onTriggerStay(event: ITriggerEvent) {
console.log(event.type, event);
}
onTriggerEnter(event: ICollisionEvent) {
console.log("⚽ 触发器进入:", event.otherCollider.node.name);
}
onCollisionEnter(event: ICollisionEvent) {
console.log("⚽ 碰撞进入:", event.otherCollider.node.name);
}
init() {
this.rb = this.node.getComponent(RigidBody)!;
this.wind.set(this.windDir).normalize();
this.pos = this.node.worldPosition.clone();
}
setWind(direction: Vec3, strength: number) {
this.wind.set(direction).normalize();
this.windStrength = strength;
}
resetBall() {
if (!this.spawnPoint) return;
this.rb.clearState();
this.node.worldPosition = this.pos; //this.spawnPoint.worldPosition.clone();
this.rb.setLinearVelocity(new Vec3());
this.rb.setAngularVelocity(new Vec3());
}
/** 设置自旋(由 KickInput 调用) */
public setSpin(spin: Vec3) {
this.spin.set(spin);
}
/** 发射足球 */
shootBall(dir: Vec3, speed: number) {
this.rb.clearState();
this.rb.setLinearVelocity(dir.clone().multiplyScalar(speed));
this.rb.setAngularVelocity(this.spin); // 应用横向旋转
}
update(dt: number) {
if (!this.rb) return;
this.rb.getLinearVelocity(this.v);
// 1. 空气阻力(与预测保持一致: -k * v * |v|
const speed = this.v.length();
if (speed > 0.01) {
const drag = this.v.clone().multiplyScalar(-this.dragK * speed);
this.rb.applyForce(drag);
}
// 2. 马格努斯力: F = k * (spin × v)
if (!this.spin.equals(Vec3.ZERO) && speed > 0.01) {
const magnus = new Vec3();
Vec3.cross(magnus, this.spin, this.v);
magnus.multiplyScalar(this.magnusK);
this.rb.applyForce(magnus);
}
// if (!this.spin.equals(Vec3.ZERO) && speed > 0.01) {
// const magnus = new Vec3();
// const vNorm = this.v.clone().normalize();
// Vec3.cross(magnus, this.spin, vNorm);
// magnus.multiplyScalar(this.magnusK * Math.sqrt(speed));
// this.rb.applyForce(magnus);
// }
// 3. 风
if (this.windStrength > 0.0001) {
this.tmpF.set(this.wind).multiplyScalar(this.windStrength);
this.rb.applyForce(this.tmpF);
}
// 4. 掉出场地自动重置
if (this.node.worldPosition.y < this.resetY && this.spawnPoint) {
this.resetBall();
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "3aece382-c6ed-4971-82a0-cf40507368f7",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,32 @@
// assets/scripts/CameraRig.ts
import { _decorator, Component, Node, Vec3, math } from "cc";
const { ccclass, property } = _decorator;
@ccclass("CameraRig")
export class CameraRig extends Component {
@property(Node) ball!: Node;
@property({ tooltip: "跟随平滑0~1越小越平滑" }) smooth = 0.1;
@property(Vec3) offset = new Vec3(0, 2.5, -5);
private _mode: "static" | "follow" = "static";
setModeFollow() {
this._mode = "follow";
}
setModeStatic() {
this._mode = "static";
}
lateUpdate(dt: number) {
if (this._mode !== "follow" || !this.ball) return;
const target = this.ball.worldPosition.clone().add(this.offset);
const p = this.node.worldPosition.clone();
const lerp = new Vec3(
math.lerp(p.x, target.x, 1 - this.smooth),
math.lerp(p.y, target.y, 1 - this.smooth),
math.lerp(p.z, target.z, 1 - this.smooth)
);
this.node.setWorldPosition(lerp);
this.node.lookAt(this.ball.worldPosition);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "ffb4877f-b15c-46cd-b252-dedbe7540235",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,58 @@
// assets/scripts/GameManager.ts
import { _decorator, Component, Node, Label, Vec3 } from "cc";
import { BallPhysics } from "./BallPhysics";
// import { CameraRig } from "./CameraRig";
const { ccclass, property } = _decorator;
@ccclass("GameManager")
export class GameManager extends Component {
@property(Node) ball!: Node;
// @property(Node) cameraRig!: Node;
// @property(Label) scoreLabel!: Label;
// @property(Label) windLabel!: Label;
private score = 0;
private ballPhys!: BallPhysics;
// private cam!: CameraRig;
onLoad() {
this.ballPhys = this.ball.getComponent(BallPhysics)!;
this.ballPhys.init();
// this.cam = this.cameraRig.getComponent(CameraRig)!;
// 监听进球
this.node.on("GOAL", this.onGoal, this);
this.newRound();
}
onGoal() {
this.score += 1;
this.updateScore();
this.scheduleOnce(() => {
this.newRound();
}, 1.0);
}
private updateScore() {
// if (this.scoreLabel) this.scoreLabel.string = `${this.score}`;
}
private newRound() {
// 重置球与镜头
this.ballPhys.resetBall();
// this.cam.setModeStatic();
// 随机风
// const dir = new Vec3(Math.random() - 0.5, 0, Math.random() - 0.5);
// const strength = 4 * Math.random(); // 0~4
// this.ballPhys.setWind(dir, strength);
// if (this.windLabel) {
// const a = (Math.atan2(dir.x, dir.z) * 180) / Math.PI; // 粗略角度显示
// this.windLabel.string = `Wind ${strength.toFixed(1)} @ ${a.toFixed(0)}°`;
// }
// 几百毫秒后把镜头切为跟随(等玩家射门后也可在 KickInput 里触发)
// this.scheduleOnce(() => this.cam.setModeFollow(), 0.6);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "81883817-9f1d-4d28-8109-f62a0dc23285",
"files": [],
"subMetas": {},
"userData": {}
}

109
assets/scripts/GameMrg.ts Normal file
View File

@@ -0,0 +1,109 @@
import {
_decorator,
Component,
Vec3,
RigidBody,
input,
Input,
EventTouch,
} from "cc";
import { TrajectoryPreview } from "./TrajectoryPreview";
const { ccclass, property } = _decorator;
@ccclass("GameMrg")
export class GameMrg extends Component {
@property(RigidBody)
rigidBody: RigidBody = null!;
@property(TrajectoryPreview)
trajectory: TrajectoryPreview = null!;
@property
maxPower: number = 25;
private startPos: Vec3 = new Vec3();
private endPos: Vec3 = new Vec3();
private isDragging: boolean = false;
start() {
input.on(Input.EventType.TOUCH_START, this.onTouchStart, this);
input.on(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this);
input.on(Input.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
}
onDestroy() {
input.off(Input.EventType.TOUCH_START, this.onTouchStart, this);
input.off(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
input.off(Input.EventType.TOUCH_END, this.onTouchEnd, this);
input.off(Input.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
}
onTouchStart(e: EventTouch) {
this.startPos.set(e.getLocation().x, e.getLocation().y, 0);
this.isDragging = true;
}
onTouchMove(e: EventTouch) {
if (!this.isDragging) return;
this.endPos.set(e.getLocation().x, e.getLocation().y, 0);
const dragVec = new Vec3(
this.startPos.x - this.endPos.x,
this.startPos.y - this.endPos.y,
0
);
if (dragVec.length() < 10) {
this.trajectory.hideTrajectory();
return;
}
// 主方向(始终向前 + 抬升)
const forward = new Vec3(0, 0.5, 1).normalize();
// 力度
const power = Math.min(dragVec.y * 0.05 + 10, this.maxPower);
// 左右弧线
const sideForce = dragVec.x * 0.02;
// 显示轨迹
this.trajectory.showTrajectory(
// this.rigidBody.node.worldPosition,
this.startPos.clone(),
forward,
power,
sideForce
);
}
onTouchEnd(e: EventTouch) {
if (!this.isDragging) return;
this.isDragging = false;
this.trajectory.hideTrajectory();
this.endPos.set(e.getLocation().x, e.getLocation().y, 0);
const dragVec = new Vec3(
this.startPos.x - this.endPos.x,
this.startPos.y - this.endPos.y,
0
);
if (dragVec.length() < 10) return;
// 主方向
const forward = new Vec3(0, 0.5, 1).normalize();
// 力度
const power = Math.min(dragVec.y * 0.05 + 10, this.maxPower);
// 左右弧线
const sideForce = dragVec.x * 0.02;
// 发射冲量
const impulse = forward.multiplyScalar(power);
impulse.x += sideForce;
this.rigidBody.applyImpulse(impulse);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "cb6a4184-4c18-405f-8e18-f0d5829517cd",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,19 @@
// assets/scripts/GoalDetector.ts
import { _decorator, Component, ITriggerEvent } from "cc";
const { ccclass, property } = _decorator;
@ccclass("GoalDetector")
export class GoalDetector extends Component {
@property({ tooltip: "GameManager 节点(接收进球事件)" }) gameManagerPath =
"";
onTriggerEnter(event: ITriggerEvent) {
const other = event.otherCollider?.node;
if (!other) return;
if (other.name.includes("Ball")) {
// 简单通知
const gm = this.node.scene.getChildByPath(this.gameManagerPath);
gm?.emit("GOAL");
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "85ff5590-99dd-47f5-8422-0702fc260fb3",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,45 @@
// assets/scripts/GoalkeeperAI.ts
import { _decorator, Component, Node, RigidBody, Vec3, math } from "cc";
const { ccclass, property } = _decorator;
@ccclass("GoalkeeperAI")
export class GoalkeeperAI extends Component {
@property(Node) ball!: Node;
@property({ tooltip: "球门线Z世界坐标" }) goalLineZ = 10;
@property({ tooltip: "守门员水平活动范围X" }) rangeX = 3.5;
@property({ tooltip: "移动速度m/s" }) speed = 6;
@property({ tooltip: "反应延迟(秒)" }) reactDelay = 0.15;
private targetX = 0;
private tAcc = 0;
private tmp = new Vec3();
update(dt: number) {
if (!this.ball) return;
// 简化预测:用当前速度线性外推到球门线的交点
const rb = this.ball.getComponent(RigidBody);
if (!rb) return;
rb.getLinearVelocity(this.tmp);
const v = this.tmp.clone();
const bp = this.ball.worldPosition.clone();
if (Math.abs(v.z) < 0.1) return; // 几乎不朝球门方向飞
const timeToLine = (this.goalLineZ - bp.z) / v.z; // z 轴朝向球门
if (timeToLine < 0) return; // 已过线或背离
this.tAcc += dt;
if (this.tAcc >= this.reactDelay) {
const predictX = bp.x + v.x * timeToLine;
this.targetX = math.clamp(predictX, -this.rangeX, this.rangeX);
this.tAcc = 0; // 下次再延迟
}
// 移动守门员
const p = this.node.worldPosition;
const dx = this.targetX - p.x;
const step = math.clamp(dx, -this.speed * dt, this.speed * dt);
this.node.setWorldPosition(p.x + step, p.y, p.z);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "30f1b3d4-4345-4002-a1ae-d9a745976704",
"files": [],
"subMetas": {},
"userData": {}
}

172
assets/scripts/KickInput.ts Normal file
View File

@@ -0,0 +1,172 @@
import {
_decorator,
Component,
Node,
EventTouch,
Input,
input,
Vec2,
Vec3,
RigidBody,
math,
Graphics,
MeshRenderer,
geometry,
} from "cc";
import { TrajectoryPreview } from "./TrajectoryPreview";
import { Preview } from "./Preview";
import { BallPhysics } from "./BallPhysics";
const { ccclass, property } = _decorator;
@ccclass("KickInput")
export class KickInput extends Component {
@property(Node) ball!: Node;
// @property(Node) goal!: Node;
@property(Node) cameraRig!: Node;
// 力度与角度控制
@property({ tooltip: "最大初速度m/s" }) maxSpeed = 20;
@property({ tooltip: "最小仰角(度)" }) minPitchDeg = 8;
@property({ tooltip: "最大仰角(度)" }) maxPitchDeg = 18;
// 自旋与弧线
@property({ tooltip: "自旋强度系数" }) spinFactor = 20;
@property(Preview) preview: Preview = null!;
private startPos = new Vec2();
private dragging = false;
private lastPos = new Vec2();
private trail: Vec2[] = [];
private ballPhys: BallPhysics = null;
protected onLoad(): void {
this.ballPhys = this.ball.getComponent(BallPhysics)!;
}
onEnable() {
input.on(Input.EventType.TOUCH_START, this.onStart, this);
input.on(Input.EventType.TOUCH_MOVE, this.onMove, this);
input.on(Input.EventType.TOUCH_END, this.onEnd, this);
input.on(Input.EventType.TOUCH_CANCEL, this.onEnd, this);
}
onDisable() {
input.off(Input.EventType.TOUCH_START, this.onStart, this);
input.off(Input.EventType.TOUCH_MOVE, this.onMove, this);
input.off(Input.EventType.TOUCH_END, this.onEnd, this);
input.off(Input.EventType.TOUCH_CANCEL, this.onEnd, this);
}
private onStart(e: EventTouch) {
this.dragging = true;
e.getLocation(this.startPos);
e.getLocation(this.lastPos);
this.trail.length = 0;
this.trail.push(this.startPos.clone());
}
private onMove(e: EventTouch) {
if (!this.dragging) return;
const p = e.getLocation();
this.trail.push(p.clone());
this.lastPos.set(p);
// 1) 拖拽向量
const drag = this.lastPos.clone().subtract(this.startPos);
const dragLen = drag.length();
// 2) 速度(映射拖拽长度)
const speed = math.clamp(dragLen / 8, 5, this.maxSpeed);
// const speed = 12;
// 3) 仰角 pitch映射拖拽的纵向
const t = math.clamp(drag.y / 300, 0, 1);
const pitchDeg = math.lerp(this.minPitchDeg, this.maxPitchDeg, t);
// 4) 偏航角 yaw映射拖拽的横向
const yawDeg = math.clamp(-drag.x / 8, -45, 45);
// 5) 得到方向向量
const dir = this.dirFromAngles(yawDeg, pitchDeg);
// 6) 自旋(用轨迹估计)
const spin = this.estimateSpin(this.trail, drag).multiplyScalar(
this.spinFactor
);
this.preview.showTrajectory(this.ball.worldPosition, dir, speed, spin);
}
private onEnd() {
if (!this.dragging) return;
this.dragging = false;
this.preview.clear();
// 拖拽向量
const drag = this.lastPos.clone().subtract(this.startPos);
const dragLen = drag.length();
if (!this.ball || !this.ballPhys) return;
// 1) 计算初速度
const speed = math.clamp(dragLen / 8, 5, this.maxSpeed);
// const speed = 12;
// 2) 仰角 pitch
const t = math.clamp(drag.y / 300, 0, 1);
const pitchDeg = math.lerp(this.minPitchDeg, this.maxPitchDeg, t);
// 3) 偏航 yaw
const yawDeg = math.clamp(-drag.x / 8, -45, 45);
// 4) 方向向量
const dir = this.dirFromAngles(yawDeg, pitchDeg);
// 5) 自旋
const spin = this.estimateSpin(this.trail, drag).multiplyScalar(
this.spinFactor
);
this.ballPhys.setSpin(spin);
console.log(`t: ${t}, pitchDeg:${pitchDeg}`);
// 6) 发射
this.ballPhys.shootBall(dir, speed);
}
private dirFromAngles(yawDeg: number, pitchDeg: number) {
const yaw = math.toRadian(yawDeg);
const pitch = math.toRadian(pitchDeg);
// 水平前方Z轴右手坐标根据你场景轴向可能需调整
const x = Math.sin(yaw) * Math.cos(pitch);
const y = Math.sin(pitch);
const z = Math.cos(yaw) * Math.cos(pitch);
return new Vec3(x, y, z).normalize();
}
// 简单估计自旋方向:用拖拽路径的“弯曲方向”
private estimateSpin(path: Vec2[], drag: Vec2): Vec3 {
if (path.length < 4) return new Vec3(0, 0, 0);
// 取开始、中点、终点计算平面曲率符号映射为Y轴为主或Z轴自旋
const a = path[0],
b = path[Math.floor(path.length / 2)],
c = path[path.length - 1];
// 2D 叉积 z 分量(曲率方向)
const ab = new Vec2(b.x - a.x, b.y - a.y);
const bc = new Vec2(c.x - b.x, c.y - b.y);
const cross = ab.x * bc.y - ab.y * bc.x; // >0 逆时针弯曲
// 横向弯 -> 绕Y轴的自旋产生左右弧线
const spinY = math.clamp(drag.x / 200, -3.5, 3.5);
return new Vec3(0, spinY, 0); // 只绕Y轴产生左右弧度
// const spinY = math.clamp(cross / 20000, -1.5, 1.5) * 2.0; // 横向弧度提前
// // 竖向弯 -> 绕X轴/或Z轴的自旋下坠/挑球这里简单给点Z
// const dy = c.y - a.y;
// const spinZ = math.clamp(dy / 1500, -1.0, 1.0);
// return new Vec3(0, spinY, spinZ);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "4e90f5ed-3b95-4d1d-90b7-3b9eb5d8c130",
"files": [],
"subMetas": {},
"userData": {}
}

82
assets/scripts/Preview.ts Normal file
View File

@@ -0,0 +1,82 @@
// assets/scripts/Preview.ts
import { _decorator, Component, Node, Prefab, instantiate, Vec3 } from "cc";
import { BallPhysics } from "./BallPhysics";
const { ccclass, property } = _decorator;
@ccclass("Preview")
export class Preview extends Component {
@property(Prefab) pointPrefab: Prefab = null!;
@property(Node) goal: Node = null!;
@property({ tooltip: "轨迹点数量" }) maxPoints = 30;
@property({ tooltip: "步长时间(秒)" }) timeStep = 0.05;
@property(BallPhysics) ballPhys: BallPhysics = null!;
private points: Node[] = [];
clear() {
for (const p of this.points) p.destroy();
this.points.length = 0;
}
/** 显示预测轨迹 */
showTrajectory(startPos: Vec3, dir: Vec3, speed: number, spin: Vec3) {
this.clear();
if (!this.pointPrefab || !this.ballPhys) return;
const goalZ = this.goal?.worldPosition?.z;
// 初始状态
let pos = startPos.clone();
let v = dir.clone().multiplyScalar(speed);
let w = spin.clone(); // 自旋
// 迭代预测
// let age = 0;
for (let i = 0; i < this.maxPoints; i++) {
// age += this.timeStep;
// 保存点
const p = instantiate(this.pointPrefab);
p.setWorldPosition(pos);
p.setParent(this.node);
this.points.push(p);
// === 模拟 BallPhysics.update(dt) ===
// const dt = this.timeStep;
const dt = this.timeStep * 0.5;
// 空气阻力
const speedNow = v.length();
if (speedNow > 0.01) {
const drag = v.clone().multiplyScalar(-this.ballPhys.dragK * speedNow);
v.add(drag.multiplyScalar(dt));
}
// 马格努斯效应F = k * (ω × v)
if (w.lengthSqr() > 0.0001 && v.lengthSqr() > 0.0001) {
// const tFactor = 1.0 / (1.0 + age * 2.0); // age 是飞行时间,越久衰减越快
// const magnus = w
// .clone()
// .cross(v)
// .multiplyScalar(this.ballPhys.magnusK * tFactor);
// v.add(magnus.multiplyScalar(dt));
const magnus = w.clone().cross(v).multiplyScalar(this.ballPhys.magnusK);
v.add(magnus.multiplyScalar(dt));
}
// 重力
v.y += -9.8 * dt;
// 移动
pos = pos.add(v.clone().multiplyScalar(dt));
if (
(startPos.z > goalZ && pos.z <= goalZ) ||
(startPos.z < goalZ && pos.z >= goalZ)
) {
break;
}
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "274524cf-5678-498a-9486-122ab0771b04",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,63 @@
import { _decorator, Component, Node, Prefab, instantiate, Vec3 } from "cc";
const { ccclass, property } = _decorator;
@ccclass("TrajectoryPreview")
export class TrajectoryPreview extends Component {
@property(Prefab)
pointPrefab: Prefab = null!;
@property
maxPoints: number = 20;
@property
timeStep: number = 0.1;
@property
gravity: Vec3 = new Vec3(0, -9.8, 0);
private points: Node[] = [];
onLoad() {
for (let i = 0; i < this.maxPoints; i++) {
const p = instantiate(this.pointPrefab);
p.active = false;
this.node.addChild(p); // 确保挂在场景下的空节点
this.points.push(p);
}
}
showTrajectory(
startPos: Vec3,
forward: Vec3,
power: number,
sideForce: number
) {
const velocity = forward.multiplyScalar(power);
velocity.x += sideForce;
const ballRadius = 0.01; // ⚽ 球半径
const groundY = 0; // 🟩 地面高度
for (let i = 0; i < this.maxPoints; i++) {
const t = i * this.timeStep;
const pos = new Vec3(
startPos.x + velocity.x * t,
startPos.y + velocity.y * t + 0.5 * this.gravity.y * t * t,
startPos.z + velocity.z * t
);
// ⭐ 限制最低不低于地面
if (pos.y < groundY + ballRadius * 0.5) {
pos.y = groundY + ballRadius * 0.5;
}
this.points[i].setWorldPosition(pos);
this.points[i].active = true;
}
}
hideTrajectory() {
this.points.forEach((p) => (p.active = false));
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "4210fa5e-4d43-4bef-bd48-c7949dccf074",
"files": [],
"subMetas": {},
"userData": {}
}