pinball/assets/_Game/Scripts/Gameplay/Ball.ts

156 lines
5.3 KiB
TypeScript
Raw Normal View History

2024-02-28 03:25:11 -08:00
import {
_decorator,
2024-03-06 10:08:30 -08:00
AudioClip,
2024-02-28 03:25:11 -08:00
CCFloat,
Collider2D,
Component,
Contact2DType,
Director,
director,
EventTarget,
IPhysics2DContact,
RigidBody2D,
2024-03-06 10:08:30 -08:00
Vec2,
2024-03-07 03:15:08 -08:00
geometry,
math,
Node,
Vec3,
ParticleSystem,
Prefab,
CircleCollider2D,
2024-02-28 03:25:11 -08:00
} from 'cc';
2024-03-06 07:10:31 -08:00
import IPoolable from '../Pool/IPoolable';
2024-03-06 10:08:30 -08:00
import { SoundManager } from '../Manager/SoundManager';
2024-03-07 03:15:08 -08:00
import PhysicsGroup from '../Enum/PhysicGroup';
import ObjectPool from '../Pool/ObjectPool';
2024-03-07 09:45:13 -08:00
import Utilities from '../Utilities/Utilities';
2024-02-27 18:19:33 -08:00
const { ccclass, property } = _decorator;
@ccclass('Ball')
2024-03-06 07:10:31 -08:00
export class Ball extends Component implements IPoolable {
2024-03-07 03:15:08 -08:00
@property({ type: Prefab, visible: true })
private _impactPrefab: Prefab;
2024-03-06 10:08:30 -08:00
@property({ type: CCFloat, visible: true })
private _maxSpeed: number;
@property({ type: RigidBody2D, visible: true })
private _rigidbody: RigidBody2D;
2024-03-07 03:15:08 -08:00
@property({ type: Node, visible: true })
private _sprite: Node;
@property({ type: ParticleSystem, visible: true })
private _trail: ParticleSystem;
@property({ type: CircleCollider2D, visible: true })
private _collider: CircleCollider2D;
2024-03-06 10:08:30 -08:00
@property({ type: AudioClip, visible: true })
private _hitSound: AudioClip;
2024-03-07 03:15:08 -08:00
@property({ type: geometry.AnimationCurve, visible: true })
private _jumpCurve: geometry.AnimationCurve = new geometry.AnimationCurve();
2024-03-06 10:08:30 -08:00
2024-03-07 03:15:08 -08:00
private _impactPool: ObjectPool;
2024-03-06 10:08:30 -08:00
private _hitted = false;
2024-03-07 03:15:08 -08:00
private _isJumping = false;
private _jumpTime: number;
private _jumpDuration: number;
2024-02-27 18:19:33 -08:00
public eventHitObstacle = new EventTarget();
public eventGoal = new EventTarget();
2024-02-28 03:25:11 -08:00
protected onLoad(): void {
2024-03-06 10:08:30 -08:00
if (this._collider) {
this._collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
this._collider.on(Contact2DType.END_CONTACT, this.onEndContact, this);
2024-02-27 18:19:33 -08:00
}
2024-02-28 03:25:11 -08:00
director.on(Director.EVENT_AFTER_PHYSICS, this.setMaxVelocity, this);
2024-03-07 03:15:08 -08:00
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;
}
}
2024-02-27 18:19:33 -08:00
}
2024-03-07 09:45:13 -08:00
private async onBeginContact(
selfCollider: Collider2D,
otherCollider: Collider2D,
contact: IPhysics2DContact | null,
) {
2024-03-06 10:08:30 -08:00
if (this._hitted) return;
this._hitted = true;
if (this._rigidbody.linearVelocity.length() >= 3) {
2024-03-07 03:15:08 -08:00
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();
2024-03-06 10:08:30 -08:00
SoundManager.instance.playSfx(this._hitSound);
2024-03-07 09:45:13 -08:00
await Utilities.waitUntil(() => hitfx.isStopped, 100);
this._impactPool.release(hitfx.node);
2024-03-06 10:08:30 -08:00
}
}
2024-02-28 03:25:11 -08:00
2024-03-07 03:15:08 -08:00
private onEndContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
this._hitted = false;
}
2024-02-28 03:25:11 -08:00
private setMaxVelocity() {
2024-03-06 10:08:30 -08:00
if (this._rigidbody.linearVelocity.length() > this._maxSpeed) {
this._rigidbody.linearVelocity = this._rigidbody.linearVelocity.normalize().multiplyScalar(this._maxSpeed);
2024-02-28 03:25:11 -08:00
}
2024-02-27 18:19:33 -08:00
}
2024-03-07 03:15:08 -08:00
public addFocre(force: Vec2) {
this._rigidbody.applyLinearImpulseToCenter(force, true);
2024-02-27 18:19:33 -08:00
}
2024-03-06 07:10:31 -08:00
2024-03-07 03:15:08 -08:00
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;
2024-03-06 07:10:31 -08:00
}
2024-03-08 03:07:41 -08:00
public setActiveRigi(value: boolean) {
this._rigidbody.enabled = value;
if (!value) {
this._rigidbody.linearVelocity = Vec2.ZERO.clone();
this._rigidbody.angularVelocity = 0;
}
}
2024-03-07 03:15:08 -08:00
reuse() {
this._isJumping = false;
this._hitted = false;
2024-03-08 03:07:41 -08:00
this._rigidbody.enabled = true;
2024-03-07 03:15:08 -08:00
}
2024-03-06 10:08:30 -08:00
2024-03-06 07:10:31 -08:00
unuse() {
2024-03-06 10:08:30 -08:00
this._rigidbody.linearVelocity = Vec2.ZERO.clone();
this._rigidbody.angularVelocity = 0;
2024-03-06 07:10:31 -08:00
}
2024-02-28 03:25:11 -08:00
}