feat: add builder configuration file

feat: add cocos-service configuration file
feat: add device configuration file
feat: add engine configuration file
feat: add information configuration file
feat: add program configuration file
feat: add project configuration file
feat: add TypeScript configuration file
This commit is contained in:
ZhouXiao
2025-12-22 11:42:51 +08:00
parent 66cfa73345
commit 487c68994d
202 changed files with 57615 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
[InternetShortcut]
URL=https://docs.cocos.com/creator/manual/en/scripting/setup.html#custom-script-template

View File

@@ -0,0 +1,5 @@
{
"image": {
"type": "sprite-frame"
}
}

23
.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
#///////////////////////////
# Cocos Creator 3D Project
#///////////////////////////
library/
temp/
local/
profiles/
native
#//////////////////////////
# NPM
#//////////////////////////
node_modules/
#//////////////////////////
# VSCode
#//////////////////////////
.vscode/
#//////////////////////////
# WebStorm
#//////////////////////////
.idea/

Binary file not shown.

9
assets/music.meta Normal file
View File

@@ -0,0 +1,9 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "28332136-19e6-4c1d-8555-e108ef3ab5b2",
"files": [],
"subMetas": {},
"userData": {}
}

BIN
assets/music/bgm2.mp3 Normal file

Binary file not shown.

View File

@@ -0,0 +1,14 @@
{
"ver": "1.0.0",
"importer": "audio-clip",
"imported": true,
"uuid": "8cb692c5-0c41-4a9b-b756-e892f17174a9",
"files": [
".json",
".mp3"
],
"subMetas": {},
"userData": {
"downloadMode": 0
}
}

BIN
assets/music/click.mp3 Normal file

Binary file not shown.

View File

@@ -0,0 +1,14 @@
{
"ver": "1.0.0",
"importer": "audio-clip",
"imported": true,
"uuid": "5820b826-c2ed-45b2-a6c0-b4150bcef586",
"files": [
".json",
".mp3"
],
"subMetas": {},
"userData": {
"downloadMode": 0
}
}

BIN
assets/music/success.mp3 Normal file

Binary file not shown.

View File

@@ -0,0 +1,14 @@
{
"ver": "1.0.0",
"importer": "audio-clip",
"imported": true,
"uuid": "535db2a0-6556-4550-825e-31620cae8de0",
"files": [
".json",
".mp3"
],
"subMetas": {},
"userData": {
"downloadMode": 0
}
}

12
assets/prefab.meta Normal file
View File

@@ -0,0 +1,12 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "689726a6-3b07-4134-8a2e-efab9605bafc",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

9
assets/prefab/game.meta Normal file
View File

@@ -0,0 +1,9 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "69e3ca4b-d141-4234-9dd1-f405d9dee36e",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "136ad607-0e1c-4b90-91ca-76da5adabc8b",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,117 @@
[
{
"__type__": "cc.Prefab",
"_name": "res_main",
"_objFlags": 0,
"__editorExtras__": {},
"_native": "",
"data": {
"__id__": 1
},
"optimizationPolicy": 0,
"persistent": false
},
{
"__type__": "cc.Node",
"_name": "res_main",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": null,
"_children": [],
"_active": true,
"_components": [
{
"__id__": 2
}
],
"_prefab": {
"__id__": 4
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"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": 1073741824,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "f5f44OeETZIQbcE4fdL9Jmh",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 3
},
"SpriteFrameArr": [],
"PrefabArr": [
{
"__uuid__": "9e3c81a2-06f0-455e-8582-70359b348cf8",
"__expectedType__": "cc.Prefab"
}
],
"audiosArr": [
{
"__uuid__": "5820b826-c2ed-45b2-a6c0-b4150bcef586",
"__expectedType__": "cc.AudioClip"
},
{
"__uuid__": "8cb692c5-0c41-4a9b-b756-e892f17174a9",
"__expectedType__": "cc.AudioClip"
},
{
"__uuid__": "535db2a0-6556-4550-825e-31620cae8de0",
"__expectedType__": "cc.AudioClip"
}
],
"MaterialArr": [],
"Texture2DArr": [
{
"__uuid__": "56a35c4e-18ad-4795-86ad-f11bb3ebaa5a@f9941",
"__expectedType__": "cc.SpriteFrame"
}
],
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "a096fKgtdGxJWFFGGAdi8y"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "f8xZbaNcFJS4LctnHRpRy4",
"instance": null,
"targetOverrides": null
}
]

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.50",
"importer": "prefab",
"imported": true,
"uuid": "57d04e5b-f2fe-4c57-945c-c9a9b7f07d78",
"files": [
".json"
],
"subMetas": {},
"userData": {
"syncNodeName": "res_main"
}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "b775cc71-acaf-4bb6-93e8-4cca0d702eb1",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.50",
"importer": "prefab",
"imported": true,
"uuid": "9e3c81a2-06f0-455e-8582-70359b348cf8",
"files": [
".json"
],
"subMetas": {},
"userData": {
"syncNodeName": "OverUI"
}
}

14
assets/resources.meta Normal file
View File

@@ -0,0 +1,14 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "19090a79-a429-4a83-a4e2-37e32242724f",
"files": [],
"subMetas": {},
"userData": {
"isBundle": true,
"bundleConfigID": "default",
"bundleName": "resources",
"priority": 8
}
}

12
assets/scene.meta Normal file
View File

@@ -0,0 +1,12 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "cf0ffdde-f515-4857-8cd7-67ffbb88096f",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

1030
assets/scene/MainGame.scene Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
{
"ver": "1.1.50",
"importer": "scene",
"imported": true,
"uuid": "0401497c-b8be-4d0f-a5dd-55ed73f46146",
"files": [
".json"
],
"subMetas": {},
"userData": {}
}

12
assets/script.meta Normal file
View File

@@ -0,0 +1,12 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "bf2d81e3-8bc8-4e82-9ec5-d0cdf57a27d4",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "33cb078c-e75a-4d19-86e6-9b12cd55b22e",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,408 @@
import { Component, Node, Vec3, tween, Quat, Sprite, Color, math, easing, Camera, ITweenOption, IPunchTweenOption, IShakeTweenOption, Label } from 'cc';
import { calcPunchData, calcShakeData, quadraticCurve } from './Util';
//////////////////////
// Transform
//////////////////////
Node.prototype.qtPosition = function (to: Vec3, duration: number, opts?: ITweenOption) {
return tween(this).to(duration, { position: to }, opts);
}
Node.prototype.qtPositionX = function (to: number, duration: number, opts?: ITweenOption) {
const startPos = this.position;
return tween(this).to(duration, { position: new Vec3(to, startPos.y, startPos.z) }, opts);
}
Node.prototype.qtPositionY = function (to: number, duration: number, opts?: ITweenOption) {
const startPos = this.position;
return tween(this).to(duration, { position: new Vec3(startPos.x, to, startPos.z) }, opts);
}
Node.prototype.qtPositionZ = function (to: number, duration: number, opts?: ITweenOption) {
const startPos = this.position;
return tween(this).to(duration, { position: new Vec3(startPos.x, startPos.y, to) }, opts);
}
Node.prototype.qtWorldPosition = function (to: Vec3, duration: number, opts?: ITweenOption) {
return tween(this).to(duration, { worldPosition: to }, opts);
}
Node.prototype.qtWorldPositionX = function (to: number, duration: number, opts?: ITweenOption) {
const startPos = this.worldPosition;
return tween(this).to(duration, { worldPosition: new Vec3(to, startPos.y, startPos.z) }, opts);
}
Node.prototype.qtWorldPositionY = function (to: number, duration: number, opts?: ITweenOption) {
const startPos = this.worldPosition;
return tween(this).to(duration, { worldPosition: new Vec3(startPos.x, to, startPos.z) }, opts);
}
Node.prototype.qtWorldPositionZ = function (to: number, duration: number, opts?: ITweenOption) {
const startPos = this.worldPosition;
return tween(this).to(duration, { worldPosition: new Vec3(startPos.x, startPos.y, to) }, opts);
}
Node.prototype.qtRotation = function (to: Vec3, duration: number, opts?: ITweenOption) {
return tween(this).to(duration, { eulerAngles: to }, opts);
}
Node.prototype.qtRotationQuat = function (to: Quat, duration: number, opts?: ITweenOption) {
return tween(this).to(duration, { rotation: to }, opts);
}
Node.prototype.qtScale = function (to: Vec3 | number, duration: number, opts?: ITweenOption) {
let toScale = to;
if (!(to instanceof Vec3)) {
toScale = new Vec3(to, to, to);
}
return tween(this).to(duration, { scale: toScale }, opts);
}
Node.prototype.qtScaleX = function (to: number, duration: number, opts?: ITweenOption) {
const startScale = this.scale;
return tween(this).to(duration, { scale: new Vec3(to, startScale.y, startScale.z) }, opts);
}
Node.prototype.qtScaleY = function (to: number, duration: number, opts?: ITweenOption) {
const startScale = this.scale;
return tween(this).to(duration, { scale: new Vec3(startScale.x, to, startScale.z) }, opts);
}
Node.prototype.qtScaleZ = function (to: number, duration: number, opts?: ITweenOption) {
const startScale = this.scale;
return tween(this).to(duration, { scale: new Vec3(startScale.x, startScale.y, to) }, opts);
}
Node.prototype.qtPunchPosition = function (punch: Vec3, duration: number, opts?: IPunchTweenOption) {
const vibrato = opts?.vibrato ?? 3;
const elasticity = opts?.elasticity ?? 0.5;
const { tos, durations } = calcPunchData(this.position.clone(), punch, duration, vibrato, elasticity);
const punchTween = tween(this);
tos.forEach((to, index) => {
const d = durations[index];
let tweenOpts: ITweenOption | undefined;
if (index === 0) {
tweenOpts = {
onStart: opts.onStart
}
} else if (index === tos.length - 1) {
tweenOpts = {
onComplete: opts.onComplete
}
}
punchTween.then(tween().to(d, { position: to }, tweenOpts));
});
return punchTween.union();
}
Node.prototype.qtPunchRotation = function (punch: Vec3, duration: number, opts?: IPunchTweenOption) {
const vibrato = opts?.vibrato ?? 3;
const elasticity = opts?.elasticity ?? 0.5;
const { tos, durations } = calcPunchData(this.rotation.clone(), punch, duration, vibrato, elasticity);
const punchTween = tween(this);
tos.forEach((to, index) => {
const d = durations[index];
let tweenOpts: ITweenOption | undefined;
if (index === 0) {
tweenOpts = {
onStart: opts.onStart
}
} else if (index === tos.length - 1) {
tweenOpts = {
onComplete: opts.onComplete
}
}
punchTween.then(tween().to(d, { eulerAngles: to }, tweenOpts));
});
return punchTween.union();
}
Node.prototype.qtPunchScale = function (punch: Vec3, duration: number, opts?: IPunchTweenOption) {
const vibrato = opts?.vibrato ?? 3;
const elasticity = opts?.elasticity ?? 0.5;
const { tos, durations } = calcPunchData(this.scale.clone(), punch, duration, vibrato, elasticity);
const punchTween = tween(this);
tos.forEach((to, index) => {
const d = durations[index];
let tweenOpts: ITweenOption | undefined;
if (index === 0) {
tweenOpts = {
onStart: opts.onStart
}
} else if (index === tos.length - 1) {
tweenOpts = {
onComplete: opts.onComplete
}
}
punchTween.then(tween().to(d, { scale: to }, tweenOpts));
});
return punchTween.union();
}
Node.prototype.qtJumpPosition = function (to: Vec3, jumpHeight: number, jumpNum: number, duration: number, opts?: ITweenOption) {
const tweenPos = new Vec3();
const jumpTween = tween(this);
const totalNum = jumpNum * 2;
// 初始化选项对象,确保即使没有传入选项也不会出错
const options = opts || {};
this.jumpY = 0;
let startPosY = 0;
const yUpTween = tween().to(duration / totalNum, { jumpY: jumpHeight }, {
onStart: (target: Node) => {
startPosY = target.position.y;
target.jumpY = 0;
},
onUpdate: (target: Node, ratio) => {
tweenPos.set(target.position);
tweenPos.y = startPosY + target.jumpY;
target.position = tweenPos;
},
onComplete: (target: Node) => {
target.jumpY = 0;
}, easing: 'quadOut'
}).to(duration / totalNum, { jumpY: jumpHeight }, {
onStart: (target: Node) => {
startPosY = target.position.y;
},
onUpdate: (target: Node, ratio) => {
tweenPos.set(target.position);
tweenPos.y = startPosY - target.jumpY;
target.position = tweenPos;
},
onComplete: (target: Node) => {
target.jumpY = 0;
}, easing: 'quadIn',
}).union().repeat(jumpNum);
this.jumpOffsetY = 0;
let offsetY = 0;
const offsetYTween = tween().to(duration, { jumpOffsetY: to.y - this.position.y }, {
onStart: (target: Node) => {
offsetY = to.y - target.position.y;
target.jumpOffsetY = 0;
},
onUpdate: (target: Node, ratio) => {
const interpOffsetY = easing.quadOut(ratio) * offsetY;
tweenPos.set(target.position);
tweenPos.y += interpOffsetY;
target.position = tweenPos;
},
onComplete: (target: Node) => {
target.jumpOffsetY = 0;
}, easing: 'quadOut'
});
this.jumpX = this.position.x;
this.jumpZ = this.position.z;
const xzTween = tween().to(duration, { jumpX: to.x, jumpZ: to.z }, {
// 使用可选链运算符或者默认值
onStart: options.onStart,
onUpdate: (target: Node, ratio) => {
tweenPos.set(target.position);
tweenPos.x = target.jumpX;
tweenPos.z = target.jumpZ;
target.position = tweenPos;
options.onUpdate?.();
},
onComplete: (target: Node) => {
// delete target.jumpX;
// delete target.jumpY;
// delete target.jumpZ;
// delete target.jumpOffsetY;
target.jumpX = target.position.x;
target.jumpZ = target.position.z;
options.onComplete?.();
}
})
jumpTween.parallel(yUpTween, offsetYTween, xzTween);
return jumpTween;
}
Node.prototype.qtShakePosition = function (strength: Vec3 | number, duration: number, opts?: IShakeTweenOption) {
const options = opts || {};
const vibrato = options?.vibrato ?? 10;
const randomness = options?.randomness ?? 90;
const fadeOut = options?.fadeOut ?? true;
let toStrength: Vec3;
let vectorBased = false;
if (!(strength instanceof Vec3)) {
toStrength = new Vec3(strength, strength, strength);
} else {
toStrength = strength;
vectorBased = true;
}
const { tos, durations } = calcShakeData(this.position.clone(), duration, toStrength, vibrato, randomness, false, vectorBased, fadeOut)
const shakeTween = tween(this);
tos.forEach((to, index) => {
const d = durations[index];
let tweenOpts: ITweenOption | undefined;
if (index === 0) {
tweenOpts = {
onStart: options.onStart
}
} else if (index === tos.length - 1) {
tweenOpts = {
onComplete: options.onComplete
}
}
shakeTween.then(tween().to(d, { position: to }, tweenOpts));
});
return shakeTween.union();
}
Node.prototype.qtShakeRotation = function (strength: Vec3 | number, duration: number, opts?: IShakeTweenOption) {
const vibrato = opts?.vibrato ?? 10;
const randomness = opts?.randomness ?? 90;
const fadeOut = opts?.fadeOut ?? true;
let toStrength: Vec3;
let vectorBased = false;
if (!(strength instanceof Vec3)) {
toStrength = new Vec3(strength, strength, strength);
} else {
toStrength = strength;
vectorBased = true;
}
const { tos, durations } = calcShakeData(this.eulerAngles.clone(), duration, toStrength, vibrato, randomness, false, vectorBased, fadeOut)
const shakeTween = tween(this);
tos.forEach((to, index) => {
const d = durations[index];
let tweenOpts: ITweenOption | undefined;
if (index === 0) {
tweenOpts = {
onStart: opts.onStart
}
} else if (index === tos.length - 1) {
tweenOpts = {
onComplete: opts.onComplete
}
}
shakeTween.then(tween().to(d, { eulerAngles: to }, tweenOpts));
});
return shakeTween.union();
}
Node.prototype.qtShakeScale = function (strength: Vec3 | number, duration: number, opts?: IShakeTweenOption) {
const vibrato = opts?.vibrato ?? 10;
const randomness = opts?.randomness ?? 90;
const fadeOut = opts?.fadeOut ?? true;
let toStrength: Vec3;
let vectorBased = false;
if (!(strength instanceof Vec3)) {
toStrength = new Vec3(strength, strength, strength);
} else {
toStrength = strength;
vectorBased = true;
}
const { tos, durations } = calcShakeData(this.scale.clone(), duration, toStrength, vibrato, randomness, false, vectorBased, fadeOut)
const shakeTween = tween(this);
tos.forEach((to, index) => {
const d = durations[index];
let tweenOpts: ITweenOption | undefined;
if (index === 0) {
tweenOpts = {
onStart: opts.onStart
}
} else if (index === tos.length - 1) {
tweenOpts = {
onComplete: opts.onComplete
}
}
shakeTween.then(tween().to(d, { scale: to }, tweenOpts));
});
return shakeTween.union();
}
Node.prototype.qtQuadraticCurve = function (p1: Vec3, cp: Vec3, p2: Vec3, duration: number, opts?: ITweenOption) {
return tween(this).to(duration, { position: p2, easing: opts?.easing }, {
onUpdate(target, ratio) {
target.setPosition(quadraticCurve(ratio, p1, cp, p2));
},
onComplete: opts?.onComplete,
onStart: opts?.onStart
}).union();
}
//////////////////////
// Sprite
//////////////////////
// good color lerp
// https://www.alanzucconi.com/2016/01/06/colour-interpolation/
Sprite.prototype.qtColor = function (to: Color, duration: number, opts?: ITweenOption) {
return tween(this).to(duration, { color: to }, opts);
}
Sprite.prototype.qtOpacity = function (to: number, duration: number, opts?: ITweenOption) {
const startColor = this.color.clone();
const tempColor = new Color();
return tween(this).to(duration, { color: new Color(startColor.r, startColor.g, startColor.b, to) }, {
onStart: opts.onStart,
onUpdate: (target: { _val: number }, ratio: number) => {
const lerpA = startColor.a + (to - startColor.a) * ratio
tempColor.set(startColor.r, startColor.g, startColor.b, lerpA);
this.color = tempColor;
opts.onUpdate?.();
},
onComplete: opts.onComplete
});
}
//////////////////////
// Label
//////////////////////
Label.prototype.qtColor = function (to: Color, duration: number, opts?: ITweenOption) {
return tween(this).to(duration, { color: to }, opts);
}
Label.prototype.qtString = function (to: string, duration: number, opts?: ITweenOption) {
return tween(this).to(duration, { string: to }, opts);
}
//////////////////////
// Camera
//////////////////////
Camera.prototype.qtShakePosition = function (strength: Vec3 | number, duration: number, opts?: IShakeTweenOption) {
const vibrato = opts?.vibrato ?? 10;
const randomness = opts?.randomness ?? 90;
const fadeOut = opts?.fadeOut ?? true;
let toStrength: Vec3;
let vectorBased = false;
if (!(strength instanceof Vec3)) {
toStrength = new Vec3(strength, strength, strength);
} else {
toStrength = strength;
vectorBased = true;
}
const { tos, durations } = calcShakeData(this.node.position.clone(), duration, toStrength, vibrato, randomness, true, vectorBased, fadeOut)
const shakeTween = tween(this.node);
tos.forEach((to, index) => {
const d = durations[index];
let tweenOpts: ITweenOption | undefined;
if (index === 0) {
tweenOpts = {
onStart: opts.onStart
}
} else if (index === tos.length - 1) {
tweenOpts = {
onComplete: opts.onComplete
}
}
shakeTween.then(tween().to(d, { position: to }, tweenOpts));
});
return shakeTween.union();
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "7068ad06-e307-495f-befa-d9239208e9f1",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,160 @@
import { KeyCode, math, Quat, Vec3 } from "cc";
export function clampLength(vec: Vec3, maxLength: number) {
if (vec.lengthSqr() > maxLength * maxLength) {
const clampVec = new Vec3();
Vec3.normalize(clampVec, vec);
clampVec.multiplyScalar(maxLength);
return clampVec;
}
return vec;
}
export function vec3FromAngle(degree: number, length: number) {
const radian = math.toRadian(degree);
return new Vec3(length * Math.cos(radian), length * Math.sin(radian), 0);
}
export function calcPunchData(start: Vec3, direction: Vec3, duration: number, vibrato: number, elasticity: number) {
math.clamp01(elasticity);
let strength = direction.length();
let toIterations = Math.round(vibrato * duration);
if (toIterations < 2) {
toIterations = 2;
}
const deltaStrength = strength / toIterations;
let durations = [];
let sum = 0;
for (let i = 0; i < toIterations; i++) {
const iterationPercent = (i + 1) / toIterations;
const deltaDuration = duration * iterationPercent;
sum += deltaDuration;
durations[i] = deltaDuration;
}
const durationMultiplier = duration / sum;
durations = durations.map((d) => d * durationMultiplier);
// create to vec3 array
const tos: Vec3[] = [];
for (let i = 0; i < toIterations; i++) {
if (i < toIterations - 1) {
if (i === 0) {
tos[i] = Vec3.add(new Vec3(), start, direction);
} else if (i % 2 !== 0) {
const deltaVec = clampLength(direction, strength * elasticity);
deltaVec.negative();
tos[i] = deltaVec.add(start);
} else {
const deltaVec = clampLength(direction, strength);
tos[i] = deltaVec.add(start);
}
} else {
tos[i] = start;
}
strength -= deltaStrength;
}
return {
tos,
durations
}
}
export function calcShakeData(start: Vec3, duration: number, strength: Vec3, vibrato: number, randomness: number, ignoreZAxis: boolean, vectorBased: boolean,
fadeOut: boolean) {
KeyCode
let shakeLength = vectorBased ? strength.length() : strength.x;
let toIterations = Math.floor(vibrato * duration);
if (toIterations < 2) {
toIterations = 2;
}
const deltaShakeLen = shakeLength / toIterations;
let durations = [];
let sum = 0;
for (let i = 0; i < toIterations; i++) {
const iterationPercent = (i + 1) / toIterations;
const deltaDuration = fadeOut ? duration * iterationPercent : duration / toIterations;
sum += deltaDuration;
durations[i] = deltaDuration;
}
const durationMultiplier = duration / sum;
durations = durations.map((d) => d * durationMultiplier);
let angle = math.randomRange(0, 360);
const tos: Vec3[] = [];
for (let i = 0; i < toIterations; i++) {
if (i < toIterations - 1) {
let randQuat = new Quat();
if (i > 0) {
angle = angle - 180 + math.randomRange(-randomness, randomness);
}
// switch(randomnessMode) {
// case ShakeRandomnessMode.Harmonic:
// if (i > 0) {
// angle = angle - 180 + math.randomRange(0, randomness);
// }
// if (vectorBased || !ignoreZAxis) {
// Quat.fromAxisAngle(randQuat, Vec3.UP, math.randomRange(0, randomness));
// }
// break;
// default:
// if (i > 0) {
// angle = angle - 180 + math.randomRange(-randomness, randomness);
// }
// if (vectorBased || !ignoreZAxis) {
// Quat.fromAxisAngle(randQuat, Vec3.UP, math.randomRange(-randomness, randomness));
// }
// break;
// }
if (vectorBased) {
let to = vec3FromAngle(angle, shakeLength);
Vec3.transformQuat(to, to, randQuat);
to.x = clampLength(to, strength.x).x;
to.y = clampLength(to, strength.y).y;
to.z = clampLength(to, strength.z).z;
to.normalize().multiplyScalar(shakeLength);
tos[i] = to.add(start);
if (fadeOut) {
shakeLength -= deltaShakeLen;
}
strength = clampLength(strength, shakeLength);
} else {
if (ignoreZAxis) {
tos[i] = vec3FromAngle(angle, shakeLength).add(start);
} else {
Quat.fromAxisAngle(randQuat, Vec3.UP, math.randomRange(-randomness, randomness));
let to = vec3FromAngle(angle, shakeLength);
Vec3.transformQuat(to, to, randQuat);
tos[i] = to.add(start);
}
if (fadeOut) {
shakeLength -= deltaShakeLen;
}
}
} else {
tos[i] = start;
}
}
return {
tos,
durations
}
}
export function quadraticCurve(t: number, p1: Vec3, cp: Vec3, p2: Vec3) {
let out = new Vec3(0, 0, 0);
out.x = (1 - t) * (1 - t) * p1.x + 2 * t * (1 - t) * cp.x + t * t * p2.x;
out.y = (1 - t) * (1 - t) * p1.y + 2 * t * (1 - t) * cp.y + t * t * p2.y;
out.z = (1 - t) * (1 - t) * p1.z + 2 * t * (1 - t) * cp.z + t * t * p2.z;
return out;
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "9d1ed435-17ef-4b92-9fd9-391c112c8f2a",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,310 @@
declare module 'cc' {
interface IPunchTweenOption extends ITweenOption {
// How much the punch will vibrate
vibrato?: number,
// Represents how much (0 to 1) the vector will go beyond the starting position
// when bouncing backwards. 1 creates a full oscillation between the punch direction and the
// opposite direction, while 0 oscillates only between the punch and the start scale.
elasticity?: number
}
interface IShakeTweenOption extends ITweenOption {
vibrato?: number //每秒振动次数
randomness?: number // 随机角度值
fadeOut?: boolean // 是否淡出
}
interface Node {
/**
* @zh
* 移动目标的坐标到指定位置
* @en
* Moves the target's position to the given value
* @param to dest position
* @param duration time in seconds
* @param {ITweenOption} opts options for tween
* @param {Function} opts.onStart start callback
*/
qtPosition(to: Vec3, duration: number, opts?: ITweenOption): Tween<Node>;
/**
* @zh
* 移动目标的坐标到指定位置, 只移动X坐标
* @en
* Moves the target's position to the given value, tweening only the X axis.
* @param to dest position
* @param duration time in seconds
* @param opts options for tween
*/
qtPositionX(to: number, duration: number, opts?: ITweenOption): Tween<Node>;
/**
* @zh
* 移动目标的坐标到指定位置, 只移动Y坐标
* @en
* Moves the target's position to the given value, tweening only the Y axis.
* @param to dest position
* @param duration time in seconds
* @param opts options for tween
*/
qtPositionY(to: number, duration: number, opts?: ITweenOption): Tween<Node>;
/**
* @zh
* 移动目标的坐标到指定位置, 只移动Z坐标
* @en
* Moves the target's position to the given value, tweening only the Z axis.
* @param to dest position
* @param duration time in seconds
* @param opts options for tween
*/
qtPositionZ(to: number, duration: number, opts?: ITweenOption): Tween<Node>;
/**
* @zh
* 移动目标的世界坐标到指定位置
* @en
* Moves the target's worldPosition to the given value
* @param to dest position
* @param duration time in seconds
* @param opts options for tween
*/
qtWorldPosition(to: Vec3, duration: number, opts?: ITweenOption): Tween<Node>;
/**
* @zh
* 移动目标的世界坐标到指定位置, 只移动X坐标
* @en
* Moves the target's worldPosition to the given value, tweening only the X axis.
* @param to dest position
* @param duration time in seconds
* @param opts options for tween
*/
qtWorldPositionX(to: number, duration: number, opts?: ITweenOption): Tween<Node>;
/**
* @zh
* 移动目标的世界坐标到指定位置, 只移动Y坐标
* @en
* Moves the target's worldPosition to the given value, tweening only the Y axis.
* @param to dest position
* @param duration time in seconds
* @param opts options for tween
*/
qtWorldPositionY(to: number, duration: number, opts?: ITweenOption): Tween<Node>;
/**
* @zh
* 移动目标的世界坐标到指定位置, 只移动Z坐标
* @en
* Moves the target's worldPosition to the given value, tweening only the Z axis.
* @param to dest position
* @param duration time in seconds
* @param opts options for tween
*/
qtWorldPositionZ(to: number, duration: number, opts?: ITweenOption): Tween<Node>;
/**
* @zh
* 旋转目标到指定值
* @en
* Rotates the target to ghe given value
* @param to dest rotation in eulerAngle
* @param duration time in seconds
* @param opts options for tween
*/
qtRotation(to: Vec3, duration: number, opts?: ITweenOption): Tween<Node>;
/**
* @zh
* 旋转目标到指定值
* @en
* Rotates the target to ghe given value
* @param to dest rotation in quaternion
* @param duration time in seconds
* @param opts options for tween
*/
qtRotationQuat(to: Quat, duration: number, opts?: ITweenOption): Tween<Node>;
/**
* @zh
* 缩放目标到指定值
* @en
* Scales the target to ghe given value
* @param to dest scale value
* @param duration time in seconds
* @param opts options for tween
*/
qtScale(to: Vec3 | number, duration: number, opts?: ITweenOption): Tween<Node>;
/**
* @zh
* 缩放目标到指定值只影响X轴
* @en
* Scales the target to ghe given value, tweening only X axis
* @param to dest scale value
* @param duration time in seconds
* @param opts options for tween
*/
qtScaleX(to: number, duration: number, opts?: ITweenOption): Tween<Node>;
/**
* @zh
* 缩放目标到指定值只影响Y轴
* @en
* Scales the target to ghe given value, tweening only Y axis
* @param to dest scale value
* @param duration time in seconds
* @param opts options for tween
*/
qtScaleY(to: number, duration: number, opts?: ITweenOption): Tween<Node>;
/**
* @zh
* 缩放目标到指定值只影响Z轴
* @en
* Scales the target to ghe given value, tweening only Z axis
* @param to dest scale value
* @param duration time in seconds
* @param opts options for tween
*/
qtScaleZ(to: number, duration: number, opts?: ITweenOption): Tween<Node>;
/**
* @zh
* 击打目标位置到指定方向,然后回到初始值
* @en
* Punches a position towards the given direction and then
* back to the starting one as if it was connected to the starting position
* via an elastic.
* @param punch The direction and strength of the punch, (added to the node's current position)
* @param duration Time in seconds
* @param {IPunchTweenOption} opts punch tween options
* @param {number} opts.vibrato How much the punch will vibrate
* @param {number} opts.elasticity Represents how much (0 to 1) the vector will go beyond the starting position
* when bouncing backwards. 1 creates a full oscillation between the punch direction and the
* opposite direction, while 0 oscillates only between the punch and the start position.
*/
qtPunchPosition(punch: Vec3, duration: number, opts?: IPunchTweenOption): Tween<Node>;
/**
* @zh
* 击打目标旋转方向到指定值,然后回到初始值
* @en
* Punches a rotation to the given value and then back to the starting one as if it was connected
* to the starting rotation via an elastic.
* @param punch The strength of punch, (added to the node's current rotation)
* @param duration Time in seconds
* @param {IPunchTweenOption} opts punch tween options
* @param {number} opts.vibrato How much the punch will vibrate
* @param {number} opts.elasticity Represents how much (0 to 1) the vector will go beyond the starting position
* when bouncing backwards. 1 creates a full oscillation between the punch direction and the
* opposite direction, while 0 oscillates only between the punch and the start rotation.
*/
qtPunchRotation(punch: Vec3, duration: number, opts?: IPunchTweenOption): Tween<Node>;
/**
* @zh
* 击打目标缩放到指定值,然后回到初始值
* @en
* Punches a scale to the given value and then back to the starting one as if it was connected
* to the starting scale via an elastic.
* @param punch The strength of punch, (added to the node's current scale)
* @param duration Time in seconds
* @param {IPunchTweenOption} opts punch tween options
* @param {number} opts.vibrato How much the punch will vibrate
* @param {number} opts.elasticity Represents how much (0 to 1) the vector will go beyond the starting position
* when bouncing backwards. 1 creates a full oscillation between the punch direction and the
* opposite direction, while 0 oscillates only between the punch and the start scale.
*/
qtPunchScale(punch: Vec3, duration: number, opts?: IPunchTweenOption): Tween<Node>;
jumpX?: number;
jumpY?: number;
jumpZ?: number;
jumpOffsetY?: number;
/**
* @zh
* 缓动目标的坐标到指定值在移动过程中同时附加一个Y坐标的高度值来模拟跳跃动作
* @en
* Tweens the target's position to the given value, while also applying a jump effect along the Y axis.
* @param to 目标坐标值
* @param jumpHeight 跳跃高度
* @param jumpNum 跳跃次数
* @param duration 时间
* @param opts tween options
*/
qtJumpPosition(to: Vec3, jumpHeight: number, jumpNum: number, duration: number, opts?: ITweenOption): Tween<Node>;
/**
* @zh
* 使目标的位置在设定的参数下抖动
* @en
* Shakes the target's position with the given values
* @param strength 强度
* @param duration 时间
* @param {IShakeTweenOption} opts shake tween options
* @param {number} opts.vibrato 每秒振动次数
* @param {number} opts.randomness 随机角度值
* @param {boolean} opts.fadeOut 是否淡出
*/
qtShakePosition(strength: Vec3 | number, duration: number, opts?: IShakeTweenOption): Tween<Node>;
/**
* @zh
* 使目标的旋转在设定的参数下抖动
* @en
* Shakes the target's rotation with the given values
* @param strength 强度
* @param duration 时间
* @param {IShakeTweenOption} opts shake tween options
* @param {number} opts.vibrato 每秒振动次数
* @param {number} opts.randomness 随机角度值
* @param {boolean} opts.fadeOut 是否淡出
*/
qtShakeRotation(strength: Vec3 | number, duration: number, opts?: IShakeTweenOption): Tween<Node>;
/**
* @zh
* 使目标的缩放在设定的参数下抖动
* @en
* Shakes the target's scale with the given values
* @param strength 强度
* @param duration 时间
* @param {IShakeTweenOption} opts shake tween options
* @param {number} opts.vibrato 每秒振动次数
* @param {number} opts.randomness 随机角度值
* @param {boolean} opts.fadeOut 是否淡出
*/
qtShakeScale(strength: Vec3 | number, duration: number, opts?: IShakeTweenOption): Tween<Node>;
/**
* @zh
* 缓动目标的坐标到指定值,使用二次贝塞尔曲线
* @en
* Tweens the target's position to the given value, using a quadratic Bezier curve.
* @param p1 起点
* @param cp 控制点
* @param p2 终点
* @param duration 时间
* @param opts tween options
*/
qtQuadraticCurve(p1: Vec3, cp: Vec3, p2: Vec3, duration: number, opts?: ITweenOption): Tween<Node>;
}
interface Sprite {
qtColor(to: Color, duration: number, opts?: ITweenOption): Tween<Sprite>;
qtOpacity(to: number, duration: number, opts?: ITweenOption): Tween<Sprite>;
}
interface Label extends Omit<UIRenderer, 'spriteFrame'> {
//@ts-expect-error
spriteFrame: SpriteFrame | null;
qtColor(to: Color, duration: number, opts?: ITweenOption): Tween<Label>;
qtString(to: string, duration: number, opts?: ITweenOption): Tween<Label>;
}
interface Camera {
/**
* @zh
* 使目标的位置在设定的参数下抖动
* @en
* Shakes the target's position with the given values
* @param strength 强度
* @param duration 时间
* @param {IShakeTweenOption} opts shake tween options
* @param {number} opts.vibrato 每秒振动次数
* @param {number} opts.randomness 随机角度值
* @param {boolean} opts.fadeOut 是否淡出
*/
qtShakePosition(strength: Vec3 | number, duration: number, opts?: IShakeTweenOption): Tween<Node>;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "af3336d7-4ce2-4d9d-a529-51281d4eaa30",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "c2d2e44a-ed28-4c85-ba6d-af272f33815c",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,325 @@
import { SplitRenderHelper } from "./SplitRenderHelper";
import { _decorator, Node, Vec3, IAssembler, ccenum, Vec2, Mat4, Texture2D, v2, v3, EventTouch, gfx, SpriteFrame, UIRenderer } from 'cc';
const { ccclass, property, executeInEditMode } = _decorator;
const vec3_temps: Vec3[] = [];
for (let i = 0; i < 4; i++) {
vec3_temps.push(new Vec3());
}
class AssemblerSplit implements IAssembler {
createData(com: SplitRender) {
let vertexCount = 4;
let indexCount = 6;
const renderData = com.requestRenderData();
renderData.dataLength = vertexCount;
renderData.resize(vertexCount, indexCount);
return renderData;
}
resetData(com: SplitRender) {
let points = com.polygon;
if (!points || points.length < 3) return;
let vertexCount = points.length;
let indexCount = vertexCount + (vertexCount - 3) * 2;
com.renderData.clear();
com.renderData.dataLength = vertexCount;
com.renderData.resize(vertexCount, indexCount);
let material = com.renderData.material;
com.renderData.material = material;
}
updateRenderData(com: SplitRender) {
// dynamicAtlasManager.packToDynamicAtlas(com, frame);
const renderData = com.renderData;
if (renderData.vertDirty) {
this.resetData(com);
this.updateVertexData(com);
this.updateUvs(com);
this.updateColor(com);
renderData.updateRenderData(com, com.spriteFrame);
}
}
updateWorldVerts(com: SplitRender, verts: Float32Array) {
let floatsPerVert = 9;
let matrix: Mat4 = com.node.worldMatrix;
let a = matrix.m00, b = matrix.m01, c = matrix.m04, d = matrix.m05,
tx = matrix.m12, ty = matrix.m13;
let justTranslate = a === 1 && b === 0 && c === 0 && d === 1;
if (justTranslate) {
let polygon = com.polygon;
for (let i = 0; i < polygon.length; i++) {
verts[i * floatsPerVert] = polygon[i].x + tx;
verts[i * floatsPerVert + 1] = polygon[i].y + ty;
}
} else {
let polygon = com.polygon;
for (let i = 0; i < polygon.length; i++) {
verts[i * floatsPerVert] = a * polygon[i].x + c * polygon[i].y + tx;
verts[i * floatsPerVert + 1] = b * polygon[i].x + d * polygon[i].y + ty;
}
}
// @ts-ignore
com.node._uiProps.uiTransformDirty = false;
}
fillBuffers(com: SplitRender, renderer: any) {
const chunk = com.renderData.chunk;
// indices generated
let indicesArr = SplitRenderHelper.splitPolygon(com.polygon);
this.updateWorldVerts(com, chunk.vb);
// quick version
const bid = chunk.bufferId;
const vid = chunk.vertexOffset;
const meshBuffer = chunk.vertexAccessor.getMeshBuffer(bid);
const ib = chunk.vertexAccessor.getIndexBuffer(bid);
let indexOffset = meshBuffer.indexOffset;
// fill indices
for (let i = 0, l = indicesArr.length; i < l; i++) {
ib[indexOffset++] = vid + indicesArr[i];
}
meshBuffer.indexOffset += indicesArr.length;
}
updateVertexData(com: SplitRender) {
const renderData = com.renderData;
if (!renderData) {
return;
}
const dataList = renderData.data;
let polygon = com.polygon;
for (let i = 0; i < polygon.length; i++) {
dataList[i].x = polygon[i].x;
dataList[i].y = polygon[i].y;
}
const chunk = com.renderData.chunk;
const vid = chunk.vertexOffset;
const ib = chunk.ib as any;
let indicesArr = SplitRenderHelper.splitPolygon(com.polygon);
for (let i = 0, l = indicesArr.length; i < l; i++) {
ib[i] = vid + indicesArr[i];
}
}
updateUvs(com: SplitRender) {
let uvOffset = 3, floatsPerVert = 9;
const vData = com.renderData.chunk.vb;
let uvs = [];
if (com.spriteFrame.texture) {
uvs = SplitRenderHelper.computeUv(com.polygon, com.spriteFrame.texture.width, com.spriteFrame.texture.height)
}
let polygon = com.polygon;
for (let i = 0; i < polygon.length; i++) {
vData[uvOffset] = uvs[i].x;
vData[uvOffset + 1] = uvs[i].y;
uvOffset += floatsPerVert;
}
}
updateColor(com: SplitRender) {
const renderData = com.renderData!;
let colorOffset = 5, floatsPerVert = renderData.floatStride;
let vData = renderData.chunk.vb;
const color = com.color;
const colorR = color.r / 255;
const colorG = color.g / 255;
const colorB = color.b / 255;
const colorA = color.a / 255;
let polygon = com.polygon;
for (let i = 0; i < polygon.length; i++) {
vData![colorOffset] = colorR;
vData![colorOffset + 1] = colorG;
vData![colorOffset + 2] = colorB;
vData![colorOffset + 3] = colorA;
colorOffset += floatsPerVert;
}
}
};
enum TextureType {
Cut, // 裁剪
Stretch // 拉伸, 暂未实现
}
ccenum(TextureType);
let _vec2_temp = new Vec2();
let _mat4_temp = new Mat4();
@ccclass('SplitRender')
@executeInEditMode
export class SplitRender extends UIRenderer {
static Type = TextureType;
@property({ type: SpriteFrame, serializable: true })
protected _spriteFrame: SpriteFrame | null = null;
@property({ type: SpriteFrame, serializable: true })
get spriteFrame() {
return this._spriteFrame;
}
set spriteFrame(value) {
if (!value || this._spriteFrame === value) {
this._spriteFrame = value;
return;
}
this._spriteFrame = value;
let l = -value.width / 2, b = -value.height / 2, t = value.height / 2, r = value.width / 2;
this.polygon = [v2(l, b), v2(r, b), v2(r, t), v2(l, t)];
this.markForUpdateRenderData(false);
this._applySpriteSize();
}
@property({ type: TextureType, serializable: true })
_type: TextureType = 0;
@property({ type: TextureType, serializable: true })
get type() {
return this._type;
}
set type(val: TextureType) {
this._type = val;
this.markForUpdateRenderData();
}
@property
editing: boolean = false;
@property({ type: [Vec2], serializable: true })
_polygon: Vec2[] = [];
@property({ type: [Vec2], serializable: true })
public get polygon() {
return this._polygon;
}
public set polygon(points: Vec2[]) {
this._polygon = points;
this.markForUpdateRenderData();
}
protected _assembler: IAssembler = null;
constructor() {
super();
}
onLoad() {
this._renderEntity.setNode(this.node);
this.node['_hitTest'] = this._hitTest.bind(this);
}
start() {
// this.node.on(Node.EventType.TOUCH_START, (e: EventTouch) => {
// console.log("click texture plus -");
// }, this);
// this.node.on(Node.EventType.TOUCH_MOVE, (e: EventTouch) => {
// console.log("click texture plus +");
// this.node.setPosition(v3(this.node.position.x + e.getDeltaX(),
// this.node.position.y + e.getDeltaY(),
// this.node.position.z));
// }, this);
// console.log(this.node.uuid);
}
_hitTest(cameraPt: Vec2) {
let node = this.node;
let testPt = _vec2_temp;
node.updateWorldTransform();
// If scale is 0, it can't be hit.
if (!Mat4.invert(_mat4_temp, node.worldMatrix)) {
return false;
}
Vec2.transformMat4(testPt, cameraPt, _mat4_temp);
return SplitRenderHelper.isPointInsidePolygon(testPt, this.polygon);
}
private _applySpriteSize() {
if (this._spriteFrame) {
const size = this._spriteFrame.originalSize;
this.node._uiProps.uiTransformComp!.setContentSize(size);
}
this._activateMaterial();
}
private _activateMaterial() {
const spriteFrame = this._spriteFrame;
const material = this.getRenderMaterial(0);
if (spriteFrame) {
if (material) {
this.markForUpdateRenderData();
}
}
if (this.renderData) {
this.renderData.material = material;
}
}
protected _render(render: any) {
render.commitComp(this, this.renderData, this._spriteFrame, this._assembler!);
}
protected _canRender() {
if (!super._canRender()) {
return false;
}
const spriteFrame = this._spriteFrame;
if (!spriteFrame || !spriteFrame.texture) {
return false;
}
return true;
}
protected _flushAssembler(): void {
if (this._assembler == null) {
this.destroyRenderData();
this._assembler = new AssemblerSplit();
}
if (!this.renderData) {
if (this._assembler && this._assembler.createData) {
this._renderData = this._assembler.createData(this);
this.renderData!.material = this.getRenderMaterial(0);
this.markForUpdateRenderData();
this._updateColor();
}
}
}
protected updateMaterial() {
if (this._customMaterial) {
this.setSharedMaterial(this._customMaterial, 0);
// this._customMaterial.overridePipelineStates({ priority: 128 }, 0);
// this._blendHash = -1;
return;
}
const mat = this._updateBuiltinMaterial();
this.setSharedMaterial(mat, 0);
this._updateBlendFunc();
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "ccd6bd1c-e0c8-4fa3-a9e3-60b1929a51d0",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,375 @@
import { math, v2, Vec2, Vec3 } from 'cc';
export class SplitRenderHelper {
//ab与ac的叉积
static ab_cross_ac(a, b, c) {
return SplitRenderHelper.cross(b.x - a.x, b.y - a.y, c.x - a.x, c.y - a.y);
}
static dot(x1, y1, x2, y2) {
return x1 * x2 + y1 * y2;
}
static cross(x1, y1, x2, y2) {
return x1 * y2 - x2 * y1;
}
static dblcmp(a: number, b: number) {
if (Math.abs(a - b) <= 0.000001) return 0;
if (a > b) return 1;
else return -1;
}
//求a点是不是在线段上>0不在=0与端点重合<0在。
static point_on_line(a, p1, p2) {
return SplitRenderHelper.dblcmp(SplitRenderHelper.dot(p1.x - a.x, p1.y - a.y, p2.x - a.x, p2.y - a.y), 0);
}
// 判断一个点是否在三角形内
static isInTriangle(point: Vec2, triA: Vec2, triB: Vec2, triC: Vec2) {
let AB: Vec2 = new Vec2();
Vec2.subtract(AB, triB, triA);
let AC: Vec2 = new Vec2();
Vec2.subtract(AC, triC, triA);
let BC: Vec2 = new Vec2();
Vec2.subtract(BC, triC, triB);
let AD: Vec2 = new Vec2();
Vec2.subtract(AD, point, triA);
let BD: Vec2 = new Vec2();
Vec2.subtract(BD, point, triB);
//@ts-ignore
return (AB.cross(AC) >= 0 ^ AB.cross(AD) < 0) && (AB.cross(AC) >= 0 ^ AC.cross(AD) >= 0) && (BC.cross(AB) > 0 ^ BC.cross(BD) >= 0);
}
static isInPolygon(checkPoint: Vec2, polygonPoints: Vec2[]) {
var counter = 0;
var i: number;
var xinters;
var p1: Vec2, p2: Vec2;
var pointCount = polygonPoints.length;
p1 = polygonPoints[0];
for (i = 1; i <= pointCount; i++) {
p2 = polygonPoints[i % pointCount];
if (
checkPoint.x > Math.min(p1.x, p2.x) &&
checkPoint.x <= Math.max(p1.x, p2.x)
) {
if (checkPoint.y <= Math.max(p1.y, p2.y)) {
if (p1.x != p2.x) {
xinters = (checkPoint.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;
if (p1.y == p2.y || checkPoint.y <= xinters) {
counter++;
}
}
}
}
p1 = p2;
}
if (counter % 2 == 0) {
return false;
}
return true;
}
static computeUv(points: Vec2[], width: number, height: number) {
let uvs: Vec2[] = [];
for (const p of points) {
// uv原点是左上角
let x = math.clamp(0, 1, (p.x + width / 2) / width);
let y = math.clamp(0, 1, 1. - (p.y + height / 2) / height);
uvs.push(v2(x, y));
}
return uvs;
}
static splitPolygon(points: Vec2[]): number[] {
if (points.length <= 3) return [0, 1, 2];
let pointMap: { [key: string]: number } = {}; // point与idx的映射
for (let i = 0; i < points.length; i++) {
let p = points[i];
pointMap[`${p.x}-${p.y}`] = i;
}
const getIdx = (p: Vec2) => {
return pointMap[`${p.x}-${p.y}`]
}
points = points.concat([]);
let idxs: number[] = [];
let index = 0;
while (points.length > 3) {
let p1 = points[(index) % points.length]
, p2 = points[(index + 1) % points.length]
, p3 = points[(index + 2) % points.length];
let splitPoint = (index + 1) % points.length;
let v1: Vec2 = new Vec2();
Vec2.subtract(v1, p2, p1);
let v2: Vec2 = new Vec2();
Vec2.subtract(v2, p3, p2);
if (v1.cross(v2) < 0) { // 是一个凹角, 寻找下一个
index = (index + 1) % points.length;
continue;
}
let hasPoint = false;
for (const p of points) {
if (p != p1 && p != p2 && p != p3 && this.isInTriangle(p, p1, p2, p3)) {
hasPoint = true;
break;
}
}
if (hasPoint) { // 当前三角形包含其他点, 寻找下一个
index = (index + 1) % points.length;
continue;
}
// 找到了耳朵, 切掉
idxs.push(getIdx(p1), getIdx(p2), getIdx(p3));
points.splice(splitPoint, 1);
}
for (const p of points) {
idxs.push(getIdx(p));
}
return idxs;
}
//点发出的右射线和线段的关系
// 返回值: -1:不相交, 0:相交, 1:点在线段上
static rayPointToLine(point: Vec2, linePA: Vec2, linePB: Vec2) {
// 定义最小和最大的X Y轴值
let minX = Math.min(linePA.x, linePB.x);
let maxX = Math.max(linePA.x, linePB.x);
let minY = Math.min(linePA.y, linePB.y);
let maxY = Math.max(linePA.y, linePB.y);
// 射线与边无交点的其他情况
if (point.y < minY || point.y > maxY || point.x > maxX) {
return -1;
}
// 剩下的情况, 计算射线与边所在的直线的交点的横坐标
let x0 = linePA.x + ((linePB.x - linePA.x) / (linePB.y - linePA.y)) * (point.y - linePA.y);
if (x0 > point.x) {
return 0;
}
if (x0 == point.x) {
return 1;
}
return -1;
}
//点和多边形的关系
//返回值: -1:在多边形外部, 0:在多边形内部, 1:在多边形边线内, 2:跟多边形某个顶点重合
static relationPointToPolygon(point: Vec2, polygon: Vec2[]) {
let count = 0;
for (let i = 0; i < polygon.length; ++i) {
if (polygon[i].equals(point)) {
return 2;
}
let pa = polygon[i];
let pb = polygon[0];
if (i < polygon.length - 1) {
pb = polygon[i + 1];
}
let re = SplitRenderHelper.rayPointToLine(point, pa, pb);
if (re == 1) {
return 1;
}
if (re == 0) {
count++;
}
}
if (count % 2 == 0) {
return -1;
}
return 0;
}
//求两条线段的交点
//返回值:[n,p] n:0相交1在共有点-1不相交 p:交点
static lineCrossPoint(p1: Vec2, p2: Vec2, q1: Vec2, q2: Vec2): [number, Vec2] {
let a = p1, b = p2, c = q1, d = q2;
let s1, s2, s3, s4;
let d1, d2, d3, d4;
let p: Vec2 = new Vec2(0, 0);
d1 = SplitRenderHelper.dblcmp(s1 = SplitRenderHelper.ab_cross_ac(a, b, c), 0);
d2 = SplitRenderHelper.dblcmp(s2 = SplitRenderHelper.ab_cross_ac(a, b, d), 0);
d3 = SplitRenderHelper.dblcmp(s3 = SplitRenderHelper.ab_cross_ac(c, d, a), 0);
d4 = SplitRenderHelper.dblcmp(s4 = SplitRenderHelper.ab_cross_ac(c, d, b), 0);
if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2) {
p.x = (c.x * s2 - d.x * s1) / (s2 - s1);
p.y = (c.y * s2 - d.y * s1) / (s2 - s1);
return [0, p];
}
if (d1 == 0 && SplitRenderHelper.point_on_line(c, a, b) <= 0) {
p = c;
return [1, p];
}
if (d2 == 0 && SplitRenderHelper.point_on_line(d, a, b) <= 0) {
p = d;
return [1, p];
}
if (d3 == 0 && SplitRenderHelper.point_on_line(a, c, d) <= 0) {
p = a;
return [1, p];
}
if (d4 == 0 && SplitRenderHelper.point_on_line(b, c, d) <= 0) {
p = b;
return [1, p];
}
return [-1, null];
}
//线段对多边形进行切割
//返回多边形数组
//如果没有被切割,返回空数组
static lineCutPolygon(pa: Vec2, pb: Vec2, polygon: Vec2[]) {
// 检查切割线的端点是否在多边形内部
const extendPoint = (point: Vec2, direction: Vec2) => {
const extendedPoint = new Vec2(point.x + direction.x * 1000, point.y + direction.y * 1000);
return extendedPoint;
};
if (SplitRenderHelper.isInPolygon(pa, polygon)) {
const direction = Vec2.subtract(new Vec2(), pa, pb).normalize();
pa = extendPoint(pa, direction);
}
if (SplitRenderHelper.isInPolygon(pb, polygon)) {
const direction = Vec2.subtract(new Vec2(), pb, pa).normalize();
pb = extendPoint(pb, direction);
}
let ret: Array<Vec2[]> = [];
let points: Vec2[] = [];
let pointIndex: number[] = [];
for (let i = 0; i < polygon.length; ++i) {
points.push(polygon[i]);
let a = polygon[i];
let b = polygon[0];
if (i < polygon.length - 1) b = polygon[i + 1];
let c = SplitRenderHelper.lineCrossPoint(pa, pb, a, b);
if (c[0] == 0) {
pointIndex.push(points.length);
points.push(c[1] as Vec2);
}
else if (c[0] > 0) {
if ((c[1] as Vec2).equals(a)) {
pointIndex.push(points.length - 1);
}
else {
pointIndex.push(points.length);
}
}
}
if (pointIndex.length > 1) {
let cp0 = points[pointIndex[0]];
let cp1 = points[pointIndex[1]];
let r = SplitRenderHelper.relationPointToPolygon(new Vec2((cp0.x + cp1.x) / 2, (cp0.y + cp1.y) / 2), polygon);
let inPolygon: boolean = r >= 0;
let cp0_cp1: Vec2 = new Vec2();
let len_0_1 = Vec2.subtract(cp0_cp1, cp0, cp1).length()
let cp0_cp: Vec2 = new Vec2();
let len_0_ = Vec2.subtract(cp0_cp, cp0, points[pointIndex[pointIndex.length - 1]]).length()
if (pointIndex.length > 2 && len_0_1 > len_0_) {
cp1 = points[pointIndex[pointIndex.length - 1]];
r = SplitRenderHelper.relationPointToPolygon(new Vec2((cp0.x + cp1.x) / 2, (cp0.y + cp1.y) / 2), polygon);
inPolygon = r < 0;
}
let firstInPolygon = inPolygon;
let index = 0;
let startIndex = pointIndex[index];
let oldPoints = [];
let newPoints = [];
let count = 0;
oldPoints.push(points[startIndex]);
if (inPolygon) {
newPoints.push(points[startIndex]);
}
index++;
count++;
startIndex++;
while (count < points.length) {
if (startIndex == points.length) startIndex = 0;
let p = points[startIndex];
if (index >= 0 && startIndex == pointIndex[index]) {
index++;
if (index >= pointIndex.length) index = 0;
if (inPolygon) {
newPoints.push(p);
ret.push(newPoints);
newPoints = [];
}
else {
newPoints = [];
newPoints.push(p);
}
oldPoints.push(p);
inPolygon = !inPolygon;
}
else {
if (inPolygon) {
newPoints.push(p);
}
else {
oldPoints.push(p);
}
}
startIndex++;
count++;
}
if (inPolygon) {
if (!firstInPolygon && newPoints.length > 1) {
newPoints.push(points[pointIndex[0]]);
ret.push(newPoints);
}
else {
for (let i = 0; i < newPoints.length; ++i) {
oldPoints.push(newPoints[i]);
}
}
}
ret.push(oldPoints);
}
return ret;
}
// 实用函数
static isPointInsidePolygon(p: Vec2, polygon: Vec2[]) {
let windingNumber = 0;
for (let i = 0; i < polygon.length; i++) {
const a = polygon[i];
const b = polygon[(i + 1) % polygon.length];
if (a.y <= p.y) {
if (b.y > p.y && (b.x - a.x) * (p.y - a.y) > (p.x - a.x) * (b.y - a.y))
windingNumber++;
} else {
if (b.y <= p.y && (b.x - a.x) * (p.y - a.y) < (p.x - a.x) * (b.y - a.y))
windingNumber--;
}
}
return windingNumber !== 0;
}
static calculatePolygonArea(polygon: Vec2[]) {
let area = 0;
for (let i = 0; i < polygon.length; i++) {
const j = (i + 1) % polygon.length;
area += polygon[i].x * polygon[j].y;
area -= polygon[j].x * polygon[i].y;
}
return Math.abs(area) / 2;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "e6b2c70f-62b1-4940-988f-c42a322beca3",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,219 @@
import { SplitRenderHelper } from "./SplitRenderHelper";
import { SplitRender } from "./SplitRender";
import { _decorator, Component, Node, Texture2D, Graphics, Vec2, view, Vec3, SpriteFrame, EventTouch, v2, tween, Camera, color, Layers, profiler, UITransform, Sprite, RigidBody2D, ERigidBody2DType, PolygonCollider2D, Physics2DUtils, PhysicsSystem2D, ERaycast2DType, UIOpacity, Collider, Collider2D, director, Director, misc, Label, Color } from 'cc';
const { ccclass, property, executeInEditMode } = _decorator;
@ccclass('ToolsSplit')
export default class ToolsSplit extends Component {
@property(Node)
textureRoot: Node = null;
@property(Graphics)
graphics: Graphics = null;
@property(SpriteFrame)
pic: SpriteFrame = null;
@property(Camera)
cam: Camera = null;
@property(Label)
moveStepLabel: Label = null;
private textures: SplitRender[] = [];
private startPoint: Vec2 = null;
private endPoint: Vec2 = null;
gameWidth: number = 0;
gameHeight: number = 0;
canTouch: boolean = true;
protected onLoad(): void {
}
start() {
profiler.hideStats();
this.init();
this.node.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
this.node.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
this.node.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
this.node.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
this.graphics.node.setPosition(new Vec3(-view.getVisibleSize().width / 2, -view.getVisibleSize().height / 2));
}
init() {
// let node = new Node();
// let t = node.addComponent(SplitRender);
// node.parent = this.textureRoot;
// node.layer = Layers.Enum.UI_2D;
// t.spriteFrame = this.pic;
// this.textures.push(t);
this.gameWidth = view.getVisibleSize().width;
this.gameHeight = view.getVisibleSize().height;
this.node.getComponent(UITransform).setContentSize(this.gameWidth, this.gameHeight);
}
setTextures(textures: SplitRender[]) {
for (let i = 0; i < textures.length; i++) {
const texture = textures[i];
this.textures.push(texture);
}
}
setPerTexture(texture: SplitRender) {
this.textures.push(texture);
}
onTouchStart(e: EventTouch) {
if (this.moveStepLabel.string == "0") {
return;
}
if (!this.canTouch) {
return;
}
this.startPoint = e.getUILocation();
}
onTouchMove(e: EventTouch) {
if (this.moveStepLabel.string == "0") {
return;
}
if (!this.canTouch) {
return;
}
this.graphics.clear();
this.graphics.moveTo(this.startPoint.x, this.startPoint.y);
let p = e.getUILocation();
this.graphics.lineTo(p.x, p.y);
this.graphics.stroke();
}
onTouchEnd(e: EventTouch) {
if (this.moveStepLabel.string == "0") {
return;
}
if (!this.canTouch) {
return;
}
this.canTouch = false;
this.graphics.clear();
this.endPoint = e.getUILocation();
}
cutPolygonCollider2D(originPolygon: Vec2[], startPoint: Vec2, endPoint: Vec2) {
let newPolygon = SplitRenderHelper.lineCutPolygon(startPoint, endPoint, originPolygon);
return newPolygon;
}
private doSplit() {
let h = this.pic.height, w = this.pic.width;
for (let i = 0; i < 15; i++) {
let p0 = v2(-(w / 2 + 10), (Math.random() * h) - h / 2);
let p1 = v2(w / 2 + 10, (Math.random() * h) - h / 2);
this.useLineCutPolygon(p0, p1, false);
}
for (let i = 0; i < 15; i++) {
let p0 = v2(Math.random() * w - w / 2, -(h / 2 + 10));
let p1 = v2(Math.random() * w - w / 2, (h / 2 + 10));
this.useLineCutPolygon(p0, p1, false);
}
}
private useLineCutPolygon(p0: Vec2, p1: Vec2, isWorld = true) {
for (let i = this.textures.length - 1; i >= 0; i--) {
let texture = this.textures[i];
let pa = p0.clone();
let pb = p1.clone();
if (isWorld) {
let mat = texture.node.worldMatrix.clone().invert();
pa = pa.transformMat4(mat);
pb = pb.transformMat4(mat);
}
let polygons = SplitRenderHelper.lineCutPolygon(pa, pb, texture.polygon);
if (polygons.length <= 0) {
console.log("No Polygon")
continue
};
this.splitTexture(texture, polygons);
}
}
private splitTexture(texture: SplitRender, polygons: Vec2[][]) {
texture.polygon = polygons[0];
let newnode: Node = null;
for (let i = 1; i < polygons.length; i++) {
let node = new Node("Split");
newnode = node;
node.layer = Layers.Enum.UI_2D;
let t = node.addComponent(SplitRender);
node.parent = this.textureRoot;
node.setPosition(new Vec3(texture.node.position.x, texture.node.position.y));
node.setRotationFromEuler(texture.node.eulerAngles.x, texture.node.eulerAngles.y, texture.node.eulerAngles.z);
t.spriteFrame = texture.spriteFrame;
t.polygon = polygons[i];
this.textures.push(t);
}
}
onClickFly() {
for (let i = 0; i < this.textures.length; i++) {
let center = this.getPolygonCenter(this.textures[i].polygon);
let dir = center.normalize();
tween(this.textures[i].node).by(0.5, { position: new Vec3(dir.x * 100, dir.y * 100, 0) }).start();
}
}
onClickReset() {
for (let i = 0; i < this.textures.length; i++) {
let center = this.getPolygonCenter(this.textures[i].polygon);
let dir = center.normalize();
tween(this.textures[i].node).by(0.5, { position: new Vec3(-dir.x * 100, -dir.y * 100, 0) }).call(() => {
if (i === this.textures.length - 1) {
this.textureRoot.destroyAllChildren();
this.textureRoot.removeAllChildren();
this.textures = [];
this.init();
}
}).start();
}
}
onFallDown() {
for (let i = 0; i < this.textures.length; i++) {
let center = this.getPolygonCenter(this.textures[i].polygon);
tween(this.textures[i].node).delay((center.y + this.pic.height) / this.pic.height).by(2, { position: new Vec3(0, -500, 0) }, { easing: 'circIn' }).start();
}
}
onResetFallDown() {
this.textureRoot.destroyAllChildren();
this.textureRoot.removeAllChildren();
this.textures = [];
this.init();
}
private getPolygonCenter(polygon: Vec2[]) {
let x = 0, y = 0;
for (let i = 0; i < polygon.length; i++) {
x += polygon[i].x;
y += polygon[i].y;
}
x = x / polygon.length;
y = y / polygon.length;
return v2(x, y)
}
getTextureList() {
return this.textures;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "0ad0fcaa-5dac-47ec-8cc4-e0c36da51296",
"files": [],
"subMetas": {},
"userData": {}
}

12
assets/script/ads.meta Normal file
View File

@@ -0,0 +1,12 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "e76c6dab-039b-4721-ab97-2fa8baa6cdca",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,71 @@
import { PlatformManager, releaseType } from "../manager/PlatformManager";
import { AdManager_H5 } from "./AdManager_H5";
import { AdManager_WX } from "./AdManager_WX";
import { AdManager_ZJ } from "./AdManager_ZJ";
export class AdManager {
private static isLoadAd: boolean = false; //是否加载过广告了
/** 初始化所有广告 */
public static initAds() {
}
/** 加载所有广告 */
public static loadAds() {
if (this.isLoadAd) { return; }
if (PlatformManager.releaseType == releaseType.applet_ziJie) {
AdManager_ZJ._ins.loadAllAd();
} else if (PlatformManager.releaseType == releaseType.applet_wechat) {
AdManager_WX._ins.loadAllAd();
}
this.isLoadAd = true;
}
/** Banner 广告 */
public static showBanner() {
if (PlatformManager.releaseType == releaseType.applet_ziJie) {
AdManager_ZJ._ins.showBanner();
} else if (PlatformManager.releaseType == releaseType.applet_wechat) {
AdManager_WX._ins.showBanner();
}
}
/** 隐藏 Banner 广告 */
public static hideBanner() {
if (PlatformManager.releaseType == releaseType.applet_ziJie) {
AdManager_ZJ._ins.hideBanner();
} else if (PlatformManager.releaseType == releaseType.applet_wechat) {
AdManager_WX._ins.hideBanner();
}
}
/** 插屏广告 */
public static showIntersAd() {
let rType = PlatformManager.releaseType;
if(rType == releaseType.test_TEST){ return; }
if(rType == releaseType.h5_weiSan || rType == releaseType.h5_common){
AdManager_H5._ins.showIntersAd();
}else if(rType == releaseType.applet_ziJie){
AdManager_ZJ._ins.showIntersAd();
}else if(PlatformManager.releaseType == releaseType.applet_wechat){
AdManager_WX._ins.showIntersAd();
}
}
/**
* 播放视频激励广告
* @param finishBack 视频完成回调
* @param errorBack 视频失败回调
*/
public static showVideoAd(finishBack?: () => void, errorBack?: () => void) {
let rType = PlatformManager.releaseType
if(rType == releaseType.test_TEST){ //测试直接成功
if(finishBack){ finishBack(); };
return;
}
if(rType == releaseType.h5_weiSan || rType == releaseType.h5_common){
AdManager_H5._ins.showVideoAd(finishBack,errorBack);
}else if( rType == releaseType.applet_ziJie ){
AdManager_ZJ._ins.showVideoAd(finishBack,errorBack);
}else if(PlatformManager.releaseType == releaseType.applet_wechat){
AdManager_WX._ins.showVideoAd(finishBack,errorBack);
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "866659ab-db45-47c7-8563-ac336d1d40bb",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,63 @@
export class AdManager_H5 {
/** 单例模式 */
private static _instance: AdManager_H5 = new AdManager_H5();
private constructor() { }
public static get _ins() {
return this._instance;
}
private videoBack:() => void | null; //视频广告完成回调
private errorBack:() => void | null; //视频广告失败回调
/** 播放插屏广告 */
public showIntersAd(){
adBreak({
type: 'next',
name: 'restart-game'
});
}
/** 播放视频广告 */
public showVideoAd( finishBack?:() => void,errorBack?:() => void ){
this.videoBack = null;
this.errorBack = null;
if(finishBack){
this.videoBack = finishBack;
}if(errorBack){
this.errorBack = errorBack;
}
adBreak({
type: 'reward',
name: 'dasdf',
beforeReward: (showAdFn: any) => {
showAdFn();
},
adDismissed: () => {
this.errorVideo();
},
adViewed: () => {
this.finishVideo();
},
adBreakDone: (placementInfo: any) => {
// this.finishVideo();
},
});
}
/** 视频播放完成 */
finishVideo(){
if(this.videoBack){
this.videoBack();
}
this.videoBack = null;
this.errorBack = null;
}
/** 视频播放失败 */
errorVideo(){
if(this.errorBack){
this.errorBack();
}
this.videoBack = null;
this.errorBack = null;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "d5436ac1-1463-4bfb-be45-9f23ca0efe0f",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,238 @@
import { Asset, resources } from "cc";
import { PlatformManager, releaseType } from "../manager/PlatformManager";
export class AdManager_WX {
/** 单例模式 */
private static _instance: AdManager_WX = new AdManager_WX();
private constructor() { }
public static get _ins() {
return this._instance;
}
/** appID */
app_id: string = "wx2c68756779fd1535";
/** bannerID */
ad_banner_id: string = "adunit-64d55f82495fec1d";
/** 插屏ID */
ad_inter_id: string = "adunit-3e889a9a21c90ec2";
/** 激励ID */
ad_video_id: string = "adunit-fc615ecff3915673";
ad_banner: any = null; //banner广告;
ad_video: any = null; //视频广告;
gameName:string = "魔方拆拆乐"; //游戏名字
videoBack: () => void | null; //视频广告完成回调
errorBack: (isOut: boolean) => void | null; //视频广告失败回调
/** 加载或者初始化所有广告 */
loadAllAd() {
if (PlatformManager.releaseType != releaseType.applet_wechat) { return; }
// this.initBanner();
this.initVideoAd();
this.addShareMenu();
};
/** 初始化加载 视频广告 */
private initVideoAd() {
let self = this;
this.ad_video = wx.createRewardedVideoAd({ adUnitId: self.ad_video_id })
this.ad_video.load().then(() => {
console.log("视频广告加载完成!");
}).catch((err: any) => {
console.log("视频加载失败:" + err.errMsg)
});
this.ad_video.onError((err: any) => {
this.errorVideo();
console.log("视频出错:" + err);
});
this.ad_video.onClose((res: any) => {
// 用户点击了【关闭广告】按钮
// 小于 2.1.0 的基础库版本res 是一个 undefined
if (res && res.isEnded || res === undefined) {
console.log("正常播放结束,可以下发游戏奖励");
this.finishVideo();
} else {
this.errorVideo(true);
console.log("播放中途退出,不下发游戏奖励");
}
});
};
/** 初始化加载 banner */
private initBanner() {
var self = this;
let bannerAd = wx.createBannerAd({
adUnitId: self.ad_banner_id,
style: { left: 0, top: 0, width: 720 }
}
)
bannerAd.onResize((res: any) => {
var phone = wx.getSystemInfoSync();
var w = phone.screenWidth / 2;
var h = phone.screenHeight;
bannerAd.style.left = w - bannerAd.style.realWidth / 2 + 0.1;
bannerAd.style.top = h - bannerAd.style.realHeight + 0.1;
bannerAd.style.width = phone.screenWidth * 0.8;
console.log("bannerAd加载成功");
})
bannerAd.onError((err: any) => {
console.log(err)
});
this.ad_banner = bannerAd;
};
/** 显示Banner广告 */
public showBanner() {
if (PlatformManager.releaseType != releaseType.applet_wechat) { return; }
if (!this.ad_banner) {
this.initBanner();
// return;
};
this.ad_banner.show();
};
/** 隐藏Banner广告 */
public hideBanner() {
if (PlatformManager.releaseType != releaseType.applet_wechat) { return; }
if (!this.ad_banner) { return; }
this.ad_banner.hide();
};
/** 播放插屏广告 */
public showIntersAd() {
// return;
if (PlatformManager.releaseType != releaseType.applet_wechat) { return; }
// 定义插屏广告
let interstitialAd = null;
interstitialAd = wx.createInterstitialAd({
adUnitId: this.ad_inter_id
});
interstitialAd.show().catch((err: any) => {
console.error(err)
});
console.log("showIntersAd");
}
/** 播放视频广告 成功回调 失败回调 */
public showVideoAd(finishBack?: () => void, errorBack?: () => void) {
if (PlatformManager.releaseType != releaseType.applet_wechat) { return; }
// wx.showToast({
// title: '暂无视频广告!',
// icon: 'none',
// duration: 1500//持续的时间
// })
// return;
this.videoBack = null;
this.errorBack = null;
if (finishBack) {
this.videoBack = finishBack;
} if (errorBack) {
this.errorBack = errorBack;
}
if (!this.ad_video) {
this.initVideoAd();
}
var self = this;
this.ad_video.show().then(() => {
console.log("广告显示成功");
}).catch((err: any) => {
this.errorVideo();
console.log("广告组件出现问题", err);
// 再手动加载一次
this.ad_video.load().then(() => {
console.log("手动加载成功");
this.ad_video.show(); // 加载成功后需要再显示广告
});
});
}
/** 视频播放完成 */
finishVideo() {
if (this.videoBack) {
this.videoBack();
}
this.videoBack = null;
this.errorBack = null;
}
/** 视频播放失败 isOut 是否中途退出*/
errorVideo(isOut: boolean = false) {
if (this.errorBack) {
this.errorBack(isOut);
}
this.videoBack = null;
this.errorBack = null;
};
/** 分享视频 shareTitle 分享内容 imgUrl 分享图路径 resource下的 */
shareFriends(shareTitle?: string , imgUrl:string = "share") {
if (PlatformManager.releaseType != releaseType.applet_wechat) { return; }
var self = this;
// wx.showShareMenu();
shareTitle = shareTitle || '快来大战25个回合~~';
resources.load(imgUrl, Asset , ( err:Error , asset: Asset ) => {
wx.shareAppMessage({
title: shareTitle ,
imageUrl: asset.nativeUrl ,
success(res:any) {
console.log("分享成功:", res);
return;
},
fail(res:any) {
// 转发失败
wx.showToast({
title: '分享失败',
icon: 'none',
duration: 1500//持续的时间
})
return;
}
});
} );
};
/** 添加右上角三个点分享 */
addShareMenu(shareTitle?: string ,imgUrl:string = "share") {
if (PlatformManager.releaseType != releaseType.applet_wechat) { return; }
shareTitle = shareTitle || ""
wx.showShareMenu( {
withShareTicket: true,
menus: ['shareAppMessage', 'shareTimeline'],
} );
console.log("添加右上角三个点分享");
resources.load( imgUrl , Asset , (err:Error , asset: Asset) => {
console.log(asset.nativeUrl);
wx.onShareAppMessage(() => {
return {
title: this.gameName,
imageUrl: asset.nativeUrl // 图片 URL
}
});
} );
};
/** 跳转其他更多游戏 */
toMoreGame() {
if (PlatformManager.releaseType != releaseType.applet_wechat) { return; }
wx.navigateToMiniProgram({
appId: 'wxda02fde13d108205', //其他游戏的appid
path: 'page/index/index?id=123',
extraData: {
foo: 'bar'
},
envVersion: 'develop',
success(res: any) {
}
});
};
/** 震动 */
vibrate(){
if (PlatformManager.releaseType != releaseType.applet_wechat) { return; }
// console.log("vibrateShort")
wx.vibrateShort({
type:'medium'
})
}
/** 添加桌面功能 */
addTable() {
}
/** 是否已经添加过桌面了 */
isAddTable() {
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "40cc7426-fd89-45f9-9c7d-70068f7a2949",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,296 @@
import { PlatformManager, releaseType } from "../manager/PlatformManager";
export class AdManager_ZJ {
/** 单例模式 */
private static _instance: AdManager_ZJ = new AdManager_ZJ();
private constructor() { }
public static get _ins() {
return this._instance;
}
/** appID */
app_id: string = "tta582d33f3abc001a02";
/** 插屏ID */
ad_inter_id: string = "2mrqugugp7e9f29c36";
/** 激励ID */
ad_video_id: string = "9e617go08ho2094lcq";
/** bannerID */
ad_banner_id: string = "5qmmte1agjm5fjd50r";
/** 录屏相关 */
recorder: any = null;
videoPath: any = null; //录屏路径
videoTimer: any = null; //录屏计时器 300s 内要停止录屏
ad_banner: any = null; //banner广告;
ad_video: any = null; //视频广告;
videoBack: () => void | null; //视频广告完成回调
errorBack: (isOut:boolean) => void | null; //视频广告失败回调
/** 加载或者初始化所有广告 */
loadAllAd() {
if (PlatformManager.releaseType != releaseType.applet_ziJie) { return; }
this.initBanner();
this.initVideoAd();
};
/** 初始化加载 视频广告 */
private initVideoAd() {
let self = this;
this.ad_video = tt.createRewardedVideoAd({
adUnitId: self.ad_video_id,
});
this.ad_video.onLoad(() => {
console.log("视频广告加载完成!");
});
this.ad_video.load();
};
/** 初始化加载 banner */
private initBanner() {
let self = this;
let iphoneData = tt.getSystemInfoSync();
var bannerData = {
left: iphoneData.screenWidth,//广告位区域左上角横坐标
top: iphoneData.screenHeight,//广告位区域左上角纵坐标
width: iphoneData.screenWidth,//广告位区域宽度
}
this.ad_banner = tt.createBannerAd({
adUnitId: self.ad_banner_id,
adIntervals: 20,
style: bannerData,
});
};
/** 显示Banner广告 */
public showBanner() {
if (PlatformManager.releaseType != releaseType.applet_ziJie) { return; }
if (!this.ad_banner) {
this.initBanner();
// return;
};
if (this.ad_banner.show) {
this.ad_banner.show();
}
this.ad_banner.onLoad( () => {
this.ad_banner.show().then(() => {
console.log("广告显示成功");
}).catch((err: any) => {
console.log("广告组件出现问题", err);
this.ad_banner = null;
});
} );
};
/** 隐藏Banner广告 */
public hideBanner() {
if (PlatformManager.releaseType != releaseType.applet_ziJie) { return; }
if (!this.ad_banner) { return; }
this.ad_banner.hide();
};
/** 播放插屏广告 */
public showIntersAd() {
if (PlatformManager.releaseType != releaseType.applet_ziJie) { return; }
let self = this;
var interstitialAd = tt.createInterstitialAd({
adUnitId: self.ad_inter_id,
});
interstitialAd.load()
.then(() => {
interstitialAd.show().then(() => {
console.log("插屏广告展示成功");
});
}).catch((err: any) => {
console.log(err);
});
console.log("showIntersAd");
}
/** 播放视频广告 成功回调 失败回调 */
public showVideoAd(finishBack?: () => void, errorBack?: () => void) {
if (PlatformManager.releaseType != releaseType.applet_ziJie) { return; }
this.videoBack = null;
this.errorBack = null;
if (finishBack) {
this.videoBack = finishBack;
} if (errorBack) {
this.errorBack = errorBack;
}
if(!this.ad_video){
this.initVideoAd();
}
this.ad_video.show().then(() => {
console.log("广告显示成功");
}).catch((err: any) => {
this.errorVideo();
console.log("广告组件出现问题", err);
// 再手动加载一次
this.ad_video.load().then(() => {
console.log("手动加载成功");
this.ad_video.show(); // 加载成功后需要再显示广告
});
});
this.ad_video.onClose((res: any) => {
if (res.isEnded) {
console.log("获取奖励")
this.finishVideo();
} else {
console.log("没有观看完毕--")
this.errorVideo(true);
}
if (res.count) {
//在支持多例模式的版本上会返回该字段并且是否返回该字段与multiton是否为true无关
//判断观看了几次广告
}
});
}
/** 视频播放完成 */
finishVideo() {
if (this.videoBack) {
this.videoBack();
}
this.videoBack = null;
this.errorBack = null;
}
/** 视频播放失败 isOut 是否中途退出*/
errorVideo(isOut:boolean = false) {
if (this.errorBack) {
this.errorBack(isOut);
}
this.videoBack = null;
this.errorBack = null;
};
/** 开始录屏 */
createVideoScreen() {
if (PlatformManager.releaseType != releaseType.applet_ziJie) { return; }
if (this.videoTimer !== null) {
clearTimeout(this.videoTimer);
this.videoTimer = null;
}
var self = this;
this.recorder = tt.getGameRecorderManager();
this.recorder.onStart((s: any) => {
console.log("开始录屏:", s);
});
this.recorder.onError((s: any) => {
console.log("录屏错误:", s);
});
this.recorder.start({
duration: 300
});
this.videoTimer = setTimeout(() => {
self.stopVideoScreen();
}, 1000 * 280); //280s 后停止录屏
};
/** 停止录屏 */
stopVideoScreen() {
if (PlatformManager.releaseType != releaseType.applet_ziJie) { return; }
if (this.videoTimer !== null) {
clearTimeout(this.videoTimer);
this.videoTimer = null;
}
var self = this;
console.log(this.recorder)
if (!this.recorder || !this.recorder.stop) { return; }
this.recorder.onStop((s: any) => {
self.videoPath = s.videoPath;
});
this.recorder.stop();
};
/** 分享视频 shareTopics 分享话题 shareTitle 分享内容 */
shareScreenVideo(shareTopics?: [string], shareTitle?: string) {
if (PlatformManager.releaseType != releaseType.applet_ziJie) { return; }
shareTopics = shareTopics || ['大战小黑'];
shareTitle = shareTitle || '来大战25个回合~~';
var self = this;
tt.shareAppMessage({
channel: 'video',
title: shareTitle,
imageUrl: '',
query: '',
extra: {
videoPath: self.videoPath, // 可用录屏得到的视频地址
videoTopics: shareTopics
},
success() {
// EventManager.dispachEvent(EventData.SHARE_SUCESS); //抛出分享成功的事件
console.log('分享视频成功');
},
fail(e: any) {
console.log('分享视频失败' + e);
}
});
console.log("shareScreenVideo")
};
/** 添加更多游戏 */
addMoreGame( ){
if (PlatformManager.releaseType != releaseType.applet_ziJie) { return; }
setTimeout( () => {
let iphoneData = tt.getSystemInfoSync();
console.log(iphoneData);
tt.showGridGamePanel({
query: { //Json 格式
'花花僵尸': 'ttd12aa7974e142ca002'
// '套个甜甜圈神龙版': 'tt3fa54918a09c3fc802',
// '山楂串': 'ttcf15b9a8502cccbb02',
// '合成大西瓜原创版': 'tt425534e8dd6e24d1'
},
type: 'one', // 'four', 'two'
size: 'medium',
position: {
top: iphoneData.screenHeight / 2 - 70,
left: iphoneData.screenWidth - 70,
},
fail(res:any) {
console.log(res);
}
});
}, 100 );
};
/** 隐藏更多游戏 */
hideMoreGame() {
if (PlatformManager.releaseType != releaseType.applet_ziJie) { return; }
tt.hideGridGamePanel();
};
/** 添加桌面功能 */
addTable(){
if (PlatformManager.releaseType != releaseType.applet_ziJie) { return; }
tt.addShortcut( {
success: function (res:any) {
console.log("添加桌面成功!" + res);
// EventManager.dispachEvent(EventData.ADD_TABLE_SUCESS);
},
fail: function (res:any) {
console.log("添加桌面失败!" + res);
}
} );
}
/** 是否已经添加过桌面了 */
isAddTable(){
if (PlatformManager.releaseType != releaseType.applet_ziJie) { return; }
tt.checkShortcut({
success: function (res:any) {
console.log(res.status);
if (res.status.exist) {
console.log("已经添加桌面了")
}
},
fail: function (res:any) {
}
});
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "f553f7d0-d48b-43f6-856a-e752bf4c4ac0",
"files": [],
"subMetas": {},
"userData": {}
}

12
assets/script/common.meta Normal file
View File

@@ -0,0 +1,12 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "48d0041f-b88c-4aee-a367-495e14e1b9d0",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,427 @@
type index = {
x: number,
y: number
}
/**
* AStar类用于实现A*寻路算法。
*/
export class AStar {
private map: any[][] = []; // 地图数组
private weightMap: number[][] = null; // 权重地图数组
private openlist: index[] = []; // 开放列表,存储待评估的节点
private closelist: index[] = []; // 封闭列表,存储已评估的节点
private parentMap: Map<index, index>; // 父节点映射,用于回溯路径
private gcostMap: Map<index, number>; // g成本映射记录从起点到当前节点的成本
private hcostMap: Map<index, number>; // h成本映射记录从当前节点到终点的估算成本
private startValue: number = 0;
private endValue: number = 0;
private pathValue: number = 0;
private isEightDirections: boolean = false;
private neighborMap: Map<index, index[]>;
constructor() {
this.openlist = [];
this.closelist = [];
this.parentMap = new Map();
this.gcostMap = new Map();
this.hcostMap = new Map();
}
/**
* 设置地图。
* @param map 二维数组表示的地图
*/
setMap(map: number[][]) {
this.map = map;
}
/**
* 设置权重地图。
* @param weightMap 二维数组表示的权重地图
*/
setWeightMap(weightMap: number[][]) {
this.weightMap = weightMap;
}
setValue(startValue: number, endValue: number, pathValue: number) {
this.startValue = startValue;
this.endValue = endValue;
this.pathValue = pathValue;
}
setEightDirections(isEightDirections: boolean) {
this.isEightDirections = isEightDirections;
}
setNeighborMap(neighborMap: Map<index, index[]>) {
this.neighborMap = neighborMap;
}
/**
* 在给定范围内根据指定类型搜索从起点到终点的路径。
* @param start 起点索引
* @param end 终点索引
* @param type 搜索类型(可能影响启发式函数的选择)
* @returns 返回找到的路径数组,如果无路径则返回空数组
*/
search(start: index, end: index, type: number) {
this.init(); // 初始化搜索环境
if (this.map[start.y][start.x] !== this.startValue ||
this.map[end.y][end.x] !== this.endValue) {
console.log("Invalid start or end point");
return [];
}
// 初始化起点信息,并将其加入开放列表
this.openlist.push(start);
this.gcostMap.set(start, 0); // 起点到起点的距离为0
this.hcostMap.set(start, this.getHeuristicCost(start, end, type)); // 起点到终点的启发式成本
this.parentMap.set(start, null); // 起点的父节点设置为null
// 循环直到开放列表为空,即所有可能的路径都被考虑
while (this.openlist.length > 0) {
const current = this.getMinCostNode(this.openlist); // 从开放列表中选择成本最小的节点
if (!current) {
this.openlist.splice(this.openlist.indexOf(current), 1);
continue;
}
// 判断是否到达终点
if (this.isArrived(current, end)) {
return this.reconstructPath(end); // 如果到达终点,返回路径重构的结果
} else {
// 从开放列表移除当前节点,将其加入封闭列表
this.openlist.splice(this.openlist.indexOf(current), 1);
this.closelist.push(current);
// 遍历当前节点的所有邻居节点
const neighbors = this.getNeighbors(current);
for (const neighbor of neighbors) {
const hCost = this.getHeuristicCost(neighbor, end, type); // 计算邻居节点到终点的启发式成本
const gCost = this.gcostMap.get(current) + this.getGCost(current, neighbor, type); // 计算当前节点到邻居节点的实际成本
const totalCost = gCost + hCost; // 计算总成本
// 根据邻居节点的状态进行处理
if (!this.isInClosedList(neighbor)) {
if (!this.isInOpenList(neighbor)) {
this.openlist.push(neighbor); // 如果邻居未在开放列表中,将其加入
this.updateCost(neighbor, gCost, hCost); // 更新邻居节点的成本信息
this.updateParent(neighbor, current); // 更新邻居节点的父节点信息
} else {
// 如果邻居已在开放列表中,但通过当前节点发现更优路径
if (totalCost < this.getCost(neighbor)) {
this.updateCost(neighbor, gCost, hCost); // 更新成本信息
this.updateParent(neighbor, current); // 更新父节点信息
}
}
}
}
}
}
console.log("No path found.");
return []; // 如果无法找到路径,返回空数组
}
/**
* 初始化算法状态。
*/
private init() {
// 重置所有列表和映射
this.openlist = [];
this.closelist = [];
this.parentMap = new Map();
this.gcostMap = new Map();
this.hcostMap = new Map();
}
/**
* 获取当前节点的邻居。
* @param current 当前节点坐标
* @returns 邻居节点数组
*/
private getNeighbors(current: index) {
if (this.neighborMap.size === 0) {
const neighbors: index[] = [];
// 尝试获取四个方向的邻居节点
const upindex = { x: current.x, y: current.y + 1 };
const downindex = { x: current.x, y: current.y - 1 };
const leftindex = { x: current.x - 1, y: current.y };
const rightindex = { x: current.x + 1, y: current.y };
const upleftindex = { x: current.x - 1, y: current.y + 1 };
const uprightindex = { x: current.x + 1, y: current.y + 1 };
const downleftindex = { x: current.x - 1, y: current.y - 1 };
const downrightindex = { x: current.x + 1, y: current.y - 1 };
// 如果合法则加入邻居列表
if (this.isValidIndex(upindex) && (this.map[upindex.y][upindex.x] == this.pathValue ||
this.map[upindex.y][upindex.x] == this.endValue)) {
neighbors.push(upindex);
}
if (this.isValidIndex(downindex) && (this.map[downindex.y][downindex.x] == this.pathValue ||
this.map[downindex.y][downindex.x] == this.endValue)) {
neighbors.push(downindex);
}
if (this.isValidIndex(leftindex) && (this.map[leftindex.y][leftindex.x] == this.pathValue ||
this.map[leftindex.y][leftindex.x] == this.endValue)) {
neighbors.push(leftindex);
}
if (this.isValidIndex(rightindex) && (this.map[rightindex.y][rightindex.x] == this.pathValue ||
this.map[rightindex.y][rightindex.x] == this.endValue)) {
neighbors.push(rightindex);
}
if (this.isEightDirections) {
if (this.isValidIndex(upleftindex) && (this.map[upleftindex.y][upleftindex.x] == this.pathValue ||
this.map[upleftindex.y][upleftindex.x] == this.endValue)) {
neighbors.push(upleftindex);
}
if (this.isValidIndex(downleftindex) && (this.map[downleftindex.y][downleftindex.x] == this.pathValue ||
this.map[downleftindex.y][downleftindex.x] == this.endValue)) {
neighbors.push(downleftindex);
}
if (this.isValidIndex(downrightindex) && (this.map[downrightindex.y][downrightindex.x] == this.pathValue ||
this.map[downrightindex.y][downrightindex.x] == this.endValue)) {
neighbors.push(downrightindex);
}
if (this.isValidIndex(uprightindex) && (this.map[uprightindex.y][uprightindex.x] == this.pathValue ||
this.map[uprightindex.y][uprightindex.x] == this.endValue)) {
neighbors.push(uprightindex);
}
}
return neighbors;
} else {
const _neighbors = this.neighborMap.get(current);
const neighbors: index[] = [];
for (const neighbor of _neighbors) {
if (this.map[neighbor.y][neighbor.x] == this.pathValue ||
this.map[neighbor.y][neighbor.x] == this.endValue) {
neighbors.push(neighbor);
}
}
return neighbors;
}
}
/**
* 计算从当前节点到邻居节点的实际成本
* @param current 当前节点
* @param neighbor 邻居节点
* @param type 距离计算类型
* @returns 返回从当前节点到邻居节点的成本
*/
private getGCost(current: index, neighbor: index, type: number = 0) {
let baseCost = 0;
// 计算基础距离成本
switch (type) {
case 0: {
// 计算曼哈顿距离
baseCost = Math.abs(current.x - neighbor.x) + Math.abs(current.y - neighbor.y);
break;
}
case 1: {
// 计算欧几里得距离
baseCost = Math.sqrt(Math.pow(current.x - neighbor.x, 2) + Math.pow(current.y - neighbor.y, 2));
break;
}
case 2: {
// 计算切比雪夫距离
const x = Math.abs(current.x - neighbor.x);
const y = Math.abs(current.y - neighbor.y);
const minxy = Math.min(x, y);
baseCost = x + y + (Math.sqrt(2) - 2) * minxy;
break;
}
}
// 如果存在权重地图,应用权重
if (this.weightMap && this.isValidIndex(neighbor)) {
const weight = this.weightMap[neighbor.y][neighbor.x];
return baseCost * weight;
}
return baseCost;
}
/**
* 根据给定的起始点和目标点,以及类型参数,计算启发式成本。
* @param start 起始点的索引,包含 x 和 y 坐标。
* @param end 目标点的索引,包含 x 和 y 坐标。
* @param type 计算成本的类型0 表示曼哈顿距离1 表示欧几里得距离2 表示切比雪夫距离,默认为 0。
* @returns 返回根据指定类型计算出的启发式成本。
*/
private getHeuristicCost(start: index, end: index, type: number = 0) {
switch (type) {
case 0: {
// 计算曼哈顿距离
return Math.abs(start.x - end.x) + Math.abs(start.y - end.y);
}
case 1: {
// 计算欧几里得距离
return Math.sqrt(Math.pow(start.x - end.x, 2) + Math.pow(start.y - end.y, 2));
}
case 2: {
// 计算切比雪夫距离
const x = Math.abs(start.x - end.x);
const y = Math.abs(start.y - end.y);
const minxy = Math.min(x, y);
return x + y + (Math.sqrt(2) - 2) * minxy;
}
}
}
/**
* 检查节点是否在封闭列表中。
* @param node 节点坐标
* @returns {boolean} 如果节点在封闭列表中返回true否则返回false
*/
private isInClosedList(node: index): boolean {
// 遍历封闭列表查找节点
let _index = -1;
for (let i = 0; i < this.closelist.length; i++) {
if (this.closelist[i].x == node.x && this.closelist[i].y == node.y) {
_index = i;
break;
}
}
return _index != -1;
}
/**
* 检查节点是否在开放列表中。
* @param node 节点坐标
* @returns {boolean} 如果节点在开放列表中返回true否则返回false
*/
private isInOpenList(node: index): boolean {
// 遍历开放列表查找节点
let _index = -1;
for (let i = 0; i < this.openlist.length; i++) {
if (this.openlist[i].x == node.x && this.openlist[i].y == node.y) {
_index = i;
break;
}
}
return _index != -1;
}
/**
* 获取节点的成本。
* @param node 节点坐标
* @returns 节点的总成本
*/
private getCost(node: index): number {
let gcost = 0;
let hcost = 0;
this.gcostMap.forEach((value, key) => {
if (key.x == node.x && key.y == node.y) {
gcost = value;
}
});
this.hcostMap.forEach((value, key) => {
if (key.x == node.x && key.y == node.y) {
hcost = value;
}
});
return gcost + hcost;
}
/**
* 获取开放列表中成本最小的节点。
* @param list 开放列表
* @returns 成本最小的节点
*/
private getMinCostNode(list: index[]): index {
let _minindex = -1;
let _mincost = Number.MAX_SAFE_INTEGER;
// 寻找成本最小的节点
for (let i = 0; i < list.length; i++) {
const cost = this.getCost(list[i]);
if (cost < _mincost) {
_minindex = i;
_mincost = cost;
}
}
return list[_minindex];
}
/**
* 更新节点的成本和父节点信息。
* @param node 节点坐标
* @param gcost 从起点到当前节点的实际成本
* @param hcost 从当前节点到终点的估算成本
*/
private updateCost(node: index, gcost: number, hcost: number) {
this.gcostMap.set(node, gcost);
this.hcostMap.set(node, hcost);
}
/**
* 更新节点的父节点。
* @param node 节点坐标
* @param parent 父节点坐标
*/
private updateParent(node: index, parent: index) {
const existingparent = this.parentMap.get(node);
// 仅当新的父节点成本更低时更新
if (!existingparent || this.getCost(parent) < this.getCost(existingparent)) {
this.parentMap.set(node, parent);
}
}
/**
* 根据父节点映射重构路径。
* @param endPoint 终点坐标
* @returns 从起点到终点的路径
*/
private reconstructPath(endPoint: index) {
const path = [endPoint];
let currentnode = endPoint;
// 逆向回溯路径
while (currentnode) {
let parent = null;
this.parentMap.forEach((value, key) => {
if (currentnode.x == key.x && currentnode.y == key.y) {
parent = value;
}
})
if (parent) {
path.unshift(parent);
currentnode = parent;
} else {
currentnode = null;
}
}
return path;
}
/**
* 检查给定的索引是否为有效索引。
* @param index 一个包含x和y属性的对象代表要检查的索引位置。
* @returns 返回一个布尔值如果索引位置有效则为true否则为false。
*/
private isValidIndex(index: index): boolean {
return index.x >= 0 && index.x < this.map[0].length && index.y >= 0 && index.y < this.map.length;
}
/**
* 判断当前节点是否到达了目标节点。
* @param node 一个包含x和y属性的对象代表当前节点的位置。
* @param end 一个包含x和y属性的对象代表目标节点的位置。
* @returns 返回一个布尔值如果当前节点已到达目标节点则为true否则为false。
*/
private isArrived(node: index, end: index) {
// 比较当前节点和目标节点的坐标,判断是否已经到达目标位置
return node.x == end.x && node.y == end.y;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "0da25f34-2c17-4ebe-a9d6-1a4c2c03a99b",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,185 @@
import { AudioClip, Node, AudioSource, tween } from "cc";
import { ResModel } from "../model/ResModel";
import { LoadTools } from "./LoadTools";
/** 声音相关工具类 */
export class AudioTools {
/** 单例模式 */
private static _instance: AudioTools = new AudioTools();
private constructor() { }
public static get _ins() {
return this._instance;
}
private audioDic: Map<string, AudioSource> = new Map();
private audioVolumeDic: Map<string, number> = new Map();
private audioPermanentDic: Map<AudioSource, boolean> = new Map();
bgAudio: AudioSource; //背景音乐
isPlayAudio: boolean = true; //是否播放音效
isPlayBG: boolean = true; //是否播放音乐
/** 播放背景音乐 */
playBG(audioUrl: string, value = 0.5) {
if (!this.isPlayBG) { return; }
if (ResModel._ins.AudioClipDic.get(audioUrl) != null) {
this.bgAudio = this.newAudioSource(ResModel._ins.AudioClipDic.get(audioUrl), value, true);
this.audioDic.set(audioUrl, this.bgAudio);
this.audioVolumeDic.set(audioUrl, value);
this.audioPermanentDic.set(this.bgAudio, true);
return this.bgAudio;
} else {
LoadTools._ins.loadResAny('music/' + audioUrl + '.mp3', AudioClip, (audio: AudioClip) => {
this.bgAudio = this.newAudioSource(audio, value, true);
this.audioDic.set(audioUrl, this.bgAudio);
this.audioVolumeDic.set(audioUrl, value);
this.audioPermanentDic.set(this.bgAudio, true);
return this.bgAudio;
});
}
};
/**
* 播放游戏音效
* @param audioUrl 音效文件的res/music/的路径 或者 resArr拖动的名字
* @param value 音量大小
* @param isLoop 是否循环播放
* @param isPermanent 是否永久播放
*/
playAudio(audioUrl: string, value = 0.5, isLoop = false, isPermanent = false) {
if (!this.isPlayAudio) { return; }
if (ResModel._ins.AudioClipDic.get(audioUrl) != null) {
let audioE = this.newAudioSource(ResModel._ins.AudioClipDic.get(audioUrl), value, isLoop);
this.audioDic.set(audioUrl, audioE);
this.audioVolumeDic.set(audioUrl, value);
this.audioPermanentDic.set(audioE, isPermanent);
return audioE;
} else {
LoadTools._ins.loadResAny('music/' + audioUrl + '.mp3', AudioClip, (audio: AudioClip) => {
let audioE = this.newAudioSource(audio, value, isLoop);
this.audioDic.set(audioUrl, audioE);
this.audioVolumeDic.set(audioUrl, value);
this.audioPermanentDic.set(audioE, isPermanent);
return audioE;
});
}
};
/** 停止播放某个音效 */
stopAudio(audioName: string) {
let audioE = this.audioDic.get(audioName);
if (audioE != null) {
audioE.stop();
}
}
/**
* 新建一个audioSource 来播放音效
* @param audioUrl 音效文件的res/music/的路径 或者 resArr拖动的名字
* @param value 音量大小
* @param isLoop 是否循环播放
*/
playAudioSource(audioUrl: string, value = 0.5, isLoop = false) {
if (!this.isPlayAudio) { return; }
if (ResModel._ins.AudioClipDic.get(audioUrl) != null) {
return this.newAudioSource(ResModel._ins.AudioClipDic.get(audioUrl), value, isLoop);
} else {
LoadTools._ins.loadResAny(audioUrl, AudioClip, (audioE: AudioClip) => {
this.newAudioSource(audioE, value, isLoop);
});
}
}
/** 新建一个 audioSource 播放音效 */
newAudioSource(audioClip: AudioClip, value = 0.5, isLoop = false) {
if (!this.isPlayAudio) { return; }
let node = new Node();
let audioE = node.addComponent(AudioSource);
audioE.clip = audioClip;
audioE.loop = isLoop;
audioE.volume = value;
audioE.play();
if (isLoop == false) {
tween(node).delay(audioE.duration + 0.1)
.removeSelf().union().start();
}
return audioE;
};
/** 停止所有音效 */
stopAllAudio() {
this.isPlayAudio = false;
this.isPlayBG = false;
for (let audioE of this.audioDic.values()) {
audioE.stop();
}
}
/** 恢复所有音效 */
resumeAllAudio() {
for (let audioE of this.audioDic.values()) {
if (this.audioPermanentDic.get(audioE) && !audioE.playing) {
audioE.play();
}
audioE.volume = this.audioVolumeDic.get(audioE.clip.name);
}
this.isPlayAudio = true;
this.isPlayBG = true;
}
/**
* 恢复指定音效
* @param audioName 音效文件的res/music/的路径 或者 resArr拖动的名字
*/
resumeAudioByName(audioName: string) {
let audioE = this.audioDic.get(audioName);
if (audioE != null) {
if (!audioE.playing) {
audioE.play();
}
audioE.volume = this.audioVolumeDic.get(audioE.clip.name);
}
}
/**
* 设置指定音效的音量
* @param audioName 音效文件的res/music/的路径 或者 resArr拖动的名字
* @param value 音量大小
*/
setAudioVolume(audioName: string, value: number) {
let audioE = this.audioDic.get(audioName);
if (audioE != null) {
audioE.volume = value;
this.audioVolumeDic.set(audioName, value);
}
}
/**
* 设置指定音效的循环播放
* @param audioName 音效文件的res/music/的路径 或者 resArr拖动的名字
* @param isLoop 是否循环播放
*/
setAudioLoop(audioName: string, isLoop: boolean) {
let audioE = this.audioDic.get(audioName);
if (audioE != null) {
audioE.loop = isLoop;
}
}
/**
* 设置指定音效的静音
* @param audioName 音效文件的res/music/的路径 或者 resArr拖动的名字
*/
muteAudio(audioName: string) {
let audioE = this.audioDic.get(audioName);
if (audioE != null) {
audioE.volume = 0.0;
}
}
/**
* 设置所有音效的静音
*/
muteAllAudio() {
for (let audioE of this.audioDic.values()) {
audioE.volume = 0.0;
}
this.isPlayAudio = false;
this.isPlayBG = false;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "b2bc325c-94d6-4223-8d5a-918cbdadfd93",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,98 @@
class Queue<T> {
private items: T[];
constructor() {
this.items = [];
}
enqueue(item: T): void {
this.items.push(item);
}
dequeue(): T | undefined {
return this.items.shift();
}
isEmpty(): boolean {
return this.items.length === 0;
}
}
type index = {
x: number;
y: number;
}
export class BFSPathfinding {
private map: number[][];
private visited: boolean[][];
private rows: number;
private cols: number;
private directions: index[];
constructor() {
this.map = [];
this.rows = 0;
this.cols = 0;
this.visited = [];
this.directions = [];
}
setMap(map: number[][]) {
this.map = map;
this.rows = this.map.length;
this.cols = this.map[0].length;
this.visited = Array.from({ length: this.rows }, () => Array(this.cols).fill(false));
this.directions = [{ x: 0, y: -1 }, { x: 0, y: 1 }, { x: -1, y: 0 }, { x: 1, y: 0 }];
}
findPath(start: index, end: index): index[] {
if (this.map[start.y][start.x] !== 0 ||
this.map[end.y][end.x] !== 0) {
return [];
}
const queue = new Queue<index>();
const prev: index[][] = Array.from({ length: this.rows }, () => Array(this.cols).fill(null));
queue.enqueue(start);
this.visited[start.y][start.x] = true;
while (!queue.isEmpty()) {
const current = queue.dequeue()!;
if (current.x === end.x && current.y === end.y) {
return this.constructPath(prev, start, end);
}
for (const direction of this.directions) {
const nextX = current.x + direction.x;
const nextY = current.y + direction.y;
if (this.isValid(nextX, nextY) && this.map[nextY][nextX] === 0 && !this.visited[nextY][nextX]) {
queue.enqueue({ x: nextX, y: nextY });
this.visited[nextY][nextX] = true;
prev[nextY][nextX] = current;
}
}
}
return [];
}
private isValid(x: number, y: number): boolean {
return x >= 0 && x < this.cols && y >= 0 && y < this.rows;
}
private constructPath(prev: index[][], start: index, end: index): index[] {
const path: index[] = [];
let current = end;
while (current !== start) {
path.unshift(current);
current = prev[current.y][current.x]!;
}
path.unshift(start);
return path;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "8ac0c6a2-e192-4b14-8cbe-97c0f60d1c1e",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,281 @@
import { director, Node, instantiate, Prefab, Vec3, v3, SpriteFrame, Sprite, AssetManager, resources, assetManager, AudioClip, Material, Texture2D, Label } from "cc";
import { ResModel } from "../model/ResModel";
import { weiSan } from "./weiSanTools";
import { EventManager, EventData } from "../manager/EventManager";
import { PoolManager } from "../manager/PoolManager";
/** 加载资源相关工具类 */
export class LoadTools {
/** 单例模式 */
private static _instance: LoadTools = new LoadTools();
private constructor() { }
public static get _ins() {
return this._instance;
}
/**
* 加载游戏场景
* @param sceneName 加载场景的名字
* @param callFunc 加载回调
*/
loadScene(sceneName: string, callFunc: any, isClear = true) {
if (isClear) {
ResModel._ins.clearResDic();
}
director.preloadScene(sceneName, () => {
director.loadScene(sceneName, callFunc);
});
};
/**
* 加载resource 下的预制体 资源
* @param url resource 下的资源路径
* @param callBack 加载完成回调
*/
loadResPrefab(url: string, callBack?: any, parent?: Node, Pos?: Vec3) {
this.loadResAny(url, Prefab, (prefab: Prefab) => {
let clone = instantiate(prefab);
if (parent) { parent.addChild(clone) };
if (Pos) { clone.position = v3(Pos.x, Pos.y, 0) };
if (callBack != null) {
callBack(clone);
}
})
}
/**
* 加载resource 下的图片资源并渲染到节点上
* @param url resource 下的资源路径
* @param callBack 加载完成回调
*/
loadResSpriteFrame(url: string, sprite: Node, parent?: Node, Pos?: Vec3, callBack?: any) {
resources.load(url + "/spriteFrame", SpriteFrame, function (error: any, SpriteFrame: SpriteFrame) {
if (error) {
weiSan.error(error);
} else {
sprite.getComponent(Sprite).spriteFrame = SpriteFrame;
if (parent) { parent.addChild(sprite) };
if (Pos) { sprite.position = v3(Pos.x, Pos.y, 0) };
if (callBack != null) {
callBack(sprite);
}
}
});
};
/**
* 加载resource 下的游戏资源
* @param url resource 下的资源路径
* @param resType 加载资源的类型
* @param callBack 加载完成回调
*/
loadResAny(url: string, resType: any, callBack?: any) {
resources.load(url, resType, function (error: any, res: any) {
if (error) {
weiSan.error(error);
} else {
if (callBack != null) {
callBack(res);
}
if (resType == Prefab) {
ResModel._ins.PrefabDic.set(url, res);
} else if (resType == SpriteFrame) {
if (!ResModel._ins.SpriteFrameDic.has(url)) {
ResModel._ins.SpriteFrameDic.set(url, res);
}
} else if (resType == AudioClip) {
if (!ResModel._ins.AudioClipDic.has(url)) {
ResModel._ins.AudioClipDic.set(url, res);
}
} else if (resType == Material) {
if (!ResModel._ins.MaterialDic.has(url)) {
ResModel._ins.MaterialDic.set(url, res);
}
} else if (resType == Texture2D) {
if (!ResModel._ins.TextureDic.has(url)) {
ResModel._ins.TextureDic.set(url, res);
}
} else if (resType === Prefab) {
if (!ResModel._ins.PrefabDic.has(url)) {
ResModel._ins.PrefabDic.set(url, res);
}
}
}
});
}
/** 加载bundle 场景 */
loadBundleScene(bundleName: string, sceneName: string, onFinishBack?: () => void, isInScene: boolean = true) {
assetManager.loadBundle(
bundleName,
(err: any, bundle: AssetManager.Bundle) => {
if (err) {
console.log(err);
}
else {
if (!isInScene) { return; }
bundle.loadScene(sceneName, (err, scene) => {
if (onFinishBack) {
onFinishBack();
}
director.runScene(scene);
});
}
}
);
}
/**
* 分帧加载预制体节点
* @param prefabName 要加载的预制体名称
* @param perFrameNum 每帧加载的数量
* @param totalNum 总共需要加载的数量
* @param usePool 是否使用对象池默认为true
* @param callBack 对每个创建的节点进行操作的回调函数
* @returns Promise<Node[]> 返回所有创建的节点数组
*/
loadPrefabByFrame(prefabName: string, perFrameNum: number, totalNum: number, usePool: boolean = true, callBack?: (node: Node, index: number) => void): Promise<Node[]> {
return new Promise((resolve, reject) => {
// 参数验证
if (!prefabName || prefabName.trim() === '') {
const error = 'prefabName cannot be empty';
weiSan.error(error);
reject(new Error(error));
return;
}
if (perFrameNum <= 0) {
const error = 'perFrameNum must be greater than 0';
weiSan.error(error);
reject(new Error(error));
return;
}
if (totalNum <= 0) {
const error = 'totalNum must be greater than 0';
weiSan.error(error);
reject(new Error(error));
return;
}
// 触发加载开始事件
EventManager.dispatchEvent(EventData.PREFAB_LOAD_START, {
prefabName: prefabName,
totalNum: totalNum,
usePool: usePool
});
// 创建节点的函数
const createNodeFunction = (prefab?: Prefab): Node | null => {
if (usePool) {
// 使用对象池创建节点
return PoolManager._ins.getNode(prefabName);
} else {
// 使用传统方式创建节点
if (!prefab) {
weiSan.error(`Prefab is required when not using pool: ${prefabName}`);
return null;
}
return instantiate(prefab);
}
};
if (usePool) {
// 使用对象池时,直接开始分帧创建
this.startFrameBasedCreation(prefabName, perFrameNum, totalNum, usePool, callBack, createNodeFunction, resolve, reject);
} else {
// 不使用对象池时,先加载预制体资源
this.loadResAny(prefabName, Prefab, (prefab: Prefab) => {
if (!prefab) {
const error = `Failed to load prefab: ${prefabName}`;
weiSan.error(error);
reject(new Error(error));
return;
}
this.startFrameBasedCreation(prefabName, perFrameNum, totalNum, usePool, callBack, createNodeFunction, resolve, reject, prefab);
});
}
});
}
/**
* 开始分帧创建节点的核心逻辑
*/
private startFrameBasedCreation(
prefabName: string,
perFrameNum: number,
totalNum: number,
usePool: boolean,
callBack: ((node: Node, index: number) => void) | undefined,
createNodeFunction: (prefab?: Prefab) => Node | null,
resolve: (value: Node[]) => void,
reject: (reason?: any) => void,
prefab?: Prefab
) {
// 开始分帧创建节点
const createdNodes: Node[] = [];
let currentCount = 0;
const createNodesPerFrame = () => {
const remainingCount = totalNum - currentCount;
const currentFrameCount = Math.min(perFrameNum, remainingCount);
// 创建当前帧的节点
for (let i = 0; i < currentFrameCount; i++) {
try {
const node = createNodeFunction(prefab);
if (!node) {
const error = `Failed to create node: ${prefabName}`;
weiSan.error(error);
reject(new Error(error));
return;
}
createdNodes.push(node);
// 执行回调函数
if (callBack) {
try {
callBack(node, currentCount);
} catch (callbackError) {
weiSan.warn(`Callback error for node ${currentCount}: ${callbackError}`);
}
}
currentCount++;
} catch (error) {
weiSan.error(`Error creating node instance: ${error}`);
reject(new Error(`Error creating node instance: ${error}`));
return;
}
}
// 计算进度并触发进度事件
const progress = (currentCount / totalNum) * 100;
EventManager.dispatchEvent(EventData.PREFAB_LOAD_PROGRESS, {
prefabName: prefabName,
currentNum: currentCount,
totalNum: totalNum,
progress: progress
});
// 检查是否完成
if (currentCount >= totalNum) {
// 触发完成事件
EventManager.dispatchEvent(EventData.PREFAB_LOAD_COMPLETE, {
prefabName: prefabName,
totalNum: totalNum,
nodes: createdNodes
});
weiSan.log(`Successfully loaded ${totalNum} instances of prefab: ${prefabName}`);
resolve(createdNodes);
} else {
// 继续下一帧,使用 setTimeout 来实现分帧
setTimeout(() => {
createNodesPerFrame();
}, 0);
}
};
// 开始第一帧的创建
createNodesPerFrame();
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "6a25c810-7df7-4956-9123-e14f337f08f5",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,706 @@
import { SpriteFrame, Vec3, Node, Vec2, Button, v2, sys, instantiate, game, Game, v3, Sprite, misc, UITransform, Layers, Camera, Canvas, director, Director, ImageAsset, renderer, RenderTexture, Size, Texture2D, view, sp } from "cc";
import { PlatformManager } from "../manager/PlatformManager";
import { ResModel } from "../model/ResModel";
import { LoadTools } from "./LoadTools";
import { AStar } from "./AStar";
import { BFSPathfinding } from "./BFS";
import { SplitRenderHelper } from "../SplitRender/SplitRenderHelper";
export type index = {
x: number;
y: number;
}
//@ts-ignore
game._calculateDT = function _calculateDT(useFixedDeltaTime: boolean): number {
this._useFixedDeltaTime = useFixedDeltaTime;
if (useFixedDeltaTime) {
this._startTime = performance.now();
return this.frameTime / 1000;
}
const now = performance.now();
this._deltaTime = now > this._startTime ? (now - this._startTime) / 1000 : 0;
if (this._deltaTime > Game.DEBUG_DT_THRESHOLD) {
this._deltaTime = this.frameTime / 1000;
}
this._startTime = now;
return this._deltaTime * Tools.getTimeScale();
}
/** 游戏常用工具类 */
export class Tools {
private static timeScale = 1;
private static aStar: AStar = new AStar();
private static bfs: BFSPathfinding = new BFSPathfinding();
/**
* 使用A*算法进行搜索。
* @param map 二维数字数组,代表地图或其他需要搜索的结构。
* @param start 搜索的起始点,其格式为 index 类型。
* @param end 搜索的终点,其格式为 index 类型。
* @param type 搜索类型,用数字表示,默认为 0。不同的类型可能影响搜索算法的行为。
* @returns 返回搜索结果,通常是一个路径数组或者搜索到的目标。
*/
public static aStarSearch(map: number[][], start: index, end: index, type: number = 0) {
this.aStar.setMap(map);
return this.aStar.search(start, end, type);
}
public static setaStarValue(startValue: number, endValue: number, pathValue: number) {
this.aStar.setValue(startValue, endValue, pathValue);
}
public static setaStarEightDirections(isEightDirections: boolean) {
this.aStar.setEightDirections(isEightDirections);
}
public static setaStarWeightMap(weightMap: number[][]) {
this.aStar.setWeightMap(weightMap);
}
public static setaStarNeighborMap(neighborMap: Map<index, index[]>) {
this.aStar.setNeighborMap(neighborMap);
}
/**
* 使用广度优先搜索算法在地图上寻找从起点到终点的路径。
* @param map 二维数字数组表示的地图。
* @param start 起点的索引。
* @param end 终点的索引。
* @returns 返回一个路径数组,如果不存在路径则返回空数组。
*/
public static bfsSearch(map: number[][], start: index, end: index) {
this.bfs.setMap(map); // 设置广度优先搜索对象的地图
return this.bfs.findPath(start, end); // 执行搜索并返回路径
}
/**
* 存储本地数据
* @param {*} isObject 是否是一个对象或者数组
*/
public static setStorage(key: string, value: any, isObject = false) {
PlatformManager.setStorage(key, value, isObject);
}
/**
* 获取存储数据
* @param {*} isObject 是否是一个对象或者数组
*/
public static getStorage(key: string, isObject = false) {
return PlatformManager.getStorage(key, isObject);
};
/**
* 获取时间缩放
* @returns 时间缩放
*/
public static getTimeScale() {
return Tools.timeScale;
}
/**
* 设置时间缩放
* @param scale 时间缩放
*/
public static setTimeScale(scale: number) {
Tools.timeScale = scale;
}
/**
* 获取截图
* @param node 截图的节点
* @param parent 截图的父节点
* @param pos 截图的位置
* @param canvas 默认摄像机渲染的canvas
* @param normalCamera 默认摄像机
*/
public static getScreenShot(node: Node, parent: Node, pos: Vec3, canvas: Canvas, normalCamera: Camera) {
const newCamera = new Node("PhotoCamera");
newCamera.setParent(canvas.node);
newCamera.setPosition(0, 0, 1000);
newCamera.addComponent(Camera);
newCamera.getComponent(Camera).visibility = normalCamera.visibility;
newCamera.getComponent(Camera).clearFlags = normalCamera.clearFlags;
newCamera.getComponent(Camera).near = normalCamera.near;
newCamera.getComponent(Camera).far = normalCamera.far;
newCamera.getComponent(Camera).projection = renderer.scene.CameraProjection.ORTHO;
newCamera.getComponent(Camera).orthoHeight = normalCamera.orthoHeight;
const newRenderTexture = new RenderTexture();
newRenderTexture.reset({
width: view.getVisibleSize().width,
height: view.getVisibleSize().height
});
newCamera.getComponent(Camera).targetTexture = newRenderTexture;
director.once(Director.EVENT_AFTER_DRAW, () => {
const newSpriteFrame = new SpriteFrame();
let data = newRenderTexture.readPixels(Math.round(node.getWorldPosition().x - node.getComponent(UITransform).width / 2 * node.scale.x),
Math.round(node.getWorldPosition().y - node.getComponent(UITransform).height / 2 * node.scale.y),
node.getComponent(UITransform).width * node.scale.x, node.getComponent(UITransform).height * node.scale.y)
let image = new ImageAsset();
image.reset({
_data: data,
_compressed: false,
width: node.getComponent(UITransform).width * node.scale.x,
height: node.getComponent(UITransform).height * node.scale.y,
format: Texture2D.PixelFormat.RGBA8888
})
let newTexture = new Texture2D();
newTexture.image = image;
newSpriteFrame.texture = newTexture;
newSpriteFrame.packable = false;
if (sys.isNative && (sys.os === sys.OS.IOS || sys.os === sys.OS.OSX)) {
newSpriteFrame.flipUVY = false;
} else {
newSpriteFrame.flipUVY = true;
}
let newSprite = new Node("PhotoSprite");
newSprite.addComponent(Sprite);
newSprite.setParent(parent);
newSprite.setPosition(pos);
newSprite.layer = Layers.Enum.UI_2D;
newSprite.getComponent(UITransform)
.setContentSize(new Size(node.getComponent(UITransform).width,
node.getComponent(UITransform).height));
newSprite.getComponent(Sprite).spriteFrame = newSpriteFrame;
newCamera.getComponent(Camera).targetTexture = null;
})
}
/**
* 保存文件。
* @param textToWrite 要保存的文件内容
* @param fileNameToSaveAs 要保存的文件名
* @param fileType 要保存的文件格式,默认为Json
*/
public static saveForBrowser(textToWrite: any, fileNameToSaveAs: string, fileType: string = 'application/json') {
if (sys.isBrowser) {
console.log("浏览器");
let textFileAsBlob = new Blob([textToWrite], { type: fileType });
let downloadLink = document.createElement("a");
downloadLink.download = fileNameToSaveAs;
downloadLink.innerHTML = "Download File";
downloadLink.href = window.webkitURL.createObjectURL(textFileAsBlob);
downloadLink.click();
}
}
/**
* 获取spine指定动画时间,如果没有该动画 则返回0
* @param spine 骨骼动画
* @param aniName 动画名称
* @returns 动画时间
*/
public static getSpineAnimationTime(spine: sp.Skeleton, aniName: string) {
const state = spine.getState();
if (state == undefined) throw `[ERROR SPINE ANIMATION] 无法获取获取动画状态`;
const { animations } = state.data.skeletonData;
let result = 0;
for (const key in animations) {
if (Object.prototype.hasOwnProperty.call(animations, key)) {
const element = animations[key];
if (element.name == aniName) {
result = element.duration;
}
}
}
return result;
}
//-----------------------------节点预制体相关-------------------------------
/**
* 新建一个预制体在场景里
* @param preName 预制体名字或url
* @param callFunc 加载预制体 完成后回调
* @param parent 存放预制体的父节点
* @param Pos 预制体的坐标
* @param zIndex 预制体的层级
*/
public static newPrefab(preName: string, parent?: Node, Pos?: Vec3, callFunc?: (age?: Node) => void): Node {
return NodeTools._ins.newPrefab(preName, callFunc, parent, Pos);
}
/**
* 新建一个图片在场景里
* @param sprName 图片名字或url
* @param callFunc 加载预制体 完成后回调
* @param parent 存放预制体的父节点
* @param Pos 预制体的坐标
* @param zIndex 预制体的层级
*/
public static newSprite(sprName: string, parent?: Node, Pos?: Vec3, callFunc?: (age?: Node) => void): Node {
return NodeTools._ins.newSprite(sprName, callFunc, parent, Pos);
}
/**
* 设置一个节点的SpriteFrame
* @param nodeT 节点的Node
* @param sprUrl 图片的url或者存放到resArr的名字
*/
public static setSpriteFrame(nodeT: Node, sprUrl: string) {
NodeTools._ins.setSpriteFrame(nodeT, sprUrl);
}
/** 设置一个节点的 groupIndex 包含子物体 */
public static setNodeLayerIndex(nodeT: Node, layer: Layers.Enum) {
NodeTools._ins.setNodeLayerIndex(nodeT, layer);
}
/**
* 设置一个Button的按下和松开的SpriteFrame
* @param norUrl 默认状态的名字或者路径
* @param preUrl 按下状态的名字或者路径
*/
public static setBtnClickSpr(Btn: Button, norUrl: string, preUrl: string) {
NodeTools._ins.setBtnClickSpr(Btn, norUrl, preUrl);
};
/** 切换父物体 不改变显示位置*/
public static setNodeParent(node: Node, parent: Node) {
NodeTools._ins.setNodeParent(node, parent);
};
//----------------------------------数学数组相关----------------------------------
/**
* 将多维数组展平
* @param arr 需要展平的多维数组
* @returns 展平后的数组
*/
public static flatArray(arr: any[]) {
return arr.reduce((acc, curr) => {
return acc.concat(Array.isArray(curr) ? Tools.flatArray(curr) : curr);
}, []);
}
/**
* 从数组中随机获取元素
* @param arr 需要随机获取元素的数组
* @param num 需要获取的元素数量
* @param sortFunc 排序函数,可选
* @returns 返回随机获取的元素数组
*/
public static randomGetElementFromArray(arr: any[], num: number, sortFunc: (a: any, b: any) => number = null) {
const tempArr = arr.slice();
const resultArr = [];
while (resultArr.length < num) {
const randomIndex = Tools.random(0, tempArr.length - 1);
resultArr.push(tempArr[randomIndex]);
tempArr.splice(randomIndex, 1);
}
if (sortFunc) {
resultArr.sort(sortFunc);
}
return resultArr;
}
/**
* 获取随机数
* @param isInteger 是否随机整数 默认整数
*/
public static random(x1: number, x2: number, isInteger = true): number {
return MathTools._ins.random(x1, x2, isInteger);
}
/**
* 根据概率数组 随机概率 返回数组的index
* @param chooseArr 需要随机概率的数组 例如[0.05,0.1,0.2,0.3]
*/
public static chooseRandom(chooseArr: Array<number>) {
return MathTools._ins.chooseRandom(chooseArr);
}
/** 传入一个弧度 返回一个Y轴折射后的弧度 */
public static refractionY(rad: number) {
return MathTools._ins.refractionY(rad);
};
/** 传入一个弧度 返回一个轴折射后的弧度 */
public static refractionX(rad: number) {
return MathTools._ins.refractionX(rad);
};
/** 重新打乱一个数组的顺序 洗牌 */
public static againSortArr(Arr: Array<any>) {
MathTools._ins.againSortArr(Arr);
};
/**
* 将一个数组 按照里面的对象排序
* @param tempArr 传入的数组
* @param sortName 对象属性名字
* @param isReverse 是否倒序
*/
public static sortArrForObject(tempArr: Array<any>, sortName: string, isReverse = false) {
MathTools._ins.sortArrForObject(tempArr, sortName, isReverse);
};
/**
* 取一定范围内不重复的数字
* @param minNum 最小取值范围
* @param maxNum 最大取值范围
* @param getNum 取几个数字
*/
public static getDiffNumRandom(minNum: number, maxNum: number, getNum: number) {
return MathTools._ins.getDiffNumRandom(minNum, maxNum, getNum);
};
//--------------------------------向量坐标计算相关------------------------------------
/**
* 根据两个点 求角度
* @param pos1 起始点坐标
* @param pos2 结束点坐标
* @param isVertical 是否以竖直方向为0度开始
*/
public static getAngleForPos(pos1: Vec2, pos2: Vec2, isVertical = false): number {
return VecTools._ins.getAngleForPos(pos1, pos2, isVertical);
};
/** 获取两个坐标之间的距离 */
public static getDistance(pos1: Vec3 | Vec2, pos2: Vec3 | Vec2): number {
if (pos1 instanceof Vec2 && pos2 instanceof Vec2) {
return Vec2.distance(pos1, pos2);
}
if (pos1 instanceof Vec3 && pos2 instanceof Vec3) {
return Vec3.distance(pos1, pos2);
}
};
/**
* 根据一个角度和长度 计算相对应的坐标
* @param angle 角度
* @param len 该角度上的长度
* @param startPos 初始的坐标
*/
public static getPosForAngleLen(angle: number, len: number, startPos: Vec2 = v2(0, 0)) {
return VecTools._ins.getPosForAngleLen(angle, len, startPos);
}
/**
* 获取节点在另一个节点下的坐标
* @param obj 节点
* @param mainObj 相对于的另一个节点
*/
public static getToNodePosForNode(obj: Node, mainObj: Node): Vec2 {
return VecTools._ins.getToNodePosForNode(obj, mainObj);
};
/** 获取节点的世界坐标 */
public static getToWorldPosAR(obj: Node) {
return VecTools._ins.getToWorldPosAR(obj);
}
/**
* 通过世界坐标 获取相对节点的坐标
* @param worldPos 世界坐标
* @param obj 相对节点下的
*/
public static getToNodePosForWorld(worldPos: Vec3, obj: Node) {
return VecTools._ins.getToNodePosForWorld(worldPos, obj);
}
/** 二维向量转三维向量 */
public static getVec3ForVec2(vec2: Vec2) {
return VecTools._ins.getVec3ForVec2(vec2);
}
/** 三维向量转二维向量 */
public static getVec2ForVec3(vec3: Vec3) {
return VecTools._ins.getVec2ForVec3(vec3);
}
/** 获取两向量方向 */
public static getDirection(startPos: Vec3 | Vec2, endPos: Vec3 | Vec2) {
return VecTools._ins.getDirection(startPos, endPos);
}
/** 获取多边形面积 */
public static getPolygonArea(polygon: Vec2[]) {
return VecTools._ins.getPolygonArea(polygon);
}
//--------------------------------数组操作相关------------------------------------
/** 根据value值 从数组里面移除 */
public static removeArrForValue(tempArr: Array<any>, value: any) {
return tempArr.splice(tempArr.indexOf(value), 1);
}
/** 从数组里面添加一个该数组里没有的元素 */
public static addArrNoValue(tempArr: Array<any>, value: any) {
if (tempArr.indexOf(value) < 0) {
tempArr.push(value);
return true;
}
return false;
}
/** 从数组指定位置 插入某个元素 */
public static addArrIndex(tempArr: Array<any>, index: number, value: any) {
return tempArr.splice(index, 0, value);
}
//--------------------------------其他------------------------------------
/**
* 字符串指定位置插入新字符
* @param source 需要操作的字符串
* @param start 从那个位置开始插入
* @param newStr 插入的新的字符
*/
public static insertStrForIndex(source: string, start: number, newStr: string): string {
return source.slice(0, start) + newStr + source.slice(start);
};
/**
* 数字整数前边补零 并返回字符串
* @param num 传入的数字
* @param length 前边补几个零
*/
public static prefixInteger(num: number, length = 2): string {
return (Array(length).join('0') + num).slice(-length);
};
/** 获取系统语言 */
public static getLanguageType(): string {
var langNumType = "EN"; //默认英语
if (sys.language == sys.Language.CHINESE) {
if (sys.languageCode.toLowerCase().indexOf("zh-cn") != -1 ||
sys.languageCode.toLowerCase().indexOf("zh_cn") != -1 ||
sys.languageCode.toLowerCase().indexOf("zh-hans-cn") != -1) {
langNumType = "CN"; //简体
} else {
langNumType = "CHT"; //繁体
}
}
// else if ( sys.language == sys.Language.KOREAN ) {
// langNumType = "KOR"; //韩语
// }
// else if (sys.language == sys.Language.JAPANESE) {
// langNumType = "JP"; //日语
// }
// else if ( window.navigator.language == "th-TH" ) {
// langNumType = "TH"; //泰语
// }
return langNumType;
}
}
/** 节点相关 工具类 */
class NodeTools {
/** 单例模式 */
private static _instance: NodeTools = new NodeTools();
private constructor() { }
public static get _ins() {
return this._instance;
}
/** 新建一个预制体在场景里 */
public newPrefab(preName: string, callFunc?: (age?: Node) => void, parent?: Node, Pos?: Vec3): Node {
let prefab = ResModel._ins.PrefabDic.get(preName);
let clone: Node = null;
if (prefab != null) {
clone = instantiate(prefab);
if (parent) { parent.addChild(clone) };
if (Pos) { clone.position = v3(Pos.x, Pos.y, Pos.z) };
if (callFunc != null) {
callFunc(clone);
}
} else {
LoadTools._ins.loadResPrefab(preName, callFunc, parent, Pos);
}
return clone;
}
/** 新建一个图片在场景里 */
public newSprite(sprName: string, callFunc?: (age?: Node) => void, parent?: Node, Pos?: Vec3) {
let sprite = new Node();
sprite.name = sprName;
sprite.layer = Layers.Enum.UI_2D;
if (ResModel._ins.SpriteFrameDic.get(sprName) != null) {
sprite.addComponent(Sprite).spriteFrame = ResModel._ins.SpriteFrameDic.get(sprName);
if (parent) { parent.addChild(sprite) };
if (Pos) { sprite.position = v3(Pos.x, Pos.y, Pos.z) };
if (callFunc != null) {
callFunc(sprite);
}
} else {
sprite.addComponent(Sprite);
LoadTools._ins.loadResSpriteFrame(sprName, sprite, parent, Pos, callFunc);
}
return sprite;
}
/** 设置一个节点的SpriteFrame */
public setSpriteFrame(nodeT: Node, sprUrl: string) {
if (nodeT.getComponent(Sprite) == null) {
nodeT.addComponent(Sprite);
}
if (ResModel._ins.SpriteFrameDic.get(sprUrl)) {
nodeT.getComponent(Sprite).spriteFrame = ResModel._ins.SpriteFrameDic.get(sprUrl)
} else {
LoadTools._ins.loadResAny(sprUrl, SpriteFrame, (spriteFrame: SpriteFrame) => {
nodeT.getComponent(Sprite).spriteFrame = spriteFrame;
})
}
}
/** 设置一个节点的 所属层 包含子物体 */
public setNodeLayerIndex(nodeT: Node, layer: Layers.Enum) {
nodeT.layer = layer;
for (let i = 0; i < nodeT.children.length; i++) {
this.setNodeLayerIndex(nodeT.children[i], layer);
}
}
/** 设置一个Button的按下和松开的SpriteFrame */
public setBtnClickSpr(Btn: Button, norUrl: string, preUrl: string) {
if (ResModel._ins.SpriteFrameDic.get(norUrl)) {
Btn.getComponent(Button).normalSprite = ResModel._ins.SpriteFrameDic.get(norUrl)
Btn.getComponent(Button).hoverSprite = ResModel._ins.SpriteFrameDic.get(norUrl)
Btn.getComponent(Button).pressedSprite = ResModel._ins.SpriteFrameDic.get(preUrl)
} else {
LoadTools._ins.loadResAny(norUrl, SpriteFrame, (spr: SpriteFrame) => {
Btn.getComponent(Button).normalSprite = spr;
Btn.getComponent(Button).hoverSprite = spr;
});
LoadTools._ins.loadResAny(preUrl, SpriteFrame, (spr: SpriteFrame) => {
Btn.getComponent(Button).pressedSprite = spr;
});
}
};
/** 切换父物体 不改变坐标 */
public setNodeParent(node: Node, parent: Node) {
let Pos = VecTools._ins.getToNodePosForNode(node, parent);
node.parent = parent;
node.position = v3(Pos.x, Pos.y);
};
}
/** 数学数组计算相关 工具类 */
class MathTools {
/** 单例模式 */
private static _instance: MathTools = new MathTools();
private constructor() { }
public static get _ins() {
return this._instance;
}
/** 获取随机数 */
public random(x1: number, x2: number, isInteger = true): number {
if (isInteger) {
return x1 + Math.floor(Math.random() * (x2 - x1 + 1));
}
return Math.random() * (x2 - x1) + x1;
}
/** 根据概率数组 随机概率 返回数组的index */
public chooseRandom(chooseArr: Array<number>) {
let total = 0; //概率总值
//首先计算出概率的总值,用来计算随机范围
for (let i = 0; i < chooseArr.length; i++) {
total += chooseArr[i];
}
let randNum = this.random(0, total, false)
for (let i = 0; i < chooseArr.length; i++) {
if (randNum < chooseArr[i] && chooseArr[i] > 0) {
return i;
} else {
randNum -= chooseArr[i];
}
}
return chooseArr.length - 1;
}
/** 弧度折射Y轴 */
public refractionY(rad: number) {
return Math.atan2(Math.sin(rad), -Math.cos(rad));
};
/** 弧度折射X轴 */
public refractionX(rad: number) {
return Math.atan2(-Math.sin(rad), Math.cos(rad));
};
/** 重新洗牌 一个数组 */
public againSortArr(Arr: Array<any>) {
for (let i = 0; i < Arr.length; i++) {
let tempR = Tools.random(0, Arr.length - 1);
if (tempR != i) {
let temp = Arr[i];
Arr[i] = Arr[tempR];
Arr[tempR] = temp;
}
}
}
/** 数组 对象排序 对象属性 是否倒序 */
public sortArrForObject(tempArr: Array<any>, sortName: string, isReverse = false) {
if (!isReverse) {
tempArr.sort((a, b) => {
return a[sortName] - b[sortName];
});
} else {
tempArr.sort((a, b) => {
return b[sortName] - a[sortName];
});
}
};
/** 取一定范围内不重复的数字 */
public getDiffNumRandom(minNum: number, maxNum: number, getNum: number) {
var arr = [];
for (let i = minNum; i <= maxNum; i++) {
arr.push(i);
}
const tempLen = arr.length - getNum;
for (let i = 0; i < tempLen; i++) {
let tempI = Tools.random(0, arr.length - 1);
arr.splice(tempI, 1);
}
return arr;
};
}
/** 向量坐标转换相关工具类 */
class VecTools {
/** 单例模式 */
private static _instance: VecTools = new VecTools();
private constructor() { }
public static get _ins() {
return this._instance;
}
/** 根据两个点 求角度 */
public getAngleForPos(pos1: Vec2, pos2: Vec2, isVertical = false): number {
let rad = 0;
if (isVertical) {
rad = -Math.atan2(pos2.x - pos1.x, pos2.y - pos1.y);
} else {
rad = Math.atan2(pos2.y - pos1.y, pos2.x - pos1.x);
}
return misc.radiansToDegrees(rad);
}
/** 根据一个角度和长度 计算相对应的坐标 */
public getPosForAngleLen(angle: number, len: number, startPos: Vec2 = v2(0, 0)) {
let rad = misc.degreesToRadians(angle);
return v2(startPos.x + Math.cos(rad) * len, startPos.y + Math.sin(rad) * len);
}
/** 获取节点在另一个节点下的坐标 */
public getToNodePosForNode(obj: Node, mainObj: Node): Vec2 {
let worldPos = obj.parent.getComponent(UITransform).convertToWorldSpaceAR(obj.position);
let nodePos = mainObj.getComponent(UITransform).convertToNodeSpaceAR(worldPos)
return v2(nodePos.x, nodePos.y);
};
/** 获取节点的世界坐标 */
public getToWorldPosAR(obj: Node) {
return obj.parent.getComponent(UITransform).convertToWorldSpaceAR(obj.position);
}
/** 通过世界坐标 获取相对节点的坐标 */
public getToNodePosForWorld(worldPos: Vec3, obj: Node) {
return obj.getComponent(UITransform).convertToNodeSpaceAR(worldPos);
}
/** 二维向量转三维向量 */
public getVec3ForVec2(vec2: Vec2) {
return v3(vec2.x, vec2.y, 0);
}
/** 三维向量转二维向量 */
public getVec2ForVec3(vec3: Vec3) {
return v2(vec3.x, vec3.y);
}
/** 获取两向量方向和距离
* @param startPos 起始位置
* @param endPos 结束位置
*/
public getDirection(startPos: Vec3 | Vec2, endPos: Vec3 | Vec2) {
if (startPos instanceof Vec2 && endPos instanceof Vec2) {
const deltaPos = endPos.clone().subtract(startPos);
const direction = deltaPos.normalize();
return direction;
} else if (startPos instanceof Vec3 && endPos instanceof Vec3) {
const deltaPos = endPos.clone().subtract(startPos);
const direction = deltaPos.normalize();
return direction;
}
}
public getPolygonArea(polygon: Vec2[]) {
return SplitRenderHelper.calculatePolygonArea(polygon);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "f3cbf87f-aa09-459e-8527-cd6f1be98ffb",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,31 @@
export class weiSan {
/** 是否开启 Log 信息 */
private static isLog = true;
private static logName:string = "微伞游戏Log:"
private static logInfo:string = "本游戏包含的所有内容(包括但不限于:代码、图片、视像及声音内容、名称)的所有权归北京米兜科技有限公司所有。任何单位或个人将本游戏提供的内容与服务用于商业、盈利、广告性等目的时,需征得北京米兜科技有限公司相关权利人的书面许可;将本网站提供的内容与服务用于非商业用途时,应遵守著作权法以及其他相关法律的规定,不得侵犯游戏所有者及相关权利人的权益。"
/** 普通log信息 */
public static log(...data: any[]): void {
if (!this.isLog) { return; }
console.log(this.logName,...data);
}
/** 追踪函数调用的log */
public static logTrace(...data: any[]){
if (!this.isLog) { return; }
console.trace(this.logName,...data);
}
/** 打印错误log信息 */
public static error(...data: any[]): void {
if (!this.isLog) { return; }
console.error(this.logName,...data);
}
/** 打印警告log信息 */
public static warn(...data: any[]): void {
if (!this.isLog) { return; }
console.warn(this.logName,...data);
}
public static initLog(){
console.log(this.logInfo);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "289a2b50-88c3-4705-9e74-63d767daaefb",
"files": [],
"subMetas": {},
"userData": {}
}

12
assets/script/ctrl.meta Normal file
View File

@@ -0,0 +1,12 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "5a099d6e-5ecf-49ae-a8e5-058876a83969",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,41 @@
import { GameModel } from "../model/GameModel";
/** 游戏控制类相关 */
export class GameCtrl {
/** 单例模式 */
private static _instance: GameCtrl = new GameCtrl();
private constructor() { }
public static get _ins() {
return this._instance;
}
/** 当前游戏状态 */
gameState: GameState;
/** 是否可点击 屏幕 */
boolTouch: boolean = false;
/** 初始化游戏 相关数据 */
initGame() {
this.gameState = GameState.Default;
// GameModel._ins.gameScore = 0;
this.boolTouch = false;
}
/** 结束游戏 相关数据 */
overGame() {
this.gameState = GameState.Over;
this.boolTouch = false;
// PoolManager.clearAllPool();
}
}
/** 游戏状态枚举 */
export enum GameState {
/** 默认状态 */
Default = 0,
/** 游戏开始 */
Start = 1,
/** 游戏暂停 */
Pause = 2,
/** 游戏结束 */
Over = 3,
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "5607ac44-fb94-4b73-aa20-1a81fd484128",
"files": [],
"subMetas": {},
"userData": {}
}

12
assets/script/game.meta Normal file
View File

@@ -0,0 +1,12 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "f216decb-b5bd-47a9-94dd-5b7f8b17fa56",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,94 @@
import { _decorator, Component, Node, view, macro, v3, Vec3, EventTouch } from 'cc';
import { Tools } from '../common/Tools';
import { weiSan } from '../common/weiSanTools';
import { GameCtrl, GameState } from '../ctrl/GameCtrl';
import { EventData, EventManager } from '../manager/EventManager';
import { UIManager } from '../manager/UIManager';
import { GameModel } from '../model/GameModel';
const { ccclass, property } = _decorator;
@ccclass('MainGame')
export class MainGame extends Component {
/** 屏幕宽度 */
gameWidth: number;
/** 屏幕高度 */
gameHeight: number;
onLoad() {
this.gameWidth = view.getVisibleSize().width;
this.gameHeight = view.getVisibleSize().height;
GameModel._ins.mainGame = this;
this.addTouchEvents();
this.addInitListener();
}
start() {
this.initGame();
}
update(deltaTime: number) {
// [4]
}
/** 触摸开始点坐标 */
touchStartPos: Vec3;
/** 触摸事件 监听 */
addTouchEvents(): void {
macro.ENABLE_MULTI_TOUCH = false; //是否开起多点触摸
this.node.on(Node.EventType.TOUCH_START, this.touchStartBack, this);
this.node.on(Node.EventType.TOUCH_MOVE, this.touchMoveBack, this);
this.node.on(Node.EventType.TOUCH_END, this.touchEndBack, this);
};
/** 触摸开始 回调 */
touchStartBack(touches: EventTouch) {
if (!GameCtrl._ins.boolTouch) { return; }
this.touchStartPos = Tools.getToNodePosForWorld(v3(touches.getUILocation().x, touches.getUILocation().y), this.node);
}
/** 触摸移动 回调 */
touchMoveBack(touches: EventTouch) {
if (!GameCtrl._ins.boolTouch) { return; }
}
/** 触摸结束 回调 */
touchEndBack(touches: EventTouch) {
if (!GameCtrl._ins.boolTouch) { return; }
console.log("boolTouch");
this.gameEnd();
}
/** 初始化游戏 */
initGame() {
GameModel._ins.gameScore = 0;
GameCtrl._ins.initGame();
GameCtrl._ins.boolTouch = true;
}
/** 开始游戏 */
startGame() {
}
/** 游戏结束 */
gameEnd() {
if (GameCtrl._ins.gameState == GameState.Over) { return; }
GameCtrl._ins.overGame();
weiSan.log("游戏结束");
this.scheduleOnce(() => {
UIManager.OpenUI("OverUI");
}, 0.5);
};
/** 事件 监听 */
addInitListener() {
EventManager.addListener(EventData.START_GAME, this.startGame.bind(this), this.node);
}
onDestroy() {
EventManager.removeListenerForTarget(this.node);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "874b2f98-0ca1-4cc7-a253-8f1f379936be",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,61 @@
import { _decorator, Component, Node, Prefab, Enum, instantiate, sys, view, ResolutionPolicy, screen } from 'cc';
import { AdManager } from '../ads/AdManager';
import { weiSan } from '../common/weiSanTools';
import { PlatformManager, releaseType } from '../manager/PlatformManager';
import { PoolManager } from '../manager/PoolManager';
import { ResArr } from '../item/ResArr';
const { ccclass, property } = _decorator;
@ccclass('initGame')
export class initGame extends Component {
@property([Prefab])
resNodeArr: Array<Prefab> = [];
@property({ type: Enum(releaseType) })
rType: number = releaseType.test_TEST;
@property
storageKey: string = "demo_Game"
@property
fitWidth: number = 720;
@property
fitHeight: number = 1280;
onLoad() {
if (this.rType == releaseType.h5_weiSan) {
this.rType = releaseType.h5_common;
}
PlatformManager.releaseType = this.rType;
PlatformManager.storageKey = this.storageKey;
PlatformManager.initPlatform();
// AdManager.loadAds();
this.initResNode();
this.initScreen();
weiSan.initLog();
}
/** 初始化 resNode */
initResNode() {
for (let i = 0; i < this.resNodeArr.length; i++) {
let resNode = instantiate(this.resNodeArr[i]);
this.node.addChild(resNode);
PoolManager._ins.init(resNode.getComponent(ResArr).PrefabArr);
}
}
/** 电脑端 按宽高一起适配 */
initScreen() {
if (this.rType != releaseType.h5_weiSan && this.rType != releaseType.h5_common) { return; }
weiSan.log("系统OS: " + sys.os);
// view.enableAutoFullScreen(false);
// if( screen.fullScreen() ){
// screen.exitFullScreen();
// }
// view.setResolutionPolicy(ResolutionPolicy.FIXED_WIDTH);
if (sys.os == sys.OS.WINDOWS) {
view.setRealPixelResolution(this.fitWidth, this.fitHeight, ResolutionPolicy.SHOW_ALL);
}
}
// start() {
// }
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "e60100f4-853c-4d81-aa19-d9bbb181c875",
"files": [],
"subMetas": {},
"userData": {}
}

12
assets/script/item.meta Normal file
View File

@@ -0,0 +1,12 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "098f74e7-3d20-4a12-98b7-892d9c489859",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,74 @@
import { _decorator, Component, SpriteFrame, Prefab, AudioClip, Texture2D, Material, assertID, assetManager, Asset, Sprite } from 'cc';
import { Tools } from '../common/Tools';
import { ResModel } from '../model/ResModel';
const { ccclass, property } = _decorator;
@ccclass('ResArr')
export class ResArr extends Component {
@property([SpriteFrame])
public SpriteFrameArr: Array<SpriteFrame> = [];
@property([Prefab])
public PrefabArr: Array<Prefab> = [];
@property([AudioClip])
public audiosArr: Array<AudioClip> = [];
@property([Material])
public MaterialArr: Array<Material> = [];
@property([SpriteFrame])
public Texture2DArr: Array<SpriteFrame> = [];
onLoad() {
this.addAudio();
this.addPrefabs();
this.addSpriteFrame();
this.addMaterial();
this.addTexture();
}
/** 添加音效文件 到Tools字典里面 */
addAudio() {
for (let i = 0; i < this.audiosArr.length; i++) {
if (this.audiosArr[i]) {
const element = this.audiosArr[i];
ResModel._ins.AudioClipDic.set(element.name, element);
}
}
}
/** 添加图片文件 到Tools字典里面 */
addSpriteFrame() {
for (let i = 0; i < this.SpriteFrameArr.length; i++) {
if (this.SpriteFrameArr[i]) {
const element = this.SpriteFrameArr[i];
ResModel._ins.SpriteFrameDic.set(element.name, element);
}
}
}
/** 添加预制体文件 到Tools字典里面 */
addPrefabs() {
for (let i = 0; i < this.PrefabArr.length; i++) {
if (this.PrefabArr[i]) {
const element = this.PrefabArr[i];
ResModel._ins.PrefabDic.set(element.data.name, element);
}
}
}
/** 添加材质文件 到Tools字典里面 */
addMaterial() {
for (let i = 0; i < this.MaterialArr.length; i++) {
if (this.MaterialArr[i]) {
const element = this.MaterialArr[i];
ResModel._ins.MaterialDic.set(element.name, element);
}
}
}
/** 添加Texture文件 到Tools字典里面 */
addTexture() {
for (let i = 0; i < this.Texture2DArr.length; i++) {
if (this.Texture2DArr[i]) {
const element = this.Texture2DArr[i];
ResModel._ins.TextureDic.set(element.name, element.texture as Texture2D);
}
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "f5f4439e-1136-4841-b704-e1f74bf499a1",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "b9102174-9f9c-46af-91b1-0eda31236428",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,57 @@
import { director } from "cc";
export class EventManager {
/**添加一个全局监听
* @param eventName 事件名
* @param event 事件Function
* @param target 添加监听事件的脚本this
*/
public static addListener(eventName: string, event: any, target: any) {
director.on(eventName, event, target);
};
/**
* 移除一个监听事件
* @param {*} eventName 事件名
* @param {*} event 事件Function
* @param {*} target 添加监听事件的Node
*/
public static removeListener(eventName: string, event: any, target: any) {
director.off(eventName, event, target);
};
/**
* 派发一个事件 令所有监听此事件的Node执行事件
* @param {*} eventName 事件名
* @param {*} arg1 传递的参数1
* @param {*} arg2 传递的参数2
* @param {*} arg3 传递的参数3
* @param {*} arg4 传递的参数4
* @param {*} arg5 传递的参数5
*/
public static dispatchEvent(eventName: string, arg1?: any, arg2?: any, arg3?: any, arg4?: any, arg5?: any) {
director.emit(eventName, arg1, arg2, arg3, arg4, arg5);
};
/**
* 移除 Node 上的所有事件
* @param {*} target 需要移除事件的Node
*/
public static removeListenerForTarget(target: any) {
director.targetOff(target);
};
};
export enum EventData {
/** 开始游戏 */
START_GAME = "START_GAME",
/** 复活 */
REIVE_GAME = "REIVE_GAME",
/**加金币 */
AddCoin = "AddCoin",
/**加生命 */
AddLife = "AddLife",
/** 预制体分帧加载开始 */
PREFAB_LOAD_START = "PREFAB_LOAD_START",
/** 预制体分帧加载进度更新 */
PREFAB_LOAD_PROGRESS = "PREFAB_LOAD_PROGRESS",
/** 预制体分帧加载完成 */
PREFAB_LOAD_COMPLETE = "PREFAB_LOAD_COMPLETE",
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "1dddfca2-37e3-4421-9802-d1ed36cd6abf",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,123 @@
import { loader } from "cc";
import { weiSan } from "../common/weiSanTools";
import { PlatformManager, releaseType } from "./PlatformManager";
/** 网络相关 管理类 */
export class NetworkManager {
/** 游戏ID */
public static gameHttpId: number = 0;
/** 更多游戏 链接 */
public static moreGameUrl: string;
/** 初始化 网络相关 */
public static initNetwork() {
if (PlatformManager.releaseType == releaseType.h5_common || PlatformManager.releaseType == releaseType.h5_weiSan) {
this.initNet_H5()
} else if (PlatformManager.releaseType == releaseType.test_TEST) {
this.moreGameUrl = "http://m.wesane.com/";
weiSan.log("发送:---游戏加载成功!");
}
}
/** 初始化 H5 网络请求 */
private static initNet_H5() {
this.getHttpGameId();
this.sendLoadGame();
}
/** 通过url 获取gameID */
private static getHttpGameId() {
var url = document.URL;
var game_id = 0;
if (PlatformManager.releaseType == releaseType.h5_common) {
var httpUrl = window.location.href;
var httpHead = httpUrl.substring(0, httpUrl.lastIndexOf("//") + 2);
var httpMid = window.location.host;
var httpAll = httpHead + httpMid + "/Service/Share/index";
var url = document.URL;
var index = url.lastIndexOf("\/");
var str = url.substring(0, index);
var index = str.lastIndexOf("/");
game_id = parseInt(str.substring(index + 1, str.length));
this.gameHttpId = game_id;
// console.log("gameIdNew", game_id);
var endHttp = httpUrl.substring(httpUrl.lastIndexOf("//") + 4, httpUrl.lastIndexOf("com") + 3);
var curWebMoreGame = httpHead + httpMid
// console.log("moreGame", curWebMoreGame);
var urlIndex = curWebMoreGame.lastIndexOf('//')
var urlG = curWebMoreGame.substring(urlIndex + 2, curWebMoreGame.indexOf("//") + 4);
if (urlG == 'g.') {
var newMoreUrl = curWebMoreGame.replace(urlG, "");
// console.log("chagee", newMoreUrl);
curWebMoreGame = newMoreUrl;
} else {
console.log("noChange");
}
this.moreGameUrl = curWebMoreGame;
msgHttpUrl.gamePv_commonH5 = httpHead + httpMid + "/Service/GamePv/index";
msgHttpUrl.score_commonH5 = httpHead + httpMid + "/Service/Score/index";
} else {
var para = url.substring(url.lastIndexOf("/game/") + 1, url.length);
var arr = para.split("/");
if (arr.length >= 2) {
game_id = parseInt(arr[1]);
}
this.moreGameUrl = "http://m.wesane.com/"; // this.httpHead + this.endHttp;
}
this.gameHttpId = game_id;
weiSan.log("gameId:", game_id , this.moreGameUrl );
};
/** 向服务器 发送 加载游戏成功 */
private static sendLoadGame() {
if (PlatformManager.releaseType == releaseType.h5_weiSan) {
this.sendMsg(msgHttpUrl.gamePv_weiSanH5, "gameID=" + this.gameHttpId.toString(), this.loadGameBack);
} else if (PlatformManager.releaseType == releaseType.h5_common) {
this.sendMsg(msgHttpUrl.gamePv_commonH5, "gameId=" + this.gameHttpId.toString(), this.loadGameBack);
}
};
/** 发送加载游戏成功回调 */
private static loadGameBack() {
weiSan.log("gamePv加载成功");
}
/** 向服务器 提交分数 */
public static sendGameScore(Score: number, gameType: any) {
if (PlatformManager.releaseType == releaseType.h5_weiSan) {
this.sendMsg(msgHttpUrl.score_weiSanH5, "gameScore=" + Score + "&gameId=" + this.gameHttpId + "&gameType=" + gameType, this.sendScoreBack);
} else if (PlatformManager.releaseType == releaseType.h5_common) {
this.sendMsg(msgHttpUrl.score_commonH5, "gameScore=" + Score + "&gameId=" + this.gameHttpId + "&gameType=" + gameType, this.sendScoreBack);
}
};
/** 向服务器 提交分数回调 */
private static sendScoreBack(Event: any) {
weiSan.log("---提交分数成功!" + Event);
if (Event.currentTarget.response != null && Event.currentTarget.response != "") {
var endShowText = JSON.parse(Event.currentTarget.response);
// weiSan.log("sendScoreBack:",endShowText.content);
}
}
/** 向服务器 POST请求 http */
public static sendMsg(url: string, postData: any, callback?: any) {
// var request = loader.getXMLHttpRequest();
var request = new XMLHttpRequest();
request.onreadystatechange = callback;
request.open("POST", url);
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
request.send(postData);
};
}
window.NetworkManager = NetworkManager;
/** 网络 消息号数据 */
export var msgHttpUrl = {
/** 微伞游戏H5 加载成功 */
gamePv_weiSanH5: "http://www.wesane.com/admin.php/Activityshow/gamelogo",
/** 微伞游戏H5 提交分数 */
score_weiSanH5: "http://www.wesane.com/admin.php/Gamescore/saveGamescore",
/** 通用H5游戏 加载成功 */
gamePv_commonH5: "",
/** 通用H5游戏 提交分数 */
score_commonH5: "",
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "700c5a3f-bf5b-4302-96ab-e576d03dccc1",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,115 @@
import { Enum, game, view , screen, sys } from "cc";
import { AdManager } from "../ads/AdManager";
import { Tools } from "../common/Tools";
import { NetworkManager } from "./NetworkManager";
/** 游戏管理类 */
export class PlatformManager {
/** 发布平台类型 */
public static releaseType: number;
/** 发布类型 h5 安卓 IOS 小程序 */
public static osType:number;
/** 存储本地数据 key的前标 */
public static storageKey: string = "demo_Game_";
/** 游戏是否 加载成功 */
public static loadGameBool:boolean = false;
/** 初始化平台 相关 */
public static initPlatform(){
if(this.loadGameBool) { return; } //第一次加载成功后 不加载第二次
this.initOsType();
NetworkManager.initNetwork();
if(PlatformManager.releaseType == releaseType.h5_weiSan || PlatformManager.releaseType == releaseType.h5_common){
if(NetworkManager.gameHttpId.toString() == "NaN"){
if(PlatformManager.releaseType == releaseType.h5_weiSan){
window.location.href = NetworkManager.moreGameUrl;
}else{
window.location.href = "http://www.vsane.com/";
}
}
loadInScene();
}
if(PlatformManager.releaseType != releaseType.applet_ziJie){
window.AdManager = AdManager;
}
this.loadGameBool = true;
};
/** 初始化 发布类型 */
private static initOsType(){
if( this.releaseType == releaseType.APP_google ){
this.osType = osType.android;
}else if( this.releaseType == releaseType.APP_ios ){
this.osType = osType.ios;
}else if(this.releaseType == releaseType.applet_wechat ||
this.releaseType == releaseType.applet_ziJie ){
this.osType = osType.applet;
}else{
this.osType = osType.h5;
}
}
/**
* 存储本地数据
* @param {*} isObject 是否是一个对象或者数组
*/
public static setStorage(key: string, value: any, isObject = false) {
key = this.storageKey + key;
if (PlatformManager.releaseType === releaseType.applet_ziJie) {
return tt.setStorageSync(key, value);
}
if (isObject) {
value = JSON.stringify(value);
}
/** 默认cocos 存储数据方法 */
sys.localStorage.setItem(key, value);
}
/**
* 获取存储数据
* @param {*} isObject 是否是一个对象或者数组
*/
public static getStorage(key: string, isObject = false) {
key = this.storageKey + key;
let temp = null;
if (PlatformManager.releaseType === releaseType.applet_ziJie) {
temp = <any>tt.getStorageSync(key);
}else{
temp = <any>sys.localStorage.getItem(key);
if (!temp || temp.toString() == "NaN" || temp.toString() == "null") {
temp = null;
}else if( isObject ){
temp = JSON.parse(temp);
}else if ( !isNaN(temp) ) {
temp = parseInt(temp);
}
}
return temp;
};
};
window.PlatformManager = PlatformManager;
/** 发布平台类型 */
export var releaseType = Enum({
/** 测试 */
test_TEST: 1,
/** 微伞 h5 */
h5_weiSan: 2,
/** 通用 h5 */
h5_common: 3,
/** 字节跳动 小程序*/
applet_ziJie: 4,
applet_wechat: 5 ,
/** 谷歌 AppPlay*/
APP_google: 10,
/** IOS appStore */
APP_ios: 11,
});
/** 发布类型 h5 安卓 IOS 小程序 */
export enum osType{
h5 = 1,
android = 2,
ios = 3,
applet = 4,
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "4369c8b0-61d8-446e-bb87-570f94477bf4",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,126 @@
import { _decorator, Director, director, instantiate, Node, NodePool, Prefab } from 'cc';
const { ccclass, property } = _decorator;
export class PoolManager {
private static _instance: PoolManager = new PoolManager();
private constructor() { }
public static get _ins() {
return this._instance;
}
private prefabMap: Map<string, Prefab> = new Map();
private poolDic: Map<string, NodePool> = new Map();
/**
* 初始化
* @param resArr 预制体数组
* 如无出现BUG无须主动调用
*/
init(prefabs: Prefab[]) {
if (!prefabs || prefabs.length === 0) return;
// 清空之前的记录
this.prefabMap.clear();
// 使用 Map 存储,提高查询效率
prefabs.forEach(prefab => {
if (prefab && prefab.name) {
this.prefabMap.set(prefab.name, prefab);
}
});
}
/**
* 根据名称获取预制体
* @param url 预制体的res/Prefab/的路径 或者 resArr拖动的名字
*/
getNode(url: string) {
let prefab: Prefab = null;
if (this.prefabMap.has(url)) {
prefab = this.prefabMap.get(url);
}
if (!prefab) {
if (this.poolDic.has(url)) {
if (this.poolDic.get(url).size() > 0) {
const node = this.poolDic.get(url).get();
return node;
} else {
const node = new Node(url);
return node;
}
} else {
this.poolDic.set(url, new NodePool(url));
const node = new Node(url);
return node;
}
} else {
if (!this.poolDic.has(url)) {
const node = instantiate(prefab);
this.poolDic.set(url, new NodePool(url));
console.warn("First Init Pool: " + url);
return node;
} else {
const Pool = this.poolDic.get(url);
if (Pool.size() > 0) {
const node = Pool.get();
return node;
} else {
const node = instantiate(prefab);
return node;
}
}
}
}
/**
* 回收节点
* @param node 节点
* @param isRigiBody 是否是刚体
*/
recycleNode(node: Node, isRigiBody: boolean = false) {
const url = node.name;
if (isRigiBody) {
if (this.poolDic.has(url)) {
director.once(Director.EVENT_AFTER_PHYSICS, () => {
this.poolDic.get(url).put(node);
})
} else {
this.poolDic.set(url, new NodePool(url));
director.once(Director.EVENT_AFTER_PHYSICS, () => {
this.poolDic.get(url).put(node);
})
}
} else {
if (this.poolDic.has(url)) {
this.poolDic.get(url).put(node);
} else {
this.poolDic.set(url, new NodePool(url));
this.poolDic.get(url).put(node);
}
}
}
/**
* 清空所有节点池
*/
clearAllPool() {
for (let pool of this.poolDic.values()) {
pool.clear();
}
this.poolDic.clear();
}
/**
* 获取节点池字典
*/
getPoolDic() {
return this.poolDic;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "ea012293-8105-4f8a-a2f4-2be50875c717",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,123 @@
import { Component, director , find, Node} from "cc";
import { Tools } from "../common/Tools";
import { weiSan } from "../common/weiSanTools";
export abstract class UIManager extends Component {
/** 存放UI的字典 */
public static UIDic: Map<string, any> = new Map();
/**
* 打开一个UI页面
* @param panelName UIConfig配置里面的名字
* @param param 打开UI时传递的参数
* @param isRemoveOther 是否删除所有的UI
* @returns
*/
public static OpenUI(panelName: string, isRemoveOther: boolean = false, ...param: any): void {
let config;
if (UIConfig[panelName]) {
config = UIConfig[panelName];
}
if (config == null) {
weiSan.error("未找到该UI的配置信息:" + panelName);
return null;
}
if (!this.UIDic.has(panelName)) {
if (isRemoveOther == true) {
this.removeAllUI();
}
this.CreateUI(config, ...param);
} else {
weiSan.warn("已经打开过UI:" + panelName);
}
};
/**
* 关闭一个UI页面
* @param panelName UIConfig配置里面的名字
* @param param 关闭UI时传递的参数
*/
public static CloseUI(panelName: string, ...param: any): void {
let panel;
panel = this.UIDic.get(panelName);
if (panel) {
this.UIDic.delete(panelName);
if(panel.name == ""){ return; }
let component = panel.getComponent(panel.config.com);
if (component && component.closeUI) {
component.closeUI(...param);
}
} else {
weiSan.warn("已经关闭过UI:" + panelName);
}
};
/**
* 获取UI的Node
* @param {*} panelName UI配置里面的名字
*/
public static GetUI(panelName): Node {
let panel = this.UIDic.get(panelName);
if (panel != null) {
return panel;
} else {
weiSan.log("没有打开UI:" + panelName);
return null;
}
};
/**
* 获取UI上的脚本
* @param {*} panelName UI的名字
*/
public static GetUIForComponent(panelName) {
let panel = this.UIDic.get(panelName);
if (panel != null) {
return panel.getComponent(panel.config.com);
} else {
weiSan.warn("没有打开UI:" + panelName);
return null;
}
};
/**
* 创建一个UI
* @param config UI配置config
* @param param 传递参数
* @returns
*/
public static CreateUI(config: any, ...param: any): void {
if (this.UIDic.get(config.name) != null) { return; }
// let parent = director.getScene().getChildByName();
let parent = find("Canvas");
Tools.newPrefab(config.resUrl, parent , null, (node: any) => {
node.config = config;
let component = node.getComponent(config.com);
if (component && component.openUI) {
component.openUI(...param);
component.uiName = config.name;
}
// this.UIDic[config.name] = node;
this.UIDic.set(config.name, node);
});
};
/**
* 移除所有存放在字典里的UI
*/
public static removeAllUI(): void {
this.UIDic.forEach( (value:any,key:any) => {
this.CloseUI( key.toString() );
} );
};
/** 从字典中移除所有UI */
public static removeUIDic() {
this.UIDic.clear();
};
public abstract openUI(...data: any);
public abstract closeUI(...data: any);
public abstract uiName:string;
protected onDestroy(): void {
UIManager.UIDic.delete(this.uiName);
}
}
/** name UI的名字 resUrl预制体加载路径或者名字 com绑定脚本的名字 */
var UIConfig = <any>{
OverUI: { name: "OverUI", resUrl: "OverUI", com: "OverUI", zIndex: 99 },
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "fcdeb5f9-1b46-4a66-9692-5b16e61da895",
"files": [],
"subMetas": {},
"userData": {}
}

12
assets/script/model.meta Normal file
View File

@@ -0,0 +1,12 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "1b9de719-371a-4e6f-bb55-1e990eb77cac",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,24 @@
import { Sprite, SpriteFrame, resources } from "cc";
import { MainGame } from "../game/MainGame";
/** 游戏数据相关 */
export class GameModel {
/** 单例模式 */
private static _instance: GameModel = new GameModel();
private constructor() { }
public static get _ins() {
return this._instance;
}
/** 主场景 名字 */
mainScene: string = "MainGame";
/** MainGame脚本 */
mainGame: MainGame = null;
/** 游戏分数 */
gameScore: number = 1;
/** 平均分 用来计算超越了多少玩家 */
standScore: number = 80;
/** 最高分 用来计算超越了多少玩家 */
gameMaxScore: number = 200;
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "f69a5547-feea-4fbd-ad4d-f04735df5a8b",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,50 @@
import { AudioClip, Material, Prefab, SpriteFrame, Texture2D } from "cc";
import { LoadTools } from "../common/LoadTools";
/** 资源存放的数据类 */
export class ResModel {
/** 单例模式 */
private static _instance: ResModel = new ResModel();
private constructor() { }
public static get _ins() {
return this._instance;
}
/** 存储resArr脚本拖动的图片字典 */
public SpriteFrameDic: Map<string, SpriteFrame> = new Map();
/** 存储resArr脚本拖动的预制体字典 */
public PrefabDic: Map<string, Prefab> = new Map();
/** 存储resArr脚本拖动的音效字典 */
public AudioClipDic: Map<string, AudioClip> = new Map();
/** 存储resArr脚本拖动的材质字典 */
public MaterialDic: Map<string, Material> = new Map();
/** 存储resArr脚本拖动的Texture字典 */
public TextureDic: Map<string, Texture2D> = new Map();
/** resArr清理字典 */
public clearResDic() {
this.AudioClipDic.clear();
this.SpriteFrameDic.clear();
this.PrefabDic.clear();
this.MaterialDic.clear();
this.TextureDic.clear();
}
/** 获取拖动到res 里面的 Texture 资源 */
getTexture(key: string, callFunc?: (age: Texture2D) => {}): Texture2D {
if (this.TextureDic[key]) {
return this.TextureDic[key];
} else {
LoadTools._ins.loadResAny(key + "/texture", Texture2D, callFunc);
}
}
/** 获取拖动到res 里面的 材质资源 */
getMaterial(key: string, callFunc?: (age: Material) => {}) {
if (this.MaterialDic[key]) {
return this.MaterialDic[key];
} else {
LoadTools._ins.loadResAny(key, Material, callFunc);
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "90f35c76-cc50-4872-bd99-7af2500bfdf2",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,39 @@
import { Tools } from "../common/Tools";
import { weiSan } from "../common/weiSanTools";
/** 游戏内 文字翻译相关数据 */
export var WordsModel = {
gameName:{
CN: "", //中文
CHT: "", //繁体
EN: "", //英文
KOR: "", //韩文
JP: "", //日文
TH: "" //泰语
},
overScoreInfo_0: { CN:"只得0分全球独一个",CHT:"只得0分全球獨一個",
EN:"Only 0, the only one in the world!", KOR:"0점밖에 안 돼, 전 세계에서 하나야!"
, JP:"0点しか取れません。世界で唯一です", TH:"มีเพียง <NU>0 จุดหนึ่งในโลก" },
overScoreInfo_1: { CN:"击败了全球",CHT:"擊敗了全球", EN:"Beat ", KOR:"격파", JP:"打ち負かす", TH:"ทำให้พ่ายแพ้" },
overScoreInfo_2: { CN:"的玩家!",CHT:"的玩家!", EN:" of the players!", KOR:"유저!", JP:"のプレイヤー!", TH:"ผู้เล่นของ" },
overTitle_1:{
CN:"我真是太厉害了,我在",CHT:"我真是太厲害了,我在",EN:"I'm really great. I'm in ",
},
overTitle_2:{
CN:"中,",CHT:"中,",EN:". "
},
/** 通过语言 获得文字翻译 */
getStrForLanguage( modelKey:string , languageType?:string){
languageType = languageType || Tools.getLanguageType();
if(this[modelKey] ){
if( this[modelKey][languageType] ){
return this[modelKey][languageType];
}
return this[modelKey]["EN"];
}else{
weiSan.log("没有翻译:" + modelKey);
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "77968eaa-9734-4273-85e4-209355aa1cf5",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "720bc2a0-db9f-49d1-b0cf-a8558640f42f",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,164 @@
import { _decorator, Component, Node, Label, UIOpacity, v3, director, tween } from 'cc';
import { AdManager } from '../ads/AdManager';
import { Tools } from '../common/Tools';
import { weiSan } from '../common/weiSanTools';
import { NetworkManager } from '../manager/NetworkManager';
import { osType, PlatformManager, releaseType } from '../manager/PlatformManager';
import { UIManager } from '../manager/UIManager';
import { GameModel } from '../model/GameModel';
import { WordsModel } from '../model/WordsModel';
const { ccclass, property } = _decorator;
@ccclass('OverUI')
export class OverUI extends UIManager {
@property(Node)
bgSpr!: Node;
@property(Node)
viewNode!: Node;
@property(Label)
scoreLab!: Label;
@property(Label)
maxScoreLab!: Label;
@property(Label)
infoText!: Label;
@property(Node)
moreBtn!: Node;
@property(Node)
againBtn!: Node;
/** 是否可点击 按钮 */
isClick: boolean = false;
onLoad() {
this.bgSpr.getComponent(UIOpacity).opacity = 0;
this.viewNode.position.add(v3(0, 1000, 0));
this.againBtn.scale = v3(0, 0, 0);
this.moreBtn.position.add(v3(-2000, 0, 0));
// GameModel._ins.gameScore = Tools.random(100,200);
this.initShowInfo();
this.addBtnEvent();
// 提交分数 和 显示插屏广告
NetworkManager.sendGameScore(GameModel._ins.gameScore, 1);
AdManager.showIntersAd();
if (PlatformManager.osType != osType.h5) { //非H5平台 隐藏更多游戏按钮
this.moreBtn.active = false;
}
}
// start() {
// }
/** 显示结束页 信息 */
initShowInfo() {
this.scoreLab.string = GameModel._ins.gameScore.toString();
let tempNum = this.getBeatItScore(GameModel._ins.gameScore, GameModel._ins.standScore, GameModel._ins.gameMaxScore);
this.infoText.string = this.getBeatItStr(GameModel._ins.gameScore, tempNum, true);
let maxScore = Tools.getStorage("gameOverMaxScore");
if (!maxScore || maxScore <= GameModel._ins.gameScore) {
maxScore = GameModel._ins.gameScore;
Tools.setStorage("gameOverMaxScore", GameModel._ins.gameScore);
}
this.maxScoreLab.string = maxScore.toString();
if (PlatformManager.osType != osType.h5) { return; }
if (Tools.getLanguageType() == "CN" || Tools.getLanguageType() == "CHT") {
document.title = WordsModel.getStrForLanguage("overTitle_1", "CN") + "<" + WordsModel.getStrForLanguage("gameName", "CN")
+ ">" + WordsModel.getStrForLanguage("overTitle_2", "CN") + this.getBeatItStr(GameModel._ins.gameScore, tempNum, false);
} else {
document.title = WordsModel.getStrForLanguage("overTitle_1", "EN") + "<" + WordsModel.getStrForLanguage("gameName", "EN")
+ ">" + WordsModel.getStrForLanguage("overTitle_2", "EN") + this.getBeatItStr(GameModel._ins.gameScore, tempNum, false);
}
console.log(document.title);
}
addBtnEvent() {
this.againBtn.on("click", () => {
if (!this.isClick) { return; }
this.aginGame();
});
this.moreBtn.on("click", () => {
if (!this.isClick) { return; }
if (PlatformManager.releaseType == releaseType.test_TEST) {
weiSan.log("测试模式 更多游戏Url: " + NetworkManager.moreGameUrl);
return;
}
this.isClick = false;
window.location.href = NetworkManager.moreGameUrl;
});
}
/** 再玩一次 */
aginGame() {
this.isClick = false;
// AdManager.showIntersAd();
UIManager.CloseUI("OverUI");
this.scheduleOnce(() => {
director.preloadScene(GameModel._ins.mainScene, () => {
director.loadScene(GameModel._ins.mainScene);
});
}, 0.2);
}
/**
* 获取击败了 全球多少玩家
* @param gameScore 分数
* @param standScore 平均分
* @param maxScore 最高分
*/
getBeatItScore(gameScore: number, standScore: number, maxScore: number) {
if (gameScore >= maxScore) {
return 100;
}
if (gameScore <= standScore) {
let temp = (gameScore / standScore) * 80 + Tools.random(-3, 3);
return Math.max(Math.floor(temp), 5);
} else {
let temp = 80 + ((gameScore - standScore) / (maxScore - standScore)) * 20 + Tools.random(-3, 3);
return Math.min(Math.floor(temp), 99);
}
};
/**
* 获取击败了 全球多少玩家 文字 tempNum:百分之多少 是否是richText
*/
getBeatItStr(score: number, tempNum: number, isRichText = true): string {
var share_title = WordsModel.getStrForLanguage("overScoreInfo_0");
if (score > 0) {
if (isRichText) {
share_title = WordsModel.getStrForLanguage("overScoreInfo_1") +
tempNum + "%" + WordsModel.getStrForLanguage("overScoreInfo_2");
} else {
share_title = WordsModel.getStrForLanguage("overScoreInfo_1") + tempNum + "%" + WordsModel.getStrForLanguage("overScoreInfo_2");
}
}
return share_title;
};
// update (deltaTime: number) {
// }
public openUI() {
this.bgSpr.getComponent(UIOpacity)!.opacity = 0;
tween(this.bgSpr.getComponent(UIOpacity)).to(0.3, { opacity: 100 }).start();
tween(this.moreBtn).by(0.3, { position: v3(2000, 0, 0) }, { easing: "backOut" }).start();
tween(this.viewNode)
.by(0.3, { position: v3(0, -1000, 0) }, { easing: "backOut" })
.call(() => {
this.isClick = true;
tween(this.againBtn).to(0.3, { scale: v3(1, 1, 1) }, { easing: "backOut" }).start();
}).start();
}
public closeUI() {
tween(this.bgSpr.getComponent(UIOpacity)).to(0.2, { opacity: 0 }).start();
tween(this.viewNode)
.by(0.3, { position: v3(0, 100, 0) }, { easing: "backIn" })
.call(() => {
this.node.destroy();
}).start();
tween(this.moreBtn).by(0.2, { position: v3(-2000, 0, 0) }, { easing: "backIn" }).start();
}
public uiName: string;
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "37f4898e-a6c6-4320-aba4-e4dd276e959c",
"files": [],
"subMetas": {},
"userData": {}
}

12
assets/texture.meta Normal file
View File

@@ -0,0 +1,12 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "7c328e08-8fda-40bf-8feb-2dfce6ba3b88",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

View File

@@ -0,0 +1,12 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "0e76ff52-c7fc-44fe-88a9-89099cbb6e88",
"files": [],
"subMetas": {},
"userData": {
"compressionType": {},
"isRemoteBundle": {}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,134 @@
{
"ver": "1.0.27",
"importer": "image",
"imported": true,
"uuid": "9b914cf0-224a-4a7a-93cc-4ab3906fbeec",
"files": [
".json",
".png"
],
"subMetas": {
"6c48a": {
"importer": "texture",
"uuid": "9b914cf0-224a-4a7a-93cc-4ab3906fbeec@6c48a",
"displayName": "maxIcon",
"id": "6c48a",
"name": "texture",
"userData": {
"wrapModeS": "clamp-to-edge",
"wrapModeT": "clamp-to-edge",
"minfilter": "linear",
"magfilter": "linear",
"mipfilter": "none",
"anisotropy": 0,
"isUuid": true,
"imageUuidOrDatabaseUri": "9b914cf0-224a-4a7a-93cc-4ab3906fbeec",
"visible": false
},
"ver": "1.0.22",
"imported": true,
"files": [
".json"
],
"subMetas": {}
},
"f9941": {
"importer": "sprite-frame",
"uuid": "9b914cf0-224a-4a7a-93cc-4ab3906fbeec@f9941",
"displayName": "maxIcon",
"id": "f9941",
"name": "spriteFrame",
"userData": {
"trimType": "auto",
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 46,
"height": 43,
"rawWidth": 46,
"rawHeight": 43,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"packable": true,
"isUuid": true,
"imageUuidOrDatabaseUri": "9b914cf0-224a-4a7a-93cc-4ab3906fbeec@6c48a",
"atlasUuid": "",
"pixelsToUnit": 100,
"pivotX": 0.5,
"pivotY": 0.5,
"meshType": 0,
"vertices": {
"rawPosition": [
-23,
-21.5,
0,
23,
-21.5,
0,
-23,
21.5,
0,
23,
21.5,
0
],
"indexes": [
0,
1,
2,
2,
1,
3
],
"uv": [
0,
43,
46,
43,
0,
0,
46,
0
],
"nuv": [
0,
0,
1,
0,
0,
1,
1,
1
],
"minPos": [
-23,
-21.5,
0
],
"maxPos": [
23,
21.5,
0
]
}
},
"ver": "1.0.12",
"imported": true,
"files": [
".json"
],
"subMetas": {}
}
},
"userData": {
"hasAlpha": true,
"type": "sprite-frame",
"redirect": "9b914cf0-224a-4a7a-93cc-4ab3906fbeec@6c48a",
"fixAlphaTransparencyArtifacts": false
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,134 @@
{
"ver": "1.0.27",
"importer": "image",
"imported": true,
"uuid": "34ced794-7ae5-40da-b5aa-d6a8fbd39a38",
"files": [
".json",
".png"
],
"subMetas": {
"6c48a": {
"importer": "texture",
"uuid": "34ced794-7ae5-40da-b5aa-d6a8fbd39a38@6c48a",
"displayName": "moreBtn",
"id": "6c48a",
"name": "texture",
"userData": {
"wrapModeS": "clamp-to-edge",
"wrapModeT": "clamp-to-edge",
"minfilter": "linear",
"magfilter": "linear",
"mipfilter": "none",
"anisotropy": 0,
"isUuid": true,
"imageUuidOrDatabaseUri": "34ced794-7ae5-40da-b5aa-d6a8fbd39a38",
"visible": false
},
"ver": "1.0.22",
"imported": true,
"files": [
".json"
],
"subMetas": {}
},
"f9941": {
"importer": "sprite-frame",
"uuid": "34ced794-7ae5-40da-b5aa-d6a8fbd39a38@f9941",
"displayName": "moreBtn",
"id": "f9941",
"name": "spriteFrame",
"userData": {
"trimType": "auto",
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 80,
"height": 81,
"rawWidth": 80,
"rawHeight": 81,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"packable": true,
"isUuid": true,
"imageUuidOrDatabaseUri": "34ced794-7ae5-40da-b5aa-d6a8fbd39a38@6c48a",
"atlasUuid": "",
"pixelsToUnit": 100,
"pivotX": 0.5,
"pivotY": 0.5,
"meshType": 0,
"vertices": {
"rawPosition": [
-40,
-40.5,
0,
40,
-40.5,
0,
-40,
40.5,
0,
40,
40.5,
0
],
"indexes": [
0,
1,
2,
2,
1,
3
],
"uv": [
0,
81,
80,
81,
0,
0,
80,
0
],
"nuv": [
0,
0,
1,
0,
0,
1,
1,
1
],
"minPos": [
-40,
-40.5,
0
],
"maxPos": [
40,
40.5,
0
]
}
},
"ver": "1.0.12",
"imported": true,
"files": [
".json"
],
"subMetas": {}
}
},
"userData": {
"hasAlpha": true,
"type": "sprite-frame",
"redirect": "34ced794-7ae5-40da-b5aa-d6a8fbd39a38@6c48a",
"fixAlphaTransparencyArtifacts": false
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,134 @@
{
"ver": "1.0.27",
"importer": "image",
"imported": true,
"uuid": "73b3655a-f0d7-4f43-b215-efb04b1633be",
"files": [
".json",
".png"
],
"subMetas": {
"6c48a": {
"importer": "texture",
"uuid": "73b3655a-f0d7-4f43-b215-efb04b1633be@6c48a",
"displayName": "startBtn",
"id": "6c48a",
"name": "texture",
"userData": {
"wrapModeS": "clamp-to-edge",
"wrapModeT": "clamp-to-edge",
"minfilter": "linear",
"magfilter": "linear",
"mipfilter": "none",
"anisotropy": 0,
"isUuid": true,
"imageUuidOrDatabaseUri": "73b3655a-f0d7-4f43-b215-efb04b1633be",
"visible": false
},
"ver": "1.0.22",
"imported": true,
"files": [
".json"
],
"subMetas": {}
},
"f9941": {
"importer": "sprite-frame",
"uuid": "73b3655a-f0d7-4f43-b215-efb04b1633be@f9941",
"displayName": "startBtn",
"id": "f9941",
"name": "spriteFrame",
"userData": {
"trimType": "auto",
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 175,
"height": 175,
"rawWidth": 175,
"rawHeight": 175,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"packable": true,
"isUuid": true,
"imageUuidOrDatabaseUri": "73b3655a-f0d7-4f43-b215-efb04b1633be@6c48a",
"atlasUuid": "",
"pixelsToUnit": 100,
"pivotX": 0.5,
"pivotY": 0.5,
"meshType": 0,
"vertices": {
"rawPosition": [
-87.5,
-87.5,
0,
87.5,
-87.5,
0,
-87.5,
87.5,
0,
87.5,
87.5,
0
],
"indexes": [
0,
1,
2,
2,
1,
3
],
"uv": [
0,
175,
175,
175,
0,
0,
175,
0
],
"nuv": [
0,
0,
1,
0,
0,
1,
1,
1
],
"minPos": [
-87.5,
-87.5,
0
],
"maxPos": [
87.5,
87.5,
0
]
}
},
"ver": "1.0.12",
"imported": true,
"files": [
".json"
],
"subMetas": {}
}
},
"userData": {
"hasAlpha": true,
"type": "sprite-frame",
"redirect": "73b3655a-f0d7-4f43-b215-efb04b1633be@6c48a",
"fixAlphaTransparencyArtifacts": false
}
}

Some files were not shown because too many files have changed in this diff Show More