import { _decorator, AudioClip, CCFloat, Collider2D, Component, Contact2DType, Director, director, EventTarget, IPhysics2DContact, RigidBody2D, Vec2, geometry, math, Node, Vec3, ParticleSystem, Prefab, CircleCollider2D, } from 'cc'; import IPoolable from '../Pool/IPoolable'; import { SoundManager } from '../Manager/SoundManager'; import PhysicsGroup from '../Enum/PhysicGroup'; import ObjectPool from '../Pool/ObjectPool'; import Utilities from '../Utilities'; const { ccclass, property } = _decorator; @ccclass('Ball') export class Ball extends Component implements IPoolable { @property({ type: Prefab, visible: true }) private _impactPrefab: Prefab; @property({ type: CCFloat, visible: true }) private _maxSpeed: number; @property({ type: RigidBody2D, visible: true }) private _rigidBody: RigidBody2D; @property({ type: Node, visible: true }) private _sprite: Node; @property({ type: ParticleSystem, visible: true }) private _trail: ParticleSystem; @property({ type: CircleCollider2D, visible: true }) private _collider: CircleCollider2D; @property({ type: AudioClip, visible: true }) private _hitSound: AudioClip; @property({ type: geometry.AnimationCurve, visible: true }) private _jumpCurve: geometry.AnimationCurve = new geometry.AnimationCurve(); private _impactPool: ObjectPool; private _isHit = false; private _isJumping = false; private _jumpTime: number; private _jumpDuration: number; public eventHitObstacle = new EventTarget(); public eventGoal = new EventTarget(); protected onLoad(): void { if (this._collider) { this._collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this); this._collider.on(Contact2DType.END_CONTACT, this.onEndContact, this); } director.on(Director.EVENT_AFTER_PHYSICS, this.setMaxVelocity, this); this._impactPool = new ObjectPool(this._impactPrefab, 5, false); } protected update(dt: number): void { if (this._isJumping) { this._jumpTime += dt; let jumpProcess = this._jumpTime / this._jumpDuration; jumpProcess = math.clamp01(jumpProcess); let scale = Vec3.ONE.clone(); scale = scale.add(Vec3.ONE.clone().multiplyScalar(this._jumpCurve.evaluate(jumpProcess))); this._sprite.setScale(scale); this._trail.trailModule.widthRatio.multiplier = scale.x; if (jumpProcess >= 1) { this._sprite.setScale(Vec3.ONE); this._trail.trailModule.widthRatio.multiplier = 1; this._isJumping = false; this._collider.group = PhysicsGroup.BALL; this._rigidBody.group = PhysicsGroup.BALL; } } } private async onBeginContact( selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null, ) { if (this._isHit) return; this._isHit = true; console.log(contact.getTangentSpeed()); if (this._rigidBody.linearVelocity.length() >= 5) { let hitPoint = contact.getWorldManifold().points[0]; if (!hitPoint) { const dir = otherCollider.node .getWorldPosition() .subtract(selfCollider.node.getWorldPosition()) .normalize(); dir.multiplyScalar(this._collider.radius / 2); const point = selfCollider.node.getWorldPosition().add(dir); hitPoint = new Vec2(point.x, point.y); } const hitFx = this._impactPool.get(this.node.parent, ParticleSystem); hitFx.node.setWorldPosition(new Vec3(hitPoint.x, hitPoint.y, 10)); hitFx.play(); SoundManager.instance.playSfx(this._hitSound); await Utilities.waitUntil(() => hitFx.isStopped, 100); this._impactPool.release(hitFx.node); } } private onEndContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) { this._isHit = false; } private setMaxVelocity() { if (this._rigidBody.linearVelocity.length() > this._maxSpeed) { this._rigidBody.linearVelocity = this._rigidBody.linearVelocity.normalize().multiplyScalar(this._maxSpeed); } } public addForce(force: Vec2) { this._rigidBody.applyLinearImpulseToCenter(force, true); } public throwBall(force: Vec2) { this._collider.group = PhysicsGroup.BALL_THROWING; this._rigidBody.group = PhysicsGroup.BALL_THROWING; this._rigidBody.applyLinearImpulseToCenter(force, true); this._isJumping = true; this._jumpTime = 0; this._jumpDuration = this._rigidBody.linearVelocity.length() * 0.05; } public setActiveRigi(value: boolean) { this._rigidBody.enabled = value; if (!value) { this._rigidBody.linearVelocity = Vec2.ZERO.clone(); this._rigidBody.angularVelocity = 0; } } onGet() { this._isJumping = false; this._isHit = false; this._rigidBody.enabled = true; } onRelease() { this._rigidBody.linearVelocity = Vec2.ZERO.clone(); this._rigidBody.angularVelocity = 0; } }