feat: spawn score object

main
tiendat3699 2024-03-08 00:45:13 +07:00
parent a5995a7942
commit 712cb8aa30
14 changed files with 4324 additions and 178 deletions

View File

@ -0,0 +1,303 @@
[
{
"__type__": "cc.AnimationClip",
"_name": "ScoreObjectSpawn",
"_objFlags": 0,
"__editorExtras__": {
"embeddedPlayerGroups": []
},
"_native": "",
"sample": 60,
"speed": 1,
"wrapMode": 1,
"enableTrsBlending": false,
"_duration": 0.3333333333333333,
"_hash": 500763545,
"_tracks": [
{
"__id__": 1
},
{
"__id__": 12
}
],
"_exoticAnimation": null,
"_events": [],
"_embeddedPlayers": []
},
{
"__type__": "cc.animation.VectorTrack",
"_binding": {
"__type__": "cc.animation.TrackBinding",
"path": {
"__id__": 2
},
"proxy": null
},
"_channels": [
{
"__id__": 4
},
{
"__id__": 6
},
{
"__id__": 8
},
{
"__id__": 10
}
],
"_nComponents": 3
},
{
"__type__": "cc.animation.TrackPath",
"_paths": [
{
"__id__": 3
},
"position"
]
},
{
"__type__": "cc.animation.HierarchyPath",
"path": "Sprite"
},
{
"__type__": "cc.animation.Channel",
"_curve": {
"__id__": 5
}
},
{
"__type__": "cc.RealCurve",
"_times": [],
"_values": [],
"preExtrapolation": 1,
"postExtrapolation": 1
},
{
"__type__": "cc.animation.Channel",
"_curve": {
"__id__": 7
}
},
{
"__type__": "cc.RealCurve",
"_times": [
0,
0.16666666666666666
],
"_values": [
{
"__type__": "cc.RealKeyframeValue",
"interpolationMode": 0,
"tangentWeightMode": 0,
"value": 50,
"rightTangent": 0,
"rightTangentWeight": 1,
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
},
{
"__type__": "cc.RealKeyframeValue",
"interpolationMode": 0,
"tangentWeightMode": 0,
"value": -25,
"rightTangent": 0,
"rightTangentWeight": 1,
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
}
],
"preExtrapolation": 1,
"postExtrapolation": 1
},
{
"__type__": "cc.animation.Channel",
"_curve": {
"__id__": 9
}
},
{
"__type__": "cc.RealCurve",
"_times": [],
"_values": [],
"preExtrapolation": 1,
"postExtrapolation": 1
},
{
"__type__": "cc.animation.Channel",
"_curve": {
"__id__": 11
}
},
{
"__type__": "cc.RealCurve",
"_times": [],
"_values": [],
"preExtrapolation": 1,
"postExtrapolation": 1
},
{
"__type__": "cc.animation.VectorTrack",
"_binding": {
"__type__": "cc.animation.TrackBinding",
"path": {
"__id__": 13
},
"proxy": null
},
"_channels": [
{
"__id__": 15
},
{
"__id__": 17
},
{
"__id__": 19
},
{
"__id__": 21
}
],
"_nComponents": 3
},
{
"__type__": "cc.animation.TrackPath",
"_paths": [
{
"__id__": 14
},
"scale"
]
},
{
"__type__": "cc.animation.HierarchyPath",
"path": "Sprite"
},
{
"__type__": "cc.animation.Channel",
"_curve": {
"__id__": 16
}
},
{
"__type__": "cc.RealCurve",
"_times": [],
"_values": [],
"preExtrapolation": 1,
"postExtrapolation": 1
},
{
"__type__": "cc.animation.Channel",
"_curve": {
"__id__": 18
}
},
{
"__type__": "cc.RealCurve",
"_times": [
0,
0.08333333333333333,
0.18333333333333332,
0.2833333333333333,
0.3333333333333333
],
"_values": [
{
"__type__": "cc.RealKeyframeValue",
"interpolationMode": 0,
"tangentWeightMode": 0,
"value": 1,
"rightTangent": 0,
"rightTangentWeight": 1,
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
},
{
"__type__": "cc.RealKeyframeValue",
"interpolationMode": 0,
"tangentWeightMode": 0,
"value": 1,
"rightTangent": 0,
"rightTangentWeight": 1,
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
},
{
"__type__": "cc.RealKeyframeValue",
"interpolationMode": 0,
"tangentWeightMode": 0,
"value": 0.5,
"rightTangent": 0,
"rightTangentWeight": 1,
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
},
{
"__type__": "cc.RealKeyframeValue",
"interpolationMode": 0,
"tangentWeightMode": 0,
"value": 1.100000023841858,
"rightTangent": 0,
"rightTangentWeight": 1,
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
},
{
"__type__": "cc.RealKeyframeValue",
"interpolationMode": 0,
"tangentWeightMode": 0,
"value": 1,
"rightTangent": 0,
"rightTangentWeight": 1,
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
}
],
"preExtrapolation": 1,
"postExtrapolation": 1
},
{
"__type__": "cc.animation.Channel",
"_curve": {
"__id__": 20
}
},
{
"__type__": "cc.RealCurve",
"_times": [],
"_values": [],
"preExtrapolation": 1,
"postExtrapolation": 1
},
{
"__type__": "cc.animation.Channel",
"_curve": {
"__id__": 22
}
},
{
"__type__": "cc.RealCurve",
"_times": [],
"_values": [],
"preExtrapolation": 1,
"postExtrapolation": 1
}
]

View File

@ -0,0 +1,13 @@
{
"ver": "2.0.3",
"importer": "animation-clip",
"imported": true,
"uuid": "cfd9afd6-9345-446c-bf56-b7ac3dbf2c1e",
"files": [
".cconb"
],
"subMetas": {},
"userData": {
"name": "ScoreObjectSpawn"
}
}

View File

@ -16,32 +16,85 @@
"_objFlags": 0,
"__editorExtras__": {},
"_parent": null,
"_children": [],
"_active": true,
"_components": [
"_children": [
{
"__id__": 2
},
{
"__id__": 4
},
{
"__id__": 6
},
}
],
"_active": true,
"_components": [
{
"__id__": 8
},
{
"__id__": 10
},
{
"__id__": 12
},
{
"__id__": 14
},
{
"__id__": 16
}
],
"_prefab": {
"__id__": 12
"__id__": 18
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": -521.891,
"y": 0,
"z": 0
},
"_lrot": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 1,
"y": 1,
"z": 1
},
"_mobility": 0,
"_layer": 33554432,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "cc.Node",
"_name": "Sprite",
"_objFlags": 0,
"_parent": {
"__id__": 1
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 3
},
{
"__id__": 5
}
],
"_prefab": {
"__id__": 7
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": -25,
"z": 0
},
"_lrot": {
@ -72,11 +125,11 @@
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 1
"__id__": 2
},
"_enabled": true,
"__prefab": {
"__id__": 3
"__id__": 4
},
"_contentSize": {
"__type__": "cc.Size",
@ -86,24 +139,24 @@
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
"y": 0
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "dd1k5yz/9Jw7Y3h/ln7e+D"
"fileId": "7eQbUjrCxCdqX0lbHJJf5m"
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 1
"__id__": 2
},
"_enabled": true,
"__prefab": {
"__id__": 5
"__id__": 6
},
"_customMaterial": null,
"_srcBlendFactor": 2,
@ -136,10 +189,23 @@
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "33Vv42hplKv6Fr1cHzlkpg"
"fileId": "8f4n9BtRRP+4F8ppzXIOAb"
},
{
"__type__": "ab3adcigCNMO4YLHg1OcMdG",
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "58e2t45HZDcaGTymn9uf1N",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"node": {
@ -147,21 +213,23 @@
},
"_enabled": true,
"__prefab": {
"__id__": 7
"__id__": 9
},
"_collider": {
"__id__": 8
"_contentSize": {
"__type__": "cc.Size",
"width": 20,
"height": 50
},
"_sprite": {
"__id__": 4
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_score": 10,
"_flySpeed": 500,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "fc99DKgwZBHalLOt5fRIss"
"fileId": "dd1k5yz/9Jw7Y3h/ln7e+D"
},
{
"__type__": "cc.BoxCollider2D",
@ -172,7 +240,7 @@
},
"_enabled": true,
"__prefab": {
"__id__": 9
"__id__": 11
},
"tag": 0,
"_group": 16,
@ -205,7 +273,7 @@
},
"_enabled": true,
"__prefab": {
"__id__": 11
"__id__": 13
},
"enabledContactListener": true,
"bullet": false,
@ -229,6 +297,62 @@
"__type__": "cc.CompPrefabInfo",
"fileId": "bbOXJ9GJVHppWgt0OjwEBK"
},
{
"__type__": "3bd17tsjpdMZqOLOE4CWMkE",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 15
},
"_collider": {
"__id__": 10
},
"_sprite": {
"__id__": 5
},
"_spawnAnimation": {
"__id__": 16
},
"_score": 10,
"_flySpeed": 500,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "f3W6S5U0BKM6/Fx3RiEC8W"
},
{
"__type__": "cc.Animation",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 17
},
"playOnLoad": false,
"_clips": [
{
"__uuid__": "cfd9afd6-9345-446c-bf56-b7ac3dbf2c1e",
"__expectedType__": "cc.AnimationClip"
}
],
"_defaultClip": {
"__uuid__": "cfd9afd6-9345-446c-bf56-b7ac3dbf2c1e",
"__expectedType__": "cc.AnimationClip"
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "9cExf3IzxEa53jorYVyMdR"
},
{
"__type__": "cc.PrefabInfo",
"root": {
@ -238,6 +362,7 @@
"__id__": 0
},
"fileId": "31K2tUn39G47RitJdKcKW+",
"instance": null,
"targetOverrides": null
}
]

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
import { _decorator, CCInteger, Collider2D, Component, Contact2DType, Node } from 'cc';
import { GameManager } from '../Manager/GameManager';
import ObjectPool from '../Pool/ObjectPool';
const { ccclass, property, float } = _decorator;
@ccclass('Goal')
@ -14,6 +15,7 @@ export class Goal extends Component {
}
private onContactBegin(selfCollider: Collider2D, otherCollider: Collider2D) {
GameManager.instance.goal(this._score, otherCollider.node);
ObjectPool.release(otherCollider.node);
GameManager.instance.goal(this._score);
}
}

View File

@ -1,13 +1,31 @@
import { _decorator, CCFloat, CCInteger, Collider2D, Color, Component, Contact2DType, Sprite, Vec3 } from 'cc';
import {
_decorator,
Animation,
CCFloat,
CCInteger,
Collider2D,
Color,
Component,
Contact2DType,
Sprite,
Vec3,
} from 'cc';
import { GameManager } from '../Manager/GameManager';
import IPoolable from '../Pool/IPoolable';
import ObjectPool from '../Pool/ObjectPool';
import Utilities from '../Utilities/Utilities';
import { EventManger } from '../Manager/EventManger';
import GameEvent from '../Events/GameEvent';
const { ccclass, property, float } = _decorator;
@ccclass('DamageableObject')
export class DamageableObject extends Component {
@ccclass('ScoreObject')
export class ScoreObject extends Component implements IPoolable {
@property({ type: Collider2D, visible: true })
private _collider: Collider2D;
@property({ type: Sprite, visible: true })
private _sprite: Sprite;
@property({ type: Animation, visible: true })
private _spawnAnimation: Animation;
@property({ type: CCInteger, visible: true })
private _score: number;
@property({ type: CCFloat, visible: true })
@ -15,10 +33,12 @@ export class DamageableObject extends Component {
private _flyDirection: Vec3;
private _targetColor: Color;
private _originColor: Color;
private _hitted = false;
protected onLoad(): void {
this._collider.on(Contact2DType.BEGIN_CONTACT, this.onContactBegin, this);
this._originColor = this._sprite.color.clone();
}
protected update(dt: number): void {
@ -28,13 +48,15 @@ export class DamageableObject extends Component {
this.node.worldPosition = this.node.getWorldPosition().add(pos);
const scale = new Vec3();
Vec3.multiplyScalar(scale, Vec3.ONE.clone(), 0.5 * dt);
Vec3.multiplyScalar(scale, Vec3.ONE.clone(), 1 * dt);
this.node.scale = this.node.getScale().add(scale);
this.node.angle += 800 * dt;
this._sprite.color = this._sprite.color.clone().lerp(this._targetColor, 2 * dt);
this._sprite.color = this._sprite.color.clone().lerp(this._targetColor, 1 * dt);
if (this._sprite.color.a == 0) {
EventManger.instance.emit(GameEvent.ScoreObjectRelease, this.node);
ObjectPool.release(this.node);
}
}
}
@ -54,4 +76,16 @@ export class DamageableObject extends Component {
GameManager.instance.destroyEnviromentsObject(this._score);
}
}
public reuse() {
this._spawnAnimation.play();
this._collider.enabled = true;
}
public unuse() {
this._hitted = false;
this.node.angle = 0;
this.node.setScale(Vec3.ONE);
this._sprite.color = this._originColor;
}
}

View File

@ -2,7 +2,7 @@
"ver": "4.0.23",
"importer": "typescript",
"imported": true,
"uuid": "ab3ad722-8023-4c3b-860b-1e0d4e70c746",
"uuid": "3bd17b6c-8e97-4c66-a38b-384e0258c904",
"files": [],
"subMetas": {},
"userData": {}

View File

@ -1,19 +1,23 @@
import { Node } from 'cc';
import GameState from '../Enum/GameState';
import ScoreType from '../Enum/ScoreType';
enum GameEvent {
GameStateChange,
Score,
ScoreObjectRelease,
}
export interface GameEventCallbackMap {
[GameEvent.GameStateChange]: (state: GameState) => void;
[GameEvent.Score]: (type: ScoreType, score: number) => void;
[GameEvent.ScoreObjectRelease]: (obj: Node) => void;
}
export interface GameEventArgMap {
[GameEvent.GameStateChange]: GameState;
[GameEvent.Score]: [ScoreType, number];
[GameEvent.ScoreObjectRelease]: Node;
}
export default GameEvent;

View File

@ -23,6 +23,7 @@ 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')
@ -83,7 +84,11 @@ export class Ball extends Component implements IPoolable {
}
}
private onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
private async onBeginContact(
selfCollider: Collider2D,
otherCollider: Collider2D,
contact: IPhysics2DContact | null,
) {
if (this._hitted) return;
this._hitted = true;
if (this._rigidbody.linearVelocity.length() >= 3) {
@ -101,6 +106,8 @@ export class Ball extends Component implements IPoolable {
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);
}
}

View File

@ -1,5 +1,6 @@
import { _decorator, Collider2D, Component, Contact2DType, Node } from 'cc';
import { GameManager } from '../Manager/GameManager';
import ObjectPool from '../Pool/ObjectPool';
const { ccclass, property } = _decorator;
@ccclass('Outer')
@ -12,6 +13,7 @@ export class Outer extends Component {
}
private onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D) {
GameManager.instance.ballOut(otherCollider.node);
ObjectPool.release(otherCollider.node);
GameManager.instance.ballOut();
}
}

View File

@ -62,18 +62,17 @@ export class GameManager extends Component {
return ball;
}
public async ballOut(ball: Node) {
public async ballOut() {
this._balls--;
this._ballPool.release(ball);
if (this._balls === 0) {
this._ballPool.clear();
return;
}
await Utilities.delay(1000);
this.spawnBall();
}
public async goal(bonusScore: number, ball: Node) {
this._ballPool.release(ball);
public async goal(bonusScore: number) {
this.addScore(bonusScore, ScoreType.Goal);
await Utilities.delay(1000);
this.spawnBall();

View File

@ -1,4 +1,8 @@
import { _decorator, Component, Node, Prefab } from 'cc';
import { _decorator, CCFloat, Component, Node, Prefab, randomRangeInt } from 'cc';
import ObjectPool from '../Pool/ObjectPool';
import { ScoreObject } from '../Environments/ScoreObject';
import { EventManger } from './EventManger';
import GameEvent from '../Events/GameEvent';
const { ccclass, property } = _decorator;
@ccclass('SpawnObjectManager')
@ -14,8 +18,49 @@ export class SpawnObjectManager extends Component {
private _objects: Prefab[] = [];
@property({ type: [Node], visible: true })
private _spawnPoints: Node[] = [];
@property({ type: CCFloat, visible: true, range: [1, 10], slide: true })
private _spawnTime;
private _pools: ObjectPool[] = [];
private _usedPoints: { [key: string]: Node } = {};
private _timer = 0;
protected onLoad(): void {
SpawnObjectManager._instance = this;
EventManger.instance.on(GameEvent.ScoreObjectRelease, this.onObjectRelease, this);
for (let i = 0; i < this._objects.length; i++) {
const prefab = this._objects[i];
this._pools[i] = new ObjectPool(prefab, 10, true, ScoreObject);
}
}
protected start(): void {
for (let i = 0; i < randomRangeInt(5, 10); i++) {
this.spawn();
}
}
protected update(dt: number): void {
this._timer += dt;
if (this._timer >= this._spawnTime) {
this._timer = 0;
this.spawn();
}
}
private spawn() {
if (Object.keys(this._usedPoints).length == this._spawnPoints.length) return;
var randomPool = this._pools[randomRangeInt(0, this._pools.length)];
do {
var randomPoint = this._spawnPoints[randomRangeInt(0, this._spawnPoints.length)];
} while (Object.values(this._usedPoints).indexOf(randomPoint) != -1);
const obj = randomPool.get(this.node);
obj.setWorldPosition(randomPoint.worldPosition);
this._usedPoints[obj.uuid] = randomPoint;
}
private onObjectRelease(obj: Node) {
delete this._usedPoints[obj.uuid];
}
}

View File

@ -28,9 +28,17 @@ export default class ObjectPool {
let obj = instantiate(this._prefab); // create node instance
obj.removeFromParent();
this._inactives.push(obj);
ObjectPool._poolLookUp[obj.uuid] = this;
}
}
//#region Static
private static _poolLookUp: { [key: string]: ObjectPool } = {};
public static release(obj: Node) {
ObjectPool._poolLookUp[obj.uuid].release(obj);
}
//#endregion
public get(parent?: Node): Node;
public get<T extends Component>(parent?: Node, classConstructor?: new () => T): T;
public get<T extends Component>(parent?: Node, classConstructor?: new () => T): T | Node {
@ -42,6 +50,7 @@ export default class ObjectPool {
} else if (this._expandable) {
// if not enough node in the pool, we call cc.instantiate to create node
obj = instantiate(this._prefab);
ObjectPool._poolLookUp[obj.uuid] = this;
} else {
obj = this._actives.shift();
}
@ -57,7 +66,6 @@ export default class ObjectPool {
if (classConstructor) {
return handler == classConstructor ? handler : obj.getComponent(classConstructor);
}
return obj;
}
@ -84,5 +92,10 @@ export default class ObjectPool {
this._inactives.forEach((obj) => obj.destroy());
this._inactives = [];
this._actives = [];
Object.keys(ObjectPool._poolLookUp).forEach((key) => {
if (ObjectPool._poolLookUp[key] === this) {
delete ObjectPool._poolLookUp[key];
}
});
}
}

View File

@ -8,6 +8,17 @@ export default class Utilities {
return new Promise((resolve) => setTimeout(resolve, time));
}
/**
*@param predicate
* @param time (ms)
* @returns
*/
public static async waitUntil(predicate: () => boolean, timeCheck = 10) {
while (!predicate()) {
await this.delay(timeCheck);
}
}
public static getJson(json: string): any {
try {
return JSON.parse(json);