mirror of
https://gitee.com/devil_root/snake.git
synced 2025-04-09 16:51:10 +00:00
the demo
This commit is contained in:
parent
7bff1c70f1
commit
e199970fb5
.DS_Store
.creator
.gitignoreassets
.DS_StoreScripts.meta
Scripts
.DS_StoreRVO.meta
animation.metaRVO
Test.metaTest
MonsterFactory.tsMonsterFactory.ts.metaTest2.tsTest2.ts.metamonsterCtl.tsmonsterCtl.ts.metamonsterEffectCtl.tsmonsterEffectCtl.ts.meta
camera.metacamera
common.metacommon
conf.metacore.metacore
logics.metalogics
mng.metamng
rolectl.metarolectl
ui.metaanimation
res.metares
resources.metaresources
BIN
.DS_Store
vendored
Normal file
BIN
.DS_Store
vendored
Normal file
Binary file not shown.
@ -0,0 +1,2 @@
|
||||
[InternetShortcut]
|
||||
URL=https://docs.cocos.com/creator/manual/en/scripting/setup.html#custom-script-template
|
5
.creator/default-meta.json
Normal file
5
.creator/default-meta.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"image": {
|
||||
"type": "sprite-frame"
|
||||
}
|
||||
}
|
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
#///////////////////////////
|
||||
# Cocos Creator 3D Project
|
||||
#///////////////////////////
|
||||
library/
|
||||
temp/
|
||||
local/
|
||||
build/
|
||||
profiles/
|
||||
native
|
||||
#//////////////////////////
|
||||
# NPM
|
||||
#//////////////////////////
|
||||
node_modules/
|
||||
|
||||
#//////////////////////////
|
||||
# VSCode
|
||||
#//////////////////////////
|
||||
.vscode/
|
||||
|
||||
#//////////////////////////
|
||||
# WebStorm
|
||||
#//////////////////////////
|
||||
.idea/
|
BIN
assets/.DS_Store
vendored
Normal file
BIN
assets/.DS_Store
vendored
Normal file
Binary file not shown.
12
assets/Scripts.meta
Normal file
12
assets/Scripts.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "823bea19-caeb-456d-aff2-883004ae2bf0",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
BIN
assets/Scripts/.DS_Store
vendored
Normal file
BIN
assets/Scripts/.DS_Store
vendored
Normal file
Binary file not shown.
12
assets/Scripts/RVO.meta
Normal file
12
assets/Scripts/RVO.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "7a699aa3-3f5a-41b9-87db-13203bc13a10",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
557
assets/Scripts/RVO/Agent.ts
Normal file
557
assets/Scripts/RVO/Agent.ts
Normal file
@ -0,0 +1,557 @@
|
||||
import { KeyValuePair, Line, Obstacle, RVOMath, Vector2 } from "./Common";
|
||||
import { Simulator } from "./Simulator";
|
||||
|
||||
|
||||
export class Agent {
|
||||
agentNeighbors_: KeyValuePair<number, Agent>[] = [];
|
||||
obstaclNeighbors_: KeyValuePair<number, Obstacle>[] = [];
|
||||
orcaLines_: Line[] = [];
|
||||
position_: Vector2 = new Vector2(0, 0);
|
||||
prefVelocity_: Vector2 = new Vector2(0, 0);
|
||||
velocity_: Vector2 = new Vector2(0, 0);
|
||||
id: number = 0;
|
||||
maxNeighbors_: number = 0;
|
||||
maxSpeed_: number = 0.0;
|
||||
private _neighborDist: number = 0.0;
|
||||
public get neighborDist(): number {
|
||||
return this._neighborDist;
|
||||
}
|
||||
public set neighborDist(value: number) {
|
||||
this._neighborDist = value;
|
||||
}
|
||||
radius_: number = 0.0;
|
||||
timeHorizon: number = 0.0;
|
||||
timeHorizonObst: number = 0.0;
|
||||
newVelocity_: Vector2 = new Vector2(0, 0);
|
||||
mass: number = 1;
|
||||
|
||||
|
||||
computeNeighbors(sim: Simulator) {
|
||||
this.obstaclNeighbors_.length = 0;
|
||||
let rangeSq = (this.timeHorizonObst * this.maxSpeed_ + this.radius_) ** 2;
|
||||
sim.kdTree.computeObstacleNeighbors(this, rangeSq);
|
||||
|
||||
this.agentNeighbors_.length = 0;
|
||||
|
||||
if (this.maxNeighbors_ > 0) {
|
||||
rangeSq = this.neighborDist ** 2;
|
||||
rangeSq = sim.kdTree.computeAgentNeighbors(this, rangeSq);
|
||||
}
|
||||
}
|
||||
|
||||
/* Search for the best new velocity. */
|
||||
computeNewVelocity(dt: number) {
|
||||
this.orcaLines_.length = 0;
|
||||
let orcaLines = this.orcaLines_;
|
||||
|
||||
let invTimeHorizonObst = 1.0 / this.timeHorizonObst;
|
||||
|
||||
/* Create obstacle ORCA lines. */
|
||||
for (let i = 0; i < this.obstaclNeighbors_.length; ++i) {
|
||||
let obstacle1 = this.obstaclNeighbors_[i].value;
|
||||
let obstacle2 = obstacle1.next;
|
||||
|
||||
let relativePosition1 = obstacle1.point.minus(this.position_);
|
||||
let relativePosition2 = obstacle2.point.minus(this.position_);
|
||||
|
||||
/*
|
||||
* Check if velocity obstacle of obstacle is already taken care of by
|
||||
* previously constructed obstacle ORCA lines.
|
||||
*/
|
||||
let alreadyCovered = false;
|
||||
|
||||
for (let j = 0; j < orcaLines.length; ++j) {
|
||||
if (RVOMath.det(relativePosition1.scale(invTimeHorizonObst).minus(orcaLines[j].point), orcaLines[j].direction) - invTimeHorizonObst * this.radius_ >= -RVOMath.RVO_EPSILON
|
||||
&& RVOMath.det(relativePosition2.scale(invTimeHorizonObst).minus(orcaLines[j].point), orcaLines[j].direction) - invTimeHorizonObst * this.radius_ >= -RVOMath.RVO_EPSILON) {
|
||||
|
||||
alreadyCovered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (alreadyCovered) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Not yet covered. Check for collisions. */
|
||||
|
||||
let distSq1 = RVOMath.absSq(relativePosition1);
|
||||
let distSq2 = RVOMath.absSq(relativePosition2);
|
||||
|
||||
let radiusSq = RVOMath.sqr(this.radius_);
|
||||
|
||||
let obstacleVector = obstacle2.point.minus(obstacle1.point);
|
||||
let s = relativePosition1.scale(-1).multiply(obstacleVector) / RVOMath.absSq(obstacleVector);
|
||||
let distSqLine = RVOMath.absSq(relativePosition1.scale(-1).minus(obstacleVector.scale(s)));
|
||||
|
||||
let line = new Line();
|
||||
if (s < 0 && distSq1 <= radiusSq) {
|
||||
/* Collision with left vertex. Ignore if non-convex. */
|
||||
if (obstacle1.convex) {
|
||||
line.point = new Vector2(0, 0);
|
||||
line.direction = RVOMath.normalize(new Vector2(-relativePosition1.y, relativePosition1.x));
|
||||
orcaLines.push(line);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (s > 1 && distSq2 <= radiusSq) {
|
||||
/* Collision with right vertex. Ignore if non-convex
|
||||
* or if it will be taken care of by neighoring obstace */
|
||||
if (obstacle2.convex && RVOMath.det(relativePosition2, obstacle2.direction) >= 0) {
|
||||
line.point = new Vector2(0, 0);
|
||||
line.direction = RVOMath.normalize(new Vector2(-relativePosition2.y, relativePosition2.x));
|
||||
orcaLines.push(line);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (s >= 0 && s <= 1 && distSqLine <= radiusSq) {
|
||||
/* Collision with obstacle segment. */
|
||||
line.point = new Vector2(0, 0);
|
||||
line.direction = obstacle1.direction.scale(-1);
|
||||
orcaLines.push(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* No collision.
|
||||
* Compute legs. When obliquely viewed, both legs can come from a single
|
||||
* vertex. Legs extend cut-off line when nonconvex vertex.
|
||||
*/
|
||||
let leftLegDirection: Vector2, rightLegDirection: Vector2;
|
||||
|
||||
if (s < 0 && distSqLine <= radiusSq) {
|
||||
/*
|
||||
* Obstacle viewed obliquely so that left vertex
|
||||
* defines velocity obstacle.
|
||||
*/
|
||||
if (!obstacle1.convex) {
|
||||
/* Ignore obstacle. */
|
||||
continue;
|
||||
}
|
||||
|
||||
obstacle2 = obstacle1;
|
||||
|
||||
let leg1 = Math.sqrt(distSq1 - radiusSq);
|
||||
leftLegDirection = (new Vector2(relativePosition1.x * leg1 - relativePosition1.y * this.radius_, relativePosition1.x * this.radius_ + relativePosition1.y * leg1)).scale(1 / distSq1);
|
||||
rightLegDirection = (new Vector2(relativePosition1.x * leg1 + relativePosition1.y * this.radius_, -relativePosition1.x * this.radius_ + relativePosition1.y * leg1)).scale(1 / distSq1);
|
||||
}
|
||||
else if (s > 1 && distSqLine <= radiusSq) {
|
||||
/*
|
||||
* Obstacle viewed obliquely so that
|
||||
* right vertex defines velocity obstacle.
|
||||
*/
|
||||
if (!obstacle2.convex) {
|
||||
/* Ignore obstacle. */
|
||||
continue;
|
||||
}
|
||||
|
||||
obstacle1 = obstacle2;
|
||||
|
||||
let leg2 = Math.sqrt(distSq2 - radiusSq);
|
||||
leftLegDirection = (new Vector2(relativePosition2.x * leg2 - relativePosition2.y * this.radius_, relativePosition2.x * this.radius_ + relativePosition2.y * leg2)).scale(1 / distSq2);
|
||||
rightLegDirection = (new Vector2(relativePosition2.x * leg2 + relativePosition2.y * this.radius_, -relativePosition2.x * this.radius_ + relativePosition2.y * leg2)).scale(1 / distSq2);
|
||||
}
|
||||
else {
|
||||
/* Usual situation. */
|
||||
if (obstacle1.convex) {
|
||||
let leg1 = Math.sqrt(distSq1 - radiusSq);
|
||||
leftLegDirection = (new Vector2(relativePosition1.x * leg1 - relativePosition1.y * this.radius_, relativePosition1.x * this.radius_ + relativePosition1.y * leg1)).scale(1 / distSq1);
|
||||
}
|
||||
else {
|
||||
/* Left vertex non-convex; left leg extends cut-off line. */
|
||||
leftLegDirection = obstacle1.direction.scale(-1);
|
||||
}
|
||||
|
||||
if (obstacle2.convex) {
|
||||
let leg2 = Math.sqrt(distSq2 - radiusSq);
|
||||
rightLegDirection = (new Vector2(relativePosition2.x * leg2 + relativePosition2.y * this.radius_, -relativePosition2.x * this.radius_ + relativePosition2.y * leg2)).scale(1 / distSq2);
|
||||
}
|
||||
else {
|
||||
/* Right vertex non-convex; right leg extends cut-off line. */
|
||||
rightLegDirection = obstacle1.direction;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Legs can never point into neighboring edge when convex vertex,
|
||||
* take cutoff-line of neighboring edge instead. If velocity projected on
|
||||
* "foreign" leg, no constraint is added.
|
||||
*/
|
||||
|
||||
let leftNeighbor = obstacle1.previous;
|
||||
|
||||
let isLeftLegForeign = false;
|
||||
let isRightLegForeign = false;
|
||||
|
||||
if (obstacle1.convex && RVOMath.det(leftLegDirection, leftNeighbor.direction.scale(-1)) >= 0.0) {
|
||||
/* Left leg points into obstacle. */
|
||||
leftLegDirection = leftNeighbor.direction.scale(-1);
|
||||
isLeftLegForeign = true;
|
||||
}
|
||||
|
||||
if (obstacle2.convex && RVOMath.det(rightLegDirection, obstacle2.direction) <= 0.0) {
|
||||
/* Right leg points into obstacle. */
|
||||
rightLegDirection = obstacle2.direction;
|
||||
isRightLegForeign = true;
|
||||
}
|
||||
|
||||
/* Compute cut-off centers. */
|
||||
let leftCutoff = obstacle1.point.minus(this.position_).scale(invTimeHorizonObst);
|
||||
let rightCutoff = obstacle2.point.minus(this.position_).scale(invTimeHorizonObst);
|
||||
let cutoffVec = rightCutoff.minus(leftCutoff);
|
||||
|
||||
/* Project current velocity on velocity obstacle. */
|
||||
|
||||
/* Check if current velocity is projected on cutoff circles. */
|
||||
let t = (obstacle1 == obstacle2) ? 0.5 : this.velocity_.minus(leftCutoff).multiply(cutoffVec) / RVOMath.absSq(cutoffVec);
|
||||
let tLeft = this.velocity_.minus(leftCutoff).multiply(leftLegDirection);
|
||||
let tRight = this.velocity_.minus(rightCutoff).multiply(rightLegDirection);
|
||||
|
||||
if ((t < 0.0 && tLeft < 0.0) || (obstacle1 == obstacle2 && tLeft < 0.0 && tRight < 0.0)) {
|
||||
/* Project on left cut-off circle. */
|
||||
let unitW = RVOMath.normalize(this.velocity_.minus(leftCutoff));
|
||||
|
||||
line.direction = new Vector2(unitW.y, -unitW.x);
|
||||
line.point = leftCutoff.plus(unitW.scale(this.radius_ * invTimeHorizonObst));
|
||||
orcaLines.push(line);
|
||||
continue;
|
||||
}
|
||||
else if (t > 1.0 && tRight < 0.0) {
|
||||
/* Project on right cut-off circle. */
|
||||
let unitW = RVOMath.normalize(this.velocity_.minus(rightCutoff));
|
||||
|
||||
line.direction = new Vector2(unitW.y, -unitW.x);
|
||||
line.point = rightCutoff.plus(unitW.scale(this.radius_ * invTimeHorizonObst));
|
||||
orcaLines.push(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Project on left leg, right leg, or cut-off line, whichever is closest
|
||||
* to velocity.
|
||||
*/
|
||||
let distSqCutoff = ((t < 0.0 || t > 1.0 || obstacle1 == obstacle2) ? Infinity : RVOMath.absSq(this.velocity_.minus(cutoffVec.scale(t).plus(leftCutoff))));
|
||||
let distSqLeft = ((tLeft < 0.0) ? Infinity : RVOMath.absSq(this.velocity_.minus(leftLegDirection.scale(tLeft).plus(leftCutoff))));
|
||||
let distSqRight = ((tRight < 0.0) ? Infinity : RVOMath.absSq(this.velocity_.minus(rightLegDirection.scale(tRight).plus(rightCutoff))));
|
||||
|
||||
if (distSqCutoff <= distSqLeft && distSqCutoff <= distSqRight) {
|
||||
/* Project on cut-off line. */
|
||||
line.direction = obstacle1.direction.scale(-1);
|
||||
let aux = new Vector2(-line.direction.y, line.direction.x);
|
||||
line.point = aux.scale(this.radius_ * invTimeHorizonObst).plus(leftCutoff);
|
||||
orcaLines.push(line);
|
||||
continue;
|
||||
}
|
||||
else if (distSqLeft <= distSqRight) {
|
||||
/* Project on left leg. */
|
||||
if (isLeftLegForeign) {
|
||||
continue;
|
||||
}
|
||||
|
||||
line.direction = leftLegDirection;
|
||||
let aux = new Vector2(-line.direction.y, line.direction.x);
|
||||
line.point = aux.scale(this.radius_ * invTimeHorizonObst).plus(leftCutoff);
|
||||
orcaLines.push(line);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
/* Project on right leg. */
|
||||
if (isRightLegForeign) {
|
||||
continue;
|
||||
}
|
||||
|
||||
line.direction = rightLegDirection.scale(-1);
|
||||
let aux = new Vector2(-line.direction.y, line.direction.x);
|
||||
line.point = aux.scale(this.radius_ * invTimeHorizonObst).plus(rightCutoff);
|
||||
orcaLines.push(line);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let numObstLines = orcaLines.length;
|
||||
|
||||
let invTimeHorizon = 1.0 / this.timeHorizon;
|
||||
|
||||
/* Create agent ORCA lines. */
|
||||
for (let i = 0; i < this.agentNeighbors_.length; ++i) {
|
||||
let other = this.agentNeighbors_[i].value;
|
||||
|
||||
let relativePosition = other.position_.minus(this.position_);
|
||||
|
||||
// mass
|
||||
let massRatio = (other.mass / (this.mass + other.mass));
|
||||
let neighborMassRatio = (this.mass / (this.mass + other.mass));
|
||||
|
||||
let velocityOpt = (massRatio >= 0.5 ? (this.velocity_.minus(this.velocity_.scale(massRatio)).scale(2)) : this.prefVelocity_.plus(this.velocity_.minus(this.prefVelocity_).scale(massRatio * 2)));
|
||||
let neighborVelocityOpt = (neighborMassRatio >= 0.5 ? other.velocity_.scale(2).scale(1 - neighborMassRatio) : (other.prefVelocity_.plus(other.velocity_.minus(other.prefVelocity_).scale(2 * neighborMassRatio))));
|
||||
|
||||
let relativeVelocity = velocityOpt.minus(neighborVelocityOpt);//this.velocity.minus(other.velocity);
|
||||
let distSq = RVOMath.absSq(relativePosition);
|
||||
let combinedRadius = this.radius_ + other.radius_;
|
||||
let combinedRadiusSq = RVOMath.sqr(combinedRadius);
|
||||
|
||||
let line = new Line();
|
||||
let u: Vector2;
|
||||
|
||||
if (distSq > combinedRadiusSq) {
|
||||
/* No collision. */
|
||||
let w = relativeVelocity.minus(relativePosition.scale(invTimeHorizon)); // Vector
|
||||
/* Vector from cutoff center to relative velocity. */
|
||||
let wLengthSq = RVOMath.absSq(w);
|
||||
|
||||
let dotProduct1 = w.multiply(relativePosition);
|
||||
|
||||
if (dotProduct1 < 0.0 && RVOMath.sqr(dotProduct1) > combinedRadiusSq * wLengthSq) {
|
||||
/* Project on cut-off circle. */
|
||||
let wLength = Math.sqrt(wLengthSq);
|
||||
let unitW = w.scale(1 / wLength);
|
||||
|
||||
line.direction = new Vector2(unitW.y, -unitW.x);
|
||||
u = unitW.scale(combinedRadius * invTimeHorizon - wLength);
|
||||
}
|
||||
else {
|
||||
/* Project on legs. */
|
||||
let leg = Math.sqrt(distSq - combinedRadiusSq);
|
||||
|
||||
if (RVOMath.det(relativePosition, w) > 0.0) {
|
||||
/* Project on left leg. */
|
||||
let aux = new Vector2(relativePosition.x * leg - relativePosition.y * combinedRadius, relativePosition.x * combinedRadius + relativePosition.y * leg);
|
||||
line.direction = aux.scale(1 / distSq);
|
||||
}
|
||||
else {
|
||||
/* Project on right leg. */
|
||||
let aux = new Vector2(relativePosition.x * leg + relativePosition.y * combinedRadius, -relativePosition.x * combinedRadius + relativePosition.y * leg);
|
||||
line.direction = aux.scale(-1 / distSq);
|
||||
}
|
||||
|
||||
let dotProduct2 = relativeVelocity.multiply(line.direction);
|
||||
u = line.direction.scale(dotProduct2).minus(relativeVelocity);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Collision. Project on cut-off circle of time timeStep. */
|
||||
let invTimeStep = 1.0 / dt;
|
||||
|
||||
/* Vector from cutoff center to relative velocity. */
|
||||
let w = relativeVelocity.minus(relativePosition.scale(invTimeStep));
|
||||
|
||||
let wLength = RVOMath.abs(w);
|
||||
let unitW = w.scale(1 / wLength);
|
||||
|
||||
line.direction = new Vector2(unitW.y, -unitW.x);
|
||||
u = unitW.scale(combinedRadius * invTimeStep - wLength);
|
||||
}
|
||||
|
||||
|
||||
// line.point = u.scale(0.5).plus(this.velocity);
|
||||
line.point = velocityOpt.plus(u.scale(massRatio));
|
||||
orcaLines.push(line);
|
||||
}
|
||||
|
||||
let lineFail = this.linearProgram2(orcaLines, this.maxSpeed_, this.prefVelocity_, false, this.newVelocity_);
|
||||
|
||||
if (lineFail < orcaLines.length) {
|
||||
this.linearProgram3(orcaLines, numObstLines, lineFail, this.maxSpeed_, this.newVelocity_);
|
||||
}
|
||||
}
|
||||
|
||||
insertAgentNeighbor(agent: Agent, rangeSq: number) {
|
||||
if (this != agent) {
|
||||
let distSq = RVOMath.absSq(this.position_.minus(agent.position_));
|
||||
|
||||
if (distSq < rangeSq) {
|
||||
if (this.agentNeighbors_.length < this.maxNeighbors_) {
|
||||
this.agentNeighbors_.push(new KeyValuePair(distSq, agent));
|
||||
}
|
||||
let i = this.agentNeighbors_.length - 1;
|
||||
while (i != 0 && distSq < this.agentNeighbors_[i - 1].key) {
|
||||
this.agentNeighbors_[i] = this.agentNeighbors_[i - 1];
|
||||
--i;
|
||||
}
|
||||
this.agentNeighbors_[i] = new KeyValuePair<number, Agent>(distSq, agent);
|
||||
|
||||
if (this.agentNeighbors_.length == this.maxNeighbors_) {
|
||||
rangeSq = this.agentNeighbors_[this.agentNeighbors_.length - 1].key;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rangeSq;
|
||||
}
|
||||
|
||||
insertObstacleNeighbor(obstacle: Obstacle, rangeSq: number) {
|
||||
let nextObstacle = obstacle.next;
|
||||
|
||||
let distSq = RVOMath.distSqPointLineSegment(obstacle.point, nextObstacle.point, this.position_);
|
||||
|
||||
if (distSq < rangeSq) {
|
||||
this.obstaclNeighbors_.push(new KeyValuePair<number, Obstacle>(distSq, obstacle));
|
||||
|
||||
let i = this.obstaclNeighbors_.length - 1;
|
||||
while (i != 0 && distSq < this.obstaclNeighbors_[i - 1].key) {
|
||||
this.obstaclNeighbors_[i] = this.obstaclNeighbors_[i - 1];
|
||||
--i;
|
||||
}
|
||||
this.obstaclNeighbors_[i] = new KeyValuePair<number, Obstacle>(distSq, obstacle);
|
||||
}
|
||||
}
|
||||
|
||||
update(dt: number) {
|
||||
this.velocity_.copy(this.newVelocity_);
|
||||
this.position_.copy(this.position_.plus(this.velocity_.scale(dt)));
|
||||
};
|
||||
|
||||
linearProgram1(lines: Line[], lineNo: number, radius: number, optVelocity: Vector2, directionOpt: boolean, result: Vector2) {
|
||||
let dotProduct = lines[lineNo].point.multiply(lines[lineNo].direction);
|
||||
let discriminant = RVOMath.sqr(dotProduct) + RVOMath.sqr(radius) - RVOMath.absSq(lines[lineNo].point);
|
||||
|
||||
if (discriminant < 0.0) {
|
||||
/* Max speed circle fully invalidates line lineNo. */
|
||||
return false;
|
||||
}
|
||||
|
||||
let sqrtDiscriminant = Math.sqrt(discriminant);
|
||||
let tLeft = -dotProduct - sqrtDiscriminant;
|
||||
let tRight = -dotProduct + sqrtDiscriminant;
|
||||
|
||||
for (let i = 0; i < lineNo; ++i) {
|
||||
let denominator = RVOMath.det(lines[lineNo].direction, lines[i].direction);
|
||||
let numerator = RVOMath.det(lines[i].direction, lines[lineNo].point.minus(lines[i].point));
|
||||
|
||||
if (Math.abs(denominator) <= RVOMath.RVO_EPSILON) {
|
||||
/* Lines lineNo and i are (almost) parallel. */
|
||||
if (numerator < 0.0) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let t = numerator / denominator;
|
||||
|
||||
if (denominator >= 0.0) {
|
||||
/* Line i bounds line lineNo on the right. */
|
||||
tRight = Math.min(tRight, t);
|
||||
}
|
||||
else {
|
||||
/* Line i bounds line lineNo on the left. */
|
||||
tLeft = Math.max(tLeft, t);
|
||||
}
|
||||
|
||||
if (tLeft > tRight) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (directionOpt) {
|
||||
if (optVelocity.multiply(lines[lineNo].direction) > 0.0) {
|
||||
// Take right extreme
|
||||
result.copy(lines[lineNo].point.plus(lines[lineNo].direction.scale(tRight)));
|
||||
}
|
||||
else {
|
||||
// Take left extreme.
|
||||
result.copy(lines[lineNo].point.plus(lines[lineNo].direction.scale(tLeft)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Optimize closest point
|
||||
let t = lines[lineNo].direction.multiply(optVelocity.minus(lines[lineNo].point));
|
||||
if (t < tLeft) {
|
||||
result.copy(lines[lineNo].point.plus(lines[lineNo].direction.scale(tLeft)));
|
||||
}
|
||||
else if (t > tRight) {
|
||||
result.copy(lines[lineNo].point.plus(lines[lineNo].direction.scale(tRight)));
|
||||
}
|
||||
else {
|
||||
result.copy(lines[lineNo].point.plus(lines[lineNo].direction.scale(t)));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
linearProgram2(lines: Line[], radius: number, optVelocity: Vector2, directionOpt: boolean, result: Vector2) {
|
||||
// directionOpt 第一次为false,第二次为true,directionOpt主要用在 linearProgram1 里面
|
||||
if (directionOpt) {
|
||||
/*
|
||||
* Optimize direction. Note that the optimization velocity is of unit
|
||||
* length in this case.
|
||||
*/
|
||||
result.copy(optVelocity.scale(radius));
|
||||
}
|
||||
else if (RVOMath.absSq(optVelocity) > RVOMath.sqr(radius)) {
|
||||
/* Optimize closest point and outside circle. */
|
||||
result.copy(RVOMath.normalize(optVelocity).scale(radius));
|
||||
}
|
||||
else {
|
||||
/* Optimize closest point and inside circle. */
|
||||
result.copy(optVelocity);
|
||||
}
|
||||
|
||||
for (let i = 0; i < lines.length; ++i) {
|
||||
if (RVOMath.det(lines[i].direction, lines[i].point.minus(result)) > 0.0) {
|
||||
/* Result does not satisfy constraint i. Compute new optimal result. */
|
||||
let tempResult = result.clone();
|
||||
if (!this.linearProgram1(lines, i, radius, optVelocity, directionOpt, result)) {
|
||||
result.copy(tempResult);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lines.length;
|
||||
}
|
||||
|
||||
linearProgram3(lines: Line[], numObstLines: number, beginLine: number, radius: number, result: Vector2) {
|
||||
let distance = 0.0;
|
||||
// 遍历所有剩余ORCA线
|
||||
for (let i = beginLine; i < lines.length; ++i) {
|
||||
// 每一条 ORCA 线都需要精确的做出处理,distance 为 最大违规的速度
|
||||
if (RVOMath.det(lines[i].direction, lines[i].point.minus(result)) > distance) {
|
||||
/* Result does not satisfy constraint of line i. */
|
||||
//std::vector<Line> projLines(lines.begin(), lines.begin() + numObstLines);
|
||||
let projLines = []; // new List<Line>();
|
||||
// 1.静态阻挡的orca线直接加到projLines中
|
||||
for (let ii = 0; ii < numObstLines; ++ii) {
|
||||
projLines.push(lines[ii]);
|
||||
}
|
||||
// 2.动态阻挡的orca线需要重新计算line,从第一个非静态阻挡到当前的orca线
|
||||
for (let j = numObstLines; j < i; ++j) {
|
||||
let line = new Line();
|
||||
|
||||
let determinant = RVOMath.det(lines[i].direction, lines[j].direction);
|
||||
|
||||
if (Math.abs(determinant) <= RVOMath.RVO_EPSILON) {
|
||||
/* Line i and line j are parallel. */
|
||||
if (lines[i].direction.multiply(lines[j].direction) > 0.0) {
|
||||
/* Line i and line j point in the same direction. */
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
/* Line i and line j point in opposite direction. */
|
||||
line.point = lines[i].point.plus(lines[j].point).scale(0.5);
|
||||
}
|
||||
}
|
||||
else {
|
||||
line.point = lines[i].point.plus(lines[i].direction.scale(RVOMath.det(lines[j].direction, lines[i].point.minus(lines[j].point)) / determinant));
|
||||
}
|
||||
|
||||
line.direction = RVOMath.normalize(lines[j].direction.minus(lines[i].direction));
|
||||
projLines.push(line);
|
||||
}
|
||||
|
||||
let tempResult = result.clone();
|
||||
if (this.linearProgram2(projLines, radius, new Vector2(-lines[i].direction.y, lines[i].direction.x), true, result) < projLines.length) {
|
||||
/* This should in principle not happen. The result is by definition
|
||||
* already in the feasible region of this linear program. If it fails,
|
||||
* it is due to small floating point error, and the current result is
|
||||
* kept.
|
||||
*/
|
||||
result.copy(tempResult);
|
||||
}
|
||||
|
||||
distance = RVOMath.det(lines[i].direction, lines[i].point.minus(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
assets/Scripts/RVO/Agent.ts.meta
Normal file
12
assets/Scripts/RVO/Agent.ts.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "99d7f5fd-fae6-4106-ba7a-93c1e298d001",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"moduleId": "project:///assets/Scripts/GameCore/RVO2/Agent.js",
|
||||
"simulateGlobals": []
|
||||
}
|
||||
}
|
117
assets/Scripts/RVO/Common.ts
Normal file
117
assets/Scripts/RVO/Common.ts
Normal file
@ -0,0 +1,117 @@
|
||||
import { Vec2 } from "cc";
|
||||
|
||||
export class Vector2 {
|
||||
x = 0;
|
||||
y = 0;
|
||||
|
||||
constructor(x: number, y: number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
plus(vector: Vector2) {
|
||||
return new Vector2(this.x + vector.x, this.y + vector.y);
|
||||
}
|
||||
|
||||
minus(vector: Vector2) {
|
||||
return new Vector2(this.x - vector.x, this.y - vector.y);
|
||||
}
|
||||
|
||||
multiply(vector: Vector2) {
|
||||
return this.x * vector.x + this.y * vector.y;
|
||||
}
|
||||
|
||||
scale(k: number) {
|
||||
return new Vector2(this.x * k, this.y * k);
|
||||
}
|
||||
|
||||
copy(v: Vector2 | Vec2) {
|
||||
this.x = v.x;
|
||||
this.y = v.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new Vector2(this.x, this.y);
|
||||
}
|
||||
|
||||
substract(out: Vector2, other: Vector2) {
|
||||
out.x -= other.x;
|
||||
out.y -= other.y;
|
||||
return out;
|
||||
}
|
||||
|
||||
lengthSqr() {
|
||||
return this.x ** 2 + this.y ** 2;
|
||||
}
|
||||
}
|
||||
|
||||
export class Obstacle {
|
||||
next: Obstacle;
|
||||
previous: Obstacle;
|
||||
direction: Vector2;
|
||||
point: Vector2;
|
||||
id: number;
|
||||
convex: boolean;
|
||||
}
|
||||
|
||||
export class Line {
|
||||
point: Vector2;
|
||||
direction: Vector2;
|
||||
}
|
||||
|
||||
export class KeyValuePair<K, V> {
|
||||
key: K;
|
||||
value: V;
|
||||
constructor(key: K, value: V) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
export class RVOMath {
|
||||
|
||||
static RVO_EPSILON = 0.00001;
|
||||
|
||||
static absSq(v: Vector2) {
|
||||
return v.multiply(v);
|
||||
};
|
||||
|
||||
static normalize(v: Vector2) {
|
||||
return v.scale(1 / RVOMath.abs(v)); // v / abs(v)
|
||||
};
|
||||
|
||||
static distSqPointLineSegment(vector1: Vector2, vector2: Vector2, vector3: Vector2) {
|
||||
let aux1 = vector3.minus(vector1);
|
||||
let aux2 = vector2.minus(vector1);
|
||||
|
||||
let r = aux1.multiply(aux2) / RVOMath.absSq(aux2);
|
||||
|
||||
if (r < 0) {
|
||||
return RVOMath.absSq(aux1);
|
||||
}
|
||||
else if (r > 1) {
|
||||
return RVOMath.absSq(vector3.minus(vector2));
|
||||
}
|
||||
else {
|
||||
return RVOMath.absSq(vector3.minus(vector1.plus(aux2.scale(r))));
|
||||
}
|
||||
};
|
||||
|
||||
static sqr(p: number) {
|
||||
return p * p;
|
||||
};
|
||||
|
||||
static det(v1: Vector2, v2: Vector2) {
|
||||
return v1.x * v2.y - v1.y * v2.x;
|
||||
};
|
||||
|
||||
static abs(v: Vector2) {
|
||||
return Math.sqrt(RVOMath.absSq(v));
|
||||
};
|
||||
|
||||
static leftOf(a: Vector2, b: Vector2, c: Vector2) {
|
||||
return RVOMath.det(a.minus(c), b.minus(a));
|
||||
};
|
||||
|
||||
}
|
12
assets/Scripts/RVO/Common.ts.meta
Normal file
12
assets/Scripts/RVO/Common.ts.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "b21abffd-42c6-4d43-bbe0-0edd2fb59f53",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"moduleId": "project:///assets/Scripts/GameCore/RVO2/Common.js",
|
||||
"simulateGlobals": []
|
||||
}
|
||||
}
|
236
assets/Scripts/RVO/Simulator.ts
Normal file
236
assets/Scripts/RVO/Simulator.ts
Normal file
@ -0,0 +1,236 @@
|
||||
import { Vec2 } from "cc";
|
||||
import { Agent } from "./Agent";
|
||||
import { Obstacle, RVOMath, Vector2 } from "./Common";
|
||||
import { KdTree } from "./kdtree";
|
||||
|
||||
|
||||
export class Simulator {
|
||||
private agentId: number = 0;
|
||||
private agentIdLst: number[] = [];
|
||||
aid2agent: { [key: string]: Agent } = Object.create(null);
|
||||
|
||||
obstacles: Obstacle[] = [];
|
||||
kdTree: KdTree = new KdTree();
|
||||
|
||||
|
||||
defaultAgent: Agent; // Agent
|
||||
time: number = 0.0;
|
||||
|
||||
private static _inst: Simulator;
|
||||
static get instance(): Simulator {
|
||||
if (!Simulator._inst) {
|
||||
Simulator._inst = new Simulator();
|
||||
}
|
||||
return Simulator._inst;
|
||||
}
|
||||
|
||||
getAgent(idx: number) {
|
||||
return this.aid2agent[this.agentIdLst[idx]];
|
||||
}
|
||||
|
||||
getAgentByAid(aid: number) {
|
||||
return this.aid2agent[aid];
|
||||
}
|
||||
|
||||
getGlobalTime() {
|
||||
return this.time;
|
||||
};
|
||||
|
||||
getNumAgents() {
|
||||
// console.log("getNumAgents ::", this.agentIdLst.length, this.agentIdLst)
|
||||
return this.agentIdLst.length;
|
||||
};
|
||||
|
||||
getAgentAidByIdx(idx: number) {
|
||||
return this.agentIdLst[idx]
|
||||
}
|
||||
|
||||
|
||||
setAgentPrefVelocity(aid: number, velocity: Vector2 | Vec2) {
|
||||
this.aid2agent[aid].prefVelocity_.copy(velocity);
|
||||
}
|
||||
|
||||
getAgentPosition(aid: number) {
|
||||
if (this.aid2agent[aid]) {//为什么移除了 还会进入这个aid的检测
|
||||
return this.aid2agent[aid].position_;
|
||||
}
|
||||
return null
|
||||
|
||||
}
|
||||
|
||||
getAgentPrefVelocity(aid: number) {
|
||||
return this.aid2agent[aid].prefVelocity_;
|
||||
}
|
||||
|
||||
getAgentVelocity(aid: number) {
|
||||
return this.aid2agent[aid].velocity_;
|
||||
}
|
||||
|
||||
getAgentRadius(aid: number) {
|
||||
return this.aid2agent[aid].radius_;
|
||||
}
|
||||
|
||||
getAgentOrcaLines(aid: number) {
|
||||
return this.aid2agent[aid].orcaLines_;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加动态避障管理对象
|
||||
* @param position 初始位置
|
||||
* @param radius 检测半径
|
||||
* @param maxSpeed 最大速度
|
||||
* @param velocity 初始线速度(向量)
|
||||
* @param mass 转向质量
|
||||
* @returns
|
||||
*/
|
||||
addAgent(position: Vector2 | Vec2, radius: number = null, maxSpeed: number = null, velocity: Vector2 = null, mass: number = null) {
|
||||
if (!this.defaultAgent) {
|
||||
throw new Error("no default agent");
|
||||
}
|
||||
|
||||
let agent = new Agent();
|
||||
|
||||
agent.position_.copy(position);
|
||||
agent.maxNeighbors_ = this.defaultAgent.maxNeighbors_;
|
||||
agent.maxSpeed_ = maxSpeed || this.defaultAgent.maxSpeed_;
|
||||
agent.neighborDist = this.defaultAgent.neighborDist;
|
||||
agent.radius_ = radius || this.defaultAgent.radius_;
|
||||
agent.timeHorizon = this.defaultAgent.timeHorizon;
|
||||
agent.timeHorizonObst = this.defaultAgent.timeHorizonObst;
|
||||
agent.velocity_.copy(velocity || this.defaultAgent.velocity_);
|
||||
|
||||
agent.id = this.agentId++;
|
||||
|
||||
if (mass && mass >= 0) {
|
||||
agent.mass = mass
|
||||
}
|
||||
this.aid2agent[agent.id] = agent;
|
||||
this.agentIdLst.push(agent.id);
|
||||
|
||||
return agent.id;
|
||||
}
|
||||
|
||||
removeAgent(aid: number) {
|
||||
if (this.hasAgent(aid)) {
|
||||
|
||||
let idx = this.agentIdLst.indexOf(aid);
|
||||
if (idx >= 0) {
|
||||
// this.agentIdLst.splice(idx, 1) //用高效伪移除
|
||||
this.agentIdLst[idx] = this.agentIdLst[this.agentIdLst.length - 1];
|
||||
this.agentIdLst.length--;
|
||||
}
|
||||
delete this.aid2agent[aid];
|
||||
}
|
||||
}
|
||||
|
||||
hasAgent(aid: number) {
|
||||
return !!this.aid2agent[aid];
|
||||
}
|
||||
|
||||
setAgentMass(agentNo: number, mass: number) {
|
||||
this.aid2agent[agentNo].mass = mass;
|
||||
}
|
||||
|
||||
getAgentMass(agentNo: number) {
|
||||
return this.aid2agent[agentNo].mass;
|
||||
}
|
||||
|
||||
setAgentRadius(agentNo: number, radius: number) {
|
||||
this.aid2agent[agentNo].radius_ = radius;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param neighborDist 在寻找周围邻居的搜索距离,这个值设置过大,会让小球在很远距离时做出避障行为
|
||||
* @param maxNeighbors 寻找周围邻居的最大数目,这个值设置越大,最终计算的速度越精确,但会增大计算量
|
||||
* @param timeHorizon 代表计算动态的物体时的时间窗口
|
||||
* @param timeHorizonObst 代表计算静态的物体时的时间窗口,比如在RTS游戏中,小兵向城墙移动时,没必要做出避障,这个值需要 设置得很小
|
||||
* @param radius 代表计算ORCA时的小球的半径,这个值不一定与小球实际显示的半径一样,偏小有利于小球移动顺畅
|
||||
* @param maxSpeed 小球最大速度值
|
||||
* @param velocity 小球初始速度
|
||||
*/
|
||||
setAgentDefaults(neighborDist: number, maxNeighbors: number, timeHorizon: number, timeHorizonObst: number, radius: number, maxSpeed: number, velocity: Vector2) {
|
||||
if (!this.defaultAgent) {
|
||||
this.defaultAgent = new Agent();
|
||||
}
|
||||
|
||||
this.defaultAgent.maxNeighbors_ = maxNeighbors;
|
||||
this.defaultAgent.maxSpeed_ = maxSpeed;
|
||||
this.defaultAgent.neighborDist = neighborDist;
|
||||
this.defaultAgent.radius_ = radius;
|
||||
this.defaultAgent.timeHorizon = timeHorizon;
|
||||
this.defaultAgent.timeHorizonObst = timeHorizonObst;
|
||||
this.defaultAgent.velocity_ = velocity;
|
||||
}
|
||||
|
||||
run(dt: number) {
|
||||
this.kdTree.buildAgentTree(this.getNumAgents());
|
||||
let agentNum = this.agentIdLst.length;
|
||||
for (let i = 0; i < agentNum; i++) {
|
||||
this.aid2agent[this.agentIdLst[i]].computeNeighbors(this);
|
||||
this.aid2agent[this.agentIdLst[i]].computeNewVelocity(dt);
|
||||
}
|
||||
for (let i = 0; i < agentNum; i++) {
|
||||
this.aid2agent[this.agentIdLst[i]].update(dt);
|
||||
}
|
||||
|
||||
this.time += dt;
|
||||
}
|
||||
|
||||
|
||||
addObstacle(vertices: Vector2[]) {
|
||||
if (vertices.length < 2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let obstacleNo = this.obstacles.length;
|
||||
|
||||
for (let i = 0; i < vertices.length; ++i) {
|
||||
let obstacle = new Obstacle();
|
||||
obstacle.point = vertices[i];
|
||||
if (i != 0) {
|
||||
obstacle.previous = this.obstacles[this.obstacles.length - 1];
|
||||
obstacle.previous.next = obstacle;
|
||||
}
|
||||
if (i == vertices.length - 1) {
|
||||
obstacle.next = this.obstacles[obstacleNo];
|
||||
obstacle.next.previous = obstacle;
|
||||
}
|
||||
obstacle.direction = RVOMath.normalize(vertices[(i == vertices.length - 1 ? 0 : i + 1)].minus(vertices[i]));
|
||||
|
||||
if (vertices.length == 2) {
|
||||
obstacle.convex = true;
|
||||
}
|
||||
else {
|
||||
obstacle.convex = (RVOMath.leftOf(vertices[(i == 0 ? vertices.length - 1 : i - 1)], vertices[i], vertices[(i == vertices.length - 1 ? 0 : i + 1)]) >= 0);
|
||||
}
|
||||
|
||||
obstacle.id = this.obstacles.length;
|
||||
|
||||
this.obstacles.push(obstacle);
|
||||
}
|
||||
|
||||
return obstacleNo;
|
||||
}
|
||||
|
||||
processObstacles() {
|
||||
this.kdTree.buildObstacleTree();
|
||||
};
|
||||
|
||||
queryVisibility(point1: Vector2, point2: Vector2, radius: number) {
|
||||
return this.kdTree.queryVisibility(point1, point2, radius);
|
||||
};
|
||||
|
||||
getObstacles() {
|
||||
return this.obstacles;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.agentIdLst.length = 0;
|
||||
this.agentId = 0;
|
||||
this.aid2agent = Object.create(null);
|
||||
this.defaultAgent = null;
|
||||
this.kdTree = new KdTree();
|
||||
this.obstacles.length = 0;
|
||||
}
|
||||
}
|
12
assets/Scripts/RVO/Simulator.ts.meta
Normal file
12
assets/Scripts/RVO/Simulator.ts.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "c9717561-2e5e-441e-b1de-1b3ca6ee543f",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"moduleId": "project:///assets/Scripts/GameCore/RVO2/Simulator.js",
|
||||
"simulateGlobals": []
|
||||
}
|
||||
}
|
391
assets/Scripts/RVO/kdtree.ts
Normal file
391
assets/Scripts/RVO/kdtree.ts
Normal file
@ -0,0 +1,391 @@
|
||||
import { RVOMath, Obstacle, Vector2 } from "./Common";
|
||||
import { Simulator } from "./Simulator";
|
||||
import { Agent } from "./Agent";
|
||||
|
||||
class FloatPair {
|
||||
a: number;
|
||||
b: number;
|
||||
constructor(a: number, b: number) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
lessThan(rhs: FloatPair) {
|
||||
return this.a < rhs.a || !(rhs.a < this.a) && this.b < rhs.b;
|
||||
}
|
||||
|
||||
lessEqualThan(rhs: FloatPair) {
|
||||
return (this.a == rhs.a && this.b == rhs.b) || this.lessThan(rhs);
|
||||
}
|
||||
|
||||
bigThan(rhs: FloatPair) {
|
||||
return !this.lessEqualThan(rhs);
|
||||
}
|
||||
|
||||
bigEqualThan(rhs: FloatPair) {
|
||||
return !this.lessThan(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
class AgentTreeNode {
|
||||
begin: number;
|
||||
end: number;
|
||||
left: number;
|
||||
right: number;
|
||||
maxX: number;
|
||||
maxY: number;
|
||||
minX: number;
|
||||
minY: number;
|
||||
}
|
||||
|
||||
class ObstacleTreeNode {
|
||||
obstacle: Obstacle;
|
||||
left: ObstacleTreeNode;
|
||||
right: ObstacleTreeNode;
|
||||
}
|
||||
|
||||
export class KdTree {
|
||||
/**
|
||||
* The maximum size of an agent k-D tree leaf.
|
||||
*/
|
||||
MAX_LEAF_SIZE = 10;
|
||||
agents: Agent[] = null;
|
||||
agentTree: AgentTreeNode[] = [];
|
||||
obstacleTree: ObstacleTreeNode = null;
|
||||
|
||||
|
||||
buildAgentTree(agentNum: number) {
|
||||
if (!this.agents || this.agents.length != agentNum) {
|
||||
this.agents = new Array<Agent>(agentNum);
|
||||
for(let i = 0; i < this.agents.length; i++) {
|
||||
this.agents[i] = Simulator.instance.getAgent(i);
|
||||
}
|
||||
|
||||
this.agentTree = new Array<AgentTreeNode>(2 * this.agents.length);
|
||||
for (let i = 0; i < this.agentTree.length; i++) {
|
||||
this.agentTree[i] = new AgentTreeNode();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.agents.length != 0) {
|
||||
this.buildAgentTreeRecursive(0, this.agents.length, 0);
|
||||
}
|
||||
}
|
||||
|
||||
buildObstacleTree() {
|
||||
this.obstacleTree = new ObstacleTreeNode();
|
||||
let obstacles = new Array<Obstacle>(Simulator.instance.obstacles.length);
|
||||
for(let i = 0; i < obstacles.length; i++) {
|
||||
obstacles[i] = Simulator.instance.obstacles[i];
|
||||
}
|
||||
this.obstacleTree = this.buildObstacleTreeRecursive(obstacles);
|
||||
}
|
||||
|
||||
computeAgentNeighbors(agent: Agent, rangeSq: number) {
|
||||
return this.queryAgentTreeRecursive(agent, rangeSq, 0);
|
||||
}
|
||||
|
||||
computeObstacleNeighbors(agent: Agent, rangeSq: number) {
|
||||
this.queryObstacleTreeRecursive(agent, rangeSq, this.obstacleTree);
|
||||
}
|
||||
|
||||
queryVisibility (q1: Vector2, q2: Vector2, radius: number) {
|
||||
return this.queryVisibilityRecursive(q1, q2, radius, this.obstacleTree);
|
||||
}
|
||||
|
||||
buildAgentTreeRecursive(begin: number, end: number, node: number) {
|
||||
this.agentTree[node].begin = begin;
|
||||
this.agentTree[node].end = end;
|
||||
this.agentTree[node].minX = this.agentTree[node].maxX = this.agents[begin].position_.x;
|
||||
this.agentTree[node].minY = this.agentTree[node].maxY = this.agents[begin].position_.y;
|
||||
|
||||
for (let i = begin + 1; i < end; ++i) {
|
||||
this.agentTree[node].maxX = Math.max(this.agentTree[node].maxX, this.agents[i].position_.x);
|
||||
this.agentTree[node].minX = Math.min(this.agentTree[node].minX, this.agents[i].position_.x);
|
||||
this.agentTree[node].maxY = Math.max(this.agentTree[node].maxY, this.agents[i].position_.y);
|
||||
this.agentTree[node].minY = Math.min(this.agentTree[node].minY, this.agents[i].position_.y);
|
||||
}
|
||||
|
||||
if (end - begin > this.MAX_LEAF_SIZE) {
|
||||
// no leaf node
|
||||
let isVertical = (this.agentTree[node].maxX - this.agentTree[node].minX) > (this.agentTree[node].maxY - this.agentTree[node].minY);
|
||||
let splitValue = 0.5 * (isVertical ? this.agentTree[node].maxX + this.agentTree[node].minX : this.agentTree[node].maxY + this.agentTree[node].minY);
|
||||
|
||||
let left = begin;
|
||||
let right = end;
|
||||
|
||||
while (left < right) {
|
||||
while (left < right && (isVertical ? this.agents[left].position_.x : this.agents[left].position_.y) < splitValue) {
|
||||
++left;
|
||||
}
|
||||
|
||||
while (right > left && (isVertical ? this.agents[right - 1].position_.x : this.agents[right - 1].position_.y) >= splitValue) {
|
||||
--right;
|
||||
}
|
||||
|
||||
if (left < right) {
|
||||
let tmp = this.agents[left];
|
||||
this.agents[left] = this.agents[right - 1];
|
||||
this.agents[right - 1] = tmp;
|
||||
++left;
|
||||
--right;
|
||||
}
|
||||
}
|
||||
|
||||
let leftSize = left - begin;
|
||||
if (leftSize == 0) {
|
||||
++leftSize;
|
||||
++left;
|
||||
++right;
|
||||
}
|
||||
|
||||
this.agentTree[node].left = node + 1;
|
||||
this.agentTree[node].right = node + 2 * leftSize;
|
||||
|
||||
this.buildAgentTreeRecursive(begin, left, this.agentTree[node].left);
|
||||
this.buildAgentTreeRecursive(left, end, this.agentTree[node].right);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
buildObstacleTreeRecursive(obstacles: Obstacle[]) {
|
||||
if (obstacles.length == 0) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
let node = new ObstacleTreeNode();
|
||||
let optimalSplit = 0;
|
||||
let minLeft = obstacles.length;
|
||||
let minRight = minLeft;
|
||||
|
||||
for (let i = 0; i < obstacles.length; ++i) {
|
||||
let leftSize = 0;
|
||||
let rightSize = 0;
|
||||
|
||||
let obstacleI1 = obstacles[i];
|
||||
let obstacleI2 = obstacleI1.next;
|
||||
|
||||
for (let j = 0; j < obstacles.length; j++) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let obstacleJ1 = obstacles[j];
|
||||
let obstacleJ2 = obstacleJ1.next;
|
||||
|
||||
let j1LeftOfI = RVOMath.leftOf(obstacleI1.point, obstacleI2.point, obstacleJ1.point);
|
||||
let j2LeftOfI = RVOMath.leftOf(obstacleI1.point, obstacleI2.point, obstacleJ2.point);
|
||||
|
||||
if (j1LeftOfI >= -RVOMath.RVO_EPSILON && j2LeftOfI >= -RVOMath.RVO_EPSILON) {
|
||||
++leftSize;
|
||||
}
|
||||
else if (j1LeftOfI <= RVOMath.RVO_EPSILON && j2LeftOfI <= RVOMath.RVO_EPSILON) {
|
||||
++rightSize;
|
||||
}
|
||||
else {
|
||||
++leftSize;
|
||||
++rightSize;
|
||||
}
|
||||
|
||||
let fp1 = new FloatPair(Math.max(leftSize, rightSize), Math.min(leftSize, rightSize));
|
||||
let fp2 = new FloatPair(Math.max(minLeft, minRight), Math.min(minLeft, minRight));
|
||||
|
||||
if (fp1.bigEqualThan(fp2)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let fp1 = new FloatPair(Math.max(leftSize, rightSize), Math.min(leftSize, rightSize));
|
||||
let fp2 = new FloatPair(Math.max(minLeft, minRight), Math.min(minLeft, minRight));
|
||||
|
||||
if (fp1.lessThan(fp2)) {
|
||||
minLeft = leftSize;
|
||||
minRight = rightSize;
|
||||
optimalSplit = i;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/* Build split node. */
|
||||
let leftObstacles: Obstacle[] = [];
|
||||
for (let n = 0; n < minLeft; ++n) leftObstacles.push(null);
|
||||
|
||||
let rightObstacles: Obstacle[] = [];
|
||||
for (let n = 0; n < minRight; ++n) rightObstacles.push(null);
|
||||
|
||||
let leftCounter = 0;
|
||||
let rightCounter = 0;
|
||||
let i = optimalSplit;
|
||||
|
||||
let obstacleI1 = obstacles[i];
|
||||
let obstacleI2 = obstacleI1.next;
|
||||
|
||||
for (let j = 0; j < obstacles.length; ++j) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let obstacleJ1 = obstacles[j];
|
||||
let obstacleJ2 = obstacleJ1.next;
|
||||
|
||||
let j1LeftOfI = RVOMath.leftOf(obstacleI1.point, obstacleI2.point, obstacleJ1.point);
|
||||
let j2LeftOfI = RVOMath.leftOf(obstacleI1.point, obstacleI2.point, obstacleJ2.point);
|
||||
|
||||
if (j1LeftOfI >= -RVOMath.RVO_EPSILON && j2LeftOfI >= -RVOMath.RVO_EPSILON) {
|
||||
leftObstacles[leftCounter++] = obstacles[j];
|
||||
}
|
||||
else if (j1LeftOfI <= RVOMath.RVO_EPSILON && j2LeftOfI <= RVOMath.RVO_EPSILON) {
|
||||
rightObstacles[rightCounter++] = obstacles[j];
|
||||
}
|
||||
else {
|
||||
/* Split obstacle j. */
|
||||
let t = RVOMath.det(obstacleI2.point.minus(obstacleI1.point), obstacleJ1.point.minus(obstacleI1.point)) /
|
||||
RVOMath.det(obstacleI2.point.minus(obstacleI1.point), obstacleJ1.point.minus(obstacleJ2.point));
|
||||
|
||||
let splitpoint = obstacleJ1.point.plus( (obstacleJ2.point.minus(obstacleJ1.point)).scale(t) );
|
||||
|
||||
let newObstacle = new Obstacle();
|
||||
newObstacle.point = splitpoint;
|
||||
newObstacle.previous = obstacleJ1;
|
||||
newObstacle.next = obstacleJ2;
|
||||
newObstacle.convex = true;
|
||||
newObstacle.direction = obstacleJ1.direction;
|
||||
|
||||
newObstacle.id = Simulator.instance.obstacles.length;
|
||||
|
||||
Simulator.instance.obstacles.push(newObstacle);
|
||||
|
||||
obstacleJ1.next = newObstacle;
|
||||
obstacleJ2.previous = newObstacle;
|
||||
|
||||
if (j1LeftOfI > 0.0) {
|
||||
leftObstacles[leftCounter++] = obstacleJ1;
|
||||
rightObstacles[rightCounter++] = newObstacle;
|
||||
}
|
||||
else {
|
||||
rightObstacles[rightCounter++] = obstacleJ1;
|
||||
leftObstacles[leftCounter++] = newObstacle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.obstacle = obstacleI1;
|
||||
node.left = this.buildObstacleTreeRecursive(leftObstacles);
|
||||
node.right = this.buildObstacleTreeRecursive(rightObstacles);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
queryAgentTreeRecursive(agent: Agent, rangeSq: number, node: number) {
|
||||
if (this.agentTree[node].end - this.agentTree[node].begin <= this.MAX_LEAF_SIZE) {
|
||||
for (let i = this.agentTree[node].begin; i < this.agentTree[node].end; ++i) {
|
||||
rangeSq = agent.insertAgentNeighbor(this.agents[i], rangeSq);
|
||||
}
|
||||
}
|
||||
else {
|
||||
let distSqLeft = RVOMath.sqr(Math.max(0, this.agentTree[this.agentTree[node].left].minX - agent.position_.x)) +
|
||||
RVOMath.sqr(Math.max(0, agent.position_.x - this.agentTree[this.agentTree[node].left].maxX)) +
|
||||
RVOMath.sqr(Math.max(0, this.agentTree[this.agentTree[node].left].minY - agent.position_.y)) +
|
||||
RVOMath.sqr(Math.max(0, agent.position_.y - this.agentTree[this.agentTree[node].left].maxY));
|
||||
|
||||
let distSqRight = RVOMath.sqr(Math.max(0, this.agentTree[this.agentTree[node].right].minX - agent.position_.x)) +
|
||||
RVOMath.sqr(Math.max(0, agent.position_.x - this.agentTree[this.agentTree[node].right].maxX)) +
|
||||
RVOMath.sqr(Math.max(0, this.agentTree[this.agentTree[node].right].minY - agent.position_.y)) +
|
||||
RVOMath.sqr(Math.max(0, agent.position_.y - this.agentTree[this.agentTree[node].right].maxY));
|
||||
|
||||
if (distSqLeft < distSqRight) {
|
||||
if (distSqLeft < rangeSq) {
|
||||
rangeSq = this.queryAgentTreeRecursive(agent, rangeSq, this.agentTree[node].left);
|
||||
|
||||
if (distSqRight < rangeSq) {
|
||||
rangeSq = this.queryAgentTreeRecursive(agent, rangeSq, this.agentTree[node].right);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (distSqRight < rangeSq) {
|
||||
rangeSq = this.queryAgentTreeRecursive(agent, rangeSq, this.agentTree[node].right);
|
||||
|
||||
if (distSqLeft < rangeSq) {
|
||||
rangeSq = this.queryAgentTreeRecursive(agent, rangeSq, this.agentTree[node].left);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return rangeSq;
|
||||
}
|
||||
|
||||
// pass ref range
|
||||
queryObstacleTreeRecursive(agent: Agent, rangeSq: number, node: ObstacleTreeNode) {
|
||||
if (node == null) {
|
||||
return rangeSq;
|
||||
}
|
||||
else {
|
||||
let obstacle1 = node.obstacle;
|
||||
let obstacle2 = obstacle1.next;
|
||||
|
||||
let agentLeftOfLine = RVOMath.leftOf(obstacle1.point, obstacle2.point, agent.position_);
|
||||
|
||||
rangeSq = this.queryObstacleTreeRecursive(agent, rangeSq, (agentLeftOfLine >= 0 ? node.left : node.right));
|
||||
|
||||
let distSqLine = RVOMath.sqr(agentLeftOfLine) / RVOMath.absSq(obstacle2.point.minus(obstacle1.point));
|
||||
|
||||
if (distSqLine < rangeSq)
|
||||
{
|
||||
if (agentLeftOfLine < 0)
|
||||
{
|
||||
/*
|
||||
* Try obstacle at this node only if is on right side of
|
||||
* obstacle (and can see obstacle).
|
||||
*/
|
||||
agent.insertObstacleNeighbor(node.obstacle, rangeSq);
|
||||
}
|
||||
|
||||
/* Try other side of line. */
|
||||
this.queryObstacleTreeRecursive(agent, rangeSq, (agentLeftOfLine >= 0 ? node.right : node.left));
|
||||
}
|
||||
return rangeSq;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
queryVisibilityRecursive(q1: Vector2, q2: Vector2, radius: number, node: ObstacleTreeNode) {
|
||||
if (node == null) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
let obstacle1 = node.obstacle;
|
||||
let obstacle2 = obstacle1.next;
|
||||
|
||||
let q1LeftOfI = RVOMath.leftOf(obstacle1.point, obstacle2.point, q1);
|
||||
let q2LeftOfI = RVOMath.leftOf(obstacle1.point, obstacle2.point, q2);
|
||||
let invLengthI = 1.0 / RVOMath.absSq(obstacle2.point.minus(obstacle1.point));
|
||||
|
||||
if (q1LeftOfI >= 0 && q2LeftOfI >= 0)
|
||||
{
|
||||
return this.queryVisibilityRecursive(q1, q2, radius, node.left) && ((RVOMath.sqr(q1LeftOfI) * invLengthI >= RVOMath.sqr(radius) && RVOMath.sqr(q2LeftOfI) * invLengthI >= RVOMath.sqr(radius)) || this.queryVisibilityRecursive(q1, q2, radius, node.right));
|
||||
}
|
||||
else if (q1LeftOfI <= 0 && q2LeftOfI <= 0)
|
||||
{
|
||||
return this.queryVisibilityRecursive(q1, q2, radius, node.right) && ((RVOMath.sqr(q1LeftOfI) * invLengthI >= RVOMath.sqr(radius) && RVOMath.sqr(q2LeftOfI) * invLengthI >= RVOMath.sqr(radius)) || this.queryVisibilityRecursive(q1, q2, radius, node.left));
|
||||
}
|
||||
else if (q1LeftOfI >= 0 && q2LeftOfI <= 0)
|
||||
{
|
||||
/* One can see through obstacle from left to right. */
|
||||
return this.queryVisibilityRecursive(q1, q2, radius, node.left) && this.queryVisibilityRecursive(q1, q2, radius, node.right);
|
||||
}
|
||||
else
|
||||
{
|
||||
let point1LeftOfQ = RVOMath.leftOf(q1, q2, obstacle1.point);
|
||||
let point2LeftOfQ = RVOMath.leftOf(q1, q2, obstacle2.point);
|
||||
let invLengthQ = 1.0 / RVOMath.absSq(q2.minus(q1));
|
||||
|
||||
return (point1LeftOfQ * point2LeftOfQ >= 0 && RVOMath.sqr(point1LeftOfQ) * invLengthQ > RVOMath.sqr(radius) && RVOMath.sqr(point2LeftOfQ) * invLengthQ > RVOMath.sqr(radius) && this.queryVisibilityRecursive(q1, q2, radius, node.left) && this.queryVisibilityRecursive(q1, q2, radius, node.right));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
assets/Scripts/RVO/kdtree.ts.meta
Normal file
12
assets/Scripts/RVO/kdtree.ts.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "a7ee4e36-baac-4720-ba0f-7f779a7fd328",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"moduleId": "project:///assets/Scripts/GameCore/RVO2/kdtree.js",
|
||||
"simulateGlobals": []
|
||||
}
|
||||
}
|
12
assets/Scripts/Test.meta
Normal file
12
assets/Scripts/Test.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "5180adb3-56df-4480-bc8e-b139f7d65ac4",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
211
assets/Scripts/Test/MonsterFactory.ts
Normal file
211
assets/Scripts/Test/MonsterFactory.ts
Normal file
@ -0,0 +1,211 @@
|
||||
import { instantiate, Intersection2D, Node, NodePool, Prefab, Rect, resources, UITransform, Vec3, view } from "cc";
|
||||
import ArrayUtil from "../core/utils/ArrayUtil";
|
||||
import { monsterCtl } from "./monsterCtl";
|
||||
import Game from "./Test2";
|
||||
|
||||
interface MonsterPool {
|
||||
name: string;
|
||||
pool: NodePool;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default class MonsterFactory {
|
||||
private static instance: MonsterFactory = null;
|
||||
public static get Instance(): MonsterFactory {
|
||||
if (this.instance == null)
|
||||
this.instance = new MonsterFactory();
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
MonsterPoolList: Array<MonsterPool> = new Array<MonsterPool>();
|
||||
monsterParent: Node;
|
||||
monsterDataCountList = {}; //每种怪物的数量数据
|
||||
|
||||
monsterAllList: Array<monsterCtl> = new Array<monsterCtl>(); //所有的怪物;
|
||||
|
||||
screenMonsterAllList: Array<monsterCtl> = new Array<monsterCtl>(); //屏幕里面的怪物;
|
||||
|
||||
arrayMonsterList: Array<Array<monsterCtl>> = new Array<Array<monsterCtl>>();
|
||||
|
||||
currenTime = -30;
|
||||
currentIndex = 0;//当前刷新到了第几次
|
||||
weekCount = 0; //第几次循环
|
||||
constructor() {
|
||||
// this.monsterParent = find("Canvas/Game/monsterCtl");
|
||||
this.monsterParent = Game.Instance.monsterRootNode
|
||||
}
|
||||
|
||||
getMonsterCountByName(name) {
|
||||
let index = 0;
|
||||
for (let i = 0; i < this.monsterAllList.length; i++) {
|
||||
if (this.monsterAllList[i].node.name == name) {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.currenTime = -30;
|
||||
this.currentIndex = 0;
|
||||
this.weekCount = 0;
|
||||
for (let i = 0; i < this.monsterAllList.length; i++) {
|
||||
let monsterCtl = this.monsterAllList[i];
|
||||
this.recoverNode(monsterCtl.node);
|
||||
i--;
|
||||
}
|
||||
this.monsterDataCountList = {};
|
||||
this.monsterAllList = new Array<monsterCtl>();
|
||||
this.arrayMonsterList = new Array<Array<monsterCtl>>();
|
||||
this.screenMonsterAllList = new Array<monsterCtl>()
|
||||
}
|
||||
|
||||
update(dt) {
|
||||
|
||||
}
|
||||
|
||||
creatorMonsterByName(name, pos: Vec3, call?) {
|
||||
let pool: NodePool = this.getEfPoolByName(name);
|
||||
let node: Node = pool.get();
|
||||
|
||||
if (this.monsterDataCountList[name] == null) {
|
||||
this.monsterDataCountList[name] = 0;
|
||||
}
|
||||
this.monsterDataCountList[name]++;
|
||||
let resetNode = (ef: Node) => {
|
||||
ef.parent = this.monsterParent;
|
||||
ef.active = true;
|
||||
ef.setPosition(pos.x, pos.y);
|
||||
call && call(ef);
|
||||
let mstCtl = ef.getComponent(monsterCtl)
|
||||
if (mstCtl) {
|
||||
mstCtl.reuse()
|
||||
this.monsterAllList.push(mstCtl);
|
||||
} else {
|
||||
console.error("monster has none monster ctl...")
|
||||
}
|
||||
};
|
||||
if (node == null) {
|
||||
resources.load("prefab/monster/" + name, Prefab, (err, resPrefab) => {
|
||||
node = instantiate(resPrefab)
|
||||
resetNode(node)
|
||||
})
|
||||
|
||||
}
|
||||
else {
|
||||
// console.log("从缓存池创建...")
|
||||
resetNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
getEfPoolByName(name) {
|
||||
let efPool: NodePool = null;
|
||||
for (let i = 0; i < this.MonsterPoolList.length; i++) {
|
||||
if (name == this.MonsterPoolList[i].name) {
|
||||
efPool = this.MonsterPoolList[i].pool;
|
||||
break
|
||||
}
|
||||
}
|
||||
if (efPool == null) {
|
||||
efPool = new NodePool();
|
||||
this.MonsterPoolList.push({
|
||||
name: name,
|
||||
pool: efPool
|
||||
})
|
||||
}
|
||||
return efPool;
|
||||
}
|
||||
|
||||
/**
|
||||
* 回收
|
||||
* @param node
|
||||
*/
|
||||
recoverNode(node) {
|
||||
let name = node.name;
|
||||
if (this.monsterDataCountList[node.name]) {
|
||||
this.monsterDataCountList[node.name]--;
|
||||
}
|
||||
for (let i = 0; i < this.MonsterPoolList.length; i++) {
|
||||
if (name == this.MonsterPoolList[i].name) {
|
||||
node.active = false;
|
||||
this.MonsterPoolList[i].pool.put(node);
|
||||
let com = node.getComponent(monsterCtl);
|
||||
let index = this.monsterAllList.indexOf(com);
|
||||
if (index != -1) {
|
||||
ArrayUtil.fastRemoveAt(this.monsterAllList, index)
|
||||
// this.monsterAllList.splice(index, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前在屏幕内的怪物
|
||||
* @param node
|
||||
*/
|
||||
getInViewMonster(node: Node = null) {
|
||||
let tNode = node ? node : Game.Instance.Player.node
|
||||
this.screenMonsterAllList = [];
|
||||
this.arrayMonsterList = new Array<Array<monsterCtl>>();
|
||||
let pos = tNode.getComponent(UITransform).convertToWorldSpaceAR(new Vec3());
|
||||
|
||||
let v2 = view.getViewportRect();
|
||||
let realView = new Vec3(v2.width, v2.height, 0);
|
||||
let allRect = new Rect(pos.x - realView.x / 2, pos.y - realView.y / 2, realView.x, realView.y);
|
||||
|
||||
let cutRectMonterList = new Array<monsterCtl>();
|
||||
this.arrayMonsterList.push(cutRectMonterList);
|
||||
for (let j = 0; j < this.monsterAllList.length; j++) {
|
||||
let monsterCtl = this.monsterAllList[j];
|
||||
let monsterRect = monsterCtl.getCircle(true);
|
||||
//顺便获取屏幕里面的怪
|
||||
if (Intersection2D.rectCircle(allRect, monsterRect.pos, monsterRect.radius)) {
|
||||
this.screenMonsterAllList.push(monsterCtl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cutRectCount = 2;
|
||||
getCutRectList(node) {
|
||||
this.screenMonsterAllList = [];
|
||||
this.arrayMonsterList = new Array<Array<monsterCtl>>();
|
||||
let pos = node.getComponent(UITransform).convertToWorldSpaceAR(new Vec3());
|
||||
let v2 = view.getViewportRect();
|
||||
let realView = new Vec3(v2.width, v2.height, 0);
|
||||
let allRect = new Rect(pos.x - realView.x / 2, pos.y - realView.y / 2, realView.x, realView.y);
|
||||
let rect1 = new Rect(pos.x - realView.x, pos.y, realView.x, realView.y);
|
||||
let rect2 = new Rect(pos.x, pos.y, realView.x, realView.y);
|
||||
let rect3 = new Rect(pos.x - realView.x, pos.y - realView.y, realView.x, realView.y);
|
||||
let rect4 = new Rect(pos.x, pos.y - realView.y, realView.x, realView.y);
|
||||
let rectList = [rect1, rect2, rect3, rect4];
|
||||
for (let i = 0; i < rectList.length; i++) {
|
||||
let rect = rectList[i];
|
||||
let cutRectMonterList = new Array<monsterCtl>();
|
||||
this.arrayMonsterList.push(cutRectMonterList);
|
||||
for (let j = 0; j < this.monsterAllList.length; j++) {
|
||||
let monsterCtl = this.monsterAllList[j];
|
||||
let monsterRect = monsterCtl.getCircle();
|
||||
// let result = rect.intersects(monsterRect);
|
||||
// rect.polygonCircle
|
||||
let result = Intersection2D.rectCircle(rect, monsterRect.pos, monsterRect.radius)
|
||||
//改为矩形圆形判断
|
||||
if (result) {
|
||||
cutRectMonterList.push(monsterCtl);
|
||||
monsterCtl.cutRectMonterList = cutRectMonterList;
|
||||
|
||||
}
|
||||
//顺便获取屏幕里面的怪
|
||||
if (i == 0) {
|
||||
if (Intersection2D.rectCircle(allRect, monsterRect.pos, monsterRect.radius)) {
|
||||
this.screenMonsterAllList.push(monsterCtl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
9
assets/Scripts/Test/MonsterFactory.ts.meta
Normal file
9
assets/Scripts/Test/MonsterFactory.ts.meta
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "411ec7f5-0831-42e0-92ab-00f47d9173c5",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
90
assets/Scripts/Test/Test2.ts
Normal file
90
assets/Scripts/Test/Test2.ts
Normal file
@ -0,0 +1,90 @@
|
||||
|
||||
import { Camera, Component, dynamicAtlasManager, Node, _decorator } from 'cc';
|
||||
import WaveMng from '../logics/mng/WaveMng';
|
||||
import { player } from '../rolectl/player';
|
||||
import { Vector2 } from '../RVO/Common';
|
||||
import { Simulator } from '../RVO/Simulator';
|
||||
import { monsterCtl } from './monsterCtl';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
// 关闭web的动态合图
|
||||
// dynamicAtlasManager.enabled = false
|
||||
|
||||
export default class Game {
|
||||
public limitMonst: number = 400
|
||||
private static instance: Game = null;
|
||||
public static get Instance(): Game {
|
||||
if (this.instance == null)
|
||||
this.instance = new Game();
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
private _Player: player;
|
||||
public get Player(): player {
|
||||
return this._Player;
|
||||
}
|
||||
public set Player(value: player) {
|
||||
this._Player = value;
|
||||
}
|
||||
|
||||
private _monsterRootNode: Node;
|
||||
public get monsterRootNode(): Node {
|
||||
return this._monsterRootNode;
|
||||
}
|
||||
public set monsterRootNode(value: Node) {
|
||||
this._monsterRootNode = value;
|
||||
}
|
||||
|
||||
private _camera: Camera;
|
||||
public get camera(): Camera {
|
||||
return this._camera;
|
||||
}
|
||||
public set camera(value: Camera) {
|
||||
this._camera = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ccclass('Test2')
|
||||
export class Test2 extends Component {
|
||||
@property(Camera)
|
||||
camera: Camera;
|
||||
@property(Node)
|
||||
avatarLayer: Node;
|
||||
@property(player)
|
||||
player: player
|
||||
|
||||
onEnable() {
|
||||
Game.Instance.Player = this.player
|
||||
Game.Instance.monsterRootNode = this.avatarLayer
|
||||
Game.Instance.camera = this.camera// this.node.getComponent(this.camera)
|
||||
}
|
||||
|
||||
onDisable() {
|
||||
}
|
||||
|
||||
_bigRedBall: Node
|
||||
start() {
|
||||
// let simulator = Simulator.instance;
|
||||
Simulator.instance.setAgentDefaults(60, 3, 1, 0.1, 14, 80, new Vector2(0, 0));
|
||||
}
|
||||
|
||||
_framTimes = 0
|
||||
_dt: number = 0
|
||||
update(dt: number) {
|
||||
//测试怪物生成RVO
|
||||
this._dt += dt
|
||||
if (this._dt > 0.5) {
|
||||
this._dt = 0
|
||||
this.getComponent(WaveMng).createMonster()
|
||||
}
|
||||
|
||||
// 更新逻辑坐标
|
||||
Simulator.instance.run(dt);
|
||||
this._framTimes = 0
|
||||
for (let index = 0; index < Game.Instance.Player.monsterList.length; index++) {
|
||||
const monster = Game.Instance.Player.monsterList[index];
|
||||
monster.getComponent(monsterCtl).moveByRvo()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
9
assets/Scripts/Test/Test2.ts.meta
Normal file
9
assets/Scripts/Test/Test2.ts.meta
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "1928e6fa-5abf-447e-9276-7b784ea79f1d",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
206
assets/Scripts/Test/monsterCtl.ts
Normal file
206
assets/Scripts/Test/monsterCtl.ts
Normal file
@ -0,0 +1,206 @@
|
||||
import { Component, Vec2, Vec3, _decorator, v2, v3 } from 'cc';
|
||||
import { RVOMath } from '../RVO/Common';
|
||||
import { Simulator } from '../RVO/Simulator';
|
||||
import Util from '../core/utils/Util';
|
||||
import MonsterFactory from './MonsterFactory';
|
||||
import Game from './Test2';
|
||||
import { monsterEffectCtl } from './monsterEffectCtl';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('monsterCtl')
|
||||
export class monsterCtl extends Component {
|
||||
private _hp: number = 5;
|
||||
public get hp(): number {
|
||||
return this._hp;
|
||||
}
|
||||
public set hp(value: number) {
|
||||
this._hp = value;
|
||||
}
|
||||
|
||||
// private state : number =
|
||||
//测试受击
|
||||
// hurtCoolTime = 0.15;
|
||||
// hurtTime = 0.2;
|
||||
|
||||
hurtCoolTime = 0.15;
|
||||
hurtTime = 0.2;
|
||||
private _hit: boolean = false
|
||||
private _hitPow: number = 3 //受击系数 系数越高 反弹力度越大
|
||||
|
||||
private orScale: Vec3 = v3(1, 1, 1)
|
||||
public cutRectMonterList = []
|
||||
|
||||
|
||||
private _agentHandleId: number = -1; //RVOid
|
||||
public get agentHandleId(): number {
|
||||
return this._agentHandleId;
|
||||
}
|
||||
public set agentHandleId(value: number) {
|
||||
this._agentHandleId = value;
|
||||
}
|
||||
|
||||
start() {
|
||||
this.orScale = this.node.scale
|
||||
this._effectCtl = this.node.getComponent(monsterEffectCtl)
|
||||
}
|
||||
|
||||
_effectCtl: monsterEffectCtl
|
||||
reuse() {
|
||||
this._hit = false
|
||||
this.hurtTime = 0
|
||||
this._effectCtl = this.node.getComponent(monsterEffectCtl)
|
||||
if (this._effectCtl)
|
||||
this._effectCtl.reuse()
|
||||
}
|
||||
|
||||
/**
|
||||
* 改为圆形判断
|
||||
*/
|
||||
getRect() {
|
||||
let agent = Simulator.instance.getAgentByAid(this.agentHandleId)
|
||||
if (agent) {
|
||||
return agent.radius_
|
||||
}
|
||||
}
|
||||
|
||||
_tmpV2: Vec2 = new Vec2()
|
||||
getCircle(world: boolean = false) {
|
||||
let agent = Simulator.instance.getAgentByAid(this.agentHandleId)
|
||||
if (agent) {
|
||||
if (world) {
|
||||
return { pos: Util.v3t2(this.node.worldPosition, this._tmpV2), radius: agent.radius_ }
|
||||
} else {
|
||||
return { pos: Util.v3t2(this.node.position, this._tmpV2), radius: agent.radius_ }
|
||||
}
|
||||
|
||||
} else {
|
||||
console.error("monster Circle error...")
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
_frames: number = 0
|
||||
update(deltaTime: number) {
|
||||
this.hurtTime -= deltaTime
|
||||
|
||||
if (this._hit) {//设置 反向并且衰减的 线速度
|
||||
this.setPreferredVelocities(this._hitPow)//this._hitPow * this.hurtTime / this.hurtCoolTime
|
||||
if (this.hurtTime <= 0) {
|
||||
this._hit = false
|
||||
}
|
||||
} else {
|
||||
if (this._frames++ > 8) {
|
||||
this._frames = 0
|
||||
this.setPreferredVelocities()//设置追踪主角的线速度
|
||||
//确定朝向
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置追踪主角的线速度方向和大小
|
||||
*/
|
||||
_tmpScale: Vec3 = new Vec3()
|
||||
setPreferredVelocities(hitPow: number = null) {
|
||||
if (this.agentHandleId < 0) {
|
||||
return
|
||||
}
|
||||
this._tmpScale = this.node.getScale()
|
||||
let agentAid = this.agentHandleId
|
||||
let agent = Simulator.instance.getAgentByAid(agentAid)
|
||||
let agentPos = Simulator.instance.getAgentPosition(agentAid)
|
||||
let moveTarget: Vec2 = v2(Game.Instance.Player.node.getPosition().x, Game.Instance.Player.node.getPosition().y)
|
||||
//受击状态的线速度处理
|
||||
if (hitPow) {
|
||||
if (agent && agentPos) {
|
||||
let goalVector: Vec2 = moveTarget.subtract2f(agentPos.x, agentPos.y)// this.goals[i].minus(Simulator.instance.getAgentPosition(agentAid));
|
||||
goalVector = goalVector.normalize().multiplyScalar(agent.maxSpeed_ * -hitPow);
|
||||
Simulator.instance.setAgentPrefVelocity(agentAid, goalVector);
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (agent && agentPos) {
|
||||
let goalVector: Vec2 = moveTarget.subtract2f(agentPos.x, agentPos.y)// this.goals[i].minus(Simulator.instance.getAgentPosition(agentAid));
|
||||
if (goalVector.lengthSqr() > 1.0) {
|
||||
goalVector = goalVector.normalize().multiplyScalar(agent.maxSpeed_);
|
||||
}
|
||||
if (goalVector.lengthSqr() < RVOMath.RVO_EPSILON) {
|
||||
Simulator.instance.setAgentPrefVelocity(agentAid, Vec2.ZERO);
|
||||
}
|
||||
else {
|
||||
Simulator.instance.setAgentPrefVelocity(agentAid, goalVector);
|
||||
//这个会导致抖动呀 宝贝...
|
||||
// let angle = Math.random() * 2.0 * Math.PI;
|
||||
// let dist = Math.random() * 0.0001;
|
||||
// Simulator.instance.setAgentPrefVelocity(i,
|
||||
// Simulator.instance.getAgentPrefVelocity(i).plus(new Vector2(Math.cos(angle), Math.sin(angle)).scale(dist)));
|
||||
}
|
||||
if (goalVector.x > 0) {
|
||||
this.node.setScale(this._tmpScale.set(Math.abs(this._tmpScale.x), this._tmpScale.y, this._tmpScale.z))
|
||||
} else {
|
||||
this.node.setScale(this._tmpScale.set(-Math.abs(this._tmpScale.x), this._tmpScale.y, this._tmpScale.z))
|
||||
}
|
||||
} else {
|
||||
console.error("RVO异常::", agent, agentPos, agentAid)
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 在此之前 请确保Simulator run执行完毕
|
||||
*/
|
||||
moveByRvo() {
|
||||
// console.log("p::", p, Simulator.instance.getAgentPosition(this.agentHandleId))
|
||||
let p = Simulator.instance.getAgentPosition(this.agentHandleId);
|
||||
this.node.setPosition(p.x, p.y)
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试效果 闪白 蓝光...
|
||||
*/
|
||||
hurtAction() {
|
||||
let randEfIdx = Math.random()
|
||||
let mstEffectCtl = this.node.getComponent(monsterEffectCtl)
|
||||
if (mstEffectCtl) {
|
||||
if (randEfIdx > 0.5) {
|
||||
mstEffectCtl.playHit()
|
||||
} else {
|
||||
mstEffectCtl.playFroze()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hurt(): boolean {
|
||||
|
||||
// if (!this.)
|
||||
if (this.hurtTime > 0) { //受击冷却中
|
||||
return
|
||||
}
|
||||
this.hurtTime = this.hurtCoolTime
|
||||
|
||||
this._hp--
|
||||
if (this._hp <= 0) {
|
||||
this.rmMonster()
|
||||
return true
|
||||
} else {
|
||||
this.hurtAction()
|
||||
this._hit = true
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
rmMonster() {
|
||||
this._effectCtl.disove(() => {
|
||||
if (this.agentHandleId >= 0) {
|
||||
Simulator.instance.removeAgent(this.agentHandleId)
|
||||
// console.log("移除RVO对象::", this.agentHandleId)
|
||||
}
|
||||
MonsterFactory.Instance.recoverNode(this.node)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
9
assets/Scripts/Test/monsterCtl.ts.meta
Normal file
9
assets/Scripts/Test/monsterCtl.ts.meta
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "cc093bda-87cd-4315-9b9e-178cd9fe6791",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
136
assets/Scripts/Test/monsterEffectCtl.ts
Normal file
136
assets/Scripts/Test/monsterEffectCtl.ts
Normal file
@ -0,0 +1,136 @@
|
||||
import { Component, Material, Sprite, UIOpacity, Vec4, _decorator, color, math, tween } from 'cc';
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('monsterEffectCtl')
|
||||
export class monsterEffectCtl extends Component {
|
||||
_mt: Material = null
|
||||
_spCmp: Sprite = null
|
||||
private _hited: boolean;
|
||||
private _froze: boolean;
|
||||
private _optCmp: UIOpacity = null;
|
||||
|
||||
|
||||
start() {
|
||||
this._spCmp = this.node.getComponent(Sprite)
|
||||
// this._optCmp = this.node.addComponent(UIOpacity)
|
||||
// this._mt = this.node.getComponent(Sprite).getMaterialInstance(0)//这个打断合批...
|
||||
}
|
||||
|
||||
reuse() {
|
||||
this.start()
|
||||
// this._hited = this._froze = false
|
||||
// this._frozeDt = this._hitDt = 0
|
||||
// this._mt = this.node.getComponent(Sprite).getMaterialInstance(0)
|
||||
//关闭蓝色内发光 关闭闪白
|
||||
this._spCmp.color = color(255, 255, 255, 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 闪白
|
||||
*/
|
||||
_showHitTotalTime: number = 0.15
|
||||
playHit() {
|
||||
//方式一 setProperty 打断合批
|
||||
//需要放开 start()中的 this._mt = this.node.getComponent(Sprite).getMaterialInstance(0)
|
||||
// tween(this._mt).to(this._showHitTotalTime, {}, {
|
||||
// onUpdate(target: Material, ratio) {
|
||||
// target.setProperty("hitwhit", ratio)
|
||||
// },
|
||||
// }).call(() => {
|
||||
// this._mt.setProperty("hitwhit", 0.0)
|
||||
// }).start()
|
||||
|
||||
|
||||
//方式二 占用alpha通道
|
||||
let tSpCmp = this._spCmp // this.node.getComponent(Sprite)
|
||||
tSpCmp.color = color(255, 255, 255, 1)
|
||||
let tmpColor: math.Color = color(255, 255, 255, 244)
|
||||
tween(tSpCmp).to(this._showHitTotalTime, {}, {
|
||||
onUpdate(target: Sprite, ratio) {
|
||||
tmpColor = color(255, 255, 255, 255 * ratio)
|
||||
target.color = tmpColor
|
||||
},
|
||||
}).call(() => {
|
||||
tmpColor = color(255, 255, 255, 1)
|
||||
tSpCmp.color = tmpColor
|
||||
}).start()
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 减速(视觉效果)
|
||||
*/
|
||||
frozeColor: Vec4 = Vec4.ZERO;
|
||||
_showFrozeTime: number = 10
|
||||
_frozeDt: number = 0
|
||||
playFroze() {
|
||||
//占用r通道 r在0~25.5之间 开启蓝色内光
|
||||
let tSpCmp = this._spCmp
|
||||
let tmpColor: math.Color = null
|
||||
tSpCmp.color = color(1, tSpCmp.color.g, tSpCmp.color.b, tSpCmp.color.a)
|
||||
tween(tSpCmp).to(this._showFrozeTime, {}, {}).call(() => {
|
||||
tmpColor = color(255, 255, 255, tSpCmp.color.a)
|
||||
tSpCmp.color = tmpColor
|
||||
}).start()
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试消融
|
||||
*/
|
||||
_disoveTime: number = 1
|
||||
disove(cb?: Function) {
|
||||
//占用g通道
|
||||
let tSpCmp = this._spCmp
|
||||
tSpCmp.color = color(this._spCmp.color.r, 1, this._spCmp.color.b, this._spCmp.color.a)
|
||||
let tmpColor: math.Color = null
|
||||
tween(tSpCmp).to(this._disoveTime, {}, {
|
||||
onUpdate(target: Sprite, ratio) {
|
||||
tmpColor = color(255, 254 * ratio, 255, 255)
|
||||
target.color = tmpColor
|
||||
},
|
||||
}).call(() => {
|
||||
if (cb) {
|
||||
cb()
|
||||
}
|
||||
// tmpColor = color(255, 255, 255, 1)
|
||||
// tSpCmp.color = tmpColor
|
||||
}).start()
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅用于测试显示效果
|
||||
* 游戏类的冰冻减速
|
||||
* 中毒燃烧掉血等逻辑 应该由逻辑层实现
|
||||
*/
|
||||
_hitDt: number
|
||||
update(dt: number) {
|
||||
// this._mt = this.node.getComponent(Sprite).getMaterial(0)
|
||||
// if (this._hited) {
|
||||
// this._hitDt += dt
|
||||
// let percent = this._hitDt / this._showHitTotalTime
|
||||
// percent = Math.min(Math.max(percent, 0.), 1.)
|
||||
// this._mt.setProperty("hitwhit", percent)
|
||||
// if (percent >= 1) {
|
||||
// this._hitDt = 0
|
||||
// this._hited = false
|
||||
// this._mt.setProperty("hitwhit", 0.0)
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (this._froze) {
|
||||
// this._frozeDt += dt
|
||||
// this._mt.setProperty("glowColor", this.frozeColor)
|
||||
// if (this._frozeDt > this._showFrozeTime) {
|
||||
// this._frozeDt = 0
|
||||
// this._mt.setProperty("glowColor", Vec4.ZERO)
|
||||
// this._froze = false
|
||||
// }
|
||||
// }
|
||||
|
||||
// console.log("this.mt::" , this._mt)
|
||||
// this.node.getComponent(Sprite).setMaterial(this._mt,0)
|
||||
}
|
||||
}
|
||||
|
||||
|
1
assets/Scripts/Test/monsterEffectCtl.ts.meta
Normal file
1
assets/Scripts/Test/monsterEffectCtl.ts.meta
Normal file
@ -0,0 +1 @@
|
||||
{"ver":"4.0.23","importer":"typescript","imported":true,"uuid":"eb420a22-bf96-4b6c-b2d9-8bab34bd9a58","files":[],"subMetas":{},"userData":{}}
|
12
assets/Scripts/camera.meta
Normal file
12
assets/Scripts/camera.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "a55debe1-4d5f-40e9-9995-8b241ef205c2",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
32
assets/Scripts/camera/camerafollow.ts
Normal file
32
assets/Scripts/camera/camerafollow.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { Camera, Component, Node, Vec3, _decorator } from 'cc';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('camerafollow')
|
||||
export class camerafollow extends Component {
|
||||
offset: Vec3 = new Vec3();
|
||||
camera: Camera;
|
||||
|
||||
@property(Node) folNode: Node = null
|
||||
start() {
|
||||
this.camera = this.node.getComponent(Camera);
|
||||
this.gameStart()
|
||||
}
|
||||
|
||||
gameStart() {
|
||||
Vec3.subtract(this.offset, this.node.position, this.folNode.position);
|
||||
}
|
||||
|
||||
_tmpV3: Vec3 = new Vec3()
|
||||
update(deltaTime: number) {
|
||||
// if (Game.Instance.gameState != GameState.GAME_PLAY || Game.Instance.player == null)
|
||||
// return;
|
||||
let targetPos = this._tmpV3;
|
||||
targetPos = Vec3.add(targetPos, this.offset, this.folNode.position);
|
||||
Vec3.lerp(targetPos, this.node.position, targetPos, 0.1);
|
||||
this.node.setPosition(targetPos);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.node.position = new Vec3(0, 0, 0);
|
||||
}
|
||||
}
|
9
assets/Scripts/camera/camerafollow.ts.meta
Normal file
9
assets/Scripts/camera/camerafollow.ts.meta
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "ff6d36e8-a512-4150-9bd8-a29280339aa8",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
12
assets/Scripts/common.meta
Normal file
12
assets/Scripts/common.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "36e78f36-d62f-40f9-901c-ea51d2e40bb2",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
170
assets/Scripts/common/QuadTree.ts
Normal file
170
assets/Scripts/common/QuadTree.ts
Normal file
@ -0,0 +1,170 @@
|
||||
import { Rect, _decorator } from "cc";
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
@ccclass("QuadTree")
|
||||
export default class QuadTree {
|
||||
|
||||
nodes = [];
|
||||
objects = []; //該容器的物體集合
|
||||
max_levels: number; // 最大深度
|
||||
max_objects: number; // 該容器最大容量
|
||||
level: number; //當前深度
|
||||
bounds: Rect = null; // 容器範圍(Rect)
|
||||
|
||||
constructor(boundary, level) {
|
||||
this.max_objects = 4;
|
||||
this.max_levels = 4;
|
||||
|
||||
this.level = level || 0;
|
||||
this.bounds = boundary;
|
||||
|
||||
this.objects = [];
|
||||
this.nodes = [];
|
||||
}
|
||||
|
||||
insert(pRect) {
|
||||
var i = 0, indexes;
|
||||
|
||||
//if we have subnodes, call insert on matching subnodes
|
||||
if (this.nodes.length) {
|
||||
indexes = this.getIndex(pRect);
|
||||
|
||||
for (i = 0; i < indexes.length; i++) {
|
||||
this.nodes[indexes[i]].insert(pRect);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//otherwise, store object here
|
||||
this.objects.push(pRect);
|
||||
|
||||
//max_objects reached
|
||||
if (this.objects.length > this.max_objects && this.level < this.max_levels) {
|
||||
|
||||
//split if we don't already have subnodes
|
||||
if (!this.nodes.length) {
|
||||
this.split();
|
||||
}
|
||||
|
||||
//add all objects to their corresponding subnode
|
||||
for (i = 0; i < this.objects.length; i++) {
|
||||
indexes = this.getIndex(this.objects[i]);
|
||||
for (var k = 0; k < indexes.length; k++) {
|
||||
this.nodes[indexes[k]].insert(this.objects[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//clean up this node
|
||||
this.objects = [];
|
||||
}
|
||||
}
|
||||
|
||||
split() {
|
||||
var nextLevel = this.level + 1,
|
||||
subWidth = this.bounds.width / 2,
|
||||
subHeight = this.bounds.height / 2,
|
||||
x = this.bounds.x,
|
||||
y = this.bounds.y;
|
||||
|
||||
//top right node
|
||||
this.nodes[0] = new QuadTree({
|
||||
x: x + subWidth,
|
||||
y: y,
|
||||
width: subWidth,
|
||||
height: subHeight
|
||||
}, nextLevel);
|
||||
|
||||
//top left node
|
||||
this.nodes[1] = new QuadTree({
|
||||
x: x,
|
||||
y: y,
|
||||
width: subWidth,
|
||||
height: subHeight
|
||||
}, nextLevel);
|
||||
|
||||
//bottom left node
|
||||
this.nodes[2] = new QuadTree({
|
||||
x: x,
|
||||
y: y + subHeight,
|
||||
width: subWidth,
|
||||
height: subHeight
|
||||
}, nextLevel);
|
||||
|
||||
//bottom right node
|
||||
this.nodes[3] = new QuadTree({
|
||||
x: x + subWidth,
|
||||
y: y + subHeight,
|
||||
width: subWidth,
|
||||
height: subHeight
|
||||
}, nextLevel);
|
||||
}
|
||||
|
||||
|
||||
getIndex(pRect) {
|
||||
var indexes = [],
|
||||
verticalMidpoint = this.bounds.x + (this.bounds.width / 2),
|
||||
horizontalMidpoint = this.bounds.y + (this.bounds.height / 2);
|
||||
|
||||
var startIsNorth = pRect.y < horizontalMidpoint,
|
||||
startIsWest = pRect.x < verticalMidpoint,
|
||||
endIsEast = pRect.x + pRect.width > verticalMidpoint,
|
||||
endIsSouth = pRect.y + pRect.height > horizontalMidpoint;
|
||||
|
||||
//top-right quad
|
||||
if (startIsNorth && endIsEast) {
|
||||
indexes.push(0);
|
||||
}
|
||||
|
||||
//top-left quad
|
||||
if (startIsWest && startIsNorth) {
|
||||
indexes.push(1);
|
||||
}
|
||||
|
||||
//bottom-left quad
|
||||
if (startIsWest && endIsSouth) {
|
||||
indexes.push(2);
|
||||
}
|
||||
|
||||
//bottom-right quad
|
||||
if (endIsEast && endIsSouth) {
|
||||
indexes.push(3);
|
||||
}
|
||||
|
||||
return indexes;
|
||||
}
|
||||
|
||||
|
||||
retrieve(pRect) {
|
||||
var indexes = this.getIndex(pRect),
|
||||
returnObjects = this.objects;
|
||||
|
||||
//if we have subnodes, retrieve their objects
|
||||
if (this.nodes.length) {
|
||||
for (var i = 0; i < indexes.length; i++) {
|
||||
returnObjects = returnObjects.concat(this.nodes[indexes[i]].retrieve(pRect));
|
||||
}
|
||||
}
|
||||
|
||||
//remove duplicates
|
||||
returnObjects = returnObjects.filter(function (item, index) {
|
||||
return returnObjects.indexOf(item) >= index;
|
||||
});
|
||||
|
||||
return returnObjects;
|
||||
}
|
||||
|
||||
objLen() {
|
||||
|
||||
if (this.nodes.length > 0) {
|
||||
let len = 0;
|
||||
for (let node of this.nodes) {
|
||||
len += node.objLen();
|
||||
}
|
||||
return len;
|
||||
} else {
|
||||
//console.log('this.objects.length------',this.objects.length)
|
||||
return this.objects.length;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
9
assets/Scripts/common/QuadTree.ts.meta
Normal file
9
assets/Scripts/common/QuadTree.ts.meta
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "469a12f5-f4ff-4458-94aa-0a440aaa114b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
12
assets/Scripts/conf.meta
Normal file
12
assets/Scripts/conf.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "898d135e-bd05-458a-9341-3330818414fa",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/Scripts/core.meta
Normal file
12
assets/Scripts/core.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "ac65df09-c708-442b-aecb-03ab39ca0520",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/Scripts/core/audio.meta
Normal file
12
assets/Scripts/core/audio.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "83df4394-0767-4beb-b866-a462e2cdc935",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/Scripts/core/conf.meta
Normal file
12
assets/Scripts/core/conf.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "208bfb92-0ad4-43a2-aee6-f6ac8ade12f1",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/Scripts/core/data.meta
Normal file
12
assets/Scripts/core/data.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "52f9fe41-4b8f-4a28-a615-9738f52a1974",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/Scripts/core/event.meta
Normal file
12
assets/Scripts/core/event.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "c447163d-4e7c-4993-ae73-1daf18176e15",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
139
assets/Scripts/core/event/EventManager.ts
Normal file
139
assets/Scripts/core/event/EventManager.ts
Normal file
@ -0,0 +1,139 @@
|
||||
import ArrayUtil from "../utils/ArrayUtil";
|
||||
|
||||
interface EventInfo {
|
||||
func: Function;
|
||||
target: any;
|
||||
}
|
||||
|
||||
class EventInfoList {
|
||||
infos: EventInfo[] = null;
|
||||
constructor() {
|
||||
this.infos = [];
|
||||
}
|
||||
|
||||
has(cb: Function, target: any) {
|
||||
if (this.infos.length <= 0) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < this.infos.length; i++) {
|
||||
if (this.infos[i].func === cb && this.infos[i].target === target) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* 添加事件
|
||||
* @param cb 回调
|
||||
* @param target 目标
|
||||
*/
|
||||
add(cb: Function, target: any) {
|
||||
if (this.has(cb, target)) {
|
||||
console.log('event repeat--------')
|
||||
return;
|
||||
}
|
||||
let info = Object.create(null);
|
||||
info.func = cb;
|
||||
info.target = target;
|
||||
|
||||
this.infos.push(info);
|
||||
}
|
||||
|
||||
/**移除指定 */
|
||||
remove(cb: Function, target: any) {
|
||||
|
||||
if (this.infos.length <= 0) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < this.infos.length; i++) {
|
||||
if (this.infos[i].func === cb && this.infos[i].target === target) {
|
||||
ArrayUtil.fastRemoveAt(this.infos, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**移除目标所有 */
|
||||
removeByTarget(target: any) {
|
||||
if (this.infos.length <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.infos.length; i++) {
|
||||
if (this.infos[i].target === target) {
|
||||
ArrayUtil.fastRemoveAt(this.infos, i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**移除所有 */
|
||||
removeAll() {
|
||||
this.infos.forEach(info => {
|
||||
info.func = null;
|
||||
info.target = null;
|
||||
})
|
||||
this.infos = [];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class EventManager {
|
||||
private _evtMap: { [k: string]: EventInfoList } = Object.create(null);
|
||||
on(evtType: string, cb: Function, target?: any) {
|
||||
let list = this._evtMap[evtType];
|
||||
if (!list) {
|
||||
list = new EventInfoList();
|
||||
this._evtMap[evtType] = list;
|
||||
}
|
||||
list.add(cb, target);
|
||||
}
|
||||
|
||||
emit(evtType: string, ...params: any) {
|
||||
let list = this._evtMap[evtType];
|
||||
if (!list || list.infos.length <= 0) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < list.infos.length; i++) {
|
||||
let info = list.infos[i];
|
||||
if (info.func) {
|
||||
info.func.apply(info.target, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**移除 指定*/
|
||||
off(evtType: string, cb: Function, target?: any) {
|
||||
let list = this._evtMap[evtType];
|
||||
if (!list) return;
|
||||
list.remove(cb, target);
|
||||
}
|
||||
|
||||
/**事件名或目标移除 */
|
||||
offByKeyOrTarget(key: string | Object) {
|
||||
let list: EventInfoList = null;
|
||||
if (typeof key == 'string') {
|
||||
list = this._evtMap[key];
|
||||
if (list) {
|
||||
list.removeAll();
|
||||
}
|
||||
} else {
|
||||
|
||||
for (let k in this._evtMap) {
|
||||
list = this._evtMap[k];
|
||||
list.removeByTarget(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* [1] Class member could be defined like this.
|
||||
* [2] Use `property` decorator if your want the member to be serializable.
|
||||
* [3] Your initialization goes here.
|
||||
* [4] Your update function goes here.
|
||||
*
|
||||
* Learn more about scripting: https://docs.cocos.com/creator/3.4/manual/zh/scripting/
|
||||
* Learn more about CCClass: https://docs.cocos.com/creator/3.4/manual/zh/scripting/ccclass.html
|
||||
* Learn more about life-cycle callbacks: https://docs.cocos.com/creator/3.4/manual/zh/scripting/life-cycle-callbacks.html
|
||||
*/
|
11
assets/Scripts/core/event/EventManager.ts.meta
Normal file
11
assets/Scripts/core/event/EventManager.ts.meta
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "8069a6da-1c33-4e0f-bd20-cbe42cdf93c0",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"simulateGlobals": []
|
||||
}
|
||||
}
|
12
assets/Scripts/core/gui.meta
Normal file
12
assets/Scripts/core/gui.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "a60ec33d-05ca-4c72-af63-b9114ccf5f19",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/Scripts/core/libs.meta
Normal file
12
assets/Scripts/core/libs.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "592a89e6-ccc0-4774-9ee7-9ae076b12106",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/Scripts/core/libs/encrypt.meta
Normal file
12
assets/Scripts/core/libs/encrypt.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "51ab9e9b-4ad5-4d65-aa54-46a3cdddf83d",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/Scripts/core/resLoad.meta
Normal file
12
assets/Scripts/core/resLoad.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "c84048a4-78cc-469b-b4de-3eacd1548372",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/Scripts/core/utils.meta
Normal file
12
assets/Scripts/core/utils.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "226f498a-54ae-42df-a53b-444ed7d701dc",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
120
assets/Scripts/core/utils/ArrayUtil.ts
Normal file
120
assets/Scripts/core/utils/ArrayUtil.ts
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* @Author: dgflash
|
||||
* @Date: 2021-08-11 16:41:12
|
||||
* @LastEditors: dgflash
|
||||
* @LastEditTime: 2022-01-24 15:00:52
|
||||
*/
|
||||
/**
|
||||
* 数组工具
|
||||
*/
|
||||
export default class ArrayUtil {
|
||||
/** 去重 */
|
||||
public static noRepeated(arr: any[]) {
|
||||
var res = [arr[0]];
|
||||
for (var i = 1; i < arr.length; i++) {
|
||||
var repeat = false;
|
||||
for (var j = 0; j < res.length; j++) {
|
||||
if (arr[i] == res[j]) {
|
||||
repeat = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!repeat) {
|
||||
res.push(arr[i]);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制二维数组
|
||||
* @param array 目标数组
|
||||
*/
|
||||
public static copy2DArray(array: any[][]): any[][] {
|
||||
let newArray: any[][] = [];
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
newArray.push(array[i].concat());
|
||||
}
|
||||
return newArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fisher-Yates Shuffle 随机置乱算法
|
||||
* @param array 目标数组
|
||||
*/
|
||||
public static fisherYatesShuffle(array: any[]): any[] {
|
||||
let count = array.length;
|
||||
while (count) {
|
||||
let index = Math.floor(Math.random() * count--);
|
||||
let temp = array[count];
|
||||
array[count] = array[index];
|
||||
array[index] = temp;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 混淆数组
|
||||
* @param array 目标数组
|
||||
*/
|
||||
public static confound(array: []): any[] {
|
||||
let result = array.slice().sort(() => Math.random() - .5);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组扁平化
|
||||
* @param array 目标数组
|
||||
*/
|
||||
public static flattening(array: any[]) {
|
||||
for (; array.some(v => Array.isArray(v));) { // 判断 array 中是否有数组
|
||||
array = [].concat.apply([], array); // 压扁数组
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/** 删除数组中指定项 */
|
||||
public static removeItem(array: any[], item: any) {
|
||||
var temp = array.concat();
|
||||
for (let i = 0; i < temp.length; i++) {
|
||||
const value = temp[i];
|
||||
if (item == value) {
|
||||
array.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并数组
|
||||
* @param array1 目标数组1
|
||||
* @param array2 目标数组2
|
||||
*/
|
||||
public static combineArrays(array1: any[], array2: any[]): any[] {
|
||||
let newArray = [...array1, ...array2];
|
||||
return newArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机数组成员
|
||||
* @param array 目标数组
|
||||
*/
|
||||
public static getRandomValueInArray(array: any[]): any {
|
||||
let newArray = array[Math.floor(Math.random() * array.length)];
|
||||
return newArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速移除指定位置数组元素
|
||||
* @param array
|
||||
* @param idx
|
||||
* @returns
|
||||
*/
|
||||
public static fastRemoveAt(array: any[],idx:number){
|
||||
let len = array.length-1; //1
|
||||
if(idx > len) return;
|
||||
array[idx] = array[len];
|
||||
array.length = len;
|
||||
}
|
||||
}
|
11
assets/Scripts/core/utils/ArrayUtil.ts.meta
Normal file
11
assets/Scripts/core/utils/ArrayUtil.ts.meta
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "b65cfca1-1091-485a-adbf-aadd39753a50",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"simulateGlobals": []
|
||||
}
|
||||
}
|
116
assets/Scripts/core/utils/MathUtil.ts
Normal file
116
assets/Scripts/core/utils/MathUtil.ts
Normal file
@ -0,0 +1,116 @@
|
||||
export class MathUtil {
|
||||
/**
|
||||
* 角度转弧度
|
||||
*/
|
||||
public static deg2Rad: number = Math.PI / 180;
|
||||
|
||||
/**
|
||||
* 弧度转角度
|
||||
*/
|
||||
public static rad2Deg: number = 180 / Math.PI;
|
||||
|
||||
/**
|
||||
* 获得随机方向
|
||||
*/
|
||||
public static sign(x: number) {
|
||||
if (x > 0) {
|
||||
return 1;
|
||||
}
|
||||
if (x < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** 随时间变化进度值 */
|
||||
public static progress(start: number, end: number, t: number) {
|
||||
return start + (end - start) * t;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插值
|
||||
* @param numStart
|
||||
* @param numEnd
|
||||
* @param t
|
||||
*/
|
||||
public static lerp(numStart: number, numEnd: number, t: number): number {
|
||||
if (t > 1) {
|
||||
t = 1;
|
||||
}
|
||||
else if (t < 0) {
|
||||
t = 0
|
||||
}
|
||||
|
||||
return numStart * (1 - t) + (numEnd * t);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param angle1 角度插值
|
||||
* @param angle2
|
||||
* @param t
|
||||
*/
|
||||
public static lerpAngle(current: number, target: number, t: number): number {
|
||||
current %= 360;
|
||||
target %= 360;
|
||||
|
||||
var dAngle: number = target - current;
|
||||
|
||||
if (dAngle > 180) {
|
||||
target = current - (360 - dAngle);
|
||||
}
|
||||
else if (dAngle < -180) {
|
||||
target = current + (360 + dAngle);
|
||||
}
|
||||
|
||||
return (MathUtil.lerp(current, target, t) % 360 + 360) % 360;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按一定的速度从一个角度转向令一个角度
|
||||
* @param current
|
||||
* @param target
|
||||
* @param speed
|
||||
*/
|
||||
public static angleTowards(current: number, target: number, speed: number): number {
|
||||
current %= 360;
|
||||
target %= 360;
|
||||
|
||||
var dAngle: number = target - current;
|
||||
|
||||
if (dAngle > 180) {
|
||||
target = current - (360 - dAngle);
|
||||
}
|
||||
else if (dAngle < -180) {
|
||||
target = current + (360 + dAngle);
|
||||
}
|
||||
|
||||
var dir = target - current;
|
||||
|
||||
if (speed > Math.abs(dir)) {
|
||||
return target;
|
||||
}
|
||||
|
||||
return ((current + speed * Math.sign(dir)) % 360 + 360) % 360;
|
||||
}
|
||||
|
||||
public static clamp(value: number, minLimit: number, maxLimit: number) {
|
||||
if (value < minLimit) {
|
||||
return minLimit;
|
||||
}
|
||||
|
||||
if (value > maxLimit) {
|
||||
return maxLimit;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得一个值的概率
|
||||
* @param value
|
||||
*/
|
||||
public static probability(value: number) {
|
||||
return Math.random() < value;
|
||||
}
|
||||
}
|
11
assets/Scripts/core/utils/MathUtil.ts.meta
Normal file
11
assets/Scripts/core/utils/MathUtil.ts.meta
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "2df4c13d-eb96-4b3c-bf18-ba3367cc6022",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"simulateGlobals": []
|
||||
}
|
||||
}
|
231
assets/Scripts/core/utils/Util.ts
Normal file
231
assets/Scripts/core/utils/Util.ts
Normal file
@ -0,0 +1,231 @@
|
||||
// Learn TypeScript:
|
||||
// - https://docs.cocos.com/creator/manual/en/scripting/typescript.html
|
||||
// Learn Attribute:
|
||||
// - https://docs.cocos.com/creator/manual/en/scripting/reference/attributes.html
|
||||
// Learn life-cycle callbacks:
|
||||
// - https://docs.cocos.com/creator/manual/en/scripting/life-cycle-callbacks.html
|
||||
|
||||
import { v2, v3, Vec2, Vec3 } from "cc";
|
||||
|
||||
|
||||
export default class Util {
|
||||
|
||||
|
||||
static simpleV2(value, out?: Vec2) {
|
||||
if (out) {
|
||||
out.set(value, value)
|
||||
return out
|
||||
}
|
||||
return v2(value, value)
|
||||
}
|
||||
|
||||
static simpleV3(value, out?: Vec3) {
|
||||
if (out) {
|
||||
out.set(value, value, value)
|
||||
return out
|
||||
}
|
||||
return v3(value, value, value)
|
||||
}
|
||||
|
||||
static v2t3(v2Data: Vec2, out?: Vec3) {
|
||||
if (!out) {
|
||||
return v3(v2Data.x, v2Data.y, 1)
|
||||
} else {
|
||||
out.x = v2Data.x
|
||||
out.y = v2Data.y
|
||||
out.z = 1
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
static v3t2(v3Data: Vec3, out?: Vec2) {
|
||||
if (!out) {
|
||||
return v2(v3Data.x, v3Data.y)
|
||||
} else {
|
||||
out.x = v3Data.x
|
||||
out.y = v3Data.y
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return min - max 之间的数,包括min和max
|
||||
* @param min
|
||||
* @param max
|
||||
*/
|
||||
static randomNumber(min: number, max: number) {
|
||||
if (min == max) {
|
||||
return min
|
||||
}
|
||||
return Math.floor(Math.random() * (max - min + 1) + min)
|
||||
}
|
||||
|
||||
static findArrNullIdx(arr: any[]) {
|
||||
if (arr.length == 0) return 0;
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (!arr[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//分割number字符串 返回number 数组
|
||||
public static splitNumber(str: number | string, Separator: string = ',') {
|
||||
if (!str) return [];
|
||||
if (typeof str == 'number') {
|
||||
return [str];
|
||||
}
|
||||
|
||||
return str.split(Separator).map((s_num, idx) => {
|
||||
return Number(s_num);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param weights 权重数组 随机一个
|
||||
* @returns
|
||||
*/
|
||||
static weightRandomIdx(weights: number[]) {
|
||||
if (weights.length <= 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let tw: number = 0;
|
||||
for (let i = 0; i < weights.length; i++) {
|
||||
tw += weights[i];
|
||||
}
|
||||
let rw = Math.random() * tw;
|
||||
let sw: number = 0, ew: number = 0;
|
||||
|
||||
for (let i = 0; i < weights.length; i++) {
|
||||
ew = sw + weights[i];
|
||||
if (sw < rw && rw <= ew) {
|
||||
return i;
|
||||
}
|
||||
sw = ew;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**小数去0 */
|
||||
public static numMoveZoro(num: string) {
|
||||
if (num.indexOf('.') < 0) {
|
||||
return num;
|
||||
}
|
||||
num = num.replace(/0+?$/g, '')
|
||||
if (num[num.length - 1] == '.') {
|
||||
num = num.replace(/[.$]/, '');
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param num 秒 格式化 h:f:s
|
||||
*/
|
||||
public static secondFormat(num: number) {
|
||||
let str = '';
|
||||
let h: number = Math.floor(num / 3600)
|
||||
num -= h * 3600;
|
||||
let f: number = Math.floor(num / 60)
|
||||
num -= f * 60;
|
||||
num = Math.floor(num)
|
||||
|
||||
// str += (h<10?'0' + h:h);
|
||||
|
||||
// str += ':';
|
||||
str += (f < 10 ? '0' + f : f);
|
||||
|
||||
str += ':';
|
||||
str += (num < 10 ? '0' + num : num);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/**日期展示 */
|
||||
public static getDateStr(time: number, format: number = 1) {
|
||||
|
||||
let date = new Date(time);
|
||||
let y = date.getFullYear(), m = date.getMonth() + 1,
|
||||
d = date.getDate(), h = date.getHours(), mn = date.getMinutes();
|
||||
|
||||
let mnStr = '' + mn;
|
||||
if (mn < 10) mnStr = '0' + mn;
|
||||
if (format == 1) {
|
||||
return `${y}/${m}/${d} ${h}:${mnStr}`;
|
||||
} else {
|
||||
return `${y}年${m}月${d}日 ${h}:${mnStr}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static generatorCallBack(len: number, callBack: Function, ...params: any) {
|
||||
function* gen() {
|
||||
for (let i = 0; i < len; i++) {
|
||||
yield callBack(i, ...params)
|
||||
}
|
||||
}
|
||||
return this.exeGenerator(gen(), 10);
|
||||
}
|
||||
|
||||
static exeGenerator(generator: Generator, duration: number) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let gen = generator
|
||||
let execute = () => {
|
||||
let startTime = new Date().getTime()
|
||||
for (let iter = gen.next(); ; iter = gen.next()) {
|
||||
if (iter == null || iter.done) {
|
||||
resolve(null)
|
||||
return
|
||||
}
|
||||
if (new Date().getTime() - startTime > duration) {
|
||||
setTimeout(() => {
|
||||
execute()
|
||||
}, duration)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
execute()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝对象
|
||||
* @param src_obj 源对象
|
||||
* @param dst_obj 目标对象
|
||||
*/
|
||||
static copyObj(src_obj: any, dst_obj: any) {
|
||||
if (typeof dst_obj === "object") {
|
||||
for (var key in dst_obj) {
|
||||
if (typeof dst_obj[key] === "object") {
|
||||
src_obj[key] != null && Util.copyObj(src_obj[key], dst_obj[key]);
|
||||
} else if (typeof dst_obj[key] != "function") {
|
||||
src_obj[key] != null && (dst_obj[key] = src_obj[key]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log("can not copy the value type");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 克隆对象
|
||||
* @param obj 源对象
|
||||
* @returns 新的对象
|
||||
*/
|
||||
static cloneObj(obj: any) {
|
||||
var new_obj;
|
||||
if (obj != null && (obj.constructor === Object || obj.constructor === Array)) {
|
||||
new_obj = new obj.constructor();
|
||||
for (var key in obj) {
|
||||
new_obj[key] = Util.cloneObj(obj[key]);
|
||||
}
|
||||
} else {
|
||||
new_obj = obj;
|
||||
}
|
||||
return new_obj;
|
||||
}
|
||||
}
|
11
assets/Scripts/core/utils/Util.ts.meta
Normal file
11
assets/Scripts/core/utils/Util.ts.meta
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "75224168-75d8-4cf6-bad8-d73031b24457",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"simulateGlobals": []
|
||||
}
|
||||
}
|
12
assets/Scripts/logics.meta
Normal file
12
assets/Scripts/logics.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "7a60fd5f-89a1-4ee2-8dfa-68a14995f68c",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/Scripts/logics/mng.meta
Normal file
12
assets/Scripts/logics/mng.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "3df89b31-a5ec-4394-bbbd-d80e4c3b0b53",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
34
assets/Scripts/logics/mng/SortMng.ts
Normal file
34
assets/Scripts/logics/mng/SortMng.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { Component, _decorator } from "cc";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
@ccclass
|
||||
export default class SortMng extends Component {
|
||||
|
||||
private _frameCount: number = 0;
|
||||
|
||||
update(dt) {
|
||||
if (++this._frameCount % 6 == 0) {
|
||||
this._frameCount = 0
|
||||
this.sortChildrenByY();
|
||||
}
|
||||
}
|
||||
|
||||
private sortChildrenByY() {
|
||||
let listToSort = this.node.children.slice();
|
||||
listToSort.sort(function (a, b) {
|
||||
return b.getPosition().y - a.getPosition().y;
|
||||
});
|
||||
|
||||
// console.log("排序::" , listToSort.length)
|
||||
for (let i = 0; i < listToSort.length; ++i) {
|
||||
let node = listToSort[i];
|
||||
if (node.active) {
|
||||
node.setSiblingIndex(i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
assets/Scripts/logics/mng/SortMng.ts.meta
Normal file
11
assets/Scripts/logics/mng/SortMng.ts.meta
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "b52c9552-a69e-490e-957e-eb0e6f08087a",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"simulateGlobals": []
|
||||
}
|
||||
}
|
126
assets/Scripts/logics/mng/WaveMng.ts
Normal file
126
assets/Scripts/logics/mng/WaveMng.ts
Normal file
@ -0,0 +1,126 @@
|
||||
import { Component, Rect, UITransform, v2, v3, Vec2, Vec3, view, _decorator } from "cc";
|
||||
import QuadTree from "../../common/QuadTree";
|
||||
import Util from "../../core/utils/Util";
|
||||
import { Simulator } from "../../RVO/Simulator";
|
||||
import { monsterCtl } from "../../Test/monsterCtl";
|
||||
import MonsterFactory from "../../Test/MonsterFactory";
|
||||
import Game from "../../Test/Test2";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
*/
|
||||
@ccclass("WaveMng")
|
||||
export default class WaveMng extends Component {
|
||||
|
||||
public quadTree: QuadTree = null;
|
||||
private _wHalf: number = 0;
|
||||
private _hHalf: number = 0;
|
||||
monsterPrefab: Array<string> = ["A", "B", "C"];
|
||||
// monsterPrefab: Array<string> = ["A"];
|
||||
|
||||
|
||||
|
||||
update() {
|
||||
this.rebuildTree()
|
||||
}
|
||||
|
||||
rebuildTree() {
|
||||
if (!Game.Instance.camera) {
|
||||
return
|
||||
}
|
||||
let rect = view.getViewportRect()
|
||||
this.quadTree = new QuadTree(rect, 0);
|
||||
Game.Instance.Player.monsterList.forEach(enm => {
|
||||
let rect = enm.getComponent(UITransform).getBoundingBoxToWorld()
|
||||
let treeObj = { x: rect.x, y: rect.y, height: rect.height, width: rect.width, rect: rect, collider: enm }
|
||||
this.quadTree.insert(treeObj)
|
||||
})
|
||||
|
||||
}
|
||||
/**
|
||||
*/
|
||||
_tmpV2: Vec2 = new Vec2()
|
||||
_tmpV3: Vec3 = new Vec3()
|
||||
createMonster() {
|
||||
// if (Game.Instance.Player.monsterList.length <= 0) {
|
||||
// return
|
||||
if (Game.Instance.Player.monsterList.length >= Game.Instance.limitMonst) {
|
||||
return
|
||||
}
|
||||
// if (this.node.getComponent(WaveMng)) {
|
||||
let posArray = this.getPos(0, Math.random() * 20 + 10)
|
||||
let speedCfg = [120, 80 , 80]
|
||||
let radiusCfg = [20, 18, 18]
|
||||
let maxHp = 4
|
||||
// return
|
||||
for (let idx = 0; idx < posArray.length; idx++) {
|
||||
let prefabIdx = Math.floor(Math.random() * this.monsterPrefab.length)
|
||||
let prefabName: string = this.monsterPrefab[prefabIdx]// Math.random() > 0.5 ? this.sphereRed : this.sphereBlue
|
||||
let speed = speedCfg[prefabIdx]
|
||||
let radius = radiusCfg[prefabIdx]
|
||||
let mass = 1
|
||||
let sacle = 0.5
|
||||
let checkDis = radius * 2
|
||||
let hp = Math.floor(Math.random() * maxHp)
|
||||
if (prefabIdx != 0) {
|
||||
sacle = 0.5
|
||||
}
|
||||
|
||||
let p = posArray[idx]
|
||||
let agentId = Simulator.instance.addAgent(Util.v3t2(p, this._tmpV2), radius, speed, null, mass);
|
||||
let agentObj = Simulator.instance.getAgentByAid(agentId)
|
||||
agentObj.neighborDist = checkDis //动态修改每个agent的巡视范围
|
||||
|
||||
MonsterFactory.Instance.creatorMonsterByName(prefabName, p, (node) => {
|
||||
node.getComponent(monsterCtl).agentHandleId = agentId
|
||||
node.getComponent(monsterCtl).hp = hp
|
||||
node.setScale(Util.simpleV3(sacle, this._tmpV3))
|
||||
Game.Instance.Player.monsterList.push(node)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取四叉树的推荐检测列表
|
||||
* @param checkRect
|
||||
*/
|
||||
getTreeColliderList(checkRect: Rect) {
|
||||
if (this.quadTree) {
|
||||
return this.quadTree.retrieve(checkRect)
|
||||
}
|
||||
return []
|
||||
|
||||
}
|
||||
|
||||
|
||||
_tmpPosVe3: Array<Vec3> = []
|
||||
getPos(type: number, numbs: number) {
|
||||
this._tmpPosVe3.length = 0
|
||||
const size = view.getVisibleSize();
|
||||
this._wHalf = size.width * 0.25 + 300 + Math.random() * 300;
|
||||
this._hHalf = size.height * 0.25 + 200 + Math.random() * 200;
|
||||
let len = numbs
|
||||
var w = this._wHalf;//椭圆长
|
||||
var h = this._hHalf; //椭圆高
|
||||
var angle = 360 / len;
|
||||
var x, y;
|
||||
const point = Game.Instance.Player.node.getPosition();
|
||||
for (let i = 0; i < len; i++) {
|
||||
// Mathf.Deg2Rad 单位角度的弧 相当于 1° 的弧度
|
||||
x = w * Math.cos(i * (angle / 180) * Math.PI);
|
||||
y = h * Math.sin(i * (angle / 180) * Math.PI);
|
||||
this._tmpPosVe3.push(v3(x + point.x, y + point.y, 1))
|
||||
}
|
||||
return this._tmpPosVe3
|
||||
}
|
||||
|
||||
protected onEnable(): void {
|
||||
|
||||
}
|
||||
|
||||
protected onDisable(): void {
|
||||
|
||||
}
|
||||
|
||||
}
|
11
assets/Scripts/logics/mng/WaveMng.ts.meta
Normal file
11
assets/Scripts/logics/mng/WaveMng.ts.meta
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "044954b5-0556-4284-bbdd-f0362ec09a7d",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"simulateGlobals": []
|
||||
}
|
||||
}
|
12
assets/Scripts/logics/role.meta
Normal file
12
assets/Scripts/logics/role.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "7e2691b1-d8ca-4638-b82f-078495072927",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/Scripts/logics/skill.meta
Normal file
12
assets/Scripts/logics/skill.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "131cd532-4953-4327-91e0-a3b80ee3e5d2",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/Scripts/logics/skillEntity.meta
Normal file
12
assets/Scripts/logics/skillEntity.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "da6ceaaa-4625-4dae-b727-9ae8531388b7",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/Scripts/mng.meta
Normal file
12
assets/Scripts/mng.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "a8db4dac-3dca-4f08-b580-72ba8ee0e7bb",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
451
assets/Scripts/mng/wave.ts
Normal file
451
assets/Scripts/mng/wave.ts
Normal file
@ -0,0 +1,451 @@
|
||||
// import { Component, v2, Vec2, view, _decorator } from 'cc';
|
||||
// import { app } from '../../app';
|
||||
// import ItemHelper from '../common/ItemHelper';
|
||||
// import QuadTree from '../common/QuadTree ';
|
||||
// import { JsonModule } from '../conf/ClientConfigMgr';
|
||||
// import Util from '../core/utils/Util';
|
||||
// import Enemy from '../logics/Enemy';
|
||||
// const { ccclass, property } = _decorator;
|
||||
|
||||
// interface IWaveData { //波次数据
|
||||
// id: number;
|
||||
// tm: number;
|
||||
// curTm: number;
|
||||
// }
|
||||
|
||||
|
||||
// interface IFbMonsterData { //需要创建怪物数据
|
||||
// pos: Vec2;
|
||||
// mid: number;
|
||||
// fid: number;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// @ccclass('NewComponent')
|
||||
// export class NewComponent extends Component {
|
||||
// private _enemyList: Enemy[] = null;
|
||||
|
||||
// private _target: any = null;
|
||||
|
||||
// private _fbArr: IWaveData[] = [];
|
||||
|
||||
// private _cTime: number;
|
||||
// private _createList: IFbMonsterData[] = null; //创建怪物列表
|
||||
|
||||
// public checkEnemys: Enemy[] = [];
|
||||
// public quadTree: QuadTree = null;
|
||||
|
||||
// private _wHalf: number = 0;
|
||||
// private _hHalf: number = 0;
|
||||
|
||||
// initial() {
|
||||
|
||||
// this._target = app.gameMgr.camera;
|
||||
// this._fbArr = [];
|
||||
// this._enemyList = [];
|
||||
// this._createList = [];
|
||||
// this._cTime = 0;
|
||||
|
||||
// const size = view.getVisibleSize();
|
||||
// this._wHalf = size.width * 0.25 + 50;
|
||||
// this._hHalf = size.height * 0.25 + 50;
|
||||
|
||||
// // if(ItemHelper.isDoubleMode()){
|
||||
// // this.startWaves();
|
||||
// // }
|
||||
// }
|
||||
|
||||
// startWaves() {
|
||||
// let cfg;
|
||||
// if (ItemHelper.isDoubleMode()) { //双人模式
|
||||
// cfg = app.confMgr.conf.fbconfig.getConfById(420001);
|
||||
// } else { //单人模式
|
||||
|
||||
// if (app.dataMgr.battleData.player1.trySuperSkill != null) { //超武试用
|
||||
// cfg = app.confMgr.conf.fbconfig.getConfById(410001);
|
||||
// } else {
|
||||
// let fbConfs = app.confMgr.conf.fbconfig.getConfArr();
|
||||
// cfg = fbConfs[0];
|
||||
// }
|
||||
// }
|
||||
|
||||
// this._fbArr.push({ id: cfg.id, tm: cfg.fresh_cd, curTm: 0 });
|
||||
// }
|
||||
|
||||
// gameUpdate(dt) {
|
||||
|
||||
// // this._fbArr.forEach(f => {
|
||||
// // f.curTm += dt;
|
||||
// // })
|
||||
// // for (let i = 0; i < this._fbArr.length; i++) {
|
||||
// // let fb = this._fbArr[i];
|
||||
// // if (fb.curTm > fb.tm) {
|
||||
// // ArrayUtil.fastRemoveAt(this._fbArr, i);
|
||||
// // this.freshCreateWave(fb);
|
||||
// // break;
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// let rect = view.getVisibleSize() // app.gameMgr.camera.getViewRect();
|
||||
// this.quadTree = new QuadTree(rect, 0);
|
||||
|
||||
// this.checkEnemys = [];
|
||||
// this._enemyList.forEach(enemy => {
|
||||
// enemy.gameUpdate(dt);
|
||||
|
||||
// if (!enemy.isDie && enemy.inView) {
|
||||
// this.checkEnemys.push(enemy);
|
||||
// this.quadTree.insert(enemy.getCollisionData());
|
||||
// }
|
||||
|
||||
// })
|
||||
// // console.log('this._enemyList---------',this._enemyList.length,this.checkEnemys.length,this.quadTree.objLen())
|
||||
|
||||
// //逐帧创建怪物
|
||||
// if (this._createList.length > 0) {
|
||||
// this._cTime += dt;
|
||||
// if (this._cTime >= 0.01) {
|
||||
// this._cTime = 0;
|
||||
// this.createEnemy();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// protected onEnable(): void {
|
||||
// // systemEvent.on(EventName.gm_create_enemy, this.gmCreateEnemy, this);
|
||||
// }
|
||||
|
||||
// protected onDisable(): void {
|
||||
// // systemEvent.off(EventName.gm_create_enemy, this.gmCreateEnemy, this);
|
||||
// }
|
||||
|
||||
// gmCreateEnemy(enemyId, count, type) {
|
||||
// console.log("gmCreateEnemy :: ", enemyId, count, type)
|
||||
// let obj = Object.create(null);
|
||||
// obj.id = Math.random() > 0.5 ? 400001 : 400001;
|
||||
// obj.monster_id = enemyId;
|
||||
// obj.monster_num = count;
|
||||
// obj.fresh_site = type;
|
||||
// if (type == 20) {
|
||||
// //椭圆
|
||||
// this.initEllipseEnemys(obj);
|
||||
// } else if (type == 21) {
|
||||
// //生成圆形
|
||||
// this.initCircleEnemy(obj);
|
||||
// }
|
||||
// else {
|
||||
// //范围随机生成
|
||||
// this.initEnemys(obj);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// /**触发去创建一波 */
|
||||
// freshCreateWave(fb: { id: number, tm: number, curTm: number }) {
|
||||
|
||||
// // let fbconf = app.confMgr.conf.fbconfig.getConfById(fb.id);
|
||||
// // if (!fbconf) {
|
||||
// // return;
|
||||
// // }
|
||||
// // let nextIdStr: string = fbconf.next_id;
|
||||
// // if (nextIdStr) {
|
||||
// // let ids = Util.splitNumber(nextIdStr);
|
||||
// // for (let i = 0; i < ids.length; i++) {
|
||||
// // let conf = app.confMgr.conf.fbconfig.getConfById(ids[i]);
|
||||
// // this._fbArr.push({ id: conf.id, tm: conf.fresh_cd, curTm: 0 });
|
||||
// // }
|
||||
// // }
|
||||
// // console.log('timeToFreshWave--------', fbconf.id)
|
||||
|
||||
// // if (fbconf.fresh_site == 20) {
|
||||
// // this.initEllipseEnemys(fbconf);
|
||||
// // } else {
|
||||
// // this.initEnemys(fbconf);
|
||||
// // }
|
||||
// }
|
||||
// /**椭圆包围圈 */
|
||||
// private initEllipseEnemys(fbconf: JsonModule.IFbconfig) {
|
||||
// const mIds = Util.splitNumber(fbconf.monster_id);
|
||||
// const nums = Util.splitNumber(fbconf.monster_num);
|
||||
// let midArr: number[] = [];
|
||||
// for (let i = 0; i < nums.length; i++) {
|
||||
// let l = nums[i], mid = mIds[i];
|
||||
// for (let j = 0; j < l; j++) {
|
||||
// midArr.push(mid);
|
||||
// }
|
||||
// }
|
||||
// let len = midArr.length;
|
||||
|
||||
// var w = this._wHalf;//椭圆长
|
||||
// var h = this._hHalf; //椭圆高
|
||||
// var angle = 360 / len;
|
||||
// var x, y;
|
||||
// const point = this._target.node.getPosition();
|
||||
// for (let i = 0; i < len; i++) {
|
||||
// // Mathf.Deg2Rad 单位角度的弧 相当于 1° 的弧度
|
||||
// x = w * Math.cos(i * (angle / 180) * Math.PI);
|
||||
// y = h * Math.sin(i * (angle / 180) * Math.PI);
|
||||
|
||||
|
||||
// let obj: IFbMonsterData = Object.create(null);
|
||||
// obj.pos = v2(x + point.x, y + point.y);
|
||||
// obj.mid = midArr[i];
|
||||
// obj.fid = fbconf.id;
|
||||
// this._createList.push(obj)
|
||||
// }
|
||||
// }
|
||||
// /**圆形包围圈 */
|
||||
// private initCircleEnemy(fbconf: JsonModule.IFbconfig) {
|
||||
// const mIds = Util.splitNumber(fbconf.monster_id);
|
||||
// const nums = Util.splitNumber(fbconf.monster_num);
|
||||
// let midArr: number[] = [];
|
||||
// for (let i = 0; i < nums.length; i++) {
|
||||
// let l = nums[i], mid = mIds[i];
|
||||
// for (let j = 0; j < l; j++) {
|
||||
// midArr.push(mid);
|
||||
// }
|
||||
// }
|
||||
// let len = midArr.length;
|
||||
// const point = this._target.node.getPosition();
|
||||
// const averageAngle = 360 / len;
|
||||
// let a = 0;
|
||||
// for (let i = 0; i < len; i++) {
|
||||
// let r = this._hHalf + 10;//半径
|
||||
// let pos = v2();
|
||||
// pos.x = point.x + Math.sin(a * Math.PI / 180) * r;
|
||||
// pos.y = point.y + Math.cos(a * Math.PI / 180) * r;
|
||||
// a += averageAngle;
|
||||
// let obj: IFbMonsterData = Object.create(null);
|
||||
// obj.pos = pos;
|
||||
// obj.mid = midArr[i];
|
||||
// obj.fid = fbconf.id;
|
||||
// this._createList.push(obj)
|
||||
// }
|
||||
// }
|
||||
// /**方形 随机生成怪物 */
|
||||
// private initEnemys(fbconf: JsonModule.IFbconfig) {
|
||||
|
||||
// const mIds = Util.splitNumber(fbconf.monster_id);
|
||||
// const nums = Util.splitNumber(fbconf.monster_num);
|
||||
// let midArr: number[] = [];
|
||||
// for (let i = 0; i < nums.length; i++) {
|
||||
// let l = nums[i], mid = mIds[i];
|
||||
// for (let j = 0; j < l; j++) {
|
||||
// midArr.push(mid);
|
||||
// }
|
||||
// }
|
||||
// //计算四边平均怪物数量
|
||||
// let total = midArr.length;
|
||||
// let m_idx: number = 0;
|
||||
// const means = [];
|
||||
// if (total < 4) {
|
||||
// for (let i = 0; i < total; i++) {
|
||||
// means.push(1);
|
||||
// }
|
||||
// } else {
|
||||
// const mean = Math.floor(total / 4);
|
||||
// for (let i = 0; i < 4; i++) {
|
||||
// total -= mean;
|
||||
// means.push(mean);
|
||||
// }
|
||||
// for (let i = 0; i < total; i++) {
|
||||
// means[i] += 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// const point = this._target.node.getPosition();
|
||||
// const state = fbconf.fresh_site > 0 ? fbconf.fresh_site : 1;
|
||||
// const xMin = point.x - this._wHalf / state;// / 1//Util.randomNumber(1,fbconf.fresh_site);
|
||||
// const xMax = point.x + this._wHalf / state;// / 1//Util.randomNumber(1,fbconf.fresh_site);
|
||||
// const yMin = point.y - this._hHalf / state;// / 1//Util.randomNumber(1,fbconf.fresh_site);
|
||||
// const yMax = point.y + this._hHalf / state;// / 1//Util.randomNumber(1,fbconf.fresh_site);
|
||||
|
||||
// const tmpRects = [
|
||||
// new Vec2(xMin, yMin),
|
||||
// new Vec2(xMin, yMax),
|
||||
// new Vec2(xMax, yMax),
|
||||
// new Vec2(xMax, yMin)];
|
||||
// for (let i = 0; i < means.length; i++) {
|
||||
// const num = means[i];
|
||||
// let pjs = i % 2 == 0 ? (yMax - yMin) : (xMax - xMin);
|
||||
// pjs /= num;
|
||||
|
||||
// for (let j = 0; j < num; j++) {
|
||||
// const val = j * pjs;
|
||||
// const pos: Vec2 = tmpRects[i].clone();
|
||||
// if (i == 0) {
|
||||
// pos.y = Util.randomNumber(pos.y, pos.y + val);
|
||||
// if (state != 1) {
|
||||
// pos.x = Util.randomNumber(pos.x, pos.x + val)
|
||||
// }
|
||||
// // pos.x /= fbconf.fresh_site;
|
||||
// // if(num > 4) {
|
||||
// // pos.x -= Math.abs(Util.randomNumber(pos.x, pos.x - 100));
|
||||
// // }
|
||||
// } else if (i == 1) {
|
||||
// pos.x = Util.randomNumber(pos.x, pos.x + val);
|
||||
// if (state != 1)
|
||||
// pos.y = Util.randomNumber(pos.y, pos.y + val);
|
||||
// // pos.y /= fbconf.fresh_site;
|
||||
// // if(num > 4){
|
||||
// // pos.y += Util.randomNumber(pos.y, pos.y + 100);
|
||||
// // }
|
||||
// } else if (i == 2) {
|
||||
// pos.y = Util.randomNumber(pos.y, pos.y - val);
|
||||
// if (state != 1)
|
||||
// pos.x = Util.randomNumber(pos.x, pos.x - val);
|
||||
// // pos.x /= fbconf.fresh_site;
|
||||
// // if(num > 4){
|
||||
// // pos.x += Util.randomNumber(pos.x, pos.x + 100);
|
||||
// // }
|
||||
// } else {
|
||||
// pos.x = Util.randomNumber(pos.x, pos.x - val);
|
||||
// if (state != 1)
|
||||
// pos.y = Util.randomNumber(pos.y, pos.y - val);
|
||||
// // pos.x -= val;
|
||||
// // pos.y /= fbconf.fresh_site;
|
||||
// // if(num > 4){
|
||||
// // pos.y -= Math.abs(Util.randomNumber(pos.y, pos.y - 100));
|
||||
// // }
|
||||
// }
|
||||
|
||||
// let obj: IFbMonsterData = Object.create(null);
|
||||
// obj.pos = pos;
|
||||
// obj.mid = midArr[m_idx++];
|
||||
// obj.fid = fbconf.id;
|
||||
// this._createList.push(obj);
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
// /**方形 队列生成怪物 */
|
||||
// private initEnemy(fbconf: JsonModule.IFbconfig) {
|
||||
|
||||
// const mIds = Util.splitNumber(fbconf.monster_id);
|
||||
// const nums = Util.splitNumber(fbconf.monster_num);
|
||||
// let midArr: number[] = [];
|
||||
// for (let i = 0; i < nums.length; i++) {
|
||||
// let l = nums[i], mid = mIds[i];
|
||||
// for (let j = 0; j < l; j++) {
|
||||
// midArr.push(mid);
|
||||
// }
|
||||
// }
|
||||
// //计算四边平均怪物数量
|
||||
// let total = midArr.length;
|
||||
// let m_idx: number = 0;
|
||||
// const means = [];
|
||||
// if (total < 4) {
|
||||
// for (let i = 0; i < total; i++) {
|
||||
// means.push(1);
|
||||
// }
|
||||
// } else {
|
||||
// const mean = Math.floor(total / 4);
|
||||
// for (let i = 0; i < 4; i++) {
|
||||
// total -= mean;
|
||||
// means.push(mean);
|
||||
// }
|
||||
// for (let i = 0; i < total; i++) {
|
||||
// means[i] += 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// const point = this._target.node.getPosition();
|
||||
// const xMin = point.x - this._wHalf;
|
||||
// const xMax = point.x + this._wHalf;
|
||||
// const yMin = point.y - this._hHalf;
|
||||
// const yMax = point.y + this._hHalf;
|
||||
// const tmpRects = [
|
||||
// new Vec2(xMin, yMin),
|
||||
// new Vec2(xMin, yMax),
|
||||
// new Vec2(xMax, yMax),
|
||||
// new Vec2(xMax, yMin)];
|
||||
// for (let i = 0; i < means.length; i++) {
|
||||
// const num = means[i];
|
||||
// let pjs = i % 2 == 0 ? (yMax - yMin) : (xMax - xMin);
|
||||
// pjs /= num;
|
||||
|
||||
// for (let j = 0; j < num; j++) {
|
||||
// const val = j * pjs;
|
||||
// const pos: Vec2 = tmpRects[i].clone();
|
||||
// if (i == 0) {
|
||||
// pos.y += val;
|
||||
// if (num > 4) {
|
||||
// pos.x -= Math.abs(Util.randomNumber(pos.x, pos.x - 100));
|
||||
// }
|
||||
// } else if (i == 1) {
|
||||
// pos.x += val;
|
||||
// if (num > 4) {
|
||||
// pos.y += Util.randomNumber(pos.y, pos.y + 100);
|
||||
// }
|
||||
// } else if (i == 2) {
|
||||
// pos.y -= val;
|
||||
// if (num > 4) {
|
||||
// pos.x += Util.randomNumber(pos.x, pos.x + 100);
|
||||
// }
|
||||
// } else {
|
||||
// pos.x -= val;
|
||||
// if (num > 4) {
|
||||
// pos.y -= Math.abs(Util.randomNumber(pos.y, pos.y - 100));
|
||||
// }
|
||||
// }
|
||||
|
||||
// let obj: IFbMonsterData = Object.create(null);
|
||||
// obj.pos = pos;
|
||||
// obj.mid = midArr[m_idx++];
|
||||
// obj.fid = fbconf.id;
|
||||
// this._createList.push(obj);
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
// /**创建一个怪物 */
|
||||
// private createEnemy() {
|
||||
// // let cmonster: IFbMonsterData = this._createList.shift();
|
||||
// // let mCfg = app.confMgr.conf.monster.getConfById(cmonster.mid);
|
||||
|
||||
// // resLoader.loadRes<Prefab>(BoundNameEnum.subBattle, `monster/${mCfg.model_id}`).then(prefab => {
|
||||
// // let node: Node = instantiate(prefab);
|
||||
// // this.node.addChild(node);
|
||||
// // node.setPosition(cmonster.pos);
|
||||
// // let enemy: Enemy = node.getComponent(Enemy);
|
||||
|
||||
// // enemy.initial(this.findTarget(cmonster.pos), cmonster);
|
||||
// // this._enemyList.push(enemy);
|
||||
// // })
|
||||
// }
|
||||
|
||||
// /**找玩家中一个 */
|
||||
// findTarget(pos: Vec2) {
|
||||
// // let p1 = app.gameMgr.player, p2 = app.gameMgr.player2;
|
||||
// // if (!p2) return p1;
|
||||
// // return Math.abs(pos.x - p1.node.x) < Math.abs(pos.x - p2.node.x) ? p1 : p2;
|
||||
// }
|
||||
|
||||
// public getEnemyList() {
|
||||
// return this._enemyList;
|
||||
// }
|
||||
|
||||
// /** 怪物回收*/
|
||||
// public recycleEnemy(enemy: Enemy) {
|
||||
// for (let i = this._enemyList.length - 1; i >= 0; i--) {
|
||||
// const tmpEnemy = this._enemyList[i];
|
||||
// if (tmpEnemy == enemy) {
|
||||
// enemy.node.destroy();
|
||||
// this._enemyList.splice(i, 1);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// //没有怪物了,但是还未到刷怪时间,强制下一波
|
||||
// if (this._enemyList.length == 0 && this._fbArr.length > 0) {
|
||||
// this._fbArr.sort((fa, fb) => {
|
||||
// return (fa.tm - fa.curTm) - (fb.tm - fa.curTm);
|
||||
// })
|
||||
// let fb = this._fbArr[0];
|
||||
// fb.curTm = fb.tm;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
9
assets/Scripts/mng/wave.ts.meta
Normal file
9
assets/Scripts/mng/wave.ts.meta
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "9786456a-0efa-4c51-9a38-a5715e2597b0",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
12
assets/Scripts/rolectl.meta
Normal file
12
assets/Scripts/rolectl.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "ad30184e-02c7-41ef-801c-a74d7eca701b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
239
assets/Scripts/rolectl/player.ts
Normal file
239
assets/Scripts/rolectl/player.ts
Normal file
@ -0,0 +1,239 @@
|
||||
import { Component, instantiate, Node, Prefab, tween, UITransform, v2, v3, Vec2, Vec3, _decorator } from 'cc';
|
||||
import WaveMng from '../logics/mng/WaveMng';
|
||||
import { monsterCtl } from '../Test/monsterCtl';
|
||||
import { snakebody } from './snakebody';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('player')
|
||||
export class player extends Component {
|
||||
|
||||
@property(Prefab) bodyPrefab: Prefab = null
|
||||
@property(Node) tmpWeapen: Node = null
|
||||
@property(WaveMng) waveMng: WaveMng = null
|
||||
|
||||
_tmpV2: Vec2 = new Vec2()
|
||||
_tmpV3: Vec3 = new Vec3(0, 1, 0)
|
||||
_tmpV32: Vec3 = new Vec3()
|
||||
dir: Vec3 = new Vec3(0, 1, 0)
|
||||
bodyNum: number = 8
|
||||
sectionLen: number = 38
|
||||
speed: number = 3
|
||||
snakeArray: Node[];
|
||||
|
||||
//蛇头走过的点数量
|
||||
headPointsNum: number = 0
|
||||
// record all points
|
||||
pointsArray = [];
|
||||
|
||||
_isMove: boolean = false
|
||||
|
||||
monsterList: Array<Node> = []
|
||||
|
||||
|
||||
start() {
|
||||
|
||||
this.tmpWeapen.active = false
|
||||
this.headPointsNum = 0
|
||||
this.snakeArray = [];
|
||||
this.pointsArray = []
|
||||
this.snakeArray.push(this.node);
|
||||
|
||||
this.initSnake()
|
||||
}
|
||||
|
||||
initSnake() {
|
||||
this.rotateHead()
|
||||
// return
|
||||
for (let i = 1; i <= this.bodyNum; i++) {
|
||||
this.getNewBody(i);
|
||||
this.recordPoints()
|
||||
}
|
||||
console.log("初始化的节点...", this.pointsArray.length, this._recordTimes)
|
||||
this._recordTimes = 0
|
||||
|
||||
}
|
||||
|
||||
|
||||
rotateHead(headPos: Vec2 = null) {
|
||||
if (!headPos) {
|
||||
headPos = new Vec2(this.dir.x, this.dir.y)
|
||||
}
|
||||
|
||||
let angle = v2(1, 0).signAngle(headPos) * 180 / Math.PI;
|
||||
this.node.angle = angle - 90;
|
||||
}
|
||||
|
||||
getNewBody(bodyIdx: number) {
|
||||
// console.log("this.snakeArray.length ::", this.snakeArray.length)
|
||||
let newBody = instantiate(this.bodyPrefab);
|
||||
let snakeCtl = newBody.getComponent(snakebody)
|
||||
snakeCtl.snakeBodyIdx = 0
|
||||
snakeCtl.preSnakeBody = this.snakeArray[this.snakeArray.length - 1]
|
||||
|
||||
// set new body's position //这是第一个蛇身
|
||||
if (this.snakeArray.length == 1) {
|
||||
let dir = this.dir.normalize()
|
||||
let pos = this.node.getPosition().subtract(dir.multiplyScalar(this.sectionLen))
|
||||
newBody.setPosition(pos);
|
||||
}
|
||||
else {
|
||||
let lastBody = this.snakeArray[this.snakeArray.length - 1];
|
||||
let lastBOBody = this.snakeArray[this.snakeArray.length - 2];
|
||||
lastBody.getPosition(this._tmpV32)
|
||||
lastBOBody.getPosition(this._tmpV3)
|
||||
let dir = this._tmpV3.subtract(this._tmpV32).normalize();
|
||||
let tmpPos = lastBody.getPosition().subtract(dir.multiplyScalar(this.sectionLen))
|
||||
// console.log("tmpPos ::", tmpPos, this.snakeArray.length)
|
||||
newBody.setPosition(tmpPos);
|
||||
}
|
||||
|
||||
this.node.parent.insertChild(newBody, 0)
|
||||
this.snakeArray.push(newBody);
|
||||
//修改蛇身zidx
|
||||
// newBody.setSiblingIndex(zIdx)
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录每两节之间的向量
|
||||
*/
|
||||
_tmpDir: Vec3 = new Vec3()
|
||||
_recordTimes: number = 0
|
||||
recordPoints(idx: number = null) {
|
||||
// record points between bodies (head is a special body)
|
||||
if (!idx) {
|
||||
idx = this.snakeArray.length - 1
|
||||
}
|
||||
let len = 0;
|
||||
let index = 0;
|
||||
this._recordTimes++
|
||||
let pointNum = Math.ceil(this.sectionLen / this.speed) //当前速度移动完一节身体需要的坐标点数
|
||||
let lastBody: Node = this.snakeArray[idx];
|
||||
let lastBOBody: Node = this.snakeArray[idx - 1];
|
||||
let dir: Vec3 = lastBOBody.getPosition().subtract(lastBody.getPosition()).normalize();
|
||||
for (let pIdx = 0; pIdx < pointNum; pIdx++) {
|
||||
len += this.speed;
|
||||
this._tmpDir = dir.clone()
|
||||
let pos: Vec3 = lastBody.getPosition().add(this._tmpDir.multiplyScalar(len));
|
||||
this.pointsArray.splice(index, 0, pos); // 每次从数组头部插入(pointNum个)坐标
|
||||
index += 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
changeSpeed(sp: number) {
|
||||
this.speed = sp
|
||||
this.pointsArray = []
|
||||
this.headPointsNum = 0
|
||||
for (let i = 1; i <= this.bodyNum; i++) {
|
||||
if (this.snakeArray[i].getComponent(snakebody))
|
||||
this.snakeArray[i].getComponent(snakebody).reset()
|
||||
this.recordPoints(i)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_time: number = 0
|
||||
_hasChan: boolean = false
|
||||
update(deltaTime: number) {
|
||||
// return
|
||||
this._time += deltaTime
|
||||
if (this._time > 0.5) {
|
||||
this.checkAtt()
|
||||
this._time = 0
|
||||
// this.changeSpeed(6)
|
||||
}
|
||||
|
||||
this._tmpV2.set(this.dir.x, this.dir.y)
|
||||
this.rotateHead(this._tmpV2)
|
||||
if (this.dir && this._isMove) {
|
||||
this.move()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getTmpAttBox(){
|
||||
let rect = this.node.getComponent(UITransform).getBoundingBoxToWorld()
|
||||
//稍微扩大点
|
||||
let biger:number = 100
|
||||
rect.x -= biger
|
||||
rect.y -= biger
|
||||
rect.width += 2 * biger
|
||||
rect.height += 2 * biger
|
||||
return rect
|
||||
}
|
||||
|
||||
/**
|
||||
* 临时攻击怪物
|
||||
*/
|
||||
checkAtt() {
|
||||
this.tmpWeapen.setPosition(this.node.getPosition())
|
||||
|
||||
this.tmpWeapen.scale = Vec3.ZERO
|
||||
this.tmpWeapen.active = true
|
||||
tween(this.tmpWeapen).to(0.2, { scale: v3(1, 1, 1) }).call(() => {
|
||||
this.tmpWeapen.active = false
|
||||
}).start()
|
||||
|
||||
let killArray = []
|
||||
// console.log("this.monsterList::", this.monsterList.length)
|
||||
|
||||
let checkRect = this.getTmpAttBox() //this.node.getComponent(UITransform).getBoundingBoxToWorld()
|
||||
let checkList = this.waveMng.getTreeColliderList(checkRect)
|
||||
// console.log("checkList::", checkList)
|
||||
for (let searchIdx = 0; searchIdx < checkList.length; searchIdx++) {
|
||||
if (this.node.getPosition().subtract(checkList[searchIdx].collider.getPosition()).lengthSqr() < 22500) {
|
||||
killArray.push(checkList[searchIdx].collider)
|
||||
}
|
||||
}
|
||||
|
||||
for (let rmIdx = killArray.length - 1; rmIdx >= 0; rmIdx--) {
|
||||
let tMonsterCtl: monsterCtl = killArray[rmIdx].getComponent(monsterCtl)
|
||||
let uuid = tMonsterCtl.node.uuid
|
||||
if (tMonsterCtl.hurt()) {
|
||||
for (let idx = 0; idx < this.monsterList.length; idx++) {
|
||||
if (this.monsterList[idx].uuid == uuid) {
|
||||
this.monsterList.splice(idx, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setDir(dir) {
|
||||
// return
|
||||
this.dir = dir
|
||||
}
|
||||
|
||||
move(dir: Vec3 = null) {
|
||||
// return
|
||||
if (!dir) {
|
||||
dir = this.dir
|
||||
}
|
||||
|
||||
let moveV2 = dir.normalize().multiplyScalar(this.speed)
|
||||
// console.log("蛇头移动距离...", moveV2.length())
|
||||
|
||||
|
||||
//最后动蛇头
|
||||
// this.node.getPosition(this._tmpV3)
|
||||
this.node.setPosition(this.node.getPosition().add(moveV2))
|
||||
this.pointsArray.push(this.node.getPosition());
|
||||
this.headPointsNum += 1;
|
||||
|
||||
//从第一个蛇身开始运动
|
||||
let lastPosIdx = -1
|
||||
for (let i = 1; i < this.snakeArray.length; i++) {
|
||||
let num = Math.floor((this.pointsArray.length - this.headPointsNum) / (this.snakeArray.length - 1) * (this.snakeArray.length - 1 - i));
|
||||
let posIdx = num + this.snakeArray[i].getComponent(snakebody).snakeBodyIdx
|
||||
this.snakeArray[i].setPosition(this.pointsArray[posIdx]);
|
||||
this.snakeArray[i].getComponent(snakebody).addIdx();
|
||||
lastPosIdx = posIdx
|
||||
// console.log("use idx :: ", num + this.snakeArray[i].getComponent(snakebody).snakeBodyIdx)
|
||||
}
|
||||
// console.log("this.pointsArray::", this.pointsArray)
|
||||
|
||||
}
|
||||
}
|
||||
|
9
assets/Scripts/rolectl/player.ts.meta
Normal file
9
assets/Scripts/rolectl/player.ts.meta
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "39b2efb2-9835-4908-b520-6d72d1e9a275",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
136
assets/Scripts/rolectl/rolejoystick.ts
Normal file
136
assets/Scripts/rolectl/rolejoystick.ts
Normal file
@ -0,0 +1,136 @@
|
||||
import { Component, EventTouch, Input, Node, Vec2, Vec3, _decorator } from 'cc';
|
||||
import Game from '../Test/Test2';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
|
||||
const TOUCH_RADIUS = 180;
|
||||
const ROLE_MOVE_FRAME = 0.2;
|
||||
const _tempPos = new Vec3();
|
||||
const _tempDelta = new Vec2();
|
||||
const Horizontal = new Vec2(1, 0);
|
||||
const MOVE_DELTA = 0.2;
|
||||
|
||||
@ccclass('rolejoystick')
|
||||
export class rolejoystick extends Component {
|
||||
|
||||
@property(Node)
|
||||
ctrlSprite: Node = null!;
|
||||
@property(Node)
|
||||
moveTagSp: Node = null!;
|
||||
@property(Node)
|
||||
role: Node = null!;
|
||||
@property(Vec3)
|
||||
originPos = new Vec3();
|
||||
|
||||
private _isTouch = false;
|
||||
private _touchPos = new Vec2();
|
||||
private _startPos = new Vec2();
|
||||
private _movePos = new Vec2();
|
||||
private _animComp: Animation = null!;
|
||||
private _animState = 'idle';
|
||||
|
||||
// _tempPos: Vec3 = new Vec3()
|
||||
start() {
|
||||
this.ctrlSprite.setPosition(this.originPos);
|
||||
// _tempPos.set(0, 90, 0);
|
||||
// this.role.eulerAngles = _tempPos;
|
||||
// this._animComp = this.role.getComponentInChildren(Animation)!;
|
||||
this.node.on(Input.EventType.TOUCH_START, this.touchStart, this);
|
||||
this.node.on(Input.EventType.TOUCH_MOVE, this.touchMove, this);
|
||||
this.node.on(Input.EventType.TOUCH_END, this.touchEnd, this);
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
this.node.off(Input.EventType.TOUCH_START, this.touchStart, this);
|
||||
this.node.off(Input.EventType.TOUCH_MOVE, this.touchMove, this);
|
||||
this.node.off(Input.EventType.TOUCH_END, this.touchEnd, this);
|
||||
}
|
||||
|
||||
touchStart(touch: EventTouch) {
|
||||
// this.changeState('cocos_anim_run');
|
||||
// touch.getUI
|
||||
touch.getUILocation(this._startPos);
|
||||
const distance = this._startPos.length();
|
||||
console.error("joystick::", touch, distance)
|
||||
// if (distance < TOUCH_RADIUS) {
|
||||
this._touchPos.set(this._startPos);
|
||||
this._movePos.set(this._startPos);
|
||||
_tempPos.set(this.ctrlSprite.position);
|
||||
this.ctrlSprite.setWorldPosition(this._startPos.x, this._startPos.y, _tempPos.z);
|
||||
this._isTouch = true;
|
||||
// }
|
||||
}
|
||||
|
||||
touchMove(touch: EventTouch) {
|
||||
if (!this._isTouch) {
|
||||
return;
|
||||
}
|
||||
|
||||
touch.getUILocation(this._movePos);
|
||||
Vec2.subtract(_tempDelta, this._movePos, this._startPos);
|
||||
// 计算角色的整体旋转值
|
||||
// const deltaRadian = _tempDelta.angle(Horizontal);
|
||||
// const angle = deltaRadian * 180 / Math.PI;
|
||||
// const rot = this.role.eulerAngles;
|
||||
// _tempPos.set(rot.x, 90 + (Math.sign(_tempDelta.y)) * angle, rot.z);
|
||||
// this.role.eulerAngles = _tempPos;
|
||||
|
||||
// 重新规划移动方向值
|
||||
// _tempDelta.multiply2f(MOVE_DELTA, MOVE_DELTA);
|
||||
// Vec2.add(this._movePos, this._startPos, _tempDelta);
|
||||
const distance = _tempDelta.length();
|
||||
|
||||
// 是否超出限制半径
|
||||
if (distance > TOUCH_RADIUS) {
|
||||
|
||||
const radian = _tempDelta.angle(Horizontal);
|
||||
// console.log("radian::", radian)
|
||||
const x = Math.cos(radian) * TOUCH_RADIUS;
|
||||
let y = Math.sin(radian) * TOUCH_RADIUS;
|
||||
if (this._startPos.y >= this._movePos.y) {
|
||||
y = -y
|
||||
}
|
||||
this._movePos.set(x, y).add(this._startPos);
|
||||
}
|
||||
|
||||
// this.ctrlSprite.setWorldPosition(this._movePos.x, this._movePos.y, 0);
|
||||
this.moveTagSp.setWorldPosition(this._movePos.x, this._movePos.y, 0);
|
||||
this._touchPos.set(this._movePos);
|
||||
}
|
||||
|
||||
touchEnd(touch: EventTouch) {
|
||||
this._isTouch = false;
|
||||
// this.changeState('cocos_anim_idle');
|
||||
// this.ctrlSprite.setPosition(this.originPos);
|
||||
// this.moveTagSp.setPosition(this.originPos)
|
||||
}
|
||||
|
||||
changeState(name: string) {
|
||||
if (this._animState === name) {
|
||||
return;
|
||||
}
|
||||
|
||||
// this._animComp.play(name);
|
||||
// this._animState = name;
|
||||
}
|
||||
|
||||
update() {
|
||||
if (!this._isTouch) {
|
||||
Game.Instance.Player._isMove = false
|
||||
return;
|
||||
}
|
||||
|
||||
// _tempPos.set(0, 0, ROLE_MOVE_FRAME);
|
||||
// this.role.translate(_tempPos);
|
||||
if (Game.Instance.Player) {
|
||||
Game.Instance.Player._isMove = true
|
||||
Game.Instance.Player.setDir(this.moveTagSp.getPosition().subtract(this.ctrlSprite.getPosition()).normalize())
|
||||
}
|
||||
// Game.Instance.Player.move(this.moveTagSp.getPosition().subtract(this.ctrlSprite.getPosition()).normalize())
|
||||
// Game.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
9
assets/Scripts/rolectl/rolejoystick.ts.meta
Normal file
9
assets/Scripts/rolectl/rolejoystick.ts.meta
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "dd6eec7f-57ad-46f6-b5fc-7a4907cdc2e1",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
66
assets/Scripts/rolectl/snakebody.ts
Normal file
66
assets/Scripts/rolectl/snakebody.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { Component, Node, Vec3, _decorator } from 'cc';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('snakebody')
|
||||
export class snakebody extends Component {
|
||||
|
||||
speed: number = 3
|
||||
|
||||
private _preSnakeBody: Node = null;
|
||||
public get preSnakeBody(): Node {
|
||||
return this._preSnakeBody;
|
||||
}
|
||||
public set preSnakeBody(value: Node) {
|
||||
this._preSnakeBody = value;
|
||||
}
|
||||
|
||||
private _snakeBodyIdx: number = 0;
|
||||
public get snakeBodyIdx(): number {
|
||||
return this._snakeBodyIdx;
|
||||
}
|
||||
public set snakeBodyIdx(value: number) {
|
||||
this._snakeBodyIdx = value;
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.snakeBodyIdx = 0
|
||||
}
|
||||
|
||||
addIdx() {
|
||||
this.snakeBodyIdx++
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前蛇身指向前一个蛇身的dir
|
||||
*/
|
||||
_tmpVec: Vec3 = new Vec3(0, 1, 0)
|
||||
getPreDir() {
|
||||
// return this.preSnakeBody.getPosition().subtract(this.node.getPosition()).normalize()
|
||||
// return this.preSnakeBody.getPosition().subtract(this.node.getPosition())
|
||||
console.log("this.preSnakeBody.getPosition() ::", this.preSnakeBody.getPosition())
|
||||
return this.node.getPosition().subtract(this.preSnakeBody.getPosition())
|
||||
}
|
||||
|
||||
|
||||
start() {
|
||||
|
||||
}
|
||||
|
||||
update(deltaTime: number) {
|
||||
|
||||
}
|
||||
|
||||
_tmpV3: Vec3 = new Vec3()
|
||||
move(length: number = null) {
|
||||
let moveV2 = this.getPreDir()//.multiplyScalar(1 / this.speed)
|
||||
|
||||
|
||||
// console.log("蛇身移动的距离...", moveV2.length())
|
||||
Vec3.lerp(this._tmpV3, this.node.getPosition(), this.preSnakeBody.getPosition().add(moveV2), 0.5)
|
||||
console.log("........:", moveV2, this._tmpV3)
|
||||
this.node.setPosition(this._tmpV3)
|
||||
// this.node.setPosition(this.node.getPosition().add(moveV2))
|
||||
}
|
||||
}
|
||||
|
||||
|
9
assets/Scripts/rolectl/snakebody.ts.meta
Normal file
9
assets/Scripts/rolectl/snakebody.ts.meta
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "285cabaf-7adc-4f7c-9950-771d5e8abc2d",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
12
assets/Scripts/ui.meta
Normal file
12
assets/Scripts/ui.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "69be3a35-cbac-416c-9ed9-30a19ee1a670",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/animation.meta
Normal file
12
assets/animation.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "ee1ea724-146d-41c2-bfb1-0987716e332e",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
88
assets/animation/monster_0.anim
Normal file
88
assets/animation/monster_0.anim
Normal file
@ -0,0 +1,88 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.AnimationClip",
|
||||
"_name": "monster_0",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {
|
||||
"embeddedPlayerGroups": []
|
||||
},
|
||||
"_native": "",
|
||||
"sample": 4,
|
||||
"speed": 1,
|
||||
"wrapMode": 2,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 1.25,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [],
|
||||
"_embeddedPlayers": []
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 2
|
||||
}
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.25,
|
||||
0.5,
|
||||
0.75,
|
||||
1
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__uuid__": "3b94c7fc-ac9b-47d7-9aca-37fc7683996a@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "c0d07a24-a091-4225-8876-c69d520ef479@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "8b8e7ea8-69ee-44ce-831b-ab570e83a271@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "11127c25-31b7-49c5-9bf5-9ac030ca13eb@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "c2706816-4f7f-43d5-bf5f-c5f42328efc0@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
13
assets/animation/monster_0.anim.meta
Normal file
13
assets/animation/monster_0.anim.meta
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "2.0.3",
|
||||
"importer": "animation-clip",
|
||||
"imported": true,
|
||||
"uuid": "3732ff9b-e2c3-49ce-90e9-ada8be3cb96f",
|
||||
"files": [
|
||||
".cconb"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"name": "monster_0"
|
||||
}
|
||||
}
|
88
assets/animation/monster_1.anim
Normal file
88
assets/animation/monster_1.anim
Normal file
@ -0,0 +1,88 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.AnimationClip",
|
||||
"_name": "monster_1",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {
|
||||
"embeddedPlayerGroups": []
|
||||
},
|
||||
"_native": "",
|
||||
"sample": 4,
|
||||
"speed": 1,
|
||||
"wrapMode": 2,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 1.25,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [],
|
||||
"_embeddedPlayers": []
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 2
|
||||
}
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.25,
|
||||
0.5,
|
||||
0.75,
|
||||
1
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__uuid__": "fd098eb2-c17a-44f9-b3b5-84e4de3a0a24@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "35e74055-4977-4ab5-9fd2-cd5032652ef1@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "67783030-db2f-449e-bc2b-ed77d4655024@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "1267c1fd-bfa8-49ac-b628-27b56bcbe2da@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "cd61c842-1375-4520-88e9-57082fb22906@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
13
assets/animation/monster_1.anim.meta
Normal file
13
assets/animation/monster_1.anim.meta
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "2.0.3",
|
||||
"importer": "animation-clip",
|
||||
"imported": true,
|
||||
"uuid": "b56c7e2b-40cd-4383-b119-1923658d8346",
|
||||
"files": [
|
||||
".cconb"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"name": "monster_1"
|
||||
}
|
||||
}
|
93
assets/animation/nvwushen.anim
Normal file
93
assets/animation/nvwushen.anim
Normal file
@ -0,0 +1,93 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.AnimationClip",
|
||||
"_name": "nvwushen",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {
|
||||
"embeddedPlayerGroups": []
|
||||
},
|
||||
"_native": "",
|
||||
"sample": 4,
|
||||
"speed": 1,
|
||||
"wrapMode": 2,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 1.5,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [],
|
||||
"_embeddedPlayers": []
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 2
|
||||
}
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.25,
|
||||
0.5,
|
||||
0.75,
|
||||
1,
|
||||
1.25
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__uuid__": "9692ee99-e3c7-4f0c-880d-a032db0e6b67@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2c08ba63-fa76-4d1d-8fdb-a35c63ff0496@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "3ef20986-f554-4baf-86e2-d0b932033888@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "1faee88b-b515-42df-8125-81a93776030e@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "7124ab51-3354-481b-b436-7aa7a308b6c0@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "8d901387-39c2-4630-a746-acebb4a6d7b1@f9941",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
13
assets/animation/nvwushen.anim.meta
Normal file
13
assets/animation/nvwushen.anim.meta
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "2.0.3",
|
||||
"importer": "animation-clip",
|
||||
"imported": true,
|
||||
"uuid": "9ed9e8c3-7532-447e-97d3-bc1ae4e19f22",
|
||||
"files": [
|
||||
".cconb"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"name": "nvwushen"
|
||||
}
|
||||
}
|
12
assets/res.meta
Normal file
12
assets/res.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "be13c1c7-cbfa-4c9e-a904-fa1cb22f01ae",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/res/shader.meta
Normal file
12
assets/res/shader.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "a4806935-3922-4d1a-b5e4-061a8719221b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
15
assets/resources.meta
Normal file
15
assets/resources.meta
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "ef14c052-6303-4e90-b227-5eb038495010",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"isBundle": true,
|
||||
"bundleName": "resources",
|
||||
"priority": 8,
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/resources/Image.meta
Normal file
12
assets/resources/Image.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "5dccb540-29c6-4f42-ab7e-be8d6ea855bc",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/resources/Image/role.meta
Normal file
12
assets/resources/Image/role.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "810304bb-6659-4736-b0a4-484b1e72dabe",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/resources/Image/role/女武神.meta
Normal file
12
assets/resources/Image/role/女武神.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "2ad25d4a-d6d6-4a16-bd14-28825d065813",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
BIN
assets/resources/Image/role/女武神/nws-move_0.png
Normal file
BIN
assets/resources/Image/role/女武神/nws-move_0.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 16 KiB |
162
assets/resources/Image/role/女武神/nws-move_0.png.meta
Normal file
162
assets/resources/Image/role/女武神/nws-move_0.png.meta
Normal file
@ -0,0 +1,162 @@
|
||||
{
|
||||
"ver": "1.0.25",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "9692ee99-e3c7-4f0c-880d-a032db0e6b67",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"nws-move_0": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "1a1caed4-600f-4403-abed-8ac79fede4a9",
|
||||
"rawTextureUuid": "9692ee99-e3c7-4f0c-880d-a032db0e6b67",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -3,
|
||||
"offsetY": 0,
|
||||
"trimX": 4,
|
||||
"trimY": 2,
|
||||
"width": 113,
|
||||
"height": 148,
|
||||
"rawWidth": 127,
|
||||
"rawHeight": 152,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {},
|
||||
"importer": "*",
|
||||
"imported": false,
|
||||
"files": [],
|
||||
"userData": {},
|
||||
"displayName": "",
|
||||
"id": "",
|
||||
"name": ""
|
||||
},
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "9692ee99-e3c7-4f0c-880d-a032db0e6b67@6c48a",
|
||||
"displayName": "nws-move_0",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "9692ee99-e3c7-4f0c-880d-a032db0e6b67",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "9692ee99-e3c7-4f0c-880d-a032db0e6b67@f9941",
|
||||
"displayName": "nws-move_0",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimType": "none",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 127,
|
||||
"height": 152,
|
||||
"rawWidth": 127,
|
||||
"rawHeight": 152,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-63.5,
|
||||
-76,
|
||||
0,
|
||||
63.5,
|
||||
-76,
|
||||
0,
|
||||
-63.5,
|
||||
76,
|
||||
0,
|
||||
63.5,
|
||||
76,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
152,
|
||||
127,
|
||||
152,
|
||||
0,
|
||||
0,
|
||||
127,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-63.5,
|
||||
-76,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
63.5,
|
||||
76,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "9692ee99-e3c7-4f0c-880d-a032db0e6b67@6c48a",
|
||||
"atlasUuid": ""
|
||||
},
|
||||
"ver": "1.0.11",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": true,
|
||||
"hasAlpha": true,
|
||||
"redirect": "9692ee99-e3c7-4f0c-880d-a032db0e6b67@f9941"
|
||||
}
|
||||
}
|
BIN
assets/resources/Image/role/女武神/nws-move_12.png
Normal file
BIN
assets/resources/Image/role/女武神/nws-move_12.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 17 KiB |
162
assets/resources/Image/role/女武神/nws-move_12.png.meta
Normal file
162
assets/resources/Image/role/女武神/nws-move_12.png.meta
Normal file
@ -0,0 +1,162 @@
|
||||
{
|
||||
"ver": "1.0.25",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "2c08ba63-fa76-4d1d-8fdb-a35c63ff0496",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"nws-move_12": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "a1fedad8-7b9d-4cfe-9932-28a657e172da",
|
||||
"rawTextureUuid": "2c08ba63-fa76-4d1d-8fdb-a35c63ff0496",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 4,
|
||||
"offsetY": -2.5,
|
||||
"trimX": 9,
|
||||
"trimY": 5,
|
||||
"width": 117,
|
||||
"height": 147,
|
||||
"rawWidth": 127,
|
||||
"rawHeight": 152,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {},
|
||||
"importer": "*",
|
||||
"imported": false,
|
||||
"files": [],
|
||||
"userData": {},
|
||||
"displayName": "",
|
||||
"id": "",
|
||||
"name": ""
|
||||
},
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "2c08ba63-fa76-4d1d-8fdb-a35c63ff0496@6c48a",
|
||||
"displayName": "nws-move_12",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "2c08ba63-fa76-4d1d-8fdb-a35c63ff0496",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "2c08ba63-fa76-4d1d-8fdb-a35c63ff0496@f9941",
|
||||
"displayName": "nws-move_12",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimType": "none",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 127,
|
||||
"height": 152,
|
||||
"rawWidth": 127,
|
||||
"rawHeight": 152,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-63.5,
|
||||
-76,
|
||||
0,
|
||||
63.5,
|
||||
-76,
|
||||
0,
|
||||
-63.5,
|
||||
76,
|
||||
0,
|
||||
63.5,
|
||||
76,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
152,
|
||||
127,
|
||||
152,
|
||||
0,
|
||||
0,
|
||||
127,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-63.5,
|
||||
-76,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
63.5,
|
||||
76,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "2c08ba63-fa76-4d1d-8fdb-a35c63ff0496@6c48a",
|
||||
"atlasUuid": ""
|
||||
},
|
||||
"ver": "1.0.11",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": true,
|
||||
"hasAlpha": true,
|
||||
"redirect": "2c08ba63-fa76-4d1d-8fdb-a35c63ff0496@f9941"
|
||||
}
|
||||
}
|
BIN
assets/resources/Image/role/女武神/nws-move_16.png
Normal file
BIN
assets/resources/Image/role/女武神/nws-move_16.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 17 KiB |
162
assets/resources/Image/role/女武神/nws-move_16.png.meta
Normal file
162
assets/resources/Image/role/女武神/nws-move_16.png.meta
Normal file
@ -0,0 +1,162 @@
|
||||
{
|
||||
"ver": "1.0.25",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "3ef20986-f554-4baf-86e2-d0b932033888",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"nws-move_16": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "dd5dab88-cbab-4001-864c-8ff5a4d3ab49",
|
||||
"rawTextureUuid": "3ef20986-f554-4baf-86e2-d0b932033888",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 3,
|
||||
"offsetY": -2,
|
||||
"trimX": 11,
|
||||
"trimY": 5,
|
||||
"width": 111,
|
||||
"height": 146,
|
||||
"rawWidth": 127,
|
||||
"rawHeight": 152,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {},
|
||||
"importer": "*",
|
||||
"imported": false,
|
||||
"files": [],
|
||||
"userData": {},
|
||||
"displayName": "",
|
||||
"id": "",
|
||||
"name": ""
|
||||
},
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "3ef20986-f554-4baf-86e2-d0b932033888@6c48a",
|
||||
"displayName": "nws-move_16",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "3ef20986-f554-4baf-86e2-d0b932033888",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "3ef20986-f554-4baf-86e2-d0b932033888@f9941",
|
||||
"displayName": "nws-move_16",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimType": "none",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 127,
|
||||
"height": 152,
|
||||
"rawWidth": 127,
|
||||
"rawHeight": 152,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-63.5,
|
||||
-76,
|
||||
0,
|
||||
63.5,
|
||||
-76,
|
||||
0,
|
||||
-63.5,
|
||||
76,
|
||||
0,
|
||||
63.5,
|
||||
76,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
152,
|
||||
127,
|
||||
152,
|
||||
0,
|
||||
0,
|
||||
127,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-63.5,
|
||||
-76,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
63.5,
|
||||
76,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "3ef20986-f554-4baf-86e2-d0b932033888@6c48a",
|
||||
"atlasUuid": ""
|
||||
},
|
||||
"ver": "1.0.11",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": true,
|
||||
"hasAlpha": true,
|
||||
"redirect": "3ef20986-f554-4baf-86e2-d0b932033888@f9941"
|
||||
}
|
||||
}
|
BIN
assets/resources/Image/role/女武神/nws-move_20.png
Normal file
BIN
assets/resources/Image/role/女武神/nws-move_20.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 16 KiB |
162
assets/resources/Image/role/女武神/nws-move_20.png.meta
Normal file
162
assets/resources/Image/role/女武神/nws-move_20.png.meta
Normal file
@ -0,0 +1,162 @@
|
||||
{
|
||||
"ver": "1.0.25",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "1faee88b-b515-42df-8125-81a93776030e",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"nws-move_20": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "a4d80f83-9414-4cbc-8393-6beb9f22b7d8",
|
||||
"rawTextureUuid": "1faee88b-b515-42df-8125-81a93776030e",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 2,
|
||||
"offsetY": -4,
|
||||
"trimX": 10,
|
||||
"trimY": 9,
|
||||
"width": 111,
|
||||
"height": 142,
|
||||
"rawWidth": 127,
|
||||
"rawHeight": 152,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {},
|
||||
"importer": "*",
|
||||
"imported": false,
|
||||
"files": [],
|
||||
"userData": {},
|
||||
"displayName": "",
|
||||
"id": "",
|
||||
"name": ""
|
||||
},
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "1faee88b-b515-42df-8125-81a93776030e@6c48a",
|
||||
"displayName": "nws-move_20",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "1faee88b-b515-42df-8125-81a93776030e",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "1faee88b-b515-42df-8125-81a93776030e@f9941",
|
||||
"displayName": "nws-move_20",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimType": "none",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 127,
|
||||
"height": 152,
|
||||
"rawWidth": 127,
|
||||
"rawHeight": 152,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-63.5,
|
||||
-76,
|
||||
0,
|
||||
63.5,
|
||||
-76,
|
||||
0,
|
||||
-63.5,
|
||||
76,
|
||||
0,
|
||||
63.5,
|
||||
76,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
152,
|
||||
127,
|
||||
152,
|
||||
0,
|
||||
0,
|
||||
127,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-63.5,
|
||||
-76,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
63.5,
|
||||
76,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "1faee88b-b515-42df-8125-81a93776030e@6c48a",
|
||||
"atlasUuid": ""
|
||||
},
|
||||
"ver": "1.0.11",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": true,
|
||||
"hasAlpha": true,
|
||||
"redirect": "1faee88b-b515-42df-8125-81a93776030e@f9941"
|
||||
}
|
||||
}
|
BIN
assets/resources/Image/role/女武神/nws-move_4.png
Normal file
BIN
assets/resources/Image/role/女武神/nws-move_4.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 17 KiB |
162
assets/resources/Image/role/女武神/nws-move_4.png.meta
Normal file
162
assets/resources/Image/role/女武神/nws-move_4.png.meta
Normal file
@ -0,0 +1,162 @@
|
||||
{
|
||||
"ver": "1.0.25",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "7124ab51-3354-481b-b436-7aa7a308b6c0",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"nws-move_4": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "34e741ac-4bbd-4088-ba83-7a2c5d7a86f8",
|
||||
"rawTextureUuid": "7124ab51-3354-481b-b436-7aa7a308b6c0",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -3,
|
||||
"offsetY": -4,
|
||||
"trimX": 2,
|
||||
"trimY": 9,
|
||||
"width": 117,
|
||||
"height": 142,
|
||||
"rawWidth": 127,
|
||||
"rawHeight": 152,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {},
|
||||
"importer": "*",
|
||||
"imported": false,
|
||||
"files": [],
|
||||
"userData": {},
|
||||
"displayName": "",
|
||||
"id": "",
|
||||
"name": ""
|
||||
},
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "7124ab51-3354-481b-b436-7aa7a308b6c0@6c48a",
|
||||
"displayName": "nws-move_4",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "7124ab51-3354-481b-b436-7aa7a308b6c0",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "7124ab51-3354-481b-b436-7aa7a308b6c0@f9941",
|
||||
"displayName": "nws-move_4",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimType": "none",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 127,
|
||||
"height": 152,
|
||||
"rawWidth": 127,
|
||||
"rawHeight": 152,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-63.5,
|
||||
-76,
|
||||
0,
|
||||
63.5,
|
||||
-76,
|
||||
0,
|
||||
-63.5,
|
||||
76,
|
||||
0,
|
||||
63.5,
|
||||
76,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
152,
|
||||
127,
|
||||
152,
|
||||
0,
|
||||
0,
|
||||
127,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-63.5,
|
||||
-76,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
63.5,
|
||||
76,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "7124ab51-3354-481b-b436-7aa7a308b6c0@6c48a",
|
||||
"atlasUuid": ""
|
||||
},
|
||||
"ver": "1.0.11",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": true,
|
||||
"hasAlpha": true,
|
||||
"redirect": "7124ab51-3354-481b-b436-7aa7a308b6c0@f9941"
|
||||
}
|
||||
}
|
BIN
assets/resources/Image/role/女武神/nws-move_8.png
Normal file
BIN
assets/resources/Image/role/女武神/nws-move_8.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 16 KiB |
162
assets/resources/Image/role/女武神/nws-move_8.png.meta
Normal file
162
assets/resources/Image/role/女武神/nws-move_8.png.meta
Normal file
@ -0,0 +1,162 @@
|
||||
{
|
||||
"ver": "1.0.25",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "8d901387-39c2-4630-a746-acebb4a6d7b1",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"nws-move_8": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "6793e976-588c-41f3-874a-ead93444eeb1",
|
||||
"rawTextureUuid": "8d901387-39c2-4630-a746-acebb4a6d7b1",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -1,
|
||||
"offsetY": -6.5,
|
||||
"trimX": 3,
|
||||
"trimY": 14,
|
||||
"width": 119,
|
||||
"height": 137,
|
||||
"rawWidth": 127,
|
||||
"rawHeight": 152,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {},
|
||||
"importer": "*",
|
||||
"imported": false,
|
||||
"files": [],
|
||||
"userData": {},
|
||||
"displayName": "",
|
||||
"id": "",
|
||||
"name": ""
|
||||
},
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "8d901387-39c2-4630-a746-acebb4a6d7b1@6c48a",
|
||||
"displayName": "nws-move_8",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "8d901387-39c2-4630-a746-acebb4a6d7b1",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "8d901387-39c2-4630-a746-acebb4a6d7b1@f9941",
|
||||
"displayName": "nws-move_8",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimType": "none",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 127,
|
||||
"height": 152,
|
||||
"rawWidth": 127,
|
||||
"rawHeight": 152,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-63.5,
|
||||
-76,
|
||||
0,
|
||||
63.5,
|
||||
-76,
|
||||
0,
|
||||
-63.5,
|
||||
76,
|
||||
0,
|
||||
63.5,
|
||||
76,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
152,
|
||||
127,
|
||||
152,
|
||||
0,
|
||||
0,
|
||||
127,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-63.5,
|
||||
-76,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
63.5,
|
||||
76,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "8d901387-39c2-4630-a746-acebb4a6d7b1@6c48a",
|
||||
"atlasUuid": ""
|
||||
},
|
||||
"ver": "1.0.11",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": true,
|
||||
"hasAlpha": true,
|
||||
"redirect": "8d901387-39c2-4630-a746-acebb4a6d7b1@f9941"
|
||||
}
|
||||
}
|
12
assets/resources/Image/role/鹰怪物.meta
Normal file
12
assets/resources/Image/role/鹰怪物.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "9dee460c-9dae-42dc-a051-924054b702e0",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
12
assets/resources/Image/role/鹰怪物/棕色.meta
Normal file
12
assets/resources/Image/role/鹰怪物/棕色.meta
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "7fd210cf-8857-4015-84ee-ac4a02cfc757",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"compressionType": {},
|
||||
"isRemoteBundle": {}
|
||||
}
|
||||
}
|
BIN
assets/resources/Image/role/鹰怪物/棕色/ygw-animation_0.png
Normal file
BIN
assets/resources/Image/role/鹰怪物/棕色/ygw-animation_0.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 9.3 KiB |
162
assets/resources/Image/role/鹰怪物/棕色/ygw-animation_0.png.meta
Normal file
162
assets/resources/Image/role/鹰怪物/棕色/ygw-animation_0.png.meta
Normal file
@ -0,0 +1,162 @@
|
||||
{
|
||||
"ver": "1.0.25",
|
||||
"importer": "image",
|
||||
"imported": true,
|
||||
"uuid": "3b94c7fc-ac9b-47d7-9aca-37fc7683996a",
|
||||
"files": [
|
||||
".json",
|
||||
".png"
|
||||
],
|
||||
"subMetas": {
|
||||
"ygw-animation_0": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "8717716c-bf93-4cfb-bfaf-f157b1cfaf90",
|
||||
"rawTextureUuid": "3b94c7fc-ac9b-47d7-9aca-37fc7683996a",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -1.5,
|
||||
"offsetY": -14.5,
|
||||
"trimX": 3,
|
||||
"trimY": 29,
|
||||
"width": 105,
|
||||
"height": 81,
|
||||
"rawWidth": 114,
|
||||
"rawHeight": 110,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {},
|
||||
"importer": "*",
|
||||
"imported": false,
|
||||
"files": [],
|
||||
"userData": {},
|
||||
"displayName": "",
|
||||
"id": "",
|
||||
"name": ""
|
||||
},
|
||||
"6c48a": {
|
||||
"importer": "texture",
|
||||
"uuid": "3b94c7fc-ac9b-47d7-9aca-37fc7683996a@6c48a",
|
||||
"displayName": "ygw-animation_0",
|
||||
"id": "6c48a",
|
||||
"name": "texture",
|
||||
"userData": {
|
||||
"wrapModeS": "clamp-to-edge",
|
||||
"wrapModeT": "clamp-to-edge",
|
||||
"imageUuidOrDatabaseUri": "3b94c7fc-ac9b-47d7-9aca-37fc7683996a",
|
||||
"isUuid": true,
|
||||
"visible": false,
|
||||
"minfilter": "linear",
|
||||
"magfilter": "linear",
|
||||
"mipfilter": "none",
|
||||
"anisotropy": 0
|
||||
},
|
||||
"ver": "1.0.22",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
},
|
||||
"f9941": {
|
||||
"importer": "sprite-frame",
|
||||
"uuid": "3b94c7fc-ac9b-47d7-9aca-37fc7683996a@f9941",
|
||||
"displayName": "ygw-animation_0",
|
||||
"id": "f9941",
|
||||
"name": "spriteFrame",
|
||||
"userData": {
|
||||
"trimType": "none",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 114,
|
||||
"height": 110,
|
||||
"rawWidth": 114,
|
||||
"rawHeight": 110,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
"pivotY": 0.5,
|
||||
"meshType": 0,
|
||||
"vertices": {
|
||||
"rawPosition": [
|
||||
-57,
|
||||
-55,
|
||||
0,
|
||||
57,
|
||||
-55,
|
||||
0,
|
||||
-57,
|
||||
55,
|
||||
0,
|
||||
57,
|
||||
55,
|
||||
0
|
||||
],
|
||||
"indexes": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
3
|
||||
],
|
||||
"uv": [
|
||||
0,
|
||||
110,
|
||||
114,
|
||||
110,
|
||||
0,
|
||||
0,
|
||||
114,
|
||||
0
|
||||
],
|
||||
"nuv": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"minPos": [
|
||||
-57,
|
||||
-55,
|
||||
0
|
||||
],
|
||||
"maxPos": [
|
||||
57,
|
||||
55,
|
||||
0
|
||||
]
|
||||
},
|
||||
"isUuid": true,
|
||||
"imageUuidOrDatabaseUri": "3b94c7fc-ac9b-47d7-9aca-37fc7683996a@6c48a",
|
||||
"atlasUuid": ""
|
||||
},
|
||||
"ver": "1.0.11",
|
||||
"imported": true,
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {}
|
||||
}
|
||||
},
|
||||
"userData": {
|
||||
"type": "sprite-frame",
|
||||
"fixAlphaTransparencyArtifacts": true,
|
||||
"hasAlpha": true,
|
||||
"redirect": "3b94c7fc-ac9b-47d7-9aca-37fc7683996a@f9941"
|
||||
}
|
||||
}
|
BIN
assets/resources/Image/role/鹰怪物/棕色/ygw-animation_12.png
Normal file
BIN
assets/resources/Image/role/鹰怪物/棕色/ygw-animation_12.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 8.4 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user