the demo
| @@ -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
									
								
							
							
						
						| @@ -0,0 +1,5 @@ | ||||
| { | ||||
|   "image": { | ||||
|     "type": "sprite-frame" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										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
									
								
							
							
						
						
							
								
								
									
										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
									
								
							
							
						
						
							
								
								
									
										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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| After Width: | Height: | Size: 16 KiB | 
							
								
								
									
										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
									
								
							
							
						
						| After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										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
									
								
							
							
						
						| After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										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
									
								
							
							
						
						| After Width: | Height: | Size: 16 KiB | 
							
								
								
									
										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
									
								
							
							
						
						| After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										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
									
								
							
							
						
						| After Width: | Height: | Size: 16 KiB | 
							
								
								
									
										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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| After Width: | Height: | Size: 9.3 KiB | 
							
								
								
									
										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
									
								
							
							
						
						| After Width: | Height: | Size: 8.4 KiB |