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/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 _hitted = 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._hitted) return; this._hitted = true; if (this._rigidbody.linearVelocity.length() >= 3) { 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._hitted = false; } private setMaxVelocity() { if (this._rigidbody.linearVelocity.length() > this._maxSpeed) { this._rigidbody.linearVelocity = this._rigidbody.linearVelocity.normalize().multiplyScalar(this._maxSpeed); } } public addFocre(force: Vec2) { this._rigidbody.applyLinearImpulseToCenter(force, true); } public throwBall(force: Vec2) { this._collider.group = PhysicsGroup.BALLTHROWING; this._rigidbody.group = PhysicsGroup.BALLTHROWING; 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; } } reuse() { this._isJumping = false; this._hitted = false; this._rigidbody.enabled = true; } unuse() { this._rigidbody.linearVelocity = Vec2.ZERO.clone(); this._rigidbody.angularVelocity = 0; } }