251 lines
9.1 KiB
TypeScript
251 lines
9.1 KiB
TypeScript
import {
|
|
_decorator,
|
|
Animation,
|
|
AudioClip,
|
|
CCFloat,
|
|
CircleCollider2D,
|
|
Collider2D,
|
|
Component,
|
|
Contact2DType,
|
|
Director,
|
|
director,
|
|
ERigidBody2DType,
|
|
geometry,
|
|
IPhysics2DContact,
|
|
math,
|
|
Node,
|
|
ParticleSystem,
|
|
Prefab,
|
|
RigidBody2D,
|
|
Sprite,
|
|
Vec2,
|
|
Vec3,
|
|
} from 'cc';
|
|
import BoosterType from '../Enum/BoosterType';
|
|
import PhysicsGroup from '../Enum/PhysicGroup';
|
|
import { SequenceSound } from '../Environments/SequenceSound';
|
|
import GameEvent from '../Events/GameEvent';
|
|
import AudioManager from '../Manager/AudioManager';
|
|
import { EventManger } from '../Manager/EventManger';
|
|
import IPoolable from '../Pool/IPoolable';
|
|
import ObjectPool from '../Pool/ObjectPool';
|
|
import Utils 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: Animation, visible: true })
|
|
private _animation: Animation;
|
|
@property({ type: ParticleSystem, visible: true })
|
|
private _trail: ParticleSystem;
|
|
@property({ type: ParticleSystem, visible: true })
|
|
private _buffParticle: ParticleSystem;
|
|
@property({ type: ParticleSystem, visible: true })
|
|
private _fireParticle: ParticleSystem;
|
|
@property({ type: CircleCollider2D, visible: true })
|
|
private _collider: CircleCollider2D;
|
|
|
|
@property({ type: Sprite, visible: true })
|
|
private _normalSprite: Sprite;
|
|
@property({ type: Sprite, visible: true })
|
|
private _cheeseModeSprite: Sprite;
|
|
@property({ type: Sprite, visible: true })
|
|
private _spriteShadow: Sprite;
|
|
|
|
@property({ type: AudioClip, visible: true })
|
|
private _impactSound: AudioClip;
|
|
@property({ type: AudioClip, visible: true })
|
|
private _impactFlipperSound: AudioClip;
|
|
@property({ type: SequenceSound, visible: true })
|
|
private _collectSound: SequenceSound;
|
|
@property({ type: SequenceSound, visible: true })
|
|
private _cheeseModeCollectSound: SequenceSound;
|
|
|
|
@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;
|
|
private _parent: Node;
|
|
private _cheeseModeOn = false;
|
|
|
|
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.afterPhysicUpdate, this);
|
|
this._impactPool = new ObjectPool(this._impactPrefab, 10, false);
|
|
|
|
EventManger.instance.on(GameEvent.BoosterActive, this.addBoosterEffect, this);
|
|
EventManger.instance.on(GameEvent.BoosterDisable, this.removeBoosterEffect, this);
|
|
}
|
|
|
|
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();
|
|
const jumpValue = this._jumpCurve.evaluate(jumpProcess);
|
|
scale = scale.add(Vec3.ONE.clone().multiplyScalar(jumpValue));
|
|
this._normalSprite.node.setScale(scale);
|
|
this._cheeseModeSprite.node.setScale(scale);
|
|
this._spriteShadow.node.setScale(scale);
|
|
this._trail.trailModule.widthRatio.multiplier = scale.x;
|
|
this._spriteShadow.node.setWorldPosition(
|
|
this.node.worldPosition.clone().add(Vec3.ONE.clone().multiplyScalar(-jumpValue * 100)),
|
|
);
|
|
|
|
if (jumpProcess >= 1) {
|
|
this._spriteShadow.node.setPosition(Vec3.ZERO);
|
|
this._normalSprite.node.setScale(Vec3.ONE);
|
|
this._cheeseModeSprite.node.setScale(Vec3.ONE);
|
|
this._trail.trailModule.widthRatio.multiplier = 1;
|
|
this._isJumping = false;
|
|
this._collider.group = PhysicsGroup.BALL;
|
|
this._rigidBody.group = PhysicsGroup.BALL;
|
|
this.node.setParent(this._parent);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected lateUpdate(dt: number): void {
|
|
if (this._rigidBody.linearVelocity.length() > 60) {
|
|
this._fireParticle.rateOverTime.constant = 8;
|
|
this._fireParticle.rateOverDistance.constant = 0.02;
|
|
// if (this._fireParticle.isStopped) {
|
|
// }
|
|
} else {
|
|
this._fireParticle.rateOverDistance.constant = 0;
|
|
this._fireParticle.rateOverTime.constant = 0;
|
|
// if (this._fireParticle.isPlaying) {
|
|
// this._fireParticle.stopEmitting();
|
|
// }
|
|
}
|
|
}
|
|
|
|
private async onBeginContact(
|
|
selfCollider: Collider2D,
|
|
otherCollider: Collider2D,
|
|
contact: IPhysics2DContact | null,
|
|
) {
|
|
if (this._isHit) return;
|
|
this._isHit = true;
|
|
const velocity = this._rigidBody.linearVelocity.length();
|
|
if (!otherCollider.sensor) {
|
|
if (velocity >= 6) {
|
|
this._animation?.play();
|
|
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(ParticleSystem, this.node.parent);
|
|
hitFx.node.setWorldPosition(new Vec3(hitPoint.x, hitPoint.y, 10));
|
|
AudioManager.playSfx(
|
|
otherCollider.group == PhysicsGroup.FLIPPER ? this._impactFlipperSound : this._impactSound,
|
|
);
|
|
await Utils.waitUntil(() => hitFx.isStopped, 0.1);
|
|
this._impactPool.release(hitFx);
|
|
}
|
|
} else if (otherCollider.tag == 1) {
|
|
if (this._cheeseModeOn) {
|
|
this._cheeseModeCollectSound.playSound();
|
|
} else {
|
|
this._collectSound.playSound();
|
|
}
|
|
}
|
|
}
|
|
|
|
private onEndContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
|
|
this._isHit = false;
|
|
}
|
|
|
|
public addBoosterEffect(boosterType: BoosterType) {
|
|
switch (boosterType) {
|
|
case BoosterType.CumulativeBar:
|
|
this._cheeseModeOn = true;
|
|
this._cheeseModeSprite.setNodeActive(true);
|
|
this._normalSprite.setNodeActive(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public removeBoosterEffect(boosterType: BoosterType) {
|
|
switch (boosterType) {
|
|
case BoosterType.CumulativeBar:
|
|
this._cheeseModeOn = false;
|
|
this._cheeseModeSprite.setNodeActive(false);
|
|
this._normalSprite.setNodeActive(true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private afterPhysicUpdate() {
|
|
let velocity = this._rigidBody.linearVelocity.length();
|
|
|
|
if (velocity > 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.applyAngularImpulse(-5 * force.x || 2, true);
|
|
this._rigidBody.applyLinearImpulseToCenter(force, true);
|
|
this._isJumping = true;
|
|
this._jumpTime = 0;
|
|
this._jumpDuration = this._rigidBody.linearVelocity.length() * 0.05;
|
|
}
|
|
|
|
public playMultiBallEffect() {
|
|
this._buffParticle.play();
|
|
}
|
|
|
|
public clearRigiState(active: boolean) {
|
|
this._rigidBody.type = active ? ERigidBody2DType.Dynamic : ERigidBody2DType.Kinematic;
|
|
this._rigidBody.linearVelocity = Vec2.ZERO.clone();
|
|
this._rigidBody.angularVelocity = 0;
|
|
}
|
|
|
|
onGet() {
|
|
this.clearRigiState(true);
|
|
this._isJumping = false;
|
|
this._isHit = false;
|
|
this._parent = this.node.getParent();
|
|
this._fireParticle.rateOverDistance.constant = 0;
|
|
this._fireParticle.rateOverTime.constant = 0;
|
|
}
|
|
|
|
onRelease() {
|
|
this._rigidBody.linearVelocity = Vec2.ZERO.clone();
|
|
this._rigidBody.angularVelocity = 0;
|
|
this._buffParticle.stop();
|
|
this.getComponentsInChildren(ParticleSystem).forEach((particle) => {
|
|
particle.clear();
|
|
});
|
|
}
|
|
}
|